@devmm/puredocs-excel-formula 1.0.1 → 1.0.2
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 +336 -30
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +336 -30
- package/dist/index.js.map +1 -1
- package/package.json +45 -45
- package/LICENSE +0 -21
package/dist/index.cjs
CHANGED
|
@@ -2358,6 +2358,7 @@ 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);
|
|
@@ -2434,6 +2435,104 @@ function hlookup(a, c) {
|
|
|
2434
2435
|
}
|
|
2435
2436
|
return best >= 0 ? tbl.get(rowIdx, best) : FormulaValue.errorNA;
|
|
2436
2437
|
}
|
|
2438
|
+
function wildcardRegex(pattern) {
|
|
2439
|
+
let out = "";
|
|
2440
|
+
for (let i = 0; i < pattern.length; i++) {
|
|
2441
|
+
const ch = pattern[i];
|
|
2442
|
+
if (ch === "~" && i + 1 < pattern.length) {
|
|
2443
|
+
const next = pattern[++i];
|
|
2444
|
+
out += next.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2445
|
+
} else if (ch === "*") out += ".*";
|
|
2446
|
+
else if (ch === "?") out += ".";
|
|
2447
|
+
else out += ch.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2448
|
+
}
|
|
2449
|
+
return new RegExp(`^${out}$`, "i");
|
|
2450
|
+
}
|
|
2451
|
+
function findMatch(n, get, lookupVal, matchMode, searchMode) {
|
|
2452
|
+
if ((searchMode === 2 || searchMode === -2) && matchMode !== 2) {
|
|
2453
|
+
const at = searchMode === 2 ? get : (j) => get(n - 1 - j);
|
|
2454
|
+
let lo = 0, hi = n - 1, lastLE = -1, firstGE = -1;
|
|
2455
|
+
while (lo <= hi) {
|
|
2456
|
+
const mid2 = lo + hi >>> 1;
|
|
2457
|
+
const cmp = FormulaValue.compare(at(mid2), lookupVal);
|
|
2458
|
+
if (cmp === 0) return searchMode === 2 ? mid2 : n - 1 - mid2;
|
|
2459
|
+
if (cmp < 0) {
|
|
2460
|
+
lastLE = mid2;
|
|
2461
|
+
lo = mid2 + 1;
|
|
2462
|
+
} else {
|
|
2463
|
+
firstGE = mid2;
|
|
2464
|
+
hi = mid2 - 1;
|
|
2465
|
+
}
|
|
2466
|
+
}
|
|
2467
|
+
const idx = matchMode === -1 ? lastLE : matchMode === 1 ? firstGE : -1;
|
|
2468
|
+
return idx < 0 ? -1 : searchMode === 2 ? idx : n - 1 - idx;
|
|
2469
|
+
}
|
|
2470
|
+
const reverse = searchMode === -1;
|
|
2471
|
+
const order = reverse ? Array.from({ length: n }, (_, k) => n - 1 - k) : Array.from({ length: n }, (_, k) => k);
|
|
2472
|
+
if (matchMode === 2) {
|
|
2473
|
+
const re = wildcardRegex(lookupVal.asText());
|
|
2474
|
+
for (const i of order) if (re.test(get(i).asText())) return i;
|
|
2475
|
+
return -1;
|
|
2476
|
+
}
|
|
2477
|
+
if (matchMode === 0) {
|
|
2478
|
+
for (const i of order) if (FormulaValue.areEqual(get(i), lookupVal)) return i;
|
|
2479
|
+
return -1;
|
|
2480
|
+
}
|
|
2481
|
+
let best = -1;
|
|
2482
|
+
let bestVal = null;
|
|
2483
|
+
for (const i of order) {
|
|
2484
|
+
const v = get(i);
|
|
2485
|
+
const cmp = FormulaValue.compare(v, lookupVal);
|
|
2486
|
+
if (cmp === 0) return i;
|
|
2487
|
+
if (matchMode === -1 && cmp < 0) {
|
|
2488
|
+
if (bestVal === null || FormulaValue.compare(v, bestVal) > 0) {
|
|
2489
|
+
best = i;
|
|
2490
|
+
bestVal = v;
|
|
2491
|
+
}
|
|
2492
|
+
} else if (matchMode === 1 && cmp > 0) {
|
|
2493
|
+
if (bestVal === null || FormulaValue.compare(v, bestVal) < 0) {
|
|
2494
|
+
best = i;
|
|
2495
|
+
bestVal = v;
|
|
2496
|
+
}
|
|
2497
|
+
}
|
|
2498
|
+
}
|
|
2499
|
+
return best;
|
|
2500
|
+
}
|
|
2501
|
+
function xlookup(a, c) {
|
|
2502
|
+
const lookupVal = a[0].evaluate(c);
|
|
2503
|
+
if (lookupVal.isError) return lookupVal;
|
|
2504
|
+
const lookupRaw = a[1].evaluate(c);
|
|
2505
|
+
if (lookupRaw.isError) return lookupRaw;
|
|
2506
|
+
const returnRaw = a[2].evaluate(c);
|
|
2507
|
+
if (returnRaw.isError) return returnRaw;
|
|
2508
|
+
const matchMode = a.length > 4 ? num2(a, c, 4) : { ok: true, n: 0 };
|
|
2509
|
+
if (!matchMode.ok) return matchMode.e;
|
|
2510
|
+
const searchMode = a.length > 5 ? num2(a, c, 5) : { ok: true, n: 1 };
|
|
2511
|
+
if (!searchMode.ok) return searchMode.e;
|
|
2512
|
+
const mm = Math.trunc(matchMode.n), sm = Math.trunc(searchMode.n);
|
|
2513
|
+
const la = lookupRaw.isArray ? lookupRaw.arrayVal : ArrayValue.fromScalar(lookupRaw);
|
|
2514
|
+
if (la.rows > 1 && la.columns > 1) return FormulaValue.errorValue;
|
|
2515
|
+
const vertical = !(la.rows === 1 && la.columns > 1);
|
|
2516
|
+
const n = vertical ? la.rows : la.columns;
|
|
2517
|
+
const get = vertical ? (i) => la.get(i, 0) : (i) => la.get(0, i);
|
|
2518
|
+
const idx = findMatch(n, get, lookupVal, mm, sm);
|
|
2519
|
+
if (idx < 0) {
|
|
2520
|
+
return a.length > 3 ? a[3].evaluate(c) : FormulaValue.errorNA;
|
|
2521
|
+
}
|
|
2522
|
+
const ret = returnRaw.isArray ? returnRaw.arrayVal : ArrayValue.fromScalar(returnRaw);
|
|
2523
|
+
if (vertical) {
|
|
2524
|
+
if (ret.rows !== n) return FormulaValue.errorValue;
|
|
2525
|
+
if (ret.columns === 1) return ret.get(idx, 0);
|
|
2526
|
+
const out2 = new ArrayValue(1, ret.columns);
|
|
2527
|
+
for (let col = 0; col < ret.columns; col++) out2.set(0, col, ret.get(idx, col));
|
|
2528
|
+
return FormulaValue.array(out2);
|
|
2529
|
+
}
|
|
2530
|
+
if (ret.columns !== n) return FormulaValue.errorValue;
|
|
2531
|
+
if (ret.rows === 1) return ret.get(0, idx);
|
|
2532
|
+
const out = new ArrayValue(ret.rows, 1);
|
|
2533
|
+
for (let r = 0; r < ret.rows; r++) out.set(r, 0, ret.get(r, idx));
|
|
2534
|
+
return FormulaValue.array(out);
|
|
2535
|
+
}
|
|
2437
2536
|
function index_(a, c) {
|
|
2438
2537
|
const arrVal = a[0].evaluate(c);
|
|
2439
2538
|
if (arrVal.isError) return arrVal;
|
|
@@ -2967,6 +3066,10 @@ function registerFinance(r) {
|
|
|
2967
3066
|
r.register("PV", pv, 3, 5);
|
|
2968
3067
|
r.register("NPV", npv, 2);
|
|
2969
3068
|
r.register("IRR", irr, 1, 2);
|
|
3069
|
+
r.register("RATE", rate, 3, 6);
|
|
3070
|
+
r.register("NPER", nper, 3, 5);
|
|
3071
|
+
r.register("XNPV", xnpv, 3, 3);
|
|
3072
|
+
r.register("XIRR", xirr, 2, 3);
|
|
2970
3073
|
}
|
|
2971
3074
|
function arg(a, c, idx, dflt) {
|
|
2972
3075
|
if (idx >= a.length) return { ok: true, n: dflt };
|
|
@@ -2974,17 +3077,17 @@ function arg(a, c, idx, dflt) {
|
|
|
2974
3077
|
return r.ok ? { ok: true, n: r.value } : { ok: false, e: r.error };
|
|
2975
3078
|
}
|
|
2976
3079
|
function pmt(a, c) {
|
|
2977
|
-
const
|
|
2978
|
-
if (!
|
|
2979
|
-
const
|
|
2980
|
-
if (!
|
|
3080
|
+
const rate2 = arg(a, c, 0, 0);
|
|
3081
|
+
if (!rate2.ok) return rate2.e;
|
|
3082
|
+
const nper2 = arg(a, c, 1, 0);
|
|
3083
|
+
if (!nper2.ok) return nper2.e;
|
|
2981
3084
|
const pvv = arg(a, c, 2, 0);
|
|
2982
3085
|
if (!pvv.ok) return pvv.e;
|
|
2983
3086
|
const fvv = arg(a, c, 3, 0);
|
|
2984
3087
|
if (!fvv.ok) return fvv.e;
|
|
2985
3088
|
const type = arg(a, c, 4, 0);
|
|
2986
3089
|
if (!type.ok) return type.e;
|
|
2987
|
-
const r =
|
|
3090
|
+
const r = rate2.n, n = nper2.n, present = pvv.n, future = fvv.n, t = type.n;
|
|
2988
3091
|
if (n === 0) return FormulaValue.errorNum;
|
|
2989
3092
|
if (r === 0) return FormulaValue.number(-(present + future) / n);
|
|
2990
3093
|
const pow = Math.pow(1 + r, n);
|
|
@@ -2993,46 +3096,46 @@ function pmt(a, c) {
|
|
|
2993
3096
|
return FormulaValue.number(payment);
|
|
2994
3097
|
}
|
|
2995
3098
|
function fv(a, c) {
|
|
2996
|
-
const
|
|
2997
|
-
if (!
|
|
2998
|
-
const
|
|
2999
|
-
if (!
|
|
3099
|
+
const rate2 = arg(a, c, 0, 0);
|
|
3100
|
+
if (!rate2.ok) return rate2.e;
|
|
3101
|
+
const nper2 = arg(a, c, 1, 0);
|
|
3102
|
+
if (!nper2.ok) return nper2.e;
|
|
3000
3103
|
const pmtv = arg(a, c, 2, 0);
|
|
3001
3104
|
if (!pmtv.ok) return pmtv.e;
|
|
3002
3105
|
const pvv = arg(a, c, 3, 0);
|
|
3003
3106
|
if (!pvv.ok) return pvv.e;
|
|
3004
3107
|
const type = arg(a, c, 4, 0);
|
|
3005
3108
|
if (!type.ok) return type.e;
|
|
3006
|
-
const r =
|
|
3109
|
+
const r = rate2.n, n = nper2.n, payment = pmtv.n, present = pvv.n, t = type.n;
|
|
3007
3110
|
if (r === 0) return FormulaValue.number(-(present + payment * n));
|
|
3008
3111
|
const pow = Math.pow(1 + r, n);
|
|
3009
3112
|
return FormulaValue.number(-(present * pow + payment * (1 + r * t) * (pow - 1) / r));
|
|
3010
3113
|
}
|
|
3011
3114
|
function pv(a, c) {
|
|
3012
|
-
const
|
|
3013
|
-
if (!
|
|
3014
|
-
const
|
|
3015
|
-
if (!
|
|
3115
|
+
const rate2 = arg(a, c, 0, 0);
|
|
3116
|
+
if (!rate2.ok) return rate2.e;
|
|
3117
|
+
const nper2 = arg(a, c, 1, 0);
|
|
3118
|
+
if (!nper2.ok) return nper2.e;
|
|
3016
3119
|
const pmtv = arg(a, c, 2, 0);
|
|
3017
3120
|
if (!pmtv.ok) return pmtv.e;
|
|
3018
3121
|
const fvv = arg(a, c, 3, 0);
|
|
3019
3122
|
if (!fvv.ok) return fvv.e;
|
|
3020
3123
|
const type = arg(a, c, 4, 0);
|
|
3021
3124
|
if (!type.ok) return type.e;
|
|
3022
|
-
const r =
|
|
3125
|
+
const r = rate2.n, n = nper2.n, payment = pmtv.n, future = fvv.n, t = type.n;
|
|
3023
3126
|
if (r === 0) return FormulaValue.number(-(future + payment * n));
|
|
3024
3127
|
const pow = Math.pow(1 + r, n);
|
|
3025
3128
|
return FormulaValue.number(-(future + payment * (1 + r * t) * (pow - 1) / r) / pow);
|
|
3026
3129
|
}
|
|
3027
3130
|
function npv(a, c) {
|
|
3028
|
-
const
|
|
3029
|
-
if (!
|
|
3030
|
-
if (
|
|
3131
|
+
const rate2 = exports.FormulaHelper.evalDouble(a[0], c);
|
|
3132
|
+
if (!rate2.ok) return rate2.error;
|
|
3133
|
+
if (rate2.value === -1) return FormulaValue.errorDiv0;
|
|
3031
3134
|
const flows = exports.FormulaHelper.collectNumbers(a.slice(1), c);
|
|
3032
3135
|
if (!flows.ok) return flows.error;
|
|
3033
3136
|
let total = 0;
|
|
3034
3137
|
for (let i = 0; i < flows.values.length; i++) {
|
|
3035
|
-
total += flows.values[i] / Math.pow(1 +
|
|
3138
|
+
total += flows.values[i] / Math.pow(1 + rate2.value, i + 1);
|
|
3036
3139
|
}
|
|
3037
3140
|
return FormulaValue.number(total);
|
|
3038
3141
|
}
|
|
@@ -3047,27 +3150,177 @@ function irr(a, c) {
|
|
|
3047
3150
|
if (!g.ok) return g.error;
|
|
3048
3151
|
guess = g.value;
|
|
3049
3152
|
}
|
|
3050
|
-
const npvAt = (
|
|
3153
|
+
const npvAt = (rate3) => {
|
|
3051
3154
|
let sum2 = 0;
|
|
3052
|
-
for (let i = 0; i < flows.length; i++) sum2 += flows[i] / Math.pow(1 +
|
|
3155
|
+
for (let i = 0; i < flows.length; i++) sum2 += flows[i] / Math.pow(1 + rate3, i);
|
|
3053
3156
|
return sum2;
|
|
3054
3157
|
};
|
|
3055
|
-
let
|
|
3158
|
+
let rate2 = guess;
|
|
3159
|
+
for (let iter = 0; iter < 100; iter++) {
|
|
3160
|
+
const f = npvAt(rate2);
|
|
3161
|
+
if (Math.abs(f) < 1e-7) return FormulaValue.number(rate2);
|
|
3162
|
+
const h = 1e-6;
|
|
3163
|
+
const deriv = (npvAt(rate2 + h) - f) / h;
|
|
3164
|
+
if (deriv === 0) break;
|
|
3165
|
+
const next = rate2 - f / deriv;
|
|
3166
|
+
if (!isFinite(next) || next <= -1) break;
|
|
3167
|
+
if (Math.abs(next - rate2) < 1e-9) {
|
|
3168
|
+
rate2 = next;
|
|
3169
|
+
break;
|
|
3170
|
+
}
|
|
3171
|
+
rate2 = next;
|
|
3172
|
+
}
|
|
3173
|
+
if (Math.abs(npvAt(rate2)) < 1e-6) return FormulaValue.number(rate2);
|
|
3174
|
+
return FormulaValue.errorNum;
|
|
3175
|
+
}
|
|
3176
|
+
function tvm(r, n, pmt2, pv2, fv2, type) {
|
|
3177
|
+
if (r === 0) return pv2 + pmt2 * n + fv2;
|
|
3178
|
+
const pow = Math.pow(1 + r, n);
|
|
3179
|
+
return pv2 * pow + pmt2 * (1 + r * type) * (pow - 1) / r + fv2;
|
|
3180
|
+
}
|
|
3181
|
+
function rate(a, c) {
|
|
3182
|
+
const nper2 = arg(a, c, 0, 0);
|
|
3183
|
+
if (!nper2.ok) return nper2.e;
|
|
3184
|
+
const pmtv = arg(a, c, 1, 0);
|
|
3185
|
+
if (!pmtv.ok) return pmtv.e;
|
|
3186
|
+
const pvv = arg(a, c, 2, 0);
|
|
3187
|
+
if (!pvv.ok) return pvv.e;
|
|
3188
|
+
const fvv = arg(a, c, 3, 0);
|
|
3189
|
+
if (!fvv.ok) return fvv.e;
|
|
3190
|
+
const type = arg(a, c, 4, 0);
|
|
3191
|
+
if (!type.ok) return type.e;
|
|
3192
|
+
const gs = arg(a, c, 5, 0.1);
|
|
3193
|
+
if (!gs.ok) return gs.e;
|
|
3194
|
+
const n = nper2.n, pmt2 = pmtv.n, pv2 = pvv.n, fv2 = fvv.n, t = type.n;
|
|
3195
|
+
if (n === 0) return FormulaValue.errorNum;
|
|
3196
|
+
let r = gs.n;
|
|
3056
3197
|
for (let iter = 0; iter < 100; iter++) {
|
|
3057
|
-
const f =
|
|
3058
|
-
if (Math.abs(f) < 1e-
|
|
3198
|
+
const f = tvm(r, n, pmt2, pv2, fv2, t);
|
|
3199
|
+
if (Math.abs(f) < 1e-8) return FormulaValue.number(r);
|
|
3059
3200
|
const h = 1e-6;
|
|
3060
|
-
const deriv = (
|
|
3201
|
+
const deriv = (tvm(r + h, n, pmt2, pv2, fv2, t) - f) / h;
|
|
3061
3202
|
if (deriv === 0) break;
|
|
3062
|
-
const next =
|
|
3203
|
+
const next = r - f / deriv;
|
|
3063
3204
|
if (!isFinite(next) || next <= -1) break;
|
|
3064
|
-
if (Math.abs(next -
|
|
3065
|
-
|
|
3205
|
+
if (Math.abs(next - r) < 1e-10) {
|
|
3206
|
+
r = next;
|
|
3066
3207
|
break;
|
|
3067
3208
|
}
|
|
3068
|
-
|
|
3209
|
+
r = next;
|
|
3069
3210
|
}
|
|
3070
|
-
if (Math.abs(
|
|
3211
|
+
if (Math.abs(tvm(r, n, pmt2, pv2, fv2, t)) < 1e-6) return FormulaValue.number(r);
|
|
3212
|
+
return FormulaValue.errorNum;
|
|
3213
|
+
}
|
|
3214
|
+
function nper(a, c) {
|
|
3215
|
+
const rate2 = arg(a, c, 0, 0);
|
|
3216
|
+
if (!rate2.ok) return rate2.e;
|
|
3217
|
+
const pmtv = arg(a, c, 1, 0);
|
|
3218
|
+
if (!pmtv.ok) return pmtv.e;
|
|
3219
|
+
const pvv = arg(a, c, 2, 0);
|
|
3220
|
+
if (!pvv.ok) return pvv.e;
|
|
3221
|
+
const fvv = arg(a, c, 3, 0);
|
|
3222
|
+
if (!fvv.ok) return fvv.e;
|
|
3223
|
+
const type = arg(a, c, 4, 0);
|
|
3224
|
+
if (!type.ok) return type.e;
|
|
3225
|
+
const r = rate2.n, pmt2 = pmtv.n, pv2 = pvv.n, fv2 = fvv.n, t = type.n;
|
|
3226
|
+
if (r === 0) {
|
|
3227
|
+
if (pmt2 === 0) return FormulaValue.errorNum;
|
|
3228
|
+
return FormulaValue.number(-(pv2 + fv2) / pmt2);
|
|
3229
|
+
}
|
|
3230
|
+
const adj = pmt2 * (1 + r * t);
|
|
3231
|
+
const num5 = adj - fv2 * r;
|
|
3232
|
+
const den = adj + pv2 * r;
|
|
3233
|
+
if (den === 0 || num5 / den <= 0) return FormulaValue.errorNum;
|
|
3234
|
+
const result = Math.log(num5 / den) / Math.log(1 + r);
|
|
3235
|
+
if (!isFinite(result)) return FormulaValue.errorNum;
|
|
3236
|
+
return FormulaValue.number(result);
|
|
3237
|
+
}
|
|
3238
|
+
function flatNumbers(node, c) {
|
|
3239
|
+
const v = node.evaluate(c);
|
|
3240
|
+
if (v.isError) return { ok: false, e: v };
|
|
3241
|
+
const out = [];
|
|
3242
|
+
const push = (item) => {
|
|
3243
|
+
if (item.isError) return item;
|
|
3244
|
+
const d = item.tryAsDouble();
|
|
3245
|
+
if (!d.ok) return FormulaValue.errorValue;
|
|
3246
|
+
out.push(d.value);
|
|
3247
|
+
return null;
|
|
3248
|
+
};
|
|
3249
|
+
if (v.isArray) {
|
|
3250
|
+
for (const item of v.arrayVal.values()) {
|
|
3251
|
+
const err = push(item);
|
|
3252
|
+
if (err) return { ok: false, e: err };
|
|
3253
|
+
}
|
|
3254
|
+
} else {
|
|
3255
|
+
const err = push(v);
|
|
3256
|
+
if (err) return { ok: false, e: err };
|
|
3257
|
+
}
|
|
3258
|
+
return { ok: true, values: out };
|
|
3259
|
+
}
|
|
3260
|
+
function xnpvAt(rate2, values, days2) {
|
|
3261
|
+
let sum2 = 0;
|
|
3262
|
+
for (let i = 0; i < values.length; i++) {
|
|
3263
|
+
sum2 += values[i] / Math.pow(1 + rate2, days2[i] / 365);
|
|
3264
|
+
}
|
|
3265
|
+
return sum2;
|
|
3266
|
+
}
|
|
3267
|
+
function xnpv(a, c) {
|
|
3268
|
+
const rateRes = exports.FormulaHelper.evalDouble(a[0], c);
|
|
3269
|
+
if (!rateRes.ok) return rateRes.error;
|
|
3270
|
+
const rate2 = rateRes.value;
|
|
3271
|
+
if (rate2 <= -1) return FormulaValue.errorNum;
|
|
3272
|
+
const valsRes = flatNumbers(a[1], c);
|
|
3273
|
+
if (!valsRes.ok) return valsRes.e;
|
|
3274
|
+
const datesRes = flatNumbers(a[2], c);
|
|
3275
|
+
if (!datesRes.ok) return datesRes.e;
|
|
3276
|
+
const values = valsRes.values;
|
|
3277
|
+
const dates = datesRes.values;
|
|
3278
|
+
if (values.length === 0 || values.length !== dates.length) return FormulaValue.errorNum;
|
|
3279
|
+
const base = Math.trunc(dates[0]);
|
|
3280
|
+
const days2 = dates.map((d) => Math.trunc(d) - base);
|
|
3281
|
+
if (days2.some((d) => d < 0)) return FormulaValue.errorNum;
|
|
3282
|
+
return FormulaValue.number(xnpvAt(rate2, values, days2));
|
|
3283
|
+
}
|
|
3284
|
+
function xirr(a, c) {
|
|
3285
|
+
const valsRes = flatNumbers(a[0], c);
|
|
3286
|
+
if (!valsRes.ok) return valsRes.e;
|
|
3287
|
+
const datesRes = flatNumbers(a[1], c);
|
|
3288
|
+
if (!datesRes.ok) return datesRes.e;
|
|
3289
|
+
const values = valsRes.values;
|
|
3290
|
+
const dates = datesRes.values;
|
|
3291
|
+
if (values.length < 2 || values.length !== dates.length) return FormulaValue.errorNum;
|
|
3292
|
+
let guess = 0.1;
|
|
3293
|
+
if (a.length > 2) {
|
|
3294
|
+
const g = exports.FormulaHelper.evalDouble(a[2], c);
|
|
3295
|
+
if (!g.ok) return g.error;
|
|
3296
|
+
guess = g.value;
|
|
3297
|
+
}
|
|
3298
|
+
const base = Math.trunc(dates[0]);
|
|
3299
|
+
const days2 = dates.map((d) => Math.trunc(d) - base);
|
|
3300
|
+
if (days2.some((d) => d < 0)) return FormulaValue.errorNum;
|
|
3301
|
+
const derivAt = (rate3) => {
|
|
3302
|
+
let sum2 = 0;
|
|
3303
|
+
for (let i = 0; i < values.length; i++) {
|
|
3304
|
+
const e = days2[i] / 365;
|
|
3305
|
+
sum2 += -e * values[i] / Math.pow(1 + rate3, e + 1);
|
|
3306
|
+
}
|
|
3307
|
+
return sum2;
|
|
3308
|
+
};
|
|
3309
|
+
let rate2 = guess;
|
|
3310
|
+
for (let iter = 0; iter < 100; iter++) {
|
|
3311
|
+
const f = xnpvAt(rate2, values, days2);
|
|
3312
|
+
if (Math.abs(f) < 1e-7) return FormulaValue.number(rate2);
|
|
3313
|
+
const deriv = derivAt(rate2);
|
|
3314
|
+
if (deriv === 0) break;
|
|
3315
|
+
const next = rate2 - f / deriv;
|
|
3316
|
+
if (!isFinite(next) || next <= -1) break;
|
|
3317
|
+
if (Math.abs(next - rate2) < 1e-9) {
|
|
3318
|
+
rate2 = next;
|
|
3319
|
+
break;
|
|
3320
|
+
}
|
|
3321
|
+
rate2 = next;
|
|
3322
|
+
}
|
|
3323
|
+
if (Math.abs(xnpvAt(rate2, values, days2)) < 1e-6) return FormulaValue.number(rate2);
|
|
3071
3324
|
return FormulaValue.errorNum;
|
|
3072
3325
|
}
|
|
3073
3326
|
|
|
@@ -3405,6 +3658,7 @@ function registerArray(r) {
|
|
|
3405
3658
|
r.register("SEQUENCE", sequence, 1, 4);
|
|
3406
3659
|
r.register("UNIQUE", unique, 1, 3);
|
|
3407
3660
|
r.register("SORT", sort, 1, 4);
|
|
3661
|
+
r.register("SORTBY", sortBy, 2);
|
|
3408
3662
|
r.register("FILTER", filter, 2, 3);
|
|
3409
3663
|
r.register("MMULT", mmult, 2, 2);
|
|
3410
3664
|
r.register("TEXTSPLIT", textSplit, 2, 6);
|
|
@@ -3499,6 +3753,58 @@ function sort(a, c) {
|
|
|
3499
3753
|
const out = fromRows(sorted);
|
|
3500
3754
|
return byCol2 ? FormulaValue.array(out.arrayVal.transpose()) : out;
|
|
3501
3755
|
}
|
|
3756
|
+
function sortBy(a, c) {
|
|
3757
|
+
const v = a[0].evaluate(c);
|
|
3758
|
+
if (v.isError) return v;
|
|
3759
|
+
const data = asArray2(v);
|
|
3760
|
+
const keys = [];
|
|
3761
|
+
let i = 1;
|
|
3762
|
+
while (i < a.length) {
|
|
3763
|
+
const byVal = a[i].evaluate(c);
|
|
3764
|
+
if (byVal.isError) return byVal;
|
|
3765
|
+
i++;
|
|
3766
|
+
let dir = 1;
|
|
3767
|
+
if (i < a.length) {
|
|
3768
|
+
const maybeOrder = a[i].evaluate(c);
|
|
3769
|
+
if (maybeOrder.isError) return maybeOrder;
|
|
3770
|
+
if (!maybeOrder.isArray && maybeOrder.isNumeric) {
|
|
3771
|
+
const d = maybeOrder.tryAsDouble();
|
|
3772
|
+
if (d.ok) {
|
|
3773
|
+
dir = d.value < 0 ? -1 : 1;
|
|
3774
|
+
i++;
|
|
3775
|
+
}
|
|
3776
|
+
}
|
|
3777
|
+
}
|
|
3778
|
+
keys.push({ vals: [...asArray2(byVal).values()], dir });
|
|
3779
|
+
}
|
|
3780
|
+
if (keys.length === 0) return FormulaValue.errorValue;
|
|
3781
|
+
const n = keys[0].vals.length;
|
|
3782
|
+
if (keys.some((k) => k.vals.length !== n)) return FormulaValue.errorValue;
|
|
3783
|
+
const byRow2 = n === data.rows;
|
|
3784
|
+
const byColumn = !byRow2 && n === data.columns;
|
|
3785
|
+
if (!byRow2 && !byColumn) return FormulaValue.errorValue;
|
|
3786
|
+
const perm = Array.from({ length: n }, (_, k) => k);
|
|
3787
|
+
perm.sort((x, y) => {
|
|
3788
|
+
for (const key of keys) {
|
|
3789
|
+
const cmp = FormulaValue.compare(key.vals[x], key.vals[y]) * key.dir;
|
|
3790
|
+
if (cmp !== 0) return cmp;
|
|
3791
|
+
}
|
|
3792
|
+
return x - y;
|
|
3793
|
+
});
|
|
3794
|
+
const out = new ArrayValue(data.rows, data.columns);
|
|
3795
|
+
if (byRow2) {
|
|
3796
|
+
for (let r = 0; r < data.rows; r++) {
|
|
3797
|
+
const src = perm[r];
|
|
3798
|
+
for (let col = 0; col < data.columns; col++) out.set(r, col, data.get(src, col));
|
|
3799
|
+
}
|
|
3800
|
+
} else {
|
|
3801
|
+
for (let col = 0; col < data.columns; col++) {
|
|
3802
|
+
const src = perm[col];
|
|
3803
|
+
for (let r = 0; r < data.rows; r++) out.set(r, col, data.get(r, src));
|
|
3804
|
+
}
|
|
3805
|
+
}
|
|
3806
|
+
return FormulaValue.array(out);
|
|
3807
|
+
}
|
|
3502
3808
|
function filter(a, c) {
|
|
3503
3809
|
const v = a[0].evaluate(c);
|
|
3504
3810
|
if (v.isError) return v;
|