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