commit
message
Refactring some functions/formulas into Math.ts
author
Ben Vogt <[email protected]>
date
2017-02-19 20:44:22
stats
2 file(s) changed,
361 insertions(+),
456 deletions(-)
files
src/RawFormulas/Math.ts
src/RawFormulas/RawFormulas.ts
1diff --git a/src/RawFormulas/Math.ts b/src/RawFormulas/Math.ts
2index 5118f85..55ef2d8 100644
3--- a/src/RawFormulas/Math.ts
4+++ b/src/RawFormulas/Math.ts
5@@ -2,7 +2,8 @@ import {
6 ArgsChecker,
7 CriteriaFunctionFactory,
8 Filter,
9- TypeCaster,
10+ Serializer,
11+ TypeCaster
12 } from "./Utils";
13 import { CellError } from "../Errors";
14 import * as ERRORS from "../Errors";
15@@ -1308,6 +1309,344 @@ var FDIST$LEFTTAILED = function (...values) : number|undefined|boolean {
16 return (cumulative) ? cdf(x, d1, d2) : pdf(x, d1, d2);
17 };
18
19+/**
20+ * Returns the complementary Gauss error function of a value.
21+ * @param values[0] The number for which to calculate the complementary Gauss error function.
22+ * @returns {number} complementary Gauss error function of a value
23+ * @constructor
24+ */
25+var ERFC = function (...values) {
26+ ArgsChecker.checkLength(values, 1);
27+ var v = TypeCaster.firstValueAsNumber(values[0]);
28+ return v === 0 ? 1 : 1 - erf(v);
29+};
30+
31+
32+/**
33+ * Returns the error function integrated between lower_limit and upper_limit.
34+ * @param values[0] lower_limit - The lower bound for integrating ERF.
35+ * @param values[1] upper_limit - [Optional]. The upper bound for integrating ERF. If omitted, ERF integrates between
36+ * zero and lower_limit.
37+ * @returns {number} error function integrated between lower_limit and upper_limit
38+ * @constructor
39+ */
40+var ERF = function (...values) : number {
41+ ArgsChecker.checkLengthWithin(values, 1, 2);
42+ var lower = TypeCaster.firstValueAsNumber(values[0]);
43+ var upper = values.length === 2 ? TypeCaster.firstValueAsNumber(values[1]) : 0;
44+ return values.length === 1 ? erf(lower) : erf(upper) - erf(lower);
45+};
46+
47+// erf function from jStat [http://www.jstat.org/]
48+function erf(x) {
49+ var cof = [-1.3026537197817094, 6.4196979235649026e-1, 1.9476473204185836e-2,
50+ -9.561514786808631e-3, -9.46595344482036e-4, 3.66839497852761e-4,
51+ 4.2523324806907e-5, -2.0278578112534e-5, -1.624290004647e-6,
52+ 1.303655835580e-6, 1.5626441722e-8, -8.5238095915e-8,
53+ 6.529054439e-9, 5.059343495e-9, -9.91364156e-10,
54+ -2.27365122e-10, 9.6467911e-11, 2.394038e-12,
55+ -6.886027e-12, 8.94487e-13, 3.13092e-13,
56+ -1.12708e-13, 3.81e-16, 7.106e-15,
57+ -1.523e-15, -9.4e-17, 1.21e-16,
58+ -2.8e-17];
59+ var j = cof.length - 1;
60+ var isneg = false;
61+ var d = 0;
62+ var dd = 0;
63+ var t, ty, tmp, res;
64+
65+ if (x < 0) {
66+ x = -x;
67+ isneg = true;
68+ }
69+
70+ t = 2 / (2 + x);
71+ ty = 4 * t - 2;
72+
73+ for(; j > 0; j--) {
74+ tmp = d;
75+ d = ty * d - dd + cof[j];
76+ dd = tmp;
77+ }
78+
79+ res = t * Math.exp(-x * x + 0.5 * (cof[0] + ty * d) - dd);
80+ return isneg ? res - 1 : 1 - res;
81+}
82+
83+
84+
85+/**
86+ * Calculates the depreciation of an asset for a specified period using the double-declining balance method.
87+ * @param values[0] cost - The initial cost of the asset.
88+ * @param values[1] salvage - The value of the asset at the end of depreciation.
89+ * @param values[2] life - The number of periods over which the asset is depreciated.
90+ * @param values[3] period - The single period within life for which to calculate depreciation.
91+ * @param values[4] factor - [ OPTIONAL - 2 by default ] - The factor by which depreciation decreases.
92+ * @returns {number} depreciation of an asset for a specified period
93+ * @constructor
94+ */
95+var DDB = function (...values) : number {
96+ ArgsChecker.checkLengthWithin(values, 4, 5);
97+ var cost = TypeCaster.firstValueAsNumber(values[0]);
98+ var salvage = TypeCaster.firstValueAsNumber(values[1]);
99+ var life = TypeCaster.firstValueAsNumber(values[2]);
100+ var period = TypeCaster.firstValueAsNumber(values[3]);
101+ var factor = values.length === 5 ? TypeCaster.firstValueAsNumber(values[4]) : 2;
102+
103+ if (cost < 0) {
104+ throw new CellError(ERRORS.NUM_ERROR, "Function DDB parameter 1 value is "
105+ + cost + ". It should be greater than or equal to 0.");
106+ }
107+ if (salvage < 0) {
108+ throw new CellError(ERRORS.NUM_ERROR, "Function DDB parameter 2 value is "
109+ + salvage + ". It should be greater than or equal to 0.");
110+ }
111+ if (life < 0) {
112+ throw new CellError(ERRORS.NUM_ERROR, "Function DDB parameter 3 value is "
113+ + life + ". It should be greater than or equal to 0.");
114+ }
115+ if (period < 0) {
116+ throw new CellError(ERRORS.NUM_ERROR, "Function DDB parameter 4 value is "
117+ + period + ". It should be greater than or equal to 0.");
118+ }
119+ if (period > life) {
120+ throw new CellError(ERRORS.NUM_ERROR, "Function DDB parameter 4 value is "
121+ + life + ". It should be less than or equal to value of Function DB parameter 3 with "+ period +".");
122+ }
123+ if (salvage >= cost) {
124+ return 0;
125+ }
126+
127+ var total = 0;
128+ var current = 0;
129+ for (var i = 1; i <= period; i++) {
130+ current = Math.min((cost - total) * (factor / life), (cost - salvage - total));
131+ total += current;
132+ }
133+ return current;
134+};
135+
136+
137+/**
138+ * Calculates the depreciation of an asset for a specified period using the arithmetic declining balance method.
139+ * @param values[0] cost - The initial cost of the asset.
140+ * @param values[1] salvage - The value of the asset at the end of depreciation.
141+ * @param values[2] life - The number of periods over which the asset is depreciated.
142+ * @param values[3] period - The single period within life for which to calculate depreciation.
143+ * @param values[4] month - [ OPTIONAL - 12 by default ] - The number of months in the first year of depreciation.
144+ * @returns {number} depreciated value
145+ * @constructor
146+ */
147+var DB = function (...values) : number {
148+ ArgsChecker.checkLengthWithin(values, 4, 5);
149+ var cost = TypeCaster.firstValueAsNumber(values[0]);
150+ var salvage = TypeCaster.firstValueAsNumber(values[1]);
151+ var life = TypeCaster.firstValueAsNumber(values[2]);
152+ var period = TypeCaster.firstValueAsNumber(values[3]);
153+ var month = values.length === 5 ? Math.floor(TypeCaster.firstValueAsNumber(values[4])) : 12;
154+ if (cost < 0) {
155+ throw new CellError(ERRORS.NUM_ERROR, "Function DB parameter 1 value is "
156+ + cost + ". It should be greater than or equal to 0.");
157+ }
158+ if (salvage < 0) {
159+ throw new CellError(ERRORS.NUM_ERROR, "Function DB parameter 2 value is "
160+ + salvage + ". It should be greater than or equal to 0.");
161+ }
162+ if (life < 0) {
163+ throw new CellError(ERRORS.NUM_ERROR, "Function DB parameter 3 value is "
164+ + life + ". It should be greater than or equal to 0.");
165+ }
166+ if (period < 0) {
167+ throw new CellError(ERRORS.NUM_ERROR, "Function DB parameter 4 value is "
168+ + period + ". It should be greater than or equal to 0.");
169+ }
170+ if (month > 12 || month < 1) {
171+ throw new CellError(ERRORS.NUM_ERROR, "Function DB parameter 5 value is "
172+ + month + ". Valid values are between 1 and 12 inclusive.");
173+ }
174+ if (period > life) {
175+ throw new CellError(ERRORS.NUM_ERROR, "Function DB parameter 4 value is "
176+ + life + ". It should be less than or equal to value of Function DB parameter 3 with "+ period +".");
177+ }
178+ if (salvage >= cost) {
179+ return 0;
180+ }
181+ var rate = (1 - Math.pow(salvage / cost, 1 / life));
182+ var initial = cost * rate * month / 12;
183+ var total = initial;
184+ var current = 0;
185+ var ceiling = (period === life) ? life - 1 : period;
186+ for (var i = 2; i <= ceiling; i++) {
187+ current = (cost - total) * rate;
188+ total += current;
189+ }
190+ if (period === 1) {
191+ return initial;
192+ } else if (period === life) {
193+ return (cost - total) * rate;
194+ } else {
195+ return current;
196+ }
197+};
198+
199+
200+
201+/**
202+ * Calculates the sum of the sums of the squares of values in two arrays.
203+ * @param values[0] array_x - The array or range of values whose squares will be added to the squares of corresponding
204+ * entries in array_y and added together.
205+ * @param values[1] array_y - The array or range of values whose squares will be added to the squares of corresponding
206+ * entries in array_x and added together.
207+ * @returns {number} sum of the sums of the squares
208+ * @constructor
209+ */
210+var SUMX2PY2 = function (...values) : number {
211+ ArgsChecker.checkLength(values, 2);
212+ var arrOne = Filter.flattenAndThrow(values[0]);
213+ var arrTwo = Filter.flattenAndThrow(values[1]);
214+ if (arrOne.length !== arrTwo.length) {
215+ throw new CellError(ERRORS.NA_ERROR, "Array arguments to SUMX2PY2 are of different size.");
216+ }
217+ var result = 0;
218+ for (var i = 0; i < arrOne.length; i++) {
219+ // If either values at this index are anything but numbers, skip them. This is the behavior in GS at least.
220+ if (typeof arrOne[i] === "number" && typeof arrTwo[i] === "number") {
221+ result += arrOne[i] * arrOne[i] + arrTwo[i] * arrTwo[i];
222+ }
223+ }
224+ return result;
225+};
226+
227+/**
228+ * Calculates the sum of the differences of the squares of values in two arrays.
229+ * @param values[0] array_x - The array or range of values whose squares will be reduced by the squares of corresponding
230+ * entries in array_y and added together.
231+ * @param values[1] array_y - The array or range of values whose squares will be subtracted from the squares of
232+ * corresponding entries in array_x and added together.
233+ * @returns {number} sum of the differences of the squares
234+ * @constructor
235+ */
236+var SUMX2MY2 = function (...values) : number {
237+ ArgsChecker.checkLength(values, 2);
238+ var arrOne = Filter.flattenAndThrow(values[0]);
239+ var arrTwo = Filter.flattenAndThrow(values[1]);
240+ if (arrOne.length !== arrTwo.length) {
241+ throw new CellError(ERRORS.NA_ERROR, "Array arguments to SUMX2MY2 are of different size.");
242+ }
243+ var result = 0;
244+ for (var i = 0; i < arrOne.length; i++) {
245+ // If either values at this index are anything but numbers, skip them. This is the behavior in GS at least.
246+ if (typeof arrOne[i] === "number" && typeof arrTwo[i] === "number") {
247+ result += arrOne[i] * arrOne[i] - arrTwo[i] * arrTwo[i];
248+ }
249+ }
250+ return result;
251+};
252+
253+
254+/**
255+ * Counts the number of unique values in a list of specified values and ranges.
256+ * @param values The values or ranges to consider for uniqueness. Supports an arbitrary number of arguments for this
257+ * function.
258+ * @returns {number} of unique values passed in.
259+ * @constructor
260+ */
261+var COUNTUNIQUE = function (...values) : number {
262+ ArgsChecker.checkAtLeastLength(values, 1);
263+
264+ // Private function that will recursively generate an array of the unique primatives
265+ var countUniquePrivate = function (values: Array<any>) : Object {
266+ var uniques = {};
267+ for (var i = 0; i < values.length; i++) {
268+ if (Array.isArray(values[i])) {
269+ // For some reasons an empty range is converted to a range with a single empty string in it.
270+ if (values[i].length === 0) {
271+ values[i] = [""];
272+ }
273+ var uniquesOfArray = countUniquePrivate(values[i]);
274+ for (var key in uniquesOfArray) {
275+ uniques[key] = true;
276+ }
277+ } else {
278+ uniques[Serializer.serialize(values[i])] = true;
279+ }
280+ }
281+ return uniques;
282+ };
283+
284+ var uniques = countUniquePrivate(values);
285+ return Object.keys(uniques).length;
286+};
287+
288+
289+/**
290+ * Calculates the sum of the products of corresponding entries in two equal-sized arrays or ranges.
291+ * @param values Arrays or ranges whose entries will be multiplied with corresponding entries in the second such array
292+ * or range.
293+ * @returns {number} sum of the products
294+ * @constructor
295+ */
296+var SUMPRODUCT = function (...values) : number {
297+ ArgsChecker.checkAtLeastLength(values, 1);
298+ // Ensuring that all values are array values
299+ for (var x = 0; x < values.length; x++) {
300+ if (!Array.isArray(values[x])) {
301+ values[x] = [values[x]];
302+ }
303+ }
304+
305+ // Flatten any nested ranges (arrays) and check for mismatched range sizes
306+ var flattenedValues = [Filter.flattenAndThrow(values[0])];
307+ for (var x = 1; x < values.length; x++) {
308+ flattenedValues.push(Filter.flattenAndThrow(values[x]));
309+ if (flattenedValues[x].length !== flattenedValues[0].length) {
310+ throw new CellError(ERRORS.VALUE_ERROR, "SUMPRODUCT has mismatched range sizes. Expected count: "
311+ + flattenedValues[0].length + ". Actual count: " + flattenedValues[0].length + ".");
312+ }
313+ }
314+
315+ // Do the actual math
316+ var result = 0;
317+ for (var i = 0; i < flattenedValues[0].length; i++) {
318+ var product = 1;
319+ for (var x = 0; x < flattenedValues.length; x++) {
320+ product *= TypeCaster.valueToNumberGracefully(flattenedValues[x][i]);
321+ }
322+ result += product;
323+ }
324+ return result;
325+};
326+
327+
328+
329+/**
330+ * Returns the Fisher transformation of a specified value.
331+ * @param values[0] value - The value for which to calculate the Fisher transformation.
332+ * @returns {number} Fisher transformation
333+ * @constructor
334+ */
335+var FISHER = function (...values) : number {
336+ ArgsChecker.checkLength(values, 1);
337+ var x = TypeCaster.firstValueAsNumber(values[0]);
338+ if (x <= -1 || x >= 1) {
339+ throw new CellError(ERRORS.NUM_ERROR, "Function FISHER parameter 1 value is " + x + ". Valid values are between -1 and 1 exclusive.");
340+ }
341+ return Math.log((1 + x) / (1 - x)) / 2;
342+};
343+
344+/**
345+ * Returns the inverse Fisher transformation of a specified value.
346+ * @param values[0] value - The value for which to calculate the inverse Fisher transformation.
347+ * @returns {number} inverse Fisher transformation
348+ * @constructor
349+ */
350+var FISHERINV = function (...values) : number {
351+ ArgsChecker.checkLength(values, 1);
352+ var y = TypeCaster.firstValueAsNumber(values[0]);
353+ var e2y = Math.exp(2 * y);
354+ return (e2y - 1) / (e2y + 1);
355+};
356+
357
358 export {
359 ABS,
360@@ -1327,9 +1666,16 @@ export {
361 COTH,
362 COSH,
363 COS,
364+ COUNTUNIQUE,
365 DEVSQ,
366+ DB,
367+ DDB,
368 EVEN,
369+ ERF,
370+ ERFC,
371 FDIST$LEFTTAILED,
372+ FISHER,
373+ FISHERINV,
374 INT,
375 ISEVEN,
376 ISODD,
377@@ -1356,7 +1702,11 @@ export {
378 ROUND,
379 ROUNDDOWN,
380 ROUNDUP,
381+ SUMPRODUCT,
382 SUMIF,
383+ SUMSQ,
384+ SUMX2MY2,
385+ SUMX2PY2,
386 FLOOR,
387 IF,
388 DELTA,
389@@ -1365,7 +1715,6 @@ export {
390 COUNTIF,
391 COUNTIFS,
392 CEILING,
393- SUMSQ,
394 TRUNC,
395 RADIANS,
396 DEGREES
397diff --git a/src/RawFormulas/RawFormulas.ts b/src/RawFormulas/RawFormulas.ts
398index c98e1c2..28896e6 100644
399--- a/src/RawFormulas/RawFormulas.ts
400+++ b/src/RawFormulas/RawFormulas.ts
401@@ -19,9 +19,16 @@ import {
402 COTH,
403 COSH,
404 COS,
405+ COUNTUNIQUE,
406 DEVSQ,
407+ DB,
408+ DDB,
409 EVEN,
410+ ERF,
411+ ERFC,
412 FDIST$LEFTTAILED,
413+ FISHER,
414+ FISHERINV,
415 INT,
416 ISEVEN,
417 ISODD,
418@@ -48,7 +55,11 @@ import {
419 ROUND,
420 ROUNDDOWN,
421 ROUNDUP,
422+ SUMPRODUCT,
423 SUMIF,
424+ SUMSQ,
425+ SUMX2MY2,
426+ SUMX2PY2,
427 FLOOR,
428 IF,
429 DELTA,
430@@ -57,7 +68,6 @@ import {
431 COUNTIF,
432 COUNTIFS,
433 CEILING,
434- SUMSQ,
435 TRUNC,
436 RADIANS,
437 DEGREES
438@@ -92,7 +102,6 @@ import {
439 } from "./Utils";
440 import {CellError, NUM_ERROR} from "../Errors"
441 import * as ERRORS from "../Errors"
442-import {Cell} from "../Cell";
443
444 var ACCRINT = Formula["ACCRINT"];
445 var COMBIN = Formula["COMBIN"];
446@@ -128,347 +137,6 @@ var __COMPLEX = {
447 var YEARFRAC = Formula["YEARFRAC"];
448
449
450-/**
451- * Returns the complementary Gauss error function of a value.
452- * @param values[0] The number for which to calculate the complementary Gauss error function.
453- * @returns {number} complementary Gauss error function of a value
454- * @constructor
455- */
456-var ERFC = function (...values) {
457- ArgsChecker.checkLength(values, 1);
458- var v = TypeCaster.firstValueAsNumber(values[0]);
459- return v === 0 ? 1 : 1 - erf(v);
460-};
461-
462-
463-
464-/**
465- * Returns the error function integrated between lower_limit and upper_limit.
466- * @param values[0] lower_limit - The lower bound for integrating ERF.
467- * @param values[1] upper_limit - [Optional]. The upper bound for integrating ERF. If omitted, ERF integrates between
468- * zero and lower_limit.
469- * @returns {number} error function integrated between lower_limit and upper_limit
470- * @constructor
471- */
472-var ERF = function (...values) : number {
473- ArgsChecker.checkLengthWithin(values, 1, 2);
474- var lower = TypeCaster.firstValueAsNumber(values[0]);
475- var upper = values.length === 2 ? TypeCaster.firstValueAsNumber(values[1]) : 0;
476- return values.length === 1 ? erf(lower) : erf(upper) - erf(lower);
477-};
478-
479-// erf function from jStat [http://www.jstat.org/]
480-function erf(x) {
481- var cof = [-1.3026537197817094, 6.4196979235649026e-1, 1.9476473204185836e-2,
482- -9.561514786808631e-3, -9.46595344482036e-4, 3.66839497852761e-4,
483- 4.2523324806907e-5, -2.0278578112534e-5, -1.624290004647e-6,
484- 1.303655835580e-6, 1.5626441722e-8, -8.5238095915e-8,
485- 6.529054439e-9, 5.059343495e-9, -9.91364156e-10,
486- -2.27365122e-10, 9.6467911e-11, 2.394038e-12,
487- -6.886027e-12, 8.94487e-13, 3.13092e-13,
488- -1.12708e-13, 3.81e-16, 7.106e-15,
489- -1.523e-15, -9.4e-17, 1.21e-16,
490- -2.8e-17];
491- var j = cof.length - 1;
492- var isneg = false;
493- var d = 0;
494- var dd = 0;
495- var t, ty, tmp, res;
496-
497- if (x < 0) {
498- x = -x;
499- isneg = true;
500- }
501-
502- t = 2 / (2 + x);
503- ty = 4 * t - 2;
504-
505- for(; j > 0; j--) {
506- tmp = d;
507- d = ty * d - dd + cof[j];
508- dd = tmp;
509- }
510-
511- res = t * Math.exp(-x * x + 0.5 * (cof[0] + ty * d) - dd);
512- return isneg ? res - 1 : 1 - res;
513-}
514-
515-
516-
517-/**
518- * Calculates the depreciation of an asset for a specified period using the double-declining balance method.
519- * @param values[0] cost - The initial cost of the asset.
520- * @param values[1] salvage - The value of the asset at the end of depreciation.
521- * @param values[2] life - The number of periods over which the asset is depreciated.
522- * @param values[3] period - The single period within life for which to calculate depreciation.
523- * @param values[4] factor - [ OPTIONAL - 2 by default ] - The factor by which depreciation decreases.
524- * @returns {number} depreciation of an asset for a specified period
525- * @constructor
526- */
527-var DDB = function (...values) : number {
528- ArgsChecker.checkLengthWithin(values, 4, 5);
529- var cost = TypeCaster.firstValueAsNumber(values[0]);
530- var salvage = TypeCaster.firstValueAsNumber(values[1]);
531- var life = TypeCaster.firstValueAsNumber(values[2]);
532- var period = TypeCaster.firstValueAsNumber(values[3]);
533- var factor = values.length === 5 ? TypeCaster.firstValueAsNumber(values[4]) : 2;
534-
535- if (cost < 0) {
536- throw new CellError(ERRORS.NUM_ERROR, "Function DDB parameter 1 value is "
537- + cost + ". It should be greater than or equal to 0.");
538- }
539- if (salvage < 0) {
540- throw new CellError(ERRORS.NUM_ERROR, "Function DDB parameter 2 value is "
541- + salvage + ". It should be greater than or equal to 0.");
542- }
543- if (life < 0) {
544- throw new CellError(ERRORS.NUM_ERROR, "Function DDB parameter 3 value is "
545- + life + ". It should be greater than or equal to 0.");
546- }
547- if (period < 0) {
548- throw new CellError(ERRORS.NUM_ERROR, "Function DDB parameter 4 value is "
549- + period + ". It should be greater than or equal to 0.");
550- }
551- if (period > life) {
552- throw new CellError(ERRORS.NUM_ERROR, "Function DDB parameter 4 value is "
553- + life + ". It should be less than or equal to value of Function DB parameter 3 with "+ period +".");
554- }
555- if (salvage >= cost) {
556- return 0;
557- }
558-
559- var total = 0;
560- var current = 0;
561- for (var i = 1; i <= period; i++) {
562- current = Math.min((cost - total) * (factor / life), (cost - salvage - total));
563- total += current;
564- }
565- return current;
566-};
567-
568-
569-/**
570- * Calculates the depreciation of an asset for a specified period using the arithmetic declining balance method.
571- * @param values[0] cost - The initial cost of the asset.
572- * @param values[1] salvage - The value of the asset at the end of depreciation.
573- * @param values[2] life - The number of periods over which the asset is depreciated.
574- * @param values[3] period - The single period within life for which to calculate depreciation.
575- * @param values[4] month - [ OPTIONAL - 12 by default ] - The number of months in the first year of depreciation.
576- * @returns {number} depreciated value
577- * @constructor
578- */
579-var DB = function (...values) : number {
580- ArgsChecker.checkLengthWithin(values, 4, 5);
581- var cost = TypeCaster.firstValueAsNumber(values[0]);
582- var salvage = TypeCaster.firstValueAsNumber(values[1]);
583- var life = TypeCaster.firstValueAsNumber(values[2]);
584- var period = TypeCaster.firstValueAsNumber(values[3]);
585- var month = values.length === 5 ? Math.floor(TypeCaster.firstValueAsNumber(values[4])) : 12;
586- if (cost < 0) {
587- throw new CellError(ERRORS.NUM_ERROR, "Function DB parameter 1 value is "
588- + cost + ". It should be greater than or equal to 0.");
589- }
590- if (salvage < 0) {
591- throw new CellError(ERRORS.NUM_ERROR, "Function DB parameter 2 value is "
592- + salvage + ". It should be greater than or equal to 0.");
593- }
594- if (life < 0) {
595- throw new CellError(ERRORS.NUM_ERROR, "Function DB parameter 3 value is "
596- + life + ". It should be greater than or equal to 0.");
597- }
598- if (period < 0) {
599- throw new CellError(ERRORS.NUM_ERROR, "Function DB parameter 4 value is "
600- + period + ". It should be greater than or equal to 0.");
601- }
602- if (month > 12 || month < 1) {
603- throw new CellError(ERRORS.NUM_ERROR, "Function DB parameter 5 value is "
604- + month + ". Valid values are between 1 and 12 inclusive.");
605- }
606- if (period > life) {
607- throw new CellError(ERRORS.NUM_ERROR, "Function DB parameter 4 value is "
608- + life + ". It should be less than or equal to value of Function DB parameter 3 with "+ period +".");
609- }
610- if (salvage >= cost) {
611- return 0;
612- }
613- var rate = (1 - Math.pow(salvage / cost, 1 / life));
614- var initial = cost * rate * month / 12;
615- var total = initial;
616- var current = 0;
617- var ceiling = (period === life) ? life - 1 : period;
618- for (var i = 2; i <= ceiling; i++) {
619- current = (cost - total) * rate;
620- total += current;
621- }
622- if (period === 1) {
623- return initial;
624- } else if (period === life) {
625- return (cost - total) * rate;
626- } else {
627- return current;
628- }
629-};
630-
631-
632-
633-/**
634- * Calculates the sum of the sums of the squares of values in two arrays.
635- * @param values[0] array_x - The array or range of values whose squares will be added to the squares of corresponding
636- * entries in array_y and added together.
637- * @param values[1] array_y - The array or range of values whose squares will be added to the squares of corresponding
638- * entries in array_x and added together.
639- * @returns {number} sum of the sums of the squares
640- * @constructor
641- */
642-var SUMX2PY2 = function (...values) : number {
643- ArgsChecker.checkLength(values, 2);
644- var arrOne = Filter.flattenAndThrow(values[0]);
645- var arrTwo = Filter.flattenAndThrow(values[1]);
646- if (arrOne.length !== arrTwo.length) {
647- throw new CellError(ERRORS.NA_ERROR, "Array arguments to SUMX2PY2 are of different size.");
648- }
649- var result = 0;
650- for (var i = 0; i < arrOne.length; i++) {
651- // If either values at this index are anything but numbers, skip them. This is the behavior in GS at least.
652- if (typeof arrOne[i] === "number" && typeof arrTwo[i] === "number") {
653- result += arrOne[i] * arrOne[i] + arrTwo[i] * arrTwo[i];
654- }
655- }
656- return result;
657-};
658-
659-/**
660- * Calculates the sum of the differences of the squares of values in two arrays.
661- * @param values[0] array_x - The array or range of values whose squares will be reduced by the squares of corresponding
662- * entries in array_y and added together.
663- * @param values[1] array_y - The array or range of values whose squares will be subtracted from the squares of
664- * corresponding entries in array_x and added together.
665- * @returns {number} sum of the differences of the squares
666- * @constructor
667- */
668-var SUMX2MY2 = function (...values) : number {
669- ArgsChecker.checkLength(values, 2);
670- var arrOne = Filter.flattenAndThrow(values[0]);
671- var arrTwo = Filter.flattenAndThrow(values[1]);
672- if (arrOne.length !== arrTwo.length) {
673- throw new CellError(ERRORS.NA_ERROR, "Array arguments to SUMX2MY2 are of different size.");
674- }
675- var result = 0;
676- for (var i = 0; i < arrOne.length; i++) {
677- // If either values at this index are anything but numbers, skip them. This is the behavior in GS at least.
678- if (typeof arrOne[i] === "number" && typeof arrTwo[i] === "number") {
679- result += arrOne[i] * arrOne[i] - arrTwo[i] * arrTwo[i];
680- }
681- }
682- return result;
683-};
684-
685-
686-/**
687- * Counts the number of unique values in a list of specified values and ranges.
688- * @param values The values or ranges to consider for uniqueness. Supports an arbitrary number of arguments for this
689- * function.
690- * @returns {number} of unique values passed in.
691- * @constructor
692- */
693-var COUNTUNIQUE = function (...values) : number {
694- ArgsChecker.checkAtLeastLength(values, 1);
695-
696- // Private function that will recursively generate an array of the unique primatives
697- var countUniquePrivate = function (values: Array<any>) : Object {
698- var uniques = {};
699- for (var i = 0; i < values.length; i++) {
700- if (Array.isArray(values[i])) {
701- // For some reasons an empty range is converted to a range with a single empty string in it.
702- if (values[i].length === 0) {
703- values[i] = [""];
704- }
705- var uniquesOfArray = countUniquePrivate(values[i]);
706- for (var key in uniquesOfArray) {
707- uniques[key] = true;
708- }
709- } else {
710- uniques[Serializer.serialize(values[i])] = true;
711- }
712- }
713- return uniques;
714- };
715-
716- var uniques = countUniquePrivate(values);
717- return Object.keys(uniques).length;
718-};
719-
720-
721-/**
722- * Calculates the sum of the products of corresponding entries in two equal-sized arrays or ranges.
723- * @param values Arrays or ranges whose entries will be multiplied with corresponding entries in the second such array
724- * or range.
725- * @returns {number} sum of the products
726- * @constructor
727- */
728-var SUMPRODUCT = function (...values) : number {
729- ArgsChecker.checkAtLeastLength(values, 1);
730- // Ensuring that all values are array values
731- for (var x = 0; x < values.length; x++) {
732- if (!Array.isArray(values[x])) {
733- values[x] = [values[x]];
734- }
735- }
736-
737- // Flatten any nested ranges (arrays) and check for mismatched range sizes
738- var flattenedValues = [Filter.flattenAndThrow(values[0])];
739- for (var x = 1; x < values.length; x++) {
740- flattenedValues.push(Filter.flattenAndThrow(values[x]));
741- if (flattenedValues[x].length !== flattenedValues[0].length) {
742- throw new CellError(ERRORS.VALUE_ERROR, "SUMPRODUCT has mismatched range sizes. Expected count: "
743- + flattenedValues[0].length + ". Actual count: " + flattenedValues[0].length + ".");
744- }
745- }
746-
747- // Do the actual math
748- var result = 0;
749- for (var i = 0; i < flattenedValues[0].length; i++) {
750- var product = 1;
751- for (var x = 0; x < flattenedValues.length; x++) {
752- product *= TypeCaster.valueToNumberGracefully(flattenedValues[x][i]);
753- }
754- result += product;
755- }
756- return result;
757-};
758-
759-
760-
761-/**
762- * Returns the Fisher transformation of a specified value.
763- * @param values[0] value - The value for which to calculate the Fisher transformation.
764- * @returns {number} Fisher transformation
765- * @constructor
766- */
767-var FISHER = function (...values) : number {
768- ArgsChecker.checkLength(values, 1);
769- var x = TypeCaster.firstValueAsNumber(values[0]);
770- if (x <= -1 || x >= 1) {
771- throw new CellError(ERRORS.NUM_ERROR, "Function FISHER parameter 1 value is " + x + ". Valid values are between -1 and 1 exclusive.");
772- }
773- return Math.log((1 + x) / (1 - x)) / 2;
774-};
775-
776-/**
777- * Returns the inverse Fisher transformation of a specified value.
778- * @param values[0] value - The value for which to calculate the inverse Fisher transformation.
779- * @returns {number} inverse Fisher transformation
780- * @constructor
781- */
782-var FISHERINV = function (...values) : number {
783- ArgsChecker.checkLength(values, 1);
784- var y = TypeCaster.firstValueAsNumber(values[0]);
785- var e2y = Math.exp(2 * y);
786- return (e2y - 1) / (e2y + 1);
787-};
788-
789-
790-
791
792 export {
793 __COMPLEX,