commit
message
Categorized functions
author
Ben Vogt <[email protected]>
date
2017-02-21 05:06:50
stats
5 file(s) changed,
725 insertions(+),
716 deletions(-)
files
README.md
src/RawFormulas/Financial.ts
src/RawFormulas/Math.ts
src/RawFormulas/RawFormulas.ts
src/RawFormulas/Statistical.ts
1diff --git a/README.md b/README.md
2index d47b149..1b05562 100644
3--- a/README.md
4+++ b/README.md
5@@ -5,6 +5,7 @@ TypeScript implementation of a spreadsheet.
6 Things I should do.
7
8 ### SUM and SUMA should be different, and I'm pretty sure they're currently the same.
9+And the same for MAX, MAXA, COUNT, COUNTA, etc. Look these over.
10
11 ### Date-Time issues
12 Here are a couple of the issues with Dates and so on:
13diff --git a/src/RawFormulas/Financial.ts b/src/RawFormulas/Financial.ts
14index 9726438..de49da0 100644
15--- a/src/RawFormulas/Financial.ts
16+++ b/src/RawFormulas/Financial.ts
17@@ -7,6 +7,120 @@ import {
18 } from "../Errors"
19 import * as ERRORS from "../Errors"
20
21+/**
22+ * Calculates the depreciation of an asset for a specified period using the double-declining balance method.
23+ * @param values[0] cost - The initial cost of the asset.
24+ * @param values[1] salvage - The value of the asset at the end of depreciation.
25+ * @param values[2] life - The number of periods over which the asset is depreciated.
26+ * @param values[3] period - The single period within life for which to calculate depreciation.
27+ * @param values[4] factor - [ OPTIONAL - 2 by default ] - The factor by which depreciation decreases.
28+ * @returns {number} depreciation of an asset for a specified period
29+ * @constructor
30+ */
31+var DDB = function (...values) : number {
32+ ArgsChecker.checkLengthWithin(values, 4, 5);
33+ var cost = TypeCaster.firstValueAsNumber(values[0]);
34+ var salvage = TypeCaster.firstValueAsNumber(values[1]);
35+ var life = TypeCaster.firstValueAsNumber(values[2]);
36+ var period = TypeCaster.firstValueAsNumber(values[3]);
37+ var factor = values.length === 5 ? TypeCaster.firstValueAsNumber(values[4]) : 2;
38+
39+ if (cost < 0) {
40+ throw new CellError(ERRORS.NUM_ERROR, "Function DDB parameter 1 value is "
41+ + cost + ". It should be greater than or equal to 0.");
42+ }
43+ if (salvage < 0) {
44+ throw new CellError(ERRORS.NUM_ERROR, "Function DDB parameter 2 value is "
45+ + salvage + ". It should be greater than or equal to 0.");
46+ }
47+ if (life < 0) {
48+ throw new CellError(ERRORS.NUM_ERROR, "Function DDB parameter 3 value is "
49+ + life + ". It should be greater than or equal to 0.");
50+ }
51+ if (period < 0) {
52+ throw new CellError(ERRORS.NUM_ERROR, "Function DDB parameter 4 value is "
53+ + period + ". It should be greater than or equal to 0.");
54+ }
55+ if (period > life) {
56+ throw new CellError(ERRORS.NUM_ERROR, "Function DDB parameter 4 value is "
57+ + life + ". It should be less than or equal to value of Function DB parameter 3 with "+ period +".");
58+ }
59+ if (salvage >= cost) {
60+ return 0;
61+ }
62+
63+ var total = 0;
64+ var current = 0;
65+ for (var i = 1; i <= period; i++) {
66+ current = Math.min((cost - total) * (factor / life), (cost - salvage - total));
67+ total += current;
68+ }
69+ return current;
70+};
71+
72+
73+/**
74+ * Calculates the depreciation of an asset for a specified period using the arithmetic declining balance method.
75+ * @param values[0] cost - The initial cost of the asset.
76+ * @param values[1] salvage - The value of the asset at the end of depreciation.
77+ * @param values[2] life - The number of periods over which the asset is depreciated.
78+ * @param values[3] period - The single period within life for which to calculate depreciation.
79+ * @param values[4] month - [ OPTIONAL - 12 by default ] - The number of months in the first year of depreciation.
80+ * @returns {number} depreciated value
81+ * @constructor
82+ */
83+var DB = function (...values) : number {
84+ ArgsChecker.checkLengthWithin(values, 4, 5);
85+ var cost = TypeCaster.firstValueAsNumber(values[0]);
86+ var salvage = TypeCaster.firstValueAsNumber(values[1]);
87+ var life = TypeCaster.firstValueAsNumber(values[2]);
88+ var period = TypeCaster.firstValueAsNumber(values[3]);
89+ var month = values.length === 5 ? Math.floor(TypeCaster.firstValueAsNumber(values[4])) : 12;
90+ if (cost < 0) {
91+ throw new CellError(ERRORS.NUM_ERROR, "Function DB parameter 1 value is "
92+ + cost + ". It should be greater than or equal to 0.");
93+ }
94+ if (salvage < 0) {
95+ throw new CellError(ERRORS.NUM_ERROR, "Function DB parameter 2 value is "
96+ + salvage + ". It should be greater than or equal to 0.");
97+ }
98+ if (life < 0) {
99+ throw new CellError(ERRORS.NUM_ERROR, "Function DB parameter 3 value is "
100+ + life + ". It should be greater than or equal to 0.");
101+ }
102+ if (period < 0) {
103+ throw new CellError(ERRORS.NUM_ERROR, "Function DB parameter 4 value is "
104+ + period + ". It should be greater than or equal to 0.");
105+ }
106+ if (month > 12 || month < 1) {
107+ throw new CellError(ERRORS.NUM_ERROR, "Function DB parameter 5 value is "
108+ + month + ". Valid values are between 1 and 12 inclusive.");
109+ }
110+ if (period > life) {
111+ throw new CellError(ERRORS.NUM_ERROR, "Function DB parameter 4 value is "
112+ + life + ". It should be less than or equal to value of Function DB parameter 3 with "+ period +".");
113+ }
114+ if (salvage >= cost) {
115+ return 0;
116+ }
117+ var rate = (1 - Math.pow(salvage / cost, 1 / life));
118+ var initial = cost * rate * month / 12;
119+ var total = initial;
120+ var current = 0;
121+ var ceiling = (period === life) ? life - 1 : period;
122+ for (var i = 2; i <= ceiling; i++) {
123+ current = (cost - total) * rate;
124+ total += current;
125+ }
126+ if (period === 1) {
127+ return initial;
128+ } else if (period === life) {
129+ return (cost - total) * rate;
130+ } else {
131+ return current;
132+ }
133+};
134+
135 /**
136 * Formats a number into the locale-specific currency format. WARNING: Currently the equivalent of TRUNC, since this
137 * returns numbers
138@@ -76,8 +190,33 @@ var DOLLARFR = function (...values) : number {
139 return result;
140 };
141
142+
143+/**
144+ * Calculates the annual effective interest rate given the nominal rate and number of compounding periods per year.
145+ * @param values[0] nominal_rate - The nominal interest rate per year.
146+ * @param values[1] periods_per_year - The number of compounding periods per year.
147+ * @returns {number} annual effective interest rate
148+ * @constructor
149+ */
150+var EFFECT = function (...values) : number {
151+ ArgsChecker.checkLength(values, 2);
152+ var rate = TypeCaster.firstValueAsNumber(values[0]);
153+ var periods = TypeCaster.firstValueAsNumber(values[1]);
154+ if (rate <= 0) {
155+ throw new CellError(ERRORS.NUM_ERROR, "Function EFFECT parameter 1 value is " + rate + ". It should be greater than to 0");
156+ }
157+ if (periods < 1) {
158+ throw new CellError(ERRORS.NUM_ERROR, "Function EFFECT parameter 2 value is " + periods + ". It should be greater than or equal to 1");
159+ }
160+ periods = Math.floor(periods);
161+ return Math.pow(1 + rate / periods, periods) - 1;
162+};
163+
164 export {
165+ DB,
166+ DDB,
167 DOLLAR,
168 DOLLARDE,
169- DOLLARFR
170+ DOLLARFR,
171+ EFFECT
172 }
173\ No newline at end of file
174diff --git a/src/RawFormulas/Math.ts b/src/RawFormulas/Math.ts
175index 62f9597..9915b86 100644
176--- a/src/RawFormulas/Math.ts
177+++ b/src/RawFormulas/Math.ts
178@@ -171,79 +171,6 @@ var EVEN = function (...values) : number {
179 return X % 2 === 1 ? X + 1 : X;
180 };
181
182-/**
183- * Returns the maximum value in a numeric dataset.
184- * @param values The values or range(s) to consider when calculating the maximum value.
185- * @returns {number} the maximum value of the dataset
186- * @constructor
187- */
188-var MAX = function (...values) {
189- ArgsChecker.checkAtLeastLength(values, 1);
190- var maxSoFar = -Infinity;
191- for (var i = 0; i < values.length; i++) {
192- if (values[i] instanceof Array) {
193- if (values[i].length === 0) {
194- throw new CellError(ERRORS.REF_ERROR, "Reference does not exist.");
195- }
196- var filtered = Filter.filterOutStringValues(values[i]);
197- if (filtered.length !== 0) {
198- maxSoFar = Math.max(MAX.apply(this, filtered), maxSoFar);
199- }
200- } else {
201- maxSoFar = Math.max(TypeCaster.valueToNumber(values[i]), maxSoFar);
202- }
203- }
204- return maxSoFar;
205-};
206-
207-/**
208- * Returns the maximum numeric value in a dataset.
209- * @param values The value(s) or range(s) to consider when calculating the maximum value.
210- * @returns {number} maximum value of the dataset
211- * @constructor
212- */
213-var MAXA = function (...values) : number {
214- return MAX.apply(this, values);
215-};
216-
217-
218-/**
219- * Returns the minimum value in a numeric dataset.
220- * @param values The value(s) or range(s) to consider when calculating the minimum value.
221- * @returns {number} the minimum value of the dataset
222- * @constructor
223- */
224-var MIN = function (...values) {
225- ArgsChecker.checkAtLeastLength(values, 1);
226- var minSoFar = Infinity;
227- for (var i = 0; i < values.length; i++) {
228- if (values[i] instanceof Array) {
229- if (values[i].length === 0) {
230- throw new CellError(ERRORS.REF_ERROR, "Reference does not exist.");
231- }
232- var filtered = Filter.filterOutStringValues(values[i]);
233- if (filtered.length !== 0) {
234- minSoFar = Math.min(MIN.apply(this, filtered), minSoFar);
235- }
236- } else {
237- minSoFar = Math.min(TypeCaster.valueToNumber(values[i]), minSoFar);
238- }
239- }
240- return minSoFar;
241-};
242-
243-
244-/**
245- * Returns the minimum numeric value in a dataset.
246- * @param values The value(s) or range(s) to consider when calculating the minimum value.
247- * @returns {number} the minimum value in the dataset
248- * @constructor
249- */
250-var MINA = function (...values) : number {
251- return MIN.apply(this, values);
252-};
253-
254-
255 /**
256 * Returns the result of the modulo operator, the remainder after a division operation.
257 * @param values[0] The number to be divided to find the remainder.
258@@ -561,37 +488,6 @@ var TANH = function (...values) : number {
259 return Math["tanh"](rad);
260 };
261
262-/**
263- * Returns the average of a range depending on criteria.
264- * @param values[0] criteria_range - The range to check against criterion.
265- * @param values[1] criterion - The pattern or test to apply to criteria_range.
266- * @param values[2] average_range - [optional] The range to average. If not included, criteria_range is used for the
267- * average instead.
268- * @returns {number}
269- * @constructor
270- * TODO: This needs to take nested range values.
271- * TODO: This needs to also accept a third parameter "average_range"
272- */
273-var AVERAGEIF = function (...values) {
274- ArgsChecker.checkLength(values, 2);
275- var range = values[0];
276- var criteriaEvaluation = CriteriaFunctionFactory.createCriteriaFunction(values[1]);
277-
278- var result = 0;
279- var count = 0;
280- for (var i = 0; i < range.length; i++) {
281- var val = TypeCaster.valueToNumber(range[i]);
282- if (criteriaEvaluation(val)) {
283- result = result + val;
284- count++;
285- }
286- }
287- if (count === 0) {
288- throw new CellError(ERRORS.DIV_ZERO_ERROR, "Evaluation of function AVERAGEIF caused a divide by zero error.");
289- }
290- return result / count;
291-};
292-
293 /**
294 * Rounds a number up to the nearest integer multiple of specified significance.
295 * @param values[0] The value to round up to the nearest integer multiple of factor.
296@@ -663,26 +559,6 @@ var IF = function (...values) : any {
297 return (TypeCaster.valueToBoolean(values[0])) ? values[1] : values[2];
298 };
299
300-/**
301- * Returns the a count of the number of numeric values in a dataset.
302- * @param values The values or ranges to consider when counting.
303- * @returns {number} number of numeric values in a dataset.
304- * @constructor
305- */
306-var COUNT = function (...values) : number {
307- ArgsChecker.checkAtLeastLength(values, 1);
308- var count = 0;
309- for (var i = 0; i < values.length; i++) {
310- if (values[i] instanceof Array) {
311- if (values[i].length > 0) {
312- count += COUNT.apply(this, values[i]);
313- }
314- } else if (TypeCaster.canCoerceToNumber(values[i])) {
315- count++;
316- }
317- }
318- return count;
319-};
320
321 /**
322 * Returns a conditional count across a range.
323@@ -754,28 +630,6 @@ var COUNTIFS = function (...values) {
324 return count;
325 };
326
327-/**
328- * Returns the a count of the number of values in a dataset.
329- * @param values The values or ranges to consider when counting.
330- * @returns {number} number of values in a dataset.
331- * @constructor
332- */
333-var COUNTA = function (...values) : number {
334- ArgsChecker.checkAtLeastLength(values, 1);
335- var count = 0;
336- for (var i = 0; i < values.length; i++) {
337- if (values[i] instanceof Array) {
338- if (values[i].length > 0) {
339- count += COUNTA.apply(this, values[i]);
340- } else {
341- count++;
342- }
343- } else {
344- count++;
345- }
346- }
347- return count;
348-};
349
350 /**
351 * Rounds a number to a certain number of decimal places according to standard rules.
352@@ -938,195 +792,6 @@ var DEGREES = function (...values) {
353 };
354
355
356-/**
357- * Calculates the sum of squares of deviations based on a sample.
358- * @param values The values or ranges of the sample.
359- * @returns {number} sum of squares of deviations
360- * @constructor
361- */
362-var DEVSQ = function (...values) : number {
363- ArgsChecker.checkAtLeastLength(values, 1);
364- var range = Filter.flattenAndThrow(values);
365- var result = 0;
366- var count = 0;
367- for (var i = 0; i < range.length; i++) {
368- result = result + TypeCaster.valueToNumber(range[i]);
369- count++;
370- }
371- var mean = result / count;
372- var result = 0;
373- for (var i = 0; i < range.length; i++) {
374- result += Math.pow((TypeCaster.valueToNumber(range[i]) - mean), 2);
375- }
376- return result;
377-};
378-
379-/**
380- * Calculates the left-tailed F probability distribution (degree of diversity) for two data sets with given input x.
381- * Alternately called Fisher-Snedecor distribution or Snecdor's F distribution.
382- * @param values[0] x - The input to the F probability distribution function. The value at which to evaluate the function.
383- * Must be a positive number.
384- * @param values[1] degrees_freedom1 - The numerator degrees of freedom.
385- * @param values[2] degrees_freedom2 - The denominator degrees of freedom.
386- * @param values[3] cumulative - Logical value that determines the form of the function. If true returns the cumulative
387- * distribution function. If false returns the probability density function.
388- * @returns {number|undefined|boolean} left-tailed F probability distribution
389- * @constructor
390- * TODO: This function should be stricter in its return type.
391- */
392-var FDIST$LEFTTAILED = function (...values) : number|undefined|boolean {
393- function gammaln(x) {
394- var j = 0;
395- var cof = [
396- 76.18009172947146, -86.50532032941677, 24.01409824083091,
397- -1.231739572450155, 0.1208650973866179e-2, -0.5395239384953e-5
398- ];
399- var ser = 1.000000000190015;
400- var xx, y, tmp;
401- tmp = (y = xx = x) + 5.5;
402- tmp -= (xx + 0.5) * Math.log(tmp);
403- for (; j < 6; j++)
404- ser += cof[j] / ++y;
405- return Math.log(2.5066282746310005 * ser / xx) - tmp;
406- }
407- function gammafn(x) {
408- var p = [-1.716185138865495, 24.76565080557592, -379.80425647094563,
409- 629.3311553128184, 866.9662027904133, -31451.272968848367,
410- -36144.413418691176, 66456.14382024054
411- ];
412- var q = [-30.8402300119739, 315.35062697960416, -1015.1563674902192,
413- -3107.771671572311, 22538.118420980151, 4755.8462775278811,
414- -134659.9598649693, -115132.2596755535];
415- var fact;
416- var n = 0;
417- var xden = 0;
418- var xnum = 0;
419- var y = x;
420- var i, z, yi, res;
421- if (y <= 0) {
422- res = y % 1 + 3.6e-16;
423- if (res) {
424- fact = (!(y & 1) ? 1 : -1) * Math.PI / Math.sin(Math.PI * res);
425- y = 1 - y;
426- } else {
427- return Infinity;
428- }
429- }
430- yi = y;
431- if (y < 1) {
432- z = y++;
433- } else {
434- z = (y -= n = (y | 0) - 1) - 1;
435- }
436- for (i = 0; i < 8; ++i) {
437- xnum = (xnum + p[i]) * z;
438- xden = xden * z + q[i];
439- }
440- res = xnum / xden + 1;
441- if (yi < y) {
442- res /= yi;
443- } else if (yi > y) {
444- for (i = 0; i < n; ++i) {
445- res *= y;
446- y++;
447- }
448- }
449- if (fact) {
450- res = fact / res;
451- }
452- return res;
453- }
454- function betacf(x, a, b) {
455- var fpmin = 1e-30;
456- var m = 1;
457- var qab = a + b;
458- var qap = a + 1;
459- var qam = a - 1;
460- var c = 1;
461- var d = 1 - qab * x / qap;
462- var m2, aa, del, h;
463-
464- // These q's will be used in factors that occur in the coefficients
465- if (Math.abs(d) < fpmin)
466- d = fpmin;
467- d = 1 / d;
468- h = d;
469-
470- for (; m <= 100; m++) {
471- m2 = 2 * m;
472- aa = m * (b - m) * x / ((qam + m2) * (a + m2));
473- // One step (the even one) of the recurrence
474- d = 1 + aa * d;
475- if (Math.abs(d) < fpmin)
476- d = fpmin;
477- c = 1 + aa / c;
478- if (Math.abs(c) < fpmin)
479- c = fpmin;
480- d = 1 / d;
481- h *= d * c;
482- aa = -(a + m) * (qab + m) * x / ((a + m2) * (qap + m2));
483- // Next step of the recurrence (the odd one)
484- d = 1 + aa * d;
485- if (Math.abs(d) < fpmin)
486- d = fpmin;
487- c = 1 + aa / c;
488- if (Math.abs(c) < fpmin)
489- c = fpmin;
490- d = 1 / d;
491- del = d * c;
492- h *= del;
493- if (Math.abs(del - 1.0) < 3e-7)
494- break;
495- }
496-
497- return h;
498- }
499- function ibeta(x, a, b) {
500- // Factors in front of the continued fraction.
501- var bt = (x === 0 || x === 1) ? 0 :
502- Math.exp(gammaln(a + b) - gammaln(a) -
503- gammaln(b) + a * Math.log(x) + b *
504- Math.log(1 - x));
505- if (x < 0 || x > 1)
506- return false;
507- if (x < (a + 1) / (a + b + 2))
508- // Use continued fraction directly.
509- return bt * betacf(x, a, b) / a;
510- // else use continued fraction after making the symmetry transformation.
511- return 1 - bt * betacf(1 - x, b, a) / b;
512- }
513- function cdf(x, df1, df2) {
514- return ibeta((df1 * x) / (df1 * x + df2), df1 / 2, df2 / 2);
515- }
516- function pdf(x, df1, df2) {
517- if (x < 0) {
518- return undefined;
519- }
520- return Math.sqrt((Math.pow(df1 * x, df1) * Math.pow(df2, df2)) /
521- (Math.pow(df1 * x + df2, df1 + df2))) /
522- (x * betafn(df1/2, df2/2));
523- }
524- function betaln(x, y) {
525- return gammaln(x) + gammaln(y) - gammaln(x + y);
526- }
527- function betafn(x, y) {
528- // ensure arguments are positive
529- if (x <= 0 || y <= 0)
530- return undefined;
531- // make sure x + y doesn't exceed the upper limit of usable values
532- return (x + y > 170) ? Math.exp(betaln(x, y)) : gammafn(x) * gammafn(y) / gammafn(x + y);
533- }
534- ArgsChecker.checkLength(values, 4);
535- var x = TypeCaster.firstValueAsNumber(values[0]);
536- if (x < 0) {
537- throw new CellError(ERRORS.NUM_ERROR, "Function F.DIST parameter 1 value is " + x + ". It should be greater than or equal to 0.");
538- }
539- var d1 = TypeCaster.firstValueAsNumber(values[1]);
540- var d2 = TypeCaster.firstValueAsNumber(values[2]);
541- var cumulative = TypeCaster.firstValueAsBoolean(values[3]);
542- return (cumulative) ? cdf(x, d1, d2) : pdf(x, d1, d2);
543-};
544-
545 /**
546 * Returns the complementary Gauss error function of a value.
547 * @param values[0] The number for which to calculate the complementary Gauss error function.
548@@ -1192,270 +857,6 @@ function erf(x) {
549 }
550
551
552-/**
553- * Returns the inverse of the (right-tailed) F probability distribution. If p = FDIST(x,...), then FINV(p,...) = x. The
554- * F distribution can be used in an F-test that compares the degree of variability in two data sets.
555- * @param values[0] probability - A probability associated with the F cumulative distribution.
556- * @param values[1] deg_freedom1 - Required. The numerator degrees of freedom.
557- * @param values[2] deg_freedom2 - Required. The denominator degrees of freedom.
558- * @returns {number} inverse of the (right-tailed) F probability distribution
559- * @constructor
560- * TODO: This function needs to be tested more thuroughly.
561- */
562-var FINV = function (...values) : number {
563- function betacf(x, a, b) {
564- var fpmin = 1e-30;
565- var m = 1;
566- var qab = a + b;
567- var qap = a + 1;
568- var qam = a - 1;
569- var c = 1;
570- var d = 1 - qab * x / qap;
571- var m2, aa, del, h;
572-
573- // These q's will be used in factors that occur in the coefficients
574- if (Math.abs(d) < fpmin)
575- d = fpmin;
576- d = 1 / d;
577- h = d;
578-
579- for (; m <= 100; m++) {
580- m2 = 2 * m;
581- aa = m * (b - m) * x / ((qam + m2) * (a + m2));
582- // One step (the even one) of the recurrence
583- d = 1 + aa * d;
584- if (Math.abs(d) < fpmin)
585- d = fpmin;
586- c = 1 + aa / c;
587- if (Math.abs(c) < fpmin)
588- c = fpmin;
589- d = 1 / d;
590- h *= d * c;
591- aa = -(a + m) * (qab + m) * x / ((a + m2) * (qap + m2));
592- // Next step of the recurrence (the odd one)
593- d = 1 + aa * d;
594- if (Math.abs(d) < fpmin)
595- d = fpmin;
596- c = 1 + aa / c;
597- if (Math.abs(c) < fpmin)
598- c = fpmin;
599- d = 1 / d;
600- del = d * c;
601- h *= del;
602- if (Math.abs(del - 1.0) < 3e-7)
603- break;
604- }
605-
606- return h;
607- }
608- function ibeta(x, a, b) : number {
609- // Factors in front of the continued fraction.
610- var bt = (x === 0 || x === 1) ? 0 :
611- Math.exp(gammaln(a + b) - gammaln(a) -
612- gammaln(b) + a * Math.log(x) + b *
613- Math.log(1 - x));
614- if (x < 0 || x > 1)
615- // WARNING: I changed this to 0, because TS complains about doing numerical operations on boolean values.
616- // Still safe in javascript, but not TS.
617- return 0;
618- if (x < (a + 1) / (a + b + 2))
619- // Use continued fraction directly.
620- return bt * betacf(x, a, b) / a;
621- // else use continued fraction after making the symmetry transformation.
622- return 1 - bt * betacf(1 - x, b, a) / b;
623- }
624- function gammaln(x) {
625- var j = 0;
626- var cof = [
627- 76.18009172947146, -86.50532032941677, 24.01409824083091,
628- -1.231739572450155, 0.1208650973866179e-2, -0.5395239384953e-5
629- ];
630- var ser = 1.000000000190015;
631- var xx, y, tmp;
632- tmp = (y = xx = x) + 5.5;
633- tmp -= (xx + 0.5) * Math.log(tmp);
634- for (; j < 6; j++)
635- ser += cof[j] / ++y;
636- return Math.log(2.5066282746310005 * ser / xx) - tmp;
637- }
638- function ibetainv(p, a, b) {
639- var EPS = 1e-8;
640- var a1 = a - 1;
641- var b1 = b - 1;
642- var j = 0;
643- var lna, lnb, pp, t, u, err, x, al, h, w, afac;
644- if (p <= 0)
645- return 0;
646- if (p >= 1)
647- return 1;
648- if (a >= 1 && b >= 1) {
649- pp = (p < 0.5) ? p : 1 - p;
650- t = Math.sqrt(-2 * Math.log(pp));
651- x = (2.30753 + t * 0.27061) / (1 + t* (0.99229 + t * 0.04481)) - t;
652- if (p < 0.5)
653- x = -x;
654- al = (x * x - 3) / 6;
655- h = 2 / (1 / (2 * a - 1) + 1 / (2 * b - 1));
656- w = (x * Math.sqrt(al + h) / h) - (1 / (2 * b - 1) - 1 / (2 * a - 1)) *
657- (al + 5 / 6 - 2 / (3 * h));
658- x = a / (a + b * Math.exp(2 * w));
659- } else {
660- lna = Math.log(a / (a + b));
661- lnb = Math.log(b / (a + b));
662- t = Math.exp(a * lna) / a;
663- u = Math.exp(b * lnb) / b;
664- w = t + u;
665- if (p < t / w)
666- x = Math.pow(a * w * p, 1 / a);
667- else
668- x = 1 - Math.pow(b * w * (1 - p), 1 / b);
669- }
670- afac = -gammaln(a) - gammaln(b) + gammaln(a + b);
671- for(; j < 10; j++) {
672- if (x === 0 || x === 1)
673- return x;
674- err = ibeta(x, a, b) - p;
675- t = Math.exp(a1 * Math.log(x) + b1 * Math.log(1 - x) + afac);
676- u = err / t;
677- x -= (t = u / (1 - 0.5 * Math.min(1, u * (a1 / x - b1 / (1 - x)))));
678- if (x <= 0)
679- x = 0.5 * (x + t);
680- if (x >= 1)
681- x = 0.5 * (x + t + 1);
682- if (Math.abs(t) < EPS * x && j > 0)
683- break;
684- }
685- return x;
686- }
687- function inv(x, df1, df2) {
688- return df2 / (df1 * (1 / ibetainv(x, df1 / 2, df2 / 2) - 1));
689- }
690- ArgsChecker.checkLength(values, 3);
691- var probability = TypeCaster.firstValueAsNumber(values[0]);
692- if (probability <= 0.0 || probability > 1.0) {
693- // TODO: Throw num error.
694- }
695- var d1 = TypeCaster.firstValueAsNumber(values[1]);
696- var d2 = TypeCaster.firstValueAsNumber(values[2]);
697- return inv(1.0 - probability, d1, d2);
698-};
699-
700-/**
701- * Calculates the depreciation of an asset for a specified period using the double-declining balance method.
702- * @param values[0] cost - The initial cost of the asset.
703- * @param values[1] salvage - The value of the asset at the end of depreciation.
704- * @param values[2] life - The number of periods over which the asset is depreciated.
705- * @param values[3] period - The single period within life for which to calculate depreciation.
706- * @param values[4] factor - [ OPTIONAL - 2 by default ] - The factor by which depreciation decreases.
707- * @returns {number} depreciation of an asset for a specified period
708- * @constructor
709- */
710-var DDB = function (...values) : number {
711- ArgsChecker.checkLengthWithin(values, 4, 5);
712- var cost = TypeCaster.firstValueAsNumber(values[0]);
713- var salvage = TypeCaster.firstValueAsNumber(values[1]);
714- var life = TypeCaster.firstValueAsNumber(values[2]);
715- var period = TypeCaster.firstValueAsNumber(values[3]);
716- var factor = values.length === 5 ? TypeCaster.firstValueAsNumber(values[4]) : 2;
717-
718- if (cost < 0) {
719- throw new CellError(ERRORS.NUM_ERROR, "Function DDB parameter 1 value is "
720- + cost + ". It should be greater than or equal to 0.");
721- }
722- if (salvage < 0) {
723- throw new CellError(ERRORS.NUM_ERROR, "Function DDB parameter 2 value is "
724- + salvage + ". It should be greater than or equal to 0.");
725- }
726- if (life < 0) {
727- throw new CellError(ERRORS.NUM_ERROR, "Function DDB parameter 3 value is "
728- + life + ". It should be greater than or equal to 0.");
729- }
730- if (period < 0) {
731- throw new CellError(ERRORS.NUM_ERROR, "Function DDB parameter 4 value is "
732- + period + ". It should be greater than or equal to 0.");
733- }
734- if (period > life) {
735- throw new CellError(ERRORS.NUM_ERROR, "Function DDB parameter 4 value is "
736- + life + ". It should be less than or equal to value of Function DB parameter 3 with "+ period +".");
737- }
738- if (salvage >= cost) {
739- return 0;
740- }
741-
742- var total = 0;
743- var current = 0;
744- for (var i = 1; i <= period; i++) {
745- current = Math.min((cost - total) * (factor / life), (cost - salvage - total));
746- total += current;
747- }
748- return current;
749-};
750-
751-
752-/**
753- * Calculates the depreciation of an asset for a specified period using the arithmetic declining balance method.
754- * @param values[0] cost - The initial cost of the asset.
755- * @param values[1] salvage - The value of the asset at the end of depreciation.
756- * @param values[2] life - The number of periods over which the asset is depreciated.
757- * @param values[3] period - The single period within life for which to calculate depreciation.
758- * @param values[4] month - [ OPTIONAL - 12 by default ] - The number of months in the first year of depreciation.
759- * @returns {number} depreciated value
760- * @constructor
761- */
762-var DB = function (...values) : number {
763- ArgsChecker.checkLengthWithin(values, 4, 5);
764- var cost = TypeCaster.firstValueAsNumber(values[0]);
765- var salvage = TypeCaster.firstValueAsNumber(values[1]);
766- var life = TypeCaster.firstValueAsNumber(values[2]);
767- var period = TypeCaster.firstValueAsNumber(values[3]);
768- var month = values.length === 5 ? Math.floor(TypeCaster.firstValueAsNumber(values[4])) : 12;
769- if (cost < 0) {
770- throw new CellError(ERRORS.NUM_ERROR, "Function DB parameter 1 value is "
771- + cost + ". It should be greater than or equal to 0.");
772- }
773- if (salvage < 0) {
774- throw new CellError(ERRORS.NUM_ERROR, "Function DB parameter 2 value is "
775- + salvage + ". It should be greater than or equal to 0.");
776- }
777- if (life < 0) {
778- throw new CellError(ERRORS.NUM_ERROR, "Function DB parameter 3 value is "
779- + life + ". It should be greater than or equal to 0.");
780- }
781- if (period < 0) {
782- throw new CellError(ERRORS.NUM_ERROR, "Function DB parameter 4 value is "
783- + period + ". It should be greater than or equal to 0.");
784- }
785- if (month > 12 || month < 1) {
786- throw new CellError(ERRORS.NUM_ERROR, "Function DB parameter 5 value is "
787- + month + ". Valid values are between 1 and 12 inclusive.");
788- }
789- if (period > life) {
790- throw new CellError(ERRORS.NUM_ERROR, "Function DB parameter 4 value is "
791- + life + ". It should be less than or equal to value of Function DB parameter 3 with "+ period +".");
792- }
793- if (salvage >= cost) {
794- return 0;
795- }
796- var rate = (1 - Math.pow(salvage / cost, 1 / life));
797- var initial = cost * rate * month / 12;
798- var total = initial;
799- var current = 0;
800- var ceiling = (period === life) ? life - 1 : period;
801- for (var i = 2; i <= ceiling; i++) {
802- current = (cost - total) * rate;
803- total += current;
804- }
805- if (period === 1) {
806- return initial;
807- } else if (period === life) {
808- return (cost - total) * rate;
809- } else {
810- return current;
811- }
812-};
813-
814-
815-
816 /**
817 * Calculates the sum of the sums of the squares of values in two arrays.
818 * @param values[0] array_x - The array or range of values whose squares will be added to the squares of corresponding
819@@ -1583,81 +984,6 @@ var SUMPRODUCT = function (...values) : number {
820 };
821
822
823-
824-/**
825- * Returns the Fisher transformation of a specified value.
826- * @param values[0] value - The value for which to calculate the Fisher transformation.
827- * @returns {number} Fisher transformation
828- * @constructor
829- */
830-var FISHER = function (...values) : number {
831- ArgsChecker.checkLength(values, 1);
832- var x = TypeCaster.firstValueAsNumber(values[0]);
833- if (x <= -1 || x >= 1) {
834- throw new CellError(ERRORS.NUM_ERROR, "Function FISHER parameter 1 value is " + x + ". Valid values are between -1 and 1 exclusive.");
835- }
836- return Math.log((1 + x) / (1 - x)) / 2;
837-};
838-
839-/**
840- * Returns the inverse Fisher transformation of a specified value.
841- * @param values[0] value - The value for which to calculate the inverse Fisher transformation.
842- * @returns {number} inverse Fisher transformation
843- * @constructor
844- */
845-var FISHERINV = function (...values) : number {
846- ArgsChecker.checkLength(values, 1);
847- var y = TypeCaster.firstValueAsNumber(values[0]);
848- var e2y = Math.exp(2 * y);
849- return (e2y - 1) / (e2y + 1);
850-};
851-
852-/**
853- * Calculates the annual effective interest rate given the nominal rate and number of compounding periods per year.
854- * @param values[0] nominal_rate - The nominal interest rate per year.
855- * @param values[1] periods_per_year - The number of compounding periods per year.
856- * @returns {number} annual effective interest rate
857- * @constructor
858- */
859-var EFFECT = function (...values) : number {
860- ArgsChecker.checkLength(values, 2);
861- var rate = TypeCaster.firstValueAsNumber(values[0]);
862- var periods = TypeCaster.firstValueAsNumber(values[1]);
863- if (rate <= 0) {
864- throw new CellError(ERRORS.NUM_ERROR, "Function EFFECT parameter 1 value is " + rate + ". It should be greater than to 0");
865- }
866- if (periods < 1) {
867- throw new CellError(ERRORS.NUM_ERROR, "Function EFFECT parameter 2 value is " + periods + ". It should be greater than or equal to 1");
868- }
869- periods = Math.floor(periods);
870- return Math.pow(1 + rate / periods, periods) - 1;
871-};
872-
873-
874-/**
875- * Returns the value of the exponential distribution function with a specified lambda at a specified value.
876- * @param values[0] x - The input to the exponential distribution function. If cumulative is TRUE then EXPONDIST returns
877- * the cumulative probability of all values up to x.
878- * @param values[1] lambda - The lambda to specify the exponential distribution function.
879- * @param values[2] cumulative - Whether to use the exponential cumulative distribution.
880- * @returns {number} value of the exponential distribution function.
881- * @constructor
882- */
883-var EXPONDIST = function (...values) : number {
884- function cdf(x, rate) {
885- return x < 0 ? 0 : 1 - Math.exp(-rate * x);
886- }
887- function pdf(x, rate) {
888- return x < 0 ? 0 : rate * Math.exp(-rate * x);
889- }
890- ArgsChecker.checkLength(values, 3);
891- var x = TypeCaster.firstValueAsNumber(values[0]);
892- var lambda = TypeCaster.firstValueAsNumber(values[1]);
893- var cumulative = TypeCaster.firstValueAsBoolean(values[2]);
894- return (cumulative) ? cdf(x, lambda) : pdf(x, lambda);
895-};
896-
897-
898 /**
899 * Returns the number of ways to choose some number of objects from a pool of a given size of objects.
900 * @param values[0] n - The size of the pool of objects to choose from.
901@@ -1709,25 +1035,12 @@ export {
902 COSH,
903 COS,
904 COUNTUNIQUE,
905- DEVSQ, // Statistical
906- DB, // Financial
907- DDB, // Financial
908- EFFECT, // Financial
909 EVEN,
910 ERF,
911 ERFC,
912- EXPONDIST, // Statistical
913- FDIST$LEFTTAILED, // Statistical
914- FINV, // Statistical
915- FISHER, // Statistical
916- FISHERINV, // Statistical
917 INT,
918 ISEVEN,
919 ISODD,
920- MAX, // Statistical
921- MAXA, // Statistical
922- MIN, // Statistical
923- MINA, // Statistical
924 MOD,
925 ODD,
926 SIN,
927@@ -1742,7 +1055,6 @@ export {
928 LN,
929 TAN,
930 TANH,
931- AVERAGEIF, // Statistical
932 ROUND,
933 ROUNDDOWN,
934 ROUNDUP,
935@@ -1753,8 +1065,6 @@ export {
936 SUMX2PY2, // Array?
937 FLOOR,
938 IF,
939- COUNT, // Statistical
940- COUNTA, // Statistical
941 COUNTIF,
942 COUNTIFS,
943 CEILING,
944diff --git a/src/RawFormulas/RawFormulas.ts b/src/RawFormulas/RawFormulas.ts
945index e78c15c..4481168 100644
946--- a/src/RawFormulas/RawFormulas.ts
947+++ b/src/RawFormulas/RawFormulas.ts
948@@ -16,25 +16,12 @@ import {
949 COSH,
950 COS,
951 COUNTUNIQUE,
952- DEVSQ,
953- DB,
954- DDB,
955- EFFECT,
956 EVEN,
957 ERF,
958 ERFC,
959- EXPONDIST,
960- FDIST$LEFTTAILED,
961- FINV,
962- FISHER,
963- FISHERINV,
964 INT,
965 ISEVEN,
966 ISODD,
967- MAX,
968- MAXA,
969- MIN,
970- MINA,
971 MOD,
972 ODD,
973 SIN,
974@@ -49,7 +36,6 @@ import {
975 LN,
976 TAN,
977 TANH,
978- AVERAGEIF,
979 ROUND,
980 ROUNDDOWN,
981 ROUNDUP,
982@@ -60,8 +46,6 @@ import {
983 SUMX2PY2,
984 FLOOR,
985 IF,
986- COUNT,
987- COUNTA,
988 COUNTIF,
989 COUNTIFS,
990 CEILING,
991@@ -89,17 +73,33 @@ import {
992 DELTA
993 } from "./Engineering";
994 import {
995+ DB,
996+ DDB,
997 DOLLAR,
998 DOLLARDE,
999- DOLLARFR
1000+ DOLLARFR,
1001+ EFFECT
1002 } from "./Financial";
1003 import {
1004 AVERAGE,
1005 AVERAGEA,
1006+ AVERAGEIF,
1007 AVEDEV,
1008 CORREL,
1009+ COUNT,
1010+ COUNTA,
1011 PEARSON,
1012- MEDIAN
1013+ MEDIAN,
1014+ DEVSQ,
1015+ EXPONDIST,
1016+ FDIST$LEFTTAILED,
1017+ FINV,
1018+ FISHER,
1019+ FISHERINV,
1020+ MAX,
1021+ MAXA,
1022+ MIN,
1023+ MINA
1024 } from "./Statistical";
1025 import {
1026 ARABIC,
1027diff --git a/src/RawFormulas/Statistical.ts b/src/RawFormulas/Statistical.ts
1028index ffb566f..15026f4 100644
1029--- a/src/RawFormulas/Statistical.ts
1030+++ b/src/RawFormulas/Statistical.ts
1031@@ -1,5 +1,6 @@
1032 import {
1033 ArgsChecker,
1034+ CriteriaFunctionFactory,
1035 Filter,
1036 TypeCaster
1037 } from "./Utils";
1038@@ -12,6 +13,30 @@ import {
1039 } from "./Math"
1040 import * as ERRORS from "../Errors";
1041
1042+
1043+/**
1044+ * Calculates the sum of squares of deviations based on a sample.
1045+ * @param values The values or ranges of the sample.
1046+ * @returns {number} sum of squares of deviations
1047+ * @constructor
1048+ */
1049+var DEVSQ = function (...values) : number {
1050+ ArgsChecker.checkAtLeastLength(values, 1);
1051+ var range = Filter.flattenAndThrow(values);
1052+ var result = 0;
1053+ var count = 0;
1054+ for (var i = 0; i < range.length; i++) {
1055+ result = result + TypeCaster.valueToNumber(range[i]);
1056+ count++;
1057+ }
1058+ var mean = result / count;
1059+ var result = 0;
1060+ for (var i = 0; i < range.length; i++) {
1061+ result += Math.pow((TypeCaster.valueToNumber(range[i]) - mean), 2);
1062+ }
1063+ return result;
1064+};
1065+
1066 /**
1067 * Returns the median value in a numeric dataset.
1068 * @param values The value(s) or range(s) to consider when calculating the median value.
1069@@ -238,12 +263,539 @@ var PEARSON = function (...values) {
1070 return CORREL.apply(this, values);
1071 };
1072
1073+/**
1074+ * Returns the value of the exponential distribution function with a specified lambda at a specified value.
1075+ * @param values[0] x - The input to the exponential distribution function. If cumulative is TRUE then EXPONDIST returns
1076+ * the cumulative probability of all values up to x.
1077+ * @param values[1] lambda - The lambda to specify the exponential distribution function.
1078+ * @param values[2] cumulative - Whether to use the exponential cumulative distribution.
1079+ * @returns {number} value of the exponential distribution function.
1080+ * @constructor
1081+ */
1082+var EXPONDIST = function (...values) : number {
1083+ function cdf(x, rate) {
1084+ return x < 0 ? 0 : 1 - Math.exp(-rate * x);
1085+ }
1086+ function pdf(x, rate) {
1087+ return x < 0 ? 0 : rate * Math.exp(-rate * x);
1088+ }
1089+ ArgsChecker.checkLength(values, 3);
1090+ var x = TypeCaster.firstValueAsNumber(values[0]);
1091+ var lambda = TypeCaster.firstValueAsNumber(values[1]);
1092+ var cumulative = TypeCaster.firstValueAsBoolean(values[2]);
1093+ return (cumulative) ? cdf(x, lambda) : pdf(x, lambda);
1094+};
1095+
1096+/**
1097+ * Calculates the left-tailed F probability distribution (degree of diversity) for two data sets with given input x.
1098+ * Alternately called Fisher-Snedecor distribution or Snecdor's F distribution.
1099+ * @param values[0] x - The input to the F probability distribution function. The value at which to evaluate the function.
1100+ * Must be a positive number.
1101+ * @param values[1] degrees_freedom1 - The numerator degrees of freedom.
1102+ * @param values[2] degrees_freedom2 - The denominator degrees of freedom.
1103+ * @param values[3] cumulative - Logical value that determines the form of the function. If true returns the cumulative
1104+ * distribution function. If false returns the probability density function.
1105+ * @returns {number|undefined|boolean} left-tailed F probability distribution
1106+ * @constructor
1107+ * TODO: This function should be stricter in its return type.
1108+ */
1109+var FDIST$LEFTTAILED = function (...values) : number|undefined|boolean {
1110+ function gammaln(x) {
1111+ var j = 0;
1112+ var cof = [
1113+ 76.18009172947146, -86.50532032941677, 24.01409824083091,
1114+ -1.231739572450155, 0.1208650973866179e-2, -0.5395239384953e-5
1115+ ];
1116+ var ser = 1.000000000190015;
1117+ var xx, y, tmp;
1118+ tmp = (y = xx = x) + 5.5;
1119+ tmp -= (xx + 0.5) * Math.log(tmp);
1120+ for (; j < 6; j++)
1121+ ser += cof[j] / ++y;
1122+ return Math.log(2.5066282746310005 * ser / xx) - tmp;
1123+ }
1124+ function gammafn(x) {
1125+ var p = [-1.716185138865495, 24.76565080557592, -379.80425647094563,
1126+ 629.3311553128184, 866.9662027904133, -31451.272968848367,
1127+ -36144.413418691176, 66456.14382024054
1128+ ];
1129+ var q = [-30.8402300119739, 315.35062697960416, -1015.1563674902192,
1130+ -3107.771671572311, 22538.118420980151, 4755.8462775278811,
1131+ -134659.9598649693, -115132.2596755535];
1132+ var fact;
1133+ var n = 0;
1134+ var xden = 0;
1135+ var xnum = 0;
1136+ var y = x;
1137+ var i, z, yi, res;
1138+ if (y <= 0) {
1139+ res = y % 1 + 3.6e-16;
1140+ if (res) {
1141+ fact = (!(y & 1) ? 1 : -1) * Math.PI / Math.sin(Math.PI * res);
1142+ y = 1 - y;
1143+ } else {
1144+ return Infinity;
1145+ }
1146+ }
1147+ yi = y;
1148+ if (y < 1) {
1149+ z = y++;
1150+ } else {
1151+ z = (y -= n = (y | 0) - 1) - 1;
1152+ }
1153+ for (i = 0; i < 8; ++i) {
1154+ xnum = (xnum + p[i]) * z;
1155+ xden = xden * z + q[i];
1156+ }
1157+ res = xnum / xden + 1;
1158+ if (yi < y) {
1159+ res /= yi;
1160+ } else if (yi > y) {
1161+ for (i = 0; i < n; ++i) {
1162+ res *= y;
1163+ y++;
1164+ }
1165+ }
1166+ if (fact) {
1167+ res = fact / res;
1168+ }
1169+ return res;
1170+ }
1171+ function betacf(x, a, b) {
1172+ var fpmin = 1e-30;
1173+ var m = 1;
1174+ var qab = a + b;
1175+ var qap = a + 1;
1176+ var qam = a - 1;
1177+ var c = 1;
1178+ var d = 1 - qab * x / qap;
1179+ var m2, aa, del, h;
1180+
1181+ // These q's will be used in factors that occur in the coefficients
1182+ if (Math.abs(d) < fpmin)
1183+ d = fpmin;
1184+ d = 1 / d;
1185+ h = d;
1186+
1187+ for (; m <= 100; m++) {
1188+ m2 = 2 * m;
1189+ aa = m * (b - m) * x / ((qam + m2) * (a + m2));
1190+ // One step (the even one) of the recurrence
1191+ d = 1 + aa * d;
1192+ if (Math.abs(d) < fpmin)
1193+ d = fpmin;
1194+ c = 1 + aa / c;
1195+ if (Math.abs(c) < fpmin)
1196+ c = fpmin;
1197+ d = 1 / d;
1198+ h *= d * c;
1199+ aa = -(a + m) * (qab + m) * x / ((a + m2) * (qap + m2));
1200+ // Next step of the recurrence (the odd one)
1201+ d = 1 + aa * d;
1202+ if (Math.abs(d) < fpmin)
1203+ d = fpmin;
1204+ c = 1 + aa / c;
1205+ if (Math.abs(c) < fpmin)
1206+ c = fpmin;
1207+ d = 1 / d;
1208+ del = d * c;
1209+ h *= del;
1210+ if (Math.abs(del - 1.0) < 3e-7)
1211+ break;
1212+ }
1213+
1214+ return h;
1215+ }
1216+ function ibeta(x, a, b) {
1217+ // Factors in front of the continued fraction.
1218+ var bt = (x === 0 || x === 1) ? 0 :
1219+ Math.exp(gammaln(a + b) - gammaln(a) -
1220+ gammaln(b) + a * Math.log(x) + b *
1221+ Math.log(1 - x));
1222+ if (x < 0 || x > 1)
1223+ return false;
1224+ if (x < (a + 1) / (a + b + 2))
1225+ // Use continued fraction directly.
1226+ return bt * betacf(x, a, b) / a;
1227+ // else use continued fraction after making the symmetry transformation.
1228+ return 1 - bt * betacf(1 - x, b, a) / b;
1229+ }
1230+ function cdf(x, df1, df2) {
1231+ return ibeta((df1 * x) / (df1 * x + df2), df1 / 2, df2 / 2);
1232+ }
1233+ function pdf(x, df1, df2) {
1234+ if (x < 0) {
1235+ return undefined;
1236+ }
1237+ return Math.sqrt((Math.pow(df1 * x, df1) * Math.pow(df2, df2)) /
1238+ (Math.pow(df1 * x + df2, df1 + df2))) /
1239+ (x * betafn(df1/2, df2/2));
1240+ }
1241+ function betaln(x, y) {
1242+ return gammaln(x) + gammaln(y) - gammaln(x + y);
1243+ }
1244+ function betafn(x, y) {
1245+ // ensure arguments are positive
1246+ if (x <= 0 || y <= 0)
1247+ return undefined;
1248+ // make sure x + y doesn't exceed the upper limit of usable values
1249+ return (x + y > 170) ? Math.exp(betaln(x, y)) : gammafn(x) * gammafn(y) / gammafn(x + y);
1250+ }
1251+ ArgsChecker.checkLength(values, 4);
1252+ var x = TypeCaster.firstValueAsNumber(values[0]);
1253+ if (x < 0) {
1254+ throw new CellError(ERRORS.NUM_ERROR, "Function F.DIST parameter 1 value is " + x + ". It should be greater than or equal to 0.");
1255+ }
1256+ var d1 = TypeCaster.firstValueAsNumber(values[1]);
1257+ var d2 = TypeCaster.firstValueAsNumber(values[2]);
1258+ var cumulative = TypeCaster.firstValueAsBoolean(values[3]);
1259+ return (cumulative) ? cdf(x, d1, d2) : pdf(x, d1, d2);
1260+};
1261+
1262+/**
1263+ * Returns the inverse of the (right-tailed) F probability distribution. If p = FDIST(x,...), then FINV(p,...) = x. The
1264+ * F distribution can be used in an F-test that compares the degree of variability in two data sets.
1265+ * @param values[0] probability - A probability associated with the F cumulative distribution.
1266+ * @param values[1] deg_freedom1 - Required. The numerator degrees of freedom.
1267+ * @param values[2] deg_freedom2 - Required. The denominator degrees of freedom.
1268+ * @returns {number} inverse of the (right-tailed) F probability distribution
1269+ * @constructor
1270+ * TODO: This function needs to be tested more thuroughly.
1271+ */
1272+var FINV = function (...values) : number {
1273+ function betacf(x, a, b) {
1274+ var fpmin = 1e-30;
1275+ var m = 1;
1276+ var qab = a + b;
1277+ var qap = a + 1;
1278+ var qam = a - 1;
1279+ var c = 1;
1280+ var d = 1 - qab * x / qap;
1281+ var m2, aa, del, h;
1282+
1283+ // These q's will be used in factors that occur in the coefficients
1284+ if (Math.abs(d) < fpmin)
1285+ d = fpmin;
1286+ d = 1 / d;
1287+ h = d;
1288+
1289+ for (; m <= 100; m++) {
1290+ m2 = 2 * m;
1291+ aa = m * (b - m) * x / ((qam + m2) * (a + m2));
1292+ // One step (the even one) of the recurrence
1293+ d = 1 + aa * d;
1294+ if (Math.abs(d) < fpmin)
1295+ d = fpmin;
1296+ c = 1 + aa / c;
1297+ if (Math.abs(c) < fpmin)
1298+ c = fpmin;
1299+ d = 1 / d;
1300+ h *= d * c;
1301+ aa = -(a + m) * (qab + m) * x / ((a + m2) * (qap + m2));
1302+ // Next step of the recurrence (the odd one)
1303+ d = 1 + aa * d;
1304+ if (Math.abs(d) < fpmin)
1305+ d = fpmin;
1306+ c = 1 + aa / c;
1307+ if (Math.abs(c) < fpmin)
1308+ c = fpmin;
1309+ d = 1 / d;
1310+ del = d * c;
1311+ h *= del;
1312+ if (Math.abs(del - 1.0) < 3e-7)
1313+ break;
1314+ }
1315+
1316+ return h;
1317+ }
1318+ function ibeta(x, a, b) : number {
1319+ // Factors in front of the continued fraction.
1320+ var bt = (x === 0 || x === 1) ? 0 :
1321+ Math.exp(gammaln(a + b) - gammaln(a) -
1322+ gammaln(b) + a * Math.log(x) + b *
1323+ Math.log(1 - x));
1324+ if (x < 0 || x > 1)
1325+ // WARNING: I changed this to 0, because TS complains about doing numerical operations on boolean values.
1326+ // Still safe in javascript, but not TS.
1327+ return 0;
1328+ if (x < (a + 1) / (a + b + 2))
1329+ // Use continued fraction directly.
1330+ return bt * betacf(x, a, b) / a;
1331+ // else use continued fraction after making the symmetry transformation.
1332+ return 1 - bt * betacf(1 - x, b, a) / b;
1333+ }
1334+ function gammaln(x) {
1335+ var j = 0;
1336+ var cof = [
1337+ 76.18009172947146, -86.50532032941677, 24.01409824083091,
1338+ -1.231739572450155, 0.1208650973866179e-2, -0.5395239384953e-5
1339+ ];
1340+ var ser = 1.000000000190015;
1341+ var xx, y, tmp;
1342+ tmp = (y = xx = x) + 5.5;
1343+ tmp -= (xx + 0.5) * Math.log(tmp);
1344+ for (; j < 6; j++)
1345+ ser += cof[j] / ++y;
1346+ return Math.log(2.5066282746310005 * ser / xx) - tmp;
1347+ }
1348+ function ibetainv(p, a, b) {
1349+ var EPS = 1e-8;
1350+ var a1 = a - 1;
1351+ var b1 = b - 1;
1352+ var j = 0;
1353+ var lna, lnb, pp, t, u, err, x, al, h, w, afac;
1354+ if (p <= 0)
1355+ return 0;
1356+ if (p >= 1)
1357+ return 1;
1358+ if (a >= 1 && b >= 1) {
1359+ pp = (p < 0.5) ? p : 1 - p;
1360+ t = Math.sqrt(-2 * Math.log(pp));
1361+ x = (2.30753 + t * 0.27061) / (1 + t* (0.99229 + t * 0.04481)) - t;
1362+ if (p < 0.5)
1363+ x = -x;
1364+ al = (x * x - 3) / 6;
1365+ h = 2 / (1 / (2 * a - 1) + 1 / (2 * b - 1));
1366+ w = (x * Math.sqrt(al + h) / h) - (1 / (2 * b - 1) - 1 / (2 * a - 1)) *
1367+ (al + 5 / 6 - 2 / (3 * h));
1368+ x = a / (a + b * Math.exp(2 * w));
1369+ } else {
1370+ lna = Math.log(a / (a + b));
1371+ lnb = Math.log(b / (a + b));
1372+ t = Math.exp(a * lna) / a;
1373+ u = Math.exp(b * lnb) / b;
1374+ w = t + u;
1375+ if (p < t / w)
1376+ x = Math.pow(a * w * p, 1 / a);
1377+ else
1378+ x = 1 - Math.pow(b * w * (1 - p), 1 / b);
1379+ }
1380+ afac = -gammaln(a) - gammaln(b) + gammaln(a + b);
1381+ for(; j < 10; j++) {
1382+ if (x === 0 || x === 1)
1383+ return x;
1384+ err = ibeta(x, a, b) - p;
1385+ t = Math.exp(a1 * Math.log(x) + b1 * Math.log(1 - x) + afac);
1386+ u = err / t;
1387+ x -= (t = u / (1 - 0.5 * Math.min(1, u * (a1 / x - b1 / (1 - x)))));
1388+ if (x <= 0)
1389+ x = 0.5 * (x + t);
1390+ if (x >= 1)
1391+ x = 0.5 * (x + t + 1);
1392+ if (Math.abs(t) < EPS * x && j > 0)
1393+ break;
1394+ }
1395+ return x;
1396+ }
1397+ function inv(x, df1, df2) {
1398+ return df2 / (df1 * (1 / ibetainv(x, df1 / 2, df2 / 2) - 1));
1399+ }
1400+ ArgsChecker.checkLength(values, 3);
1401+ var probability = TypeCaster.firstValueAsNumber(values[0]);
1402+ if (probability <= 0.0 || probability > 1.0) {
1403+ // TODO: Throw num error.
1404+ }
1405+ var d1 = TypeCaster.firstValueAsNumber(values[1]);
1406+ var d2 = TypeCaster.firstValueAsNumber(values[2]);
1407+ return inv(1.0 - probability, d1, d2);
1408+};
1409+
1410+/**
1411+ * Returns the Fisher transformation of a specified value.
1412+ * @param values[0] value - The value for which to calculate the Fisher transformation.
1413+ * @returns {number} Fisher transformation
1414+ * @constructor
1415+ */
1416+var FISHER = function (...values) : number {
1417+ ArgsChecker.checkLength(values, 1);
1418+ var x = TypeCaster.firstValueAsNumber(values[0]);
1419+ if (x <= -1 || x >= 1) {
1420+ throw new CellError(ERRORS.NUM_ERROR, "Function FISHER parameter 1 value is " + x + ". Valid values are between -1 and 1 exclusive.");
1421+ }
1422+ return Math.log((1 + x) / (1 - x)) / 2;
1423+};
1424+
1425+/**
1426+ * Returns the inverse Fisher transformation of a specified value.
1427+ * @param values[0] value - The value for which to calculate the inverse Fisher transformation.
1428+ * @returns {number} inverse Fisher transformation
1429+ * @constructor
1430+ */
1431+var FISHERINV = function (...values) : number {
1432+ ArgsChecker.checkLength(values, 1);
1433+ var y = TypeCaster.firstValueAsNumber(values[0]);
1434+ var e2y = Math.exp(2 * y);
1435+ return (e2y - 1) / (e2y + 1);
1436+};
1437+
1438+/**
1439+ * Returns the maximum value in a numeric dataset.
1440+ * @param values The values or range(s) to consider when calculating the maximum value.
1441+ * @returns {number} the maximum value of the dataset
1442+ * @constructor
1443+ */
1444+var MAX = function (...values) {
1445+ ArgsChecker.checkAtLeastLength(values, 1);
1446+ var maxSoFar = -Infinity;
1447+ for (var i = 0; i < values.length; i++) {
1448+ if (values[i] instanceof Array) {
1449+ if (values[i].length === 0) {
1450+ throw new CellError(ERRORS.REF_ERROR, "Reference does not exist.");
1451+ }
1452+ var filtered = Filter.filterOutStringValues(values[i]);
1453+ if (filtered.length !== 0) {
1454+ maxSoFar = Math.max(MAX.apply(this, filtered), maxSoFar);
1455+ }
1456+ } else {
1457+ maxSoFar = Math.max(TypeCaster.valueToNumber(values[i]), maxSoFar);
1458+ }
1459+ }
1460+ return maxSoFar;
1461+};
1462+
1463+/**
1464+ * Returns the maximum numeric value in a dataset.
1465+ * @param values The value(s) or range(s) to consider when calculating the maximum value.
1466+ * @returns {number} maximum value of the dataset
1467+ * @constructor
1468+ */
1469+var MAXA = function (...values) : number {
1470+ return MAX.apply(this, values);
1471+};
1472+
1473+
1474+/**
1475+ * Returns the minimum value in a numeric dataset.
1476+ * @param values The value(s) or range(s) to consider when calculating the minimum value.
1477+ * @returns {number} the minimum value of the dataset
1478+ * @constructor
1479+ */
1480+var MIN = function (...values) {
1481+ ArgsChecker.checkAtLeastLength(values, 1);
1482+ var minSoFar = Infinity;
1483+ for (var i = 0; i < values.length; i++) {
1484+ if (values[i] instanceof Array) {
1485+ if (values[i].length === 0) {
1486+ throw new CellError(ERRORS.REF_ERROR, "Reference does not exist.");
1487+ }
1488+ var filtered = Filter.filterOutStringValues(values[i]);
1489+ if (filtered.length !== 0) {
1490+ minSoFar = Math.min(MIN.apply(this, filtered), minSoFar);
1491+ }
1492+ } else {
1493+ minSoFar = Math.min(TypeCaster.valueToNumber(values[i]), minSoFar);
1494+ }
1495+ }
1496+ return minSoFar;
1497+};
1498+
1499+
1500+/**
1501+ * Returns the minimum numeric value in a dataset.
1502+ * @param values The value(s) or range(s) to consider when calculating the minimum value.
1503+ * @returns {number} the minimum value in the dataset
1504+ * @constructor
1505+ */
1506+var MINA = function (...values) : number {
1507+ return MIN.apply(this, values);
1508+};
1509+
1510+
1511+/**
1512+ * Returns the average of a range depending on criteria.
1513+ * @param values[0] criteria_range - The range to check against criterion.
1514+ * @param values[1] criterion - The pattern or test to apply to criteria_range.
1515+ * @param values[2] average_range - [optional] The range to average. If not included, criteria_range is used for the
1516+ * average instead.
1517+ * @returns {number}
1518+ * @constructor
1519+ * TODO: This needs to take nested range values.
1520+ * TODO: This needs to also accept a third parameter "average_range"
1521+ */
1522+var AVERAGEIF = function (...values) {
1523+ ArgsChecker.checkLength(values, 2);
1524+ var range = values[0];
1525+ var criteriaEvaluation = CriteriaFunctionFactory.createCriteriaFunction(values[1]);
1526+
1527+ var result = 0;
1528+ var count = 0;
1529+ for (var i = 0; i < range.length; i++) {
1530+ var val = TypeCaster.valueToNumber(range[i]);
1531+ if (criteriaEvaluation(val)) {
1532+ result = result + val;
1533+ count++;
1534+ }
1535+ }
1536+ if (count === 0) {
1537+ throw new CellError(ERRORS.DIV_ZERO_ERROR, "Evaluation of function AVERAGEIF caused a divide by zero error.");
1538+ }
1539+ return result / count;
1540+};
1541+
1542+
1543+/**
1544+ * Returns the a count of the number of numeric values in a dataset.
1545+ * @param values The values or ranges to consider when counting.
1546+ * @returns {number} number of numeric values in a dataset.
1547+ * @constructor
1548+ */
1549+var COUNT = function (...values) : number {
1550+ ArgsChecker.checkAtLeastLength(values, 1);
1551+ var count = 0;
1552+ for (var i = 0; i < values.length; i++) {
1553+ if (values[i] instanceof Array) {
1554+ if (values[i].length > 0) {
1555+ count += COUNT.apply(this, values[i]);
1556+ }
1557+ } else if (TypeCaster.canCoerceToNumber(values[i])) {
1558+ count++;
1559+ }
1560+ }
1561+ return count;
1562+};
1563+
1564+/**
1565+ * Returns the a count of the number of values in a dataset.
1566+ * @param values The values or ranges to consider when counting.
1567+ * @returns {number} number of values in a dataset.
1568+ * @constructor
1569+ */
1570+var COUNTA = function (...values) : number {
1571+ ArgsChecker.checkAtLeastLength(values, 1);
1572+ var count = 0;
1573+ for (var i = 0; i < values.length; i++) {
1574+ if (values[i] instanceof Array) {
1575+ if (values[i].length > 0) {
1576+ count += COUNTA.apply(this, values[i]);
1577+ } else {
1578+ count++;
1579+ }
1580+ } else {
1581+ count++;
1582+ }
1583+ }
1584+ return count;
1585+};
1586+
1587
1588 export {
1589 AVERAGE,
1590 AVERAGEA,
1591+ AVERAGEIF,
1592 AVEDEV,
1593 CORREL,
1594+ COUNT,
1595+ COUNTA,
1596 PEARSON,
1597- MEDIAN
1598+ MEDIAN,
1599+ DEVSQ,
1600+ EXPONDIST,
1601+ FDIST$LEFTTAILED,
1602+ FINV,
1603+ FISHER,
1604+ FISHERINV,
1605+ MAX,
1606+ MAXA,
1607+ MIN,
1608+ MINA
1609 }
1610\ No newline at end of file