@devmm/puredocs-excel-formula 1.0.1 → 1.0.3

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.
package/dist/index.js CHANGED
@@ -2356,9 +2356,11 @@ function datedif(a, c) {
2356
2356
  function registerLookup(r) {
2357
2357
  r.register("VLOOKUP", vlookup, 3, 4);
2358
2358
  r.register("HLOOKUP", hlookup, 3, 4);
2359
+ r.register("XLOOKUP", xlookup, 3, 6);
2359
2360
  r.register("LOOKUP", lookup, 2, 3);
2360
2361
  r.register("INDEX", index_, 2, 3);
2361
2362
  r.register("MATCH", match, 2, 3);
2363
+ r.register("XMATCH", xmatch, 2, 4);
2362
2364
  r.register("CHOOSE", choose, 2);
2363
2365
  r.register("ADDRESS", address, 2, 5);
2364
2366
  r.register("ROW", row, 0, 1);
@@ -2432,6 +2434,121 @@ function hlookup(a, c) {
2432
2434
  }
2433
2435
  return best >= 0 ? tbl.get(rowIdx, best) : FormulaValue.errorNA;
2434
2436
  }
2437
+ function wildcardRegex(pattern) {
2438
+ let out = "";
2439
+ for (let i = 0; i < pattern.length; i++) {
2440
+ const ch = pattern[i];
2441
+ if (ch === "~" && i + 1 < pattern.length) {
2442
+ const next = pattern[++i];
2443
+ out += next.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2444
+ } else if (ch === "*") out += ".*";
2445
+ else if (ch === "?") out += ".";
2446
+ else out += ch.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2447
+ }
2448
+ return new RegExp(`^${out}$`, "i");
2449
+ }
2450
+ function findMatch(n, get, lookupVal, matchMode, searchMode) {
2451
+ if ((searchMode === 2 || searchMode === -2) && matchMode !== 2) {
2452
+ const at = searchMode === 2 ? get : (j) => get(n - 1 - j);
2453
+ let lo = 0, hi = n - 1, lastLE = -1, firstGE = -1;
2454
+ while (lo <= hi) {
2455
+ const mid2 = lo + hi >>> 1;
2456
+ const cmp = FormulaValue.compare(at(mid2), lookupVal);
2457
+ if (cmp === 0) return searchMode === 2 ? mid2 : n - 1 - mid2;
2458
+ if (cmp < 0) {
2459
+ lastLE = mid2;
2460
+ lo = mid2 + 1;
2461
+ } else {
2462
+ firstGE = mid2;
2463
+ hi = mid2 - 1;
2464
+ }
2465
+ }
2466
+ const idx = matchMode === -1 ? lastLE : matchMode === 1 ? firstGE : -1;
2467
+ return idx < 0 ? -1 : searchMode === 2 ? idx : n - 1 - idx;
2468
+ }
2469
+ const reverse = searchMode === -1;
2470
+ const order = reverse ? Array.from({ length: n }, (_, k) => n - 1 - k) : Array.from({ length: n }, (_, k) => k);
2471
+ if (matchMode === 2) {
2472
+ const re = wildcardRegex(lookupVal.asText());
2473
+ for (const i of order) if (re.test(get(i).asText())) return i;
2474
+ return -1;
2475
+ }
2476
+ if (matchMode === 0) {
2477
+ for (const i of order) if (FormulaValue.areEqual(get(i), lookupVal)) return i;
2478
+ return -1;
2479
+ }
2480
+ let best = -1;
2481
+ let bestVal = null;
2482
+ for (const i of order) {
2483
+ const v = get(i);
2484
+ const cmp = FormulaValue.compare(v, lookupVal);
2485
+ if (cmp === 0) return i;
2486
+ if (matchMode === -1 && cmp < 0) {
2487
+ if (bestVal === null || FormulaValue.compare(v, bestVal) > 0) {
2488
+ best = i;
2489
+ bestVal = v;
2490
+ }
2491
+ } else if (matchMode === 1 && cmp > 0) {
2492
+ if (bestVal === null || FormulaValue.compare(v, bestVal) < 0) {
2493
+ best = i;
2494
+ bestVal = v;
2495
+ }
2496
+ }
2497
+ }
2498
+ return best;
2499
+ }
2500
+ function xlookup(a, c) {
2501
+ const lookupVal = a[0].evaluate(c);
2502
+ if (lookupVal.isError) return lookupVal;
2503
+ const lookupRaw = a[1].evaluate(c);
2504
+ if (lookupRaw.isError) return lookupRaw;
2505
+ const returnRaw = a[2].evaluate(c);
2506
+ if (returnRaw.isError) return returnRaw;
2507
+ const matchMode = a.length > 4 ? num2(a, c, 4) : { ok: true, n: 0 };
2508
+ if (!matchMode.ok) return matchMode.e;
2509
+ const searchMode = a.length > 5 ? num2(a, c, 5) : { ok: true, n: 1 };
2510
+ if (!searchMode.ok) return searchMode.e;
2511
+ const mm = Math.trunc(matchMode.n), sm = Math.trunc(searchMode.n);
2512
+ const la = lookupRaw.isArray ? lookupRaw.arrayVal : ArrayValue.fromScalar(lookupRaw);
2513
+ if (la.rows > 1 && la.columns > 1) return FormulaValue.errorValue;
2514
+ const vertical = !(la.rows === 1 && la.columns > 1);
2515
+ const n = vertical ? la.rows : la.columns;
2516
+ const get = vertical ? (i) => la.get(i, 0) : (i) => la.get(0, i);
2517
+ const idx = findMatch(n, get, lookupVal, mm, sm);
2518
+ if (idx < 0) {
2519
+ return a.length > 3 ? a[3].evaluate(c) : FormulaValue.errorNA;
2520
+ }
2521
+ const ret = returnRaw.isArray ? returnRaw.arrayVal : ArrayValue.fromScalar(returnRaw);
2522
+ if (vertical) {
2523
+ if (ret.rows !== n) return FormulaValue.errorValue;
2524
+ if (ret.columns === 1) return ret.get(idx, 0);
2525
+ const out2 = new ArrayValue(1, ret.columns);
2526
+ for (let col = 0; col < ret.columns; col++) out2.set(0, col, ret.get(idx, col));
2527
+ return FormulaValue.array(out2);
2528
+ }
2529
+ if (ret.columns !== n) return FormulaValue.errorValue;
2530
+ if (ret.rows === 1) return ret.get(0, idx);
2531
+ const out = new ArrayValue(ret.rows, 1);
2532
+ for (let r = 0; r < ret.rows; r++) out.set(r, 0, ret.get(r, idx));
2533
+ return FormulaValue.array(out);
2534
+ }
2535
+ function xmatch(a, c) {
2536
+ const lookupVal = a[0].evaluate(c);
2537
+ if (lookupVal.isError) return lookupVal;
2538
+ const lookupRaw = a[1].evaluate(c);
2539
+ if (lookupRaw.isError) return lookupRaw;
2540
+ const matchMode = a.length > 2 ? num2(a, c, 2) : { ok: true, n: 0 };
2541
+ if (!matchMode.ok) return matchMode.e;
2542
+ const searchMode = a.length > 3 ? num2(a, c, 3) : { ok: true, n: 1 };
2543
+ if (!searchMode.ok) return searchMode.e;
2544
+ const la = lookupRaw.isArray ? lookupRaw.arrayVal : ArrayValue.fromScalar(lookupRaw);
2545
+ if (la.rows > 1 && la.columns > 1) return FormulaValue.errorValue;
2546
+ const vertical = !(la.rows === 1 && la.columns > 1);
2547
+ const n = vertical ? la.rows : la.columns;
2548
+ const get = vertical ? (i) => la.get(i, 0) : (i) => la.get(0, i);
2549
+ const idx = findMatch(n, get, lookupVal, Math.trunc(matchMode.n), Math.trunc(searchMode.n));
2550
+ return idx < 0 ? FormulaValue.errorNA : FormulaValue.number(idx + 1);
2551
+ }
2435
2552
  function index_(a, c) {
2436
2553
  const arrVal = a[0].evaluate(c);
2437
2554
  if (arrVal.isError) return arrVal;
@@ -2965,6 +3082,24 @@ function registerFinance(r) {
2965
3082
  r.register("PV", pv, 3, 5);
2966
3083
  r.register("NPV", npv, 2);
2967
3084
  r.register("IRR", irr, 1, 2);
3085
+ r.register("RATE", rate, 3, 6);
3086
+ r.register("NPER", nper, 3, 5);
3087
+ r.register("XNPV", xnpv, 3, 3);
3088
+ r.register("XIRR", xirr, 2, 3);
3089
+ r.register("IPMT", ipmt, 4, 6);
3090
+ r.register("PPMT", ppmt, 4, 6);
3091
+ }
3092
+ function pmtValue(r, n, pv2, fv2, type) {
3093
+ if (r === 0) return -(pv2 + fv2) / n;
3094
+ const pow = Math.pow(1 + r, n);
3095
+ let payment = (-fv2 - pv2 * pow) * r / (pow - 1);
3096
+ if (type === 1) payment /= 1 + r;
3097
+ return payment;
3098
+ }
3099
+ function fvValue(r, n, pmt2, pv2, type) {
3100
+ if (r === 0) return -(pv2 + pmt2 * n);
3101
+ const pow = Math.pow(1 + r, n);
3102
+ return -(pv2 * pow + pmt2 * (1 + r * type) * (pow - 1) / r);
2968
3103
  }
2969
3104
  function arg(a, c, idx, dflt) {
2970
3105
  if (idx >= a.length) return { ok: true, n: dflt };
@@ -2972,65 +3107,59 @@ function arg(a, c, idx, dflt) {
2972
3107
  return r.ok ? { ok: true, n: r.value } : { ok: false, e: r.error };
2973
3108
  }
2974
3109
  function pmt(a, c) {
2975
- const rate = arg(a, c, 0, 0);
2976
- if (!rate.ok) return rate.e;
2977
- const nper = arg(a, c, 1, 0);
2978
- if (!nper.ok) return nper.e;
3110
+ const rate2 = arg(a, c, 0, 0);
3111
+ if (!rate2.ok) return rate2.e;
3112
+ const nper2 = arg(a, c, 1, 0);
3113
+ if (!nper2.ok) return nper2.e;
2979
3114
  const pvv = arg(a, c, 2, 0);
2980
3115
  if (!pvv.ok) return pvv.e;
2981
3116
  const fvv = arg(a, c, 3, 0);
2982
3117
  if (!fvv.ok) return fvv.e;
2983
3118
  const type = arg(a, c, 4, 0);
2984
3119
  if (!type.ok) return type.e;
2985
- const r = rate.n, n = nper.n, present = pvv.n, future = fvv.n, t = type.n;
3120
+ const r = rate2.n, n = nper2.n, present = pvv.n, future = fvv.n, t = type.n;
2986
3121
  if (n === 0) return FormulaValue.errorNum;
2987
- if (r === 0) return FormulaValue.number(-(present + future) / n);
2988
- const pow = Math.pow(1 + r, n);
2989
- let payment = (-future - present * pow) * r / (pow - 1);
2990
- if (t === 1) payment /= 1 + r;
2991
- return FormulaValue.number(payment);
3122
+ return FormulaValue.number(pmtValue(r, n, present, future, t));
2992
3123
  }
2993
3124
  function fv(a, c) {
2994
- const rate = arg(a, c, 0, 0);
2995
- if (!rate.ok) return rate.e;
2996
- const nper = arg(a, c, 1, 0);
2997
- if (!nper.ok) return nper.e;
3125
+ const rate2 = arg(a, c, 0, 0);
3126
+ if (!rate2.ok) return rate2.e;
3127
+ const nper2 = arg(a, c, 1, 0);
3128
+ if (!nper2.ok) return nper2.e;
2998
3129
  const pmtv = arg(a, c, 2, 0);
2999
3130
  if (!pmtv.ok) return pmtv.e;
3000
3131
  const pvv = arg(a, c, 3, 0);
3001
3132
  if (!pvv.ok) return pvv.e;
3002
3133
  const type = arg(a, c, 4, 0);
3003
3134
  if (!type.ok) return type.e;
3004
- const r = rate.n, n = nper.n, payment = pmtv.n, present = pvv.n, t = type.n;
3005
- if (r === 0) return FormulaValue.number(-(present + payment * n));
3006
- const pow = Math.pow(1 + r, n);
3007
- return FormulaValue.number(-(present * pow + payment * (1 + r * t) * (pow - 1) / r));
3135
+ const r = rate2.n, n = nper2.n, payment = pmtv.n, present = pvv.n, t = type.n;
3136
+ return FormulaValue.number(fvValue(r, n, payment, present, t));
3008
3137
  }
3009
3138
  function pv(a, c) {
3010
- const rate = arg(a, c, 0, 0);
3011
- if (!rate.ok) return rate.e;
3012
- const nper = arg(a, c, 1, 0);
3013
- if (!nper.ok) return nper.e;
3139
+ const rate2 = arg(a, c, 0, 0);
3140
+ if (!rate2.ok) return rate2.e;
3141
+ const nper2 = arg(a, c, 1, 0);
3142
+ if (!nper2.ok) return nper2.e;
3014
3143
  const pmtv = arg(a, c, 2, 0);
3015
3144
  if (!pmtv.ok) return pmtv.e;
3016
3145
  const fvv = arg(a, c, 3, 0);
3017
3146
  if (!fvv.ok) return fvv.e;
3018
3147
  const type = arg(a, c, 4, 0);
3019
3148
  if (!type.ok) return type.e;
3020
- const r = rate.n, n = nper.n, payment = pmtv.n, future = fvv.n, t = type.n;
3149
+ const r = rate2.n, n = nper2.n, payment = pmtv.n, future = fvv.n, t = type.n;
3021
3150
  if (r === 0) return FormulaValue.number(-(future + payment * n));
3022
3151
  const pow = Math.pow(1 + r, n);
3023
3152
  return FormulaValue.number(-(future + payment * (1 + r * t) * (pow - 1) / r) / pow);
3024
3153
  }
3025
3154
  function npv(a, c) {
3026
- const rate = FormulaHelper.evalDouble(a[0], c);
3027
- if (!rate.ok) return rate.error;
3028
- if (rate.value === -1) return FormulaValue.errorDiv0;
3155
+ const rate2 = FormulaHelper.evalDouble(a[0], c);
3156
+ if (!rate2.ok) return rate2.error;
3157
+ if (rate2.value === -1) return FormulaValue.errorDiv0;
3029
3158
  const flows = FormulaHelper.collectNumbers(a.slice(1), c);
3030
3159
  if (!flows.ok) return flows.error;
3031
3160
  let total = 0;
3032
3161
  for (let i = 0; i < flows.values.length; i++) {
3033
- total += flows.values[i] / Math.pow(1 + rate.value, i + 1);
3162
+ total += flows.values[i] / Math.pow(1 + rate2.value, i + 1);
3034
3163
  }
3035
3164
  return FormulaValue.number(total);
3036
3165
  }
@@ -3045,27 +3174,220 @@ function irr(a, c) {
3045
3174
  if (!g.ok) return g.error;
3046
3175
  guess = g.value;
3047
3176
  }
3048
- const npvAt = (rate2) => {
3177
+ const npvAt = (rate3) => {
3049
3178
  let sum2 = 0;
3050
- for (let i = 0; i < flows.length; i++) sum2 += flows[i] / Math.pow(1 + rate2, i);
3179
+ for (let i = 0; i < flows.length; i++) sum2 += flows[i] / Math.pow(1 + rate3, i);
3051
3180
  return sum2;
3052
3181
  };
3053
- let rate = guess;
3182
+ let rate2 = guess;
3183
+ for (let iter = 0; iter < 100; iter++) {
3184
+ const f = npvAt(rate2);
3185
+ if (Math.abs(f) < 1e-7) return FormulaValue.number(rate2);
3186
+ const h = 1e-6;
3187
+ const deriv = (npvAt(rate2 + h) - f) / h;
3188
+ if (deriv === 0) break;
3189
+ const next = rate2 - f / deriv;
3190
+ if (!isFinite(next) || next <= -1) break;
3191
+ if (Math.abs(next - rate2) < 1e-9) {
3192
+ rate2 = next;
3193
+ break;
3194
+ }
3195
+ rate2 = next;
3196
+ }
3197
+ if (Math.abs(npvAt(rate2)) < 1e-6) return FormulaValue.number(rate2);
3198
+ return FormulaValue.errorNum;
3199
+ }
3200
+ function tvm(r, n, pmt2, pv2, fv2, type) {
3201
+ if (r === 0) return pv2 + pmt2 * n + fv2;
3202
+ const pow = Math.pow(1 + r, n);
3203
+ return pv2 * pow + pmt2 * (1 + r * type) * (pow - 1) / r + fv2;
3204
+ }
3205
+ function rate(a, c) {
3206
+ const nper2 = arg(a, c, 0, 0);
3207
+ if (!nper2.ok) return nper2.e;
3208
+ const pmtv = arg(a, c, 1, 0);
3209
+ if (!pmtv.ok) return pmtv.e;
3210
+ const pvv = arg(a, c, 2, 0);
3211
+ if (!pvv.ok) return pvv.e;
3212
+ const fvv = arg(a, c, 3, 0);
3213
+ if (!fvv.ok) return fvv.e;
3214
+ const type = arg(a, c, 4, 0);
3215
+ if (!type.ok) return type.e;
3216
+ const gs = arg(a, c, 5, 0.1);
3217
+ if (!gs.ok) return gs.e;
3218
+ const n = nper2.n, pmt2 = pmtv.n, pv2 = pvv.n, fv2 = fvv.n, t = type.n;
3219
+ if (n === 0) return FormulaValue.errorNum;
3220
+ let r = gs.n;
3054
3221
  for (let iter = 0; iter < 100; iter++) {
3055
- const f = npvAt(rate);
3056
- if (Math.abs(f) < 1e-7) return FormulaValue.number(rate);
3222
+ const f = tvm(r, n, pmt2, pv2, fv2, t);
3223
+ if (Math.abs(f) < 1e-8) return FormulaValue.number(r);
3057
3224
  const h = 1e-6;
3058
- const deriv = (npvAt(rate + h) - f) / h;
3225
+ const deriv = (tvm(r + h, n, pmt2, pv2, fv2, t) - f) / h;
3059
3226
  if (deriv === 0) break;
3060
- const next = rate - f / deriv;
3227
+ const next = r - f / deriv;
3061
3228
  if (!isFinite(next) || next <= -1) break;
3062
- if (Math.abs(next - rate) < 1e-9) {
3063
- rate = next;
3229
+ if (Math.abs(next - r) < 1e-10) {
3230
+ r = next;
3064
3231
  break;
3065
3232
  }
3066
- rate = next;
3233
+ r = next;
3067
3234
  }
3068
- if (Math.abs(npvAt(rate)) < 1e-6) return FormulaValue.number(rate);
3235
+ if (Math.abs(tvm(r, n, pmt2, pv2, fv2, t)) < 1e-6) return FormulaValue.number(r);
3236
+ return FormulaValue.errorNum;
3237
+ }
3238
+ function nper(a, c) {
3239
+ const rate2 = arg(a, c, 0, 0);
3240
+ if (!rate2.ok) return rate2.e;
3241
+ const pmtv = arg(a, c, 1, 0);
3242
+ if (!pmtv.ok) return pmtv.e;
3243
+ const pvv = arg(a, c, 2, 0);
3244
+ if (!pvv.ok) return pvv.e;
3245
+ const fvv = arg(a, c, 3, 0);
3246
+ if (!fvv.ok) return fvv.e;
3247
+ const type = arg(a, c, 4, 0);
3248
+ if (!type.ok) return type.e;
3249
+ const r = rate2.n, pmt2 = pmtv.n, pv2 = pvv.n, fv2 = fvv.n, t = type.n;
3250
+ if (r === 0) {
3251
+ if (pmt2 === 0) return FormulaValue.errorNum;
3252
+ return FormulaValue.number(-(pv2 + fv2) / pmt2);
3253
+ }
3254
+ const adj = pmt2 * (1 + r * t);
3255
+ const num5 = adj - fv2 * r;
3256
+ const den = adj + pv2 * r;
3257
+ if (den === 0 || num5 / den <= 0) return FormulaValue.errorNum;
3258
+ const result = Math.log(num5 / den) / Math.log(1 + r);
3259
+ if (!isFinite(result)) return FormulaValue.errorNum;
3260
+ return FormulaValue.number(result);
3261
+ }
3262
+ function interestForPeriod(r, per, n, pv2, fv2, t) {
3263
+ const pay = pmtValue(r, n, pv2, fv2, t);
3264
+ if (t === 1) {
3265
+ if (per === 1) return 0;
3266
+ return fvValue(r, per - 2, pay, pv2, 1) * r / (1 + r);
3267
+ }
3268
+ return fvValue(r, per - 1, pay, pv2, 0) * r;
3269
+ }
3270
+ function ipmt(a, c) {
3271
+ const rate2 = arg(a, c, 0, 0);
3272
+ if (!rate2.ok) return rate2.e;
3273
+ const perv = arg(a, c, 1, 0);
3274
+ if (!perv.ok) return perv.e;
3275
+ const nper2 = arg(a, c, 2, 0);
3276
+ if (!nper2.ok) return nper2.e;
3277
+ const pvv = arg(a, c, 3, 0);
3278
+ if (!pvv.ok) return pvv.e;
3279
+ const fvv = arg(a, c, 4, 0);
3280
+ if (!fvv.ok) return fvv.e;
3281
+ const type = arg(a, c, 5, 0);
3282
+ if (!type.ok) return type.e;
3283
+ const r = rate2.n, per = perv.n, n = nper2.n, pv2 = pvv.n, fv2 = fvv.n, t = type.n;
3284
+ if (n === 0 || per < 1 || per > n) return FormulaValue.errorNum;
3285
+ return FormulaValue.number(interestForPeriod(r, per, n, pv2, fv2, t));
3286
+ }
3287
+ function ppmt(a, c) {
3288
+ const rate2 = arg(a, c, 0, 0);
3289
+ if (!rate2.ok) return rate2.e;
3290
+ const perv = arg(a, c, 1, 0);
3291
+ if (!perv.ok) return perv.e;
3292
+ const nper2 = arg(a, c, 2, 0);
3293
+ if (!nper2.ok) return nper2.e;
3294
+ const pvv = arg(a, c, 3, 0);
3295
+ if (!pvv.ok) return pvv.e;
3296
+ const fvv = arg(a, c, 4, 0);
3297
+ if (!fvv.ok) return fvv.e;
3298
+ const type = arg(a, c, 5, 0);
3299
+ if (!type.ok) return type.e;
3300
+ const r = rate2.n, per = perv.n, n = nper2.n, pv2 = pvv.n, fv2 = fvv.n, t = type.n;
3301
+ if (n === 0 || per < 1 || per > n) return FormulaValue.errorNum;
3302
+ const pay = pmtValue(r, n, pv2, fv2, t);
3303
+ return FormulaValue.number(pay - interestForPeriod(r, per, n, pv2, fv2, t));
3304
+ }
3305
+ function flatNumbers(node, c) {
3306
+ const v = node.evaluate(c);
3307
+ if (v.isError) return { ok: false, e: v };
3308
+ const out = [];
3309
+ const push = (item) => {
3310
+ if (item.isError) return item;
3311
+ const d = item.tryAsDouble();
3312
+ if (!d.ok) return FormulaValue.errorValue;
3313
+ out.push(d.value);
3314
+ return null;
3315
+ };
3316
+ if (v.isArray) {
3317
+ for (const item of v.arrayVal.values()) {
3318
+ const err = push(item);
3319
+ if (err) return { ok: false, e: err };
3320
+ }
3321
+ } else {
3322
+ const err = push(v);
3323
+ if (err) return { ok: false, e: err };
3324
+ }
3325
+ return { ok: true, values: out };
3326
+ }
3327
+ function xnpvAt(rate2, values, days2) {
3328
+ let sum2 = 0;
3329
+ for (let i = 0; i < values.length; i++) {
3330
+ sum2 += values[i] / Math.pow(1 + rate2, days2[i] / 365);
3331
+ }
3332
+ return sum2;
3333
+ }
3334
+ function xnpv(a, c) {
3335
+ const rateRes = FormulaHelper.evalDouble(a[0], c);
3336
+ if (!rateRes.ok) return rateRes.error;
3337
+ const rate2 = rateRes.value;
3338
+ if (rate2 <= -1) return FormulaValue.errorNum;
3339
+ const valsRes = flatNumbers(a[1], c);
3340
+ if (!valsRes.ok) return valsRes.e;
3341
+ const datesRes = flatNumbers(a[2], c);
3342
+ if (!datesRes.ok) return datesRes.e;
3343
+ const values = valsRes.values;
3344
+ const dates = datesRes.values;
3345
+ if (values.length === 0 || values.length !== dates.length) return FormulaValue.errorNum;
3346
+ const base = Math.trunc(dates[0]);
3347
+ const days2 = dates.map((d) => Math.trunc(d) - base);
3348
+ if (days2.some((d) => d < 0)) return FormulaValue.errorNum;
3349
+ return FormulaValue.number(xnpvAt(rate2, values, days2));
3350
+ }
3351
+ function xirr(a, c) {
3352
+ const valsRes = flatNumbers(a[0], c);
3353
+ if (!valsRes.ok) return valsRes.e;
3354
+ const datesRes = flatNumbers(a[1], c);
3355
+ if (!datesRes.ok) return datesRes.e;
3356
+ const values = valsRes.values;
3357
+ const dates = datesRes.values;
3358
+ if (values.length < 2 || values.length !== dates.length) return FormulaValue.errorNum;
3359
+ let guess = 0.1;
3360
+ if (a.length > 2) {
3361
+ const g = FormulaHelper.evalDouble(a[2], c);
3362
+ if (!g.ok) return g.error;
3363
+ guess = g.value;
3364
+ }
3365
+ const base = Math.trunc(dates[0]);
3366
+ const days2 = dates.map((d) => Math.trunc(d) - base);
3367
+ if (days2.some((d) => d < 0)) return FormulaValue.errorNum;
3368
+ const derivAt = (rate3) => {
3369
+ let sum2 = 0;
3370
+ for (let i = 0; i < values.length; i++) {
3371
+ const e = days2[i] / 365;
3372
+ sum2 += -e * values[i] / Math.pow(1 + rate3, e + 1);
3373
+ }
3374
+ return sum2;
3375
+ };
3376
+ let rate2 = guess;
3377
+ for (let iter = 0; iter < 100; iter++) {
3378
+ const f = xnpvAt(rate2, values, days2);
3379
+ if (Math.abs(f) < 1e-7) return FormulaValue.number(rate2);
3380
+ const deriv = derivAt(rate2);
3381
+ if (deriv === 0) break;
3382
+ const next = rate2 - f / deriv;
3383
+ if (!isFinite(next) || next <= -1) break;
3384
+ if (Math.abs(next - rate2) < 1e-9) {
3385
+ rate2 = next;
3386
+ break;
3387
+ }
3388
+ rate2 = next;
3389
+ }
3390
+ if (Math.abs(xnpvAt(rate2, values, days2)) < 1e-6) return FormulaValue.number(rate2);
3069
3391
  return FormulaValue.errorNum;
3070
3392
  }
3071
3393
 
@@ -3403,10 +3725,17 @@ function registerArray(r) {
3403
3725
  r.register("SEQUENCE", sequence, 1, 4);
3404
3726
  r.register("UNIQUE", unique, 1, 3);
3405
3727
  r.register("SORT", sort, 1, 4);
3728
+ r.register("SORTBY", sortBy, 2);
3406
3729
  r.register("FILTER", filter, 2, 3);
3407
3730
  r.register("MMULT", mmult, 2, 2);
3408
3731
  r.register("TEXTSPLIT", textSplit, 2, 6);
3732
+ r.register("VSTACK", vstack, 1);
3733
+ r.register("HSTACK", hstack, 1);
3734
+ r.register("TOCOL", toCol, 1, 3);
3735
+ r.register("TOROW", toRow, 1, 3);
3409
3736
  }
3737
+ var MAX_ROWS = 1048576;
3738
+ var MAX_COLS = 16384;
3410
3739
  var asArray2 = (v) => v.isArray ? v.arrayVal : ArrayValue.fromScalar(v);
3411
3740
  function fromRows(rows2) {
3412
3741
  const nr = rows2.length;
@@ -3453,26 +3782,53 @@ function sequence(a, c) {
3453
3782
  }
3454
3783
  return FormulaValue.array(arr);
3455
3784
  }
3456
- function rowsEqual(x, y) {
3457
- if (x.length !== y.length) return false;
3458
- for (let i = 0; i < x.length; i++) if (!FormulaValue.areEqual(x[i], y[i])) return false;
3459
- return true;
3785
+ function cellKey(v) {
3786
+ switch (v.kind) {
3787
+ case "number":
3788
+ return "n" + (v.numberValue === 0 ? "0" : String(v.numberValue));
3789
+ case "text":
3790
+ return "t" + v.textValue.toUpperCase();
3791
+ // case-insensitive
3792
+ case "boolean":
3793
+ return v.booleanValue ? "b1" : "b0";
3794
+ case "blank":
3795
+ return "B";
3796
+ case "error":
3797
+ return "e" + v.errorCode;
3798
+ default:
3799
+ return "x";
3800
+ }
3460
3801
  }
3461
3802
  function unique(a, c) {
3462
3803
  const v = a[0].evaluate(c);
3463
3804
  if (v.isError) return v;
3464
3805
  const byCol2 = a.length > 1 ? a[1].evaluate(c).coerceToBool().booleanValue : false;
3465
3806
  const exactlyOnce = a.length > 2 ? a[2].evaluate(c).coerceToBool().booleanValue : false;
3466
- let rows2 = toRows(byCol2 ? asArray2(v).transpose() : asArray2(v));
3467
- const counts = rows2.map((r) => rows2.filter((o) => rowsEqual(o, r)).length);
3468
- const seen = [];
3807
+ const base = byCol2 ? asArray2(v).transpose() : asArray2(v);
3808
+ const map = /* @__PURE__ */ new Map();
3809
+ const order = [];
3810
+ for (let r = 0; r < base.rows; r++) {
3811
+ const cells = new Array(base.columns);
3812
+ let key = "";
3813
+ for (let col = 0; col < base.columns; col++) {
3814
+ const cell = base.get(r, col);
3815
+ cells[col] = cell;
3816
+ const k = cellKey(cell);
3817
+ key += k.length + ":" + k;
3818
+ }
3819
+ const hit = map.get(key);
3820
+ if (hit) hit.count++;
3821
+ else {
3822
+ map.set(key, { cells, count: 1 });
3823
+ order.push(key);
3824
+ }
3825
+ }
3469
3826
  const result = [];
3470
- rows2.forEach((r, i) => {
3471
- if (seen.some((o) => rowsEqual(o, r))) return;
3472
- seen.push(r);
3473
- if (exactlyOnce && counts[i] > 1) return;
3474
- result.push(r);
3475
- });
3827
+ for (const key of order) {
3828
+ const e = map.get(key);
3829
+ if (exactlyOnce && e.count > 1) continue;
3830
+ result.push(e.cells);
3831
+ }
3476
3832
  if (result.length === 0) return FormulaValue.error(8 /* Calc */);
3477
3833
  const out = fromRows(result);
3478
3834
  return byCol2 ? FormulaValue.array(out.arrayVal.transpose()) : out;
@@ -3497,6 +3853,58 @@ function sort(a, c) {
3497
3853
  const out = fromRows(sorted);
3498
3854
  return byCol2 ? FormulaValue.array(out.arrayVal.transpose()) : out;
3499
3855
  }
3856
+ function sortBy(a, c) {
3857
+ const v = a[0].evaluate(c);
3858
+ if (v.isError) return v;
3859
+ const data = asArray2(v);
3860
+ const keys = [];
3861
+ let i = 1;
3862
+ while (i < a.length) {
3863
+ const byVal = a[i].evaluate(c);
3864
+ if (byVal.isError) return byVal;
3865
+ i++;
3866
+ let dir = 1;
3867
+ if (i < a.length) {
3868
+ const maybeOrder = a[i].evaluate(c);
3869
+ if (maybeOrder.isError) return maybeOrder;
3870
+ if (!maybeOrder.isArray && maybeOrder.isNumeric) {
3871
+ const d = maybeOrder.tryAsDouble();
3872
+ if (d.ok) {
3873
+ dir = d.value < 0 ? -1 : 1;
3874
+ i++;
3875
+ }
3876
+ }
3877
+ }
3878
+ keys.push({ vals: [...asArray2(byVal).values()], dir });
3879
+ }
3880
+ if (keys.length === 0) return FormulaValue.errorValue;
3881
+ const n = keys[0].vals.length;
3882
+ if (keys.some((k) => k.vals.length !== n)) return FormulaValue.errorValue;
3883
+ const byRow2 = n === data.rows;
3884
+ const byColumn = !byRow2 && n === data.columns;
3885
+ if (!byRow2 && !byColumn) return FormulaValue.errorValue;
3886
+ const perm = Array.from({ length: n }, (_, k) => k);
3887
+ perm.sort((x, y) => {
3888
+ for (const key of keys) {
3889
+ const cmp = FormulaValue.compare(key.vals[x], key.vals[y]) * key.dir;
3890
+ if (cmp !== 0) return cmp;
3891
+ }
3892
+ return x - y;
3893
+ });
3894
+ const out = new ArrayValue(data.rows, data.columns);
3895
+ if (byRow2) {
3896
+ for (let r = 0; r < data.rows; r++) {
3897
+ const src = perm[r];
3898
+ for (let col = 0; col < data.columns; col++) out.set(r, col, data.get(src, col));
3899
+ }
3900
+ } else {
3901
+ for (let col = 0; col < data.columns; col++) {
3902
+ const src = perm[col];
3903
+ for (let r = 0; r < data.rows; r++) out.set(r, col, data.get(r, src));
3904
+ }
3905
+ }
3906
+ return FormulaValue.array(out);
3907
+ }
3500
3908
  function filter(a, c) {
3501
3909
  const v = a[0].evaluate(c);
3502
3910
  if (v.isError) return v;
@@ -3578,6 +3986,89 @@ function textSplit(a, c) {
3578
3986
  });
3579
3987
  return fromRows(rows2);
3580
3988
  }
3989
+ function vstack(a, c) {
3990
+ const parts = [];
3991
+ let totalRows = 0, maxCols = 0;
3992
+ for (const node of a) {
3993
+ const arr = asArray2(node.evaluate(c));
3994
+ parts.push(arr);
3995
+ totalRows += arr.rows;
3996
+ if (arr.columns > maxCols) maxCols = arr.columns;
3997
+ }
3998
+ if (totalRows > MAX_ROWS || maxCols > MAX_COLS) return FormulaValue.errorNum;
3999
+ const out = new ArrayValue(totalRows, maxCols);
4000
+ let rowOff = 0;
4001
+ for (const arr of parts) {
4002
+ for (let r = 0; r < arr.rows; r++) {
4003
+ for (let col = 0; col < maxCols; col++) {
4004
+ out.set(rowOff + r, col, col < arr.columns ? arr.get(r, col) : FormulaValue.errorNA);
4005
+ }
4006
+ }
4007
+ rowOff += arr.rows;
4008
+ }
4009
+ return FormulaValue.array(out);
4010
+ }
4011
+ function hstack(a, c) {
4012
+ const parts = [];
4013
+ let totalCols = 0, maxRows = 0;
4014
+ for (const node of a) {
4015
+ const arr = asArray2(node.evaluate(c));
4016
+ parts.push(arr);
4017
+ totalCols += arr.columns;
4018
+ if (arr.rows > maxRows) maxRows = arr.rows;
4019
+ }
4020
+ if (maxRows > MAX_ROWS || totalCols > MAX_COLS) return FormulaValue.errorNum;
4021
+ const out = new ArrayValue(maxRows, totalCols);
4022
+ let colOff = 0;
4023
+ for (const arr of parts) {
4024
+ for (let col = 0; col < arr.columns; col++) {
4025
+ for (let r = 0; r < maxRows; r++) {
4026
+ out.set(r, colOff + col, r < arr.rows ? arr.get(r, col) : FormulaValue.errorNA);
4027
+ }
4028
+ }
4029
+ colOff += arr.columns;
4030
+ }
4031
+ return FormulaValue.array(out);
4032
+ }
4033
+ function flatten(a, c) {
4034
+ const arr = asArray2(a[0].evaluate(c));
4035
+ const ig = num4(a, c, 1, 0);
4036
+ if (!ig.ok) return { ok: false, e: ig.e };
4037
+ const ignore = Math.trunc(ig.n);
4038
+ const byCol2 = a.length > 2 ? a[2].evaluate(c).coerceToBool().booleanValue : false;
4039
+ const skip = (x) => (ignore & 1) !== 0 && x.isBlank || (ignore & 2) !== 0 && x.isError;
4040
+ const values = [];
4041
+ if (byCol2) {
4042
+ for (let col = 0; col < arr.columns; col++)
4043
+ for (let r = 0; r < arr.rows; r++) {
4044
+ const x = arr.get(r, col);
4045
+ if (!skip(x)) values.push(x);
4046
+ }
4047
+ } else {
4048
+ for (let r = 0; r < arr.rows; r++)
4049
+ for (let col = 0; col < arr.columns; col++) {
4050
+ const x = arr.get(r, col);
4051
+ if (!skip(x)) values.push(x);
4052
+ }
4053
+ }
4054
+ return { ok: true, values };
4055
+ }
4056
+ function toCol(a, c) {
4057
+ const f = flatten(a, c);
4058
+ if (!f.ok) return f.e;
4059
+ if (f.values.length === 0) return FormulaValue.errorNA;
4060
+ const out = new ArrayValue(f.values.length, 1);
4061
+ for (let i = 0; i < f.values.length; i++) out.set(i, 0, f.values[i]);
4062
+ return FormulaValue.array(out);
4063
+ }
4064
+ function toRow(a, c) {
4065
+ const f = flatten(a, c);
4066
+ if (!f.ok) return f.e;
4067
+ if (f.values.length === 0) return FormulaValue.errorNA;
4068
+ const out = new ArrayValue(1, f.values.length);
4069
+ for (let i = 0; i < f.values.length; i++) out.set(0, i, f.values[i]);
4070
+ return FormulaValue.array(out);
4071
+ }
3581
4072
 
3582
4073
  // src/function-registry.ts
3583
4074
  var _default, _functions, _FunctionRegistry_instances, registerBuiltIns_fn;
@@ -4380,8 +4871,8 @@ writeResult_fn = function(e) {
4380
4871
  for (let dc = 0; dc < cols && !collides; dc++) {
4381
4872
  if (dr === 0 && dc === 0) continue;
4382
4873
  const r = e.row + dr, c = e.col + dc;
4383
- const cellKey = keyOf(e.sheet, cellRefFromRowCol(r, c));
4384
- if (__privateMethod(this, _RecalcEngine_instances, isOccupied_fn).call(this, cellKey, e.sheetName, cellRefFromRowCol(r, c), anchorKey, prevSpill)) {
4874
+ const cellKey2 = keyOf(e.sheet, cellRefFromRowCol(r, c));
4875
+ if (__privateMethod(this, _RecalcEngine_instances, isOccupied_fn).call(this, cellKey2, e.sheetName, cellRefFromRowCol(r, c), anchorKey, prevSpill)) {
4385
4876
  collides = true;
4386
4877
  }
4387
4878
  }
@@ -4402,9 +4893,9 @@ writeResult_fn = function(e) {
4402
4893
  __privateGet(this, _model).setCellValue(e.sheetName, ref, arr.get(dr, dc).toObject());
4403
4894
  changed.push({ sheet: e.sheet, row: r, col: c });
4404
4895
  if (dr !== 0 || dc !== 0) {
4405
- const cellKey = keyOf(e.sheet, ref);
4406
- newSpill.add(cellKey);
4407
- __privateGet(this, _spillOwner).set(cellKey, anchorKey);
4896
+ const cellKey2 = keyOf(e.sheet, ref);
4897
+ newSpill.add(cellKey2);
4898
+ __privateGet(this, _spillOwner).set(cellKey2, anchorKey);
4408
4899
  }
4409
4900
  }
4410
4901
  }
@@ -4424,11 +4915,11 @@ writeResult_fn = function(e) {
4424
4915
  };
4425
4916
  };
4426
4917
  /** Whether a target cell already holds content that blocks a spill. */
4427
- isOccupied_fn = function(cellKey, sheetName, ref, anchorKey, prevSpill) {
4428
- if (__privateGet(this, _formulas).has(cellKey) && cellKey !== anchorKey) return true;
4429
- const owner = __privateGet(this, _spillOwner).get(cellKey);
4918
+ isOccupied_fn = function(cellKey2, sheetName, ref, anchorKey, prevSpill) {
4919
+ if (__privateGet(this, _formulas).has(cellKey2) && cellKey2 !== anchorKey) return true;
4920
+ const owner = __privateGet(this, _spillOwner).get(cellKey2);
4430
4921
  if (owner && owner !== anchorKey) return true;
4431
- if (prevSpill.has(cellKey)) return false;
4922
+ if (prevSpill.has(cellKey2)) return false;
4432
4923
  const v = __privateGet(this, _model).getCellValue(sheetName, ref);
4433
4924
  return v !== null && v !== void 0 && v !== "";
4434
4925
  };
@@ -4436,10 +4927,10 @@ isOccupied_fn = function(cellKey, sheetName, ref, anchorKey, prevSpill) {
4436
4927
  clearSpill_fn = function(anchorKey, changed) {
4437
4928
  const spill = __privateGet(this, _spills).get(anchorKey);
4438
4929
  if (!spill) return;
4439
- for (const cellKey of spill) {
4440
- const cell = parseKey(cellKey);
4930
+ for (const cellKey2 of spill) {
4931
+ const cell = parseKey(cellKey2);
4441
4932
  __privateGet(this, _model).setCellValue(__privateGet(this, _sheetNames).get(cell.sheet) ?? cell.sheet, cell.ref, null);
4442
- __privateGet(this, _spillOwner).delete(cellKey);
4933
+ __privateGet(this, _spillOwner).delete(cellKey2);
4443
4934
  changed.push({ sheet: cell.sheet, row: cell.row, col: cell.col });
4444
4935
  }
4445
4936
  __privateGet(this, _spills).delete(anchorKey);