spreadsheet
typeScript/javascript spreadsheet parser, with formulas.
git clone https://git.vogt.world/spreadsheet.git
Log | Files | README.md
← Commit log
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