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