spreadsheet
typeScript/javascript spreadsheet parser, with formulas.
git clone https://git.vogt.world/spreadsheet.git
Log | Files | README.md
← Commit log
commit
message
[npm prep] getting package ready for publishing
author
Ben Vogt <[email protected]>
date
2017-05-20 15:10:21
stats
28 file(s) changed, 7242 insertions(+), 33 deletions(-)
files
.gitignore
README.md
TODO.md
dist/Cell.js
dist/Errors.js
dist/Formulas.js
dist/Formulas/AllFormulas.js
dist/Formulas/Date.js
dist/Formulas/Engineering.js
dist/Formulas/Financial.js
dist/Formulas/Logical.js
dist/Formulas/Math.js
dist/Formulas/Statistical.js
dist/Formulas/Text.js
dist/Sheet.js
dist/Utilities/ArgsChecker.js
dist/Utilities/CriteriaFunctionFactory.js
dist/Utilities/DateRegExBuilder.js
dist/Utilities/Filter.js
dist/Utilities/MathHelpers.js
dist/Utilities/Serializer.js
dist/Utilities/TypeConverter.js
dist/parser.js
package.json
src/Main.ts
src/Sheet.ts
tests/SheetFormulaTest.ts
tsconfig.json
   1diff --git a/.gitignore b/.gitignore
   2index 00696b8..04c7580 100644
   3--- a/.gitignore
   4+++ b/.gitignore
   5@@ -7,7 +7,6 @@
   6 *.profi
   7 *.module-cache
   8 node_modules/
   9-dist/
  10 build/
  11 npm-debug.log
  12 output/
  13diff --git a/README.md b/README.md
  14index a798beb..20d7f9a 100644
  15--- a/README.md
  16+++ b/README.md
  17@@ -3,25 +3,74 @@ TypeScript/javascript spreadsheet.
  18 
  19 ## Usage
  20 
  21-### Example
  22+### Examples
  23 
  24+**Using a Sheet**
  25 ```javascript
  26+var Sheet = require("./dist/Sheet.js").Sheet;
  27 var sheet = new Sheet();
  28 sheet.setCell("A1", "10");
  29 sheet.setCell("A2", "14");
  30 sheet.setCell("A4", "10e2");
  31 sheet.setCell("A5", "99.1");
  32 sheet.setCell("B1", "=SUM(A1:A5)");
  33-console.log(sheet.getCell("B1").getValue()); // output: 1124
  34+sheet.getCell("B1").getValue(); // returns: 1123.1
  35 ```
  36 
  37-### Ranges
  38+**Using Formulas Directly**
  39+```javascript
  40+var Formulas = require("./dist/Sheet.js").AllFormulas;
  41+Formulas.SUM(1, 2, 3, [4, 5, 6], "7"); // returns: 28
  42+```
  43+
  44+For a full list of formulas, see [DOCS.md](DOCS.md)
  45+
  46+
  47+**Nested Formulas**
  48+```javascript
  49+sheet.setCell('A1', '=SIN(PI() / 2)')
  50+sheet.getCell("A1").getValue(); // returns: 1
  51+```
  52+
  53+**Date Conversion**
  54+```javascript
  55+sheet.setCell('A1', '=DATEDIF("1992-6-19", "1996-6-19", "Y")')
  56+sheet.getCell("A1").getValue(); // returns: 4
  57+```
  58+
  59+**Number Parsing**
  60+```javascript
  61+sheet.setCell('A1', '="10e1" + 44');
  62+sheet.getCell("A1").getValue(); // returns: 144
  63+
  64+sheet.setCell('A2', '="1,000,000" + 1');
  65+sheet.getCell("A2").getValue(); // returns: 1000001
  66+
  67+sheet.setCell('A3', '="-$10.00" + 0');
  68+sheet.getCell("A3").getValue(); // returns: -10
  69+
  70+sheet.setCell('A4', '=10% + 1');
  71+sheet.getCell("A4").getValue(); // returns: 1.1
  72+
  73+sheet.setCell('A5', '= 2 ^ 10');
  74+sheet.getCell("A5").getValue(); // returns: 1024
  75+```
  76+
  77+
  78+## Ranges
  79 
  80 In MS Excel, and Google Spreadsheets, literal ranges are denoted with opening and closing curly-brackets. E.g. "{1, 2, 3}". In this implementation however, literal ranges are denoted with opening and closing brackets. E.g. "[1, 2, 3]".
  81 
  82+```javascript
  83+// OK
  84+sheet.setCell('A1', '=SUM([1, 2, 3])');
  85+// NOT OK
  86+sheet.setCell('A1', '=SUM({1, 2, 3})');
  87+```
  88+
  89 
  90 ## Docs
  91-[Docs here](DOCS.md)
  92+See [DOCS.md](DOCS.md) for full list and documentation of all formulas available.
  93 
  94 
  95 ## Contributing
  96@@ -38,7 +87,7 @@ If you're adding a new formula, before you submit a pull request or push ensure
  97 6) Build DOCS.md with `npm run docs`.
  98 
  99 
 100-### Why?
 101+## Why?
 102 Near the end of 2016 I began to ask myself why I didn't know more about MS Excel and Google Spreadsheets. Why didn't I know more about the most popular programing language in the world? I began to reverse engineer Google Spreadsheets in particular, gaining a better understanding along the way.
 103 
 104 I chose TypeScript because, coming from Java, it is really nice to be able to see type errors, and catch them. I also just enjoy getting specific with my return types, even if the specifications for a spreadsheet treat type flexibly.
 105@@ -46,10 +95,10 @@ I chose TypeScript because, coming from Java, it is really nice to be able to se
 106 For the formula documentation, I tried to be at least -- if not more -- thorough as Google Spreadsheets.
 107 
 108 
 109-### License
 110+## License
 111 
 112 For this repository's code license, and related licenses, see LICENSES directory.
 113 
 114 
 115-### Acknowledgements
 116+## Acknowledgements
 117 This is largely a re-write of [Handsontable](https://github.com/handsontable)'s [https://github.com/handsontable/ruleJS](https://github.com/handsontable/ruleJS), and [https://github.com/sutoiku/formula.js/](https://github.com/sutoiku/formula.js/). The parser was derived from Handsontable's, and many of the formulas were created with FormulaJS's formulas as a reference point.
 118\ No newline at end of file
 119diff --git a/TODO.md b/TODO.md
 120index 54e290d..025b7e5 100644
 121--- a/TODO.md
 122+++ b/TODO.md
 123@@ -1,5 +1,4 @@
 124 # TODO
 125-Things I should do.
 126 
 127 
 128 ### Cells should have `formatAs` fields.
 129diff --git a/dist/Cell.js b/dist/Cell.js
 130new file mode 100644
 131index 0000000..58de5db
 132--- /dev/null
 133+++ b/dist/Cell.js
 134@@ -0,0 +1,155 @@
 135+"use strict";
 136+exports.__esModule = true;
 137+/**
 138+ * Cell represents a cell in the spreadsheet. It contains a nullable rawFormulaText, and a value, which is not nullable unless
 139+ * the parsing of the rawFormulaText results in an error.
 140+ */
 141+var Cell = (function () {
 142+    /**
 143+     * Creates an empty cell with an id.
 144+     * @param id key of the cell in A1-format.
 145+     */
 146+    function Cell(id) {
 147+        /**
 148+         * The raw formula text that can be parse, excluding the proceeding =
 149+         * E.g: SUM(A2:A4, 10)
 150+         */
 151+        this.rawFormulaText = null;
 152+        this.typedValue = null;
 153+        this.dependencies = [];
 154+        this.error = null;
 155+        var key = parseKey(id);
 156+        this.id = id;
 157+        this.row = key.y;
 158+        this.col = key.x;
 159+    }
 160+    /**
 161+     * Update this cell's dependencies, where `dependencies` is a unique list of A1-format cell IDs.
 162+     * @param dependencies to merge with existing dependencies.
 163+     */
 164+    Cell.prototype.updateDependencies = function (dependencies) {
 165+        for (var index in dependencies) {
 166+            if (this.dependencies.indexOf(dependencies[index]) === -1) {
 167+                this.dependencies.push(dependencies[index]);
 168+            }
 169+        }
 170+    };
 171+    /**
 172+     * Return a list of dependencies in A1-format cell IDs, in no particular order, but likely in order of occurrence in
 173+     * rawFormulaText.
 174+     * @returns {Array<string>} list of dependencies in A1-format
 175+     */
 176+    Cell.prototype.getDependencies = function () {
 177+        return this.dependencies;
 178+    };
 179+    /**
 180+     * Return the zero-indexed column number of this cell.
 181+     * @returns {number} column
 182+     */
 183+    Cell.prototype.getColumn = function () {
 184+        return this.col;
 185+    };
 186+    /**
 187+     * Return the zero-indexed row number of this cell.
 188+     * @returns {number} row
 189+     */
 190+    Cell.prototype.getRow = function () {
 191+        return this.row;
 192+    };
 193+    /**
 194+     * Get the A1-format ID of this cell.
 195+     * @returns {string} cell ID
 196+     */
 197+    Cell.prototype.getId = function () {
 198+        return this.id;
 199+    };
 200+    /**
 201+     * Get the rawFormulaText of this cell if set. Defaults to null, so should be used in combination with hasFormula().
 202+     * @returns {string} rawFormulaText of this cell, if set. Nullable.
 203+     */
 204+    Cell.prototype.getFormula = function () {
 205+        return this.rawFormulaText;
 206+    };
 207+    /**
 208+     * Returns true if this cell has a formula to be parsed.
 209+     * @returns {boolean}
 210+     */
 211+    Cell.prototype.hasFormula = function () {
 212+        return this.rawFormulaText !== null;
 213+    };
 214+    /**
 215+     * Set the value of this cell. If this cell has a primitive value (does not contain a rawFormulaText), it could be set to a
 216+     * value while the rawFormulaText field is still null.
 217+     * @param value to set
 218+     */
 219+    Cell.prototype.setValue = function (value) {
 220+        this.typedValue = value;
 221+    };
 222+    /**
 223+     * Sets the value or rawFormulaText for this cell. If the input begins with =, then it is considered to be a rawFormulaText. If it
 224+     * is not, then it is a value, and set as the raw value for this cell.
 225+     * @param rawFormula
 226+     */
 227+    Cell.prototype.setRawValue = function (rawFormula) {
 228+        if (rawFormula.charAt(0) === "=") {
 229+            this.rawFormulaText = rawFormula.substr(1);
 230+        }
 231+        else {
 232+            this.typedValue = rawFormula;
 233+        }
 234+    };
 235+    /**
 236+     * Get the value of this cell. Since value could be null do to an error in the rawFormulaText, this could return null.
 237+     * @returns {any}
 238+     */
 239+    Cell.prototype.getValue = function () {
 240+        return this.typedValue;
 241+    };
 242+    /**
 243+     * CLears a cells value.
 244+     */
 245+    Cell.prototype.clearValue = function () {
 246+        this.typedValue = null;
 247+    };
 248+    /**
 249+     * Set error for this cell. Usually in the case of a parse error when parsing the rawFormulaText.
 250+     * @param error to set.
 251+     */
 252+    Cell.prototype.setError = function (error) {
 253+        this.error = error;
 254+    };
 255+    /**
 256+     * Get the error for this cell. If the rawFormulaText is not parsed properly, or is null, this could be null.
 257+     * @returns {Error} error to return, could be null.
 258+     */
 259+    Cell.prototype.getError = function () {
 260+        return this.error;
 261+    };
 262+    /**
 263+     * Returns the human-readable string representation of this cell, omitting some obvious fields.
 264+     * @returns {string}
 265+     */
 266+    Cell.prototype.toString = function () {
 267+        return "id=" + this.id + ", value=" + this.typedValue + ", rawFormulaText=" + this.rawFormulaText + ", error=" + this.error;
 268+    };
 269+    return Cell;
 270+}());
 271+exports.Cell = Cell;
 272+function toNum(chr) {
 273+    chr = chr.replace(/\$/g, '');
 274+    var base = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', i, j, result = 0;
 275+    for (i = 0, j = chr.length - 1; i < chr.length; i += 1, j -= 1) {
 276+        result += Math.pow(base.length, j) * (base.indexOf(chr[i]) + 1);
 277+    }
 278+    if (result) {
 279+        --result;
 280+    }
 281+    return result;
 282+}
 283+function parseKey(cell) {
 284+    var num = cell.match(/\d+$/), alpha = cell.replace(num, '');
 285+    return {
 286+        x: toNum(alpha),
 287+        y: parseInt(num[0], 10) - 1
 288+    };
 289+}
 290diff --git a/dist/Errors.js b/dist/Errors.js
 291new file mode 100644
 292index 0000000..1123f4a
 293--- /dev/null
 294+++ b/dist/Errors.js
 295@@ -0,0 +1,96 @@
 296+"use strict";
 297+var __extends = (this && this.__extends) || (function () {
 298+    var extendStatics = Object.setPrototypeOf ||
 299+        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
 300+        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
 301+    return function (d, b) {
 302+        extendStatics(d, b);
 303+        function __() { this.constructor = d; }
 304+        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
 305+    };
 306+})();
 307+exports.__esModule = true;
 308+var NULL_ERROR = "#NULL!";
 309+exports.NULL_ERROR = NULL_ERROR;
 310+var DIV_ZERO_ERROR = "#DIV/0!";
 311+exports.DIV_ZERO_ERROR = DIV_ZERO_ERROR;
 312+var VALUE_ERROR = "#VALUE!";
 313+exports.VALUE_ERROR = VALUE_ERROR;
 314+var REF_ERROR = "#REF!";
 315+exports.REF_ERROR = REF_ERROR;
 316+var NAME_ERROR = "#NAME!";
 317+exports.NAME_ERROR = NAME_ERROR;
 318+var NUM_ERROR = "#NUM!";
 319+exports.NUM_ERROR = NUM_ERROR;
 320+var NA_ERROR = "#N/A";
 321+exports.NA_ERROR = NA_ERROR;
 322+var NullError = (function (_super) {
 323+    __extends(NullError, _super);
 324+    function NullError(message) {
 325+        var _this = _super.call(this, message) || this;
 326+        _this.name = NULL_ERROR;
 327+        return _this;
 328+    }
 329+    return NullError;
 330+}(Error));
 331+exports.NullError = NullError;
 332+var DivZeroError = (function (_super) {
 333+    __extends(DivZeroError, _super);
 334+    function DivZeroError(message) {
 335+        var _this = _super.call(this, message) || this;
 336+        _this.name = DIV_ZERO_ERROR;
 337+        return _this;
 338+    }
 339+    return DivZeroError;
 340+}(Error));
 341+exports.DivZeroError = DivZeroError;
 342+var ValueError = (function (_super) {
 343+    __extends(ValueError, _super);
 344+    function ValueError(message) {
 345+        var _this = _super.call(this, message) || this;
 346+        _this.name = VALUE_ERROR;
 347+        return _this;
 348+    }
 349+    return ValueError;
 350+}(Error));
 351+exports.ValueError = ValueError;
 352+var RefError = (function (_super) {
 353+    __extends(RefError, _super);
 354+    function RefError(message) {
 355+        var _this = _super.call(this, message) || this;
 356+        _this.name = REF_ERROR;
 357+        return _this;
 358+    }
 359+    return RefError;
 360+}(Error));
 361+exports.RefError = RefError;
 362+var NameError = (function (_super) {
 363+    __extends(NameError, _super);
 364+    function NameError(message) {
 365+        var _this = _super.call(this, message) || this;
 366+        _this.name = NAME_ERROR;
 367+        return _this;
 368+    }
 369+    return NameError;
 370+}(Error));
 371+exports.NameError = NameError;
 372+var NumError = (function (_super) {
 373+    __extends(NumError, _super);
 374+    function NumError(message) {
 375+        var _this = _super.call(this, message) || this;
 376+        _this.name = NUM_ERROR;
 377+        return _this;
 378+    }
 379+    return NumError;
 380+}(Error));
 381+exports.NumError = NumError;
 382+var NAError = (function (_super) {
 383+    __extends(NAError, _super);
 384+    function NAError(message) {
 385+        var _this = _super.call(this, message) || this;
 386+        _this.name = NA_ERROR;
 387+        return _this;
 388+    }
 389+    return NAError;
 390+}(Error));
 391+exports.NAError = NAError;
 392diff --git a/dist/Formulas.js b/dist/Formulas.js
 393new file mode 100644
 394index 0000000..630ae31
 395--- /dev/null
 396+++ b/dist/Formulas.js
 397@@ -0,0 +1,17 @@
 398+"use strict";
 399+exports.__esModule = true;
 400+var AllFormulas = require("./Formulas/AllFormulas");
 401+var Formulas = {
 402+    exists: function (fn) {
 403+        return ((fn in AllFormulas) || (fn in AllFormulas.__COMPLEX));
 404+    },
 405+    get: function (fn) {
 406+        if (fn in AllFormulas) {
 407+            return AllFormulas[fn];
 408+        }
 409+        if (fn in AllFormulas.__COMPLEX) {
 410+            return AllFormulas.__COMPLEX[fn];
 411+        }
 412+    }
 413+};
 414+exports.Formulas = Formulas;
 415diff --git a/dist/Formulas/AllFormulas.js b/dist/Formulas/AllFormulas.js
 416new file mode 100644
 417index 0000000..8fc555c
 418--- /dev/null
 419+++ b/dist/Formulas/AllFormulas.js
 420@@ -0,0 +1,138 @@
 421+"use strict";
 422+exports.__esModule = true;
 423+var Math_1 = require("./Math");
 424+exports.ABS = Math_1.ABS;
 425+exports.ACOS = Math_1.ACOS;
 426+exports.ACOSH = Math_1.ACOSH;
 427+exports.ACOTH = Math_1.ACOTH;
 428+exports.ASIN = Math_1.ASIN;
 429+exports.ASINH = Math_1.ASINH;
 430+exports.ATAN = Math_1.ATAN;
 431+exports.ATAN2 = Math_1.ATAN2;
 432+exports.ATANH = Math_1.ATANH;
 433+exports.COT = Math_1.COT;
 434+exports.COTH = Math_1.COTH;
 435+exports.COSH = Math_1.COSH;
 436+exports.COS = Math_1.COS;
 437+exports.COUNTUNIQUE = Math_1.COUNTUNIQUE;
 438+exports.EVEN = Math_1.EVEN;
 439+exports.ERF = Math_1.ERF;
 440+exports.ERFC = Math_1.ERFC;
 441+exports.INT = Math_1.INT;
 442+exports.ISEVEN = Math_1.ISEVEN;
 443+exports.ISODD = Math_1.ISODD;
 444+exports.MOD = Math_1.MOD;
 445+exports.ODD = Math_1.ODD;
 446+exports.SIN = Math_1.SIN;
 447+exports.SINH = Math_1.SINH;
 448+exports.SUM = Math_1.SUM;
 449+exports.SQRT = Math_1.SQRT;
 450+exports.SQRTPI = Math_1.SQRTPI;
 451+exports.PI = Math_1.PI;
 452+exports.POWER = Math_1.POWER;
 453+exports.LOG = Math_1.LOG;
 454+exports.LOG10 = Math_1.LOG10;
 455+exports.LN = Math_1.LN;
 456+exports.TAN = Math_1.TAN;
 457+exports.TANH = Math_1.TANH;
 458+exports.ROUND = Math_1.ROUND;
 459+exports.ROUNDDOWN = Math_1.ROUNDDOWN;
 460+exports.ROUNDUP = Math_1.ROUNDUP;
 461+exports.SUMPRODUCT = Math_1.SUMPRODUCT;
 462+exports.SUMIF = Math_1.SUMIF;
 463+exports.SUMSQ = Math_1.SUMSQ;
 464+exports.SUMX2MY2 = Math_1.SUMX2MY2;
 465+exports.SUMX2PY2 = Math_1.SUMX2PY2;
 466+exports.FLOOR = Math_1.FLOOR;
 467+exports.IF = Math_1.IF;
 468+exports.COUNTIF = Math_1.COUNTIF;
 469+exports.COUNTIFS = Math_1.COUNTIFS;
 470+exports.CEILING = Math_1.CEILING;
 471+exports.TRUNC = Math_1.TRUNC;
 472+exports.RADIANS = Math_1.RADIANS;
 473+exports.DEGREES = Math_1.DEGREES;
 474+exports.COMBIN = Math_1.COMBIN;
 475+var Logical_1 = require("./Logical");
 476+exports.AND = Logical_1.AND;
 477+exports.EXACT = Logical_1.EXACT;
 478+exports.TRUE = Logical_1.TRUE;
 479+exports.FALSE = Logical_1.FALSE;
 480+exports.NOT = Logical_1.NOT;
 481+exports.OR = Logical_1.OR;
 482+exports.XOR = Logical_1.XOR;
 483+var Engineering_1 = require("./Engineering");
 484+exports.BIN2DEC = Engineering_1.BIN2DEC;
 485+exports.BIN2HEX = Engineering_1.BIN2HEX;
 486+exports.BIN2OCT = Engineering_1.BIN2OCT;
 487+exports.DEC2BIN = Engineering_1.DEC2BIN;
 488+exports.DEC2HEX = Engineering_1.DEC2HEX;
 489+exports.DEC2OCT = Engineering_1.DEC2OCT;
 490+exports.DELTA = Engineering_1.DELTA;
 491+var Financial_1 = require("./Financial");
 492+exports.ACCRINT = Financial_1.ACCRINT;
 493+exports.CUMPRINC = Financial_1.CUMPRINC;
 494+exports.CUMIPMT = Financial_1.CUMIPMT;
 495+exports.DB = Financial_1.DB;
 496+exports.DDB = Financial_1.DDB;
 497+exports.DOLLAR = Financial_1.DOLLAR;
 498+exports.DOLLARDE = Financial_1.DOLLARDE;
 499+exports.DOLLARFR = Financial_1.DOLLARFR;
 500+exports.EFFECT = Financial_1.EFFECT;
 501+var Statistical_1 = require("./Statistical");
 502+exports.AVERAGE = Statistical_1.AVERAGE;
 503+exports.AVERAGEA = Statistical_1.AVERAGEA;
 504+exports.AVERAGEIF = Statistical_1.AVERAGEIF;
 505+exports.AVEDEV = Statistical_1.AVEDEV;
 506+exports.CORREL = Statistical_1.CORREL;
 507+exports.COUNT = Statistical_1.COUNT;
 508+exports.COUNTA = Statistical_1.COUNTA;
 509+exports.PEARSON = Statistical_1.PEARSON;
 510+exports.MEDIAN = Statistical_1.MEDIAN;
 511+exports.DEVSQ = Statistical_1.DEVSQ;
 512+exports.EXPONDIST = Statistical_1.EXPONDIST;
 513+exports.FINV = Statistical_1.FINV;
 514+exports.FISHER = Statistical_1.FISHER;
 515+exports.FISHERINV = Statistical_1.FISHERINV;
 516+exports.MAX = Statistical_1.MAX;
 517+exports.MAXA = Statistical_1.MAXA;
 518+exports.MIN = Statistical_1.MIN;
 519+exports.MINA = Statistical_1.MINA;
 520+var Text_1 = require("./Text");
 521+exports.ARABIC = Text_1.ARABIC;
 522+exports.CHAR = Text_1.CHAR;
 523+exports.CODE = Text_1.CODE;
 524+exports.SPLIT = Text_1.SPLIT;
 525+exports.CONCATENATE = Text_1.CONCATENATE;
 526+exports.CONVERT = Text_1.CONVERT;
 527+var Date_1 = require("./Date");
 528+exports.DATE = Date_1.DATE;
 529+exports.DATEVALUE = Date_1.DATEVALUE;
 530+exports.DATEDIF = Date_1.DATEDIF;
 531+exports.DAYS = Date_1.DAYS;
 532+exports.DAY = Date_1.DAY;
 533+exports.DAYS360 = Date_1.DAYS360;
 534+exports.EDATE = Date_1.EDATE;
 535+exports.EOMONTH = Date_1.EOMONTH;
 536+exports.MONTH = Date_1.MONTH;
 537+exports.YEAR = Date_1.YEAR;
 538+exports.WEEKDAY = Date_1.WEEKDAY;
 539+exports.WEEKNUM = Date_1.WEEKNUM;
 540+exports.YEARFRAC = Date_1.YEARFRAC;
 541+exports.TIMEVALUE = Date_1.TIMEVALUE;
 542+exports.HOUR = Date_1.HOUR;
 543+exports.MINUTE = Date_1.MINUTE;
 544+exports.SECOND = Date_1.SECOND;
 545+exports.NETWORKDAYS = Date_1.NETWORKDAYS;
 546+exports.NETWORKDAYS$INTL = Date_1.NETWORKDAYS$INTL;
 547+exports.NOW = Date_1.NOW;
 548+exports.TODAY = Date_1.TODAY;
 549+exports.TIME = Date_1.TIME;
 550+exports.WORKDAY = Date_1.WORKDAY;
 551+exports.WORKDAY$INTL = Date_1.WORKDAY$INTL;
 552+// Using alias to bind dot-notation function names.
 553+var __COMPLEX = {
 554+    "F.DIST": Statistical_1.FDIST$LEFTTAILED,
 555+    "NETWORKDAYS.INTL": Date_1.NETWORKDAYS$INTL,
 556+    "WORKDAY.INTL": Date_1.WORKDAY$INTL
 557+};
 558+exports.__COMPLEX = __COMPLEX;
 559diff --git a/dist/Formulas/Date.js b/dist/Formulas/Date.js
 560new file mode 100644
 561index 0000000..bbbfdd2
 562--- /dev/null
 563+++ b/dist/Formulas/Date.js
 564@@ -0,0 +1,953 @@
 565+"use strict";
 566+exports.__esModule = true;
 567+/// <reference path="../../node_modules/moment/moment.d.ts"/>
 568+var moment = require("moment");
 569+var ArgsChecker_1 = require("../Utilities/ArgsChecker");
 570+var TypeConverter_1 = require("../Utilities/TypeConverter");
 571+var Errors_1 = require("../Errors");
 572+/**
 573+ * Converts a provided year, month, and day into a date.
 574+ * @param year - The year component of the date.
 575+ * @param month - The month component of the date.
 576+ * @param day - The day component of the date.
 577+ * @returns {number} newly created date.
 578+ * @constructor
 579+ */
 580+var DATE = function (year, month, day) {
 581+    var FIRST_YEAR = 1900;
 582+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 3, "DATE");
 583+    year = Math.abs(Math.floor(TypeConverter_1.TypeConverter.firstValueAsNumber(year))); // No negative values for year
 584+    month = Math.floor(TypeConverter_1.TypeConverter.firstValueAsNumber(month)) - 1; // Months are between 0 and 11.
 585+    day = Math.floor(TypeConverter_1.TypeConverter.firstValueAsNumber(day)) - 1; // Days are also zero-indexed.
 586+    var m = moment.utc(TypeConverter_1.TypeConverter.ORIGIN_MOMENT)
 587+        .add(2, "days")
 588+        .add(year < FIRST_YEAR ? year : year - FIRST_YEAR, 'years') // If the value is less than 1900, assume 1900 as start index for year
 589+        .add(month, 'months')
 590+        .add(day, 'days');
 591+    var dateAsNumber = TypeConverter_1.TypeConverter.momentToDayNumber(m);
 592+    if (dateAsNumber < 0) {
 593+        throw new Errors_1.NumError("DATE evaluates to an out of range value " + dateAsNumber
 594+            + ". It should be greater than or equal to 0.");
 595+    }
 596+    return dateAsNumber;
 597+};
 598+exports.DATE = DATE;
 599+/**
 600+ * Converts a provided date string in a known format to a date value.
 601+ * @param dateString - The string representing the date. Understood formats include any date format which is
 602+ * normally auto-converted when entered, without quotation marks, directly into a cell. Understood formats may depend on
 603+ * region and language settings.
 604+ * @returns {number} of days since 1900/1/1, inclusively.
 605+ * @constructor
 606+ */
 607+var DATEVALUE = function (dateString) {
 608+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 1, "DATEVALUE");
 609+    dateString = TypeConverter_1.TypeConverter.firstValueAsString(dateString);
 610+    var dateAsNumber;
 611+    try {
 612+        dateAsNumber = TypeConverter_1.TypeConverter.stringToDateNumber(dateString);
 613+    }
 614+    catch (e) {
 615+        throw new Errors_1.ValueError("DATEVALUE parameter '" + dateString + "' cannot be parsed to date/time.");
 616+    }
 617+    // If we've not been able to parse the date by now, then we cannot parse it at all.
 618+    return dateAsNumber;
 619+};
 620+exports.DATEVALUE = DATEVALUE;
 621+/**
 622+ * Returns a date a specified number of months before or after another date.
 623+ * @param startDate - The date from which to calculate the result.
 624+ * @param months - The number of months before (negative) or after (positive) start_date to calculate.
 625+ * @returns {number} date a specified number of months before or after another date
 626+ * @constructor
 627+ */
 628+var EDATE = function (startDate, months) {
 629+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 2, "EDATE");
 630+    var startDateNumber = TypeConverter_1.TypeConverter.firstValueAsDateNumber(startDate, true); // tell firstValueAsDateNumber to coerce boolean
 631+    if (startDateNumber < 0) {
 632+        throw new Errors_1.NumError("Function EDATE parameter 1 value is " + startDateNumber + ". It should be greater than or equal to 0.");
 633+    }
 634+    months = Math.floor(TypeConverter_1.TypeConverter.firstValueAsNumber(months));
 635+    // While momentToDayNumber will return an inclusive count of days since 1900/1/1, moment.Moment.add assumes exclusive
 636+    // count of days.
 637+    return TypeConverter_1.TypeConverter.momentToDayNumber(moment.utc(TypeConverter_1.TypeConverter.ORIGIN_MOMENT).add(startDateNumber, "days").add(months, "months"));
 638+};
 639+exports.EDATE = EDATE;
 640+/**
 641+ * Returns a date representing the last day of a month which falls a specified number of months before or after another
 642+ * date.
 643+ * @param startDate - The date from which to calculate the the result.
 644+ * @param months - The number of months before (negative) or after (positive) start_date to consider. The last
 645+ * calendar day of the calculated month is returned.
 646+ * @returns {number} the last day of a month
 647+ * @constructor
 648+ */
 649+var EOMONTH = function (startDate, months) {
 650+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 2, "EOMONTH");
 651+    var startDateNumber = TypeConverter_1.TypeConverter.firstValueAsDateNumber(startDate, true); // tell firstValueAsDateNumber to coerce boolean
 652+    if (startDateNumber < 0) {
 653+        throw new Errors_1.NumError("Function EOMONTH parameter 1 value is " + startDateNumber + ". It should be greater than or equal to 0.");
 654+    }
 655+    months = Math.floor(TypeConverter_1.TypeConverter.firstValueAsNumber(months));
 656+    return TypeConverter_1.TypeConverter.momentToDayNumber(moment.utc(TypeConverter_1.TypeConverter.ORIGIN_MOMENT)
 657+        .add(startDateNumber, "days")
 658+        .add(months, "months")
 659+        .endOf("month"));
 660+};
 661+exports.EOMONTH = EOMONTH;
 662+/**
 663+ * Returns the day of the month that a specific date falls on, in numeric format.
 664+ * @param date - The date from which to extract the day. Must be a reference to a cell containing a date, a
 665+ * function returning a date type, or a number.
 666+ * @returns {number} day of the month
 667+ * @constructor
 668+ */
 669+var DAY = function (date) {
 670+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 1, "DAY");
 671+    date = TypeConverter_1.TypeConverter.firstValueAsDateNumber(date, true); // tell firstValueAsDateNumber to coerce boolean
 672+    if (date < 0) {
 673+        throw new Errors_1.NumError("Function DAY parameter 1 value is " + date + ". It should be greater than or equal to 0.");
 674+    }
 675+    return TypeConverter_1.TypeConverter.numberToMoment(date).date();
 676+};
 677+exports.DAY = DAY;
 678+/**
 679+ * Returns the number of days between two dates.
 680+ * @param endDate most recently occurring
 681+ * @param startDate not most recently occurring
 682+ * @returns {number} of days between start_date and end_date
 683+ * @constructor
 684+ */
 685+var DAYS = function (endDate, startDate) {
 686+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 2, "DAYS");
 687+    endDate = TypeConverter_1.TypeConverter.firstValueAsDateNumber(endDate, true); // tell firstValueAsDateNumber to coerce boolean
 688+    startDate = TypeConverter_1.TypeConverter.firstValueAsDateNumber(startDate, true); // tell firstValueAsDateNumber to coerce boolean
 689+    return endDate - startDate;
 690+};
 691+exports.DAYS = DAYS;
 692+/**
 693+ * Returns the difference between two days based on the 360 day year used in some financial interest calculations.
 694+ * @param startDate - The start date to consider in the calculation. Must be a reference to a cell containing
 695+ * a date, a function returning a date type, or a number.
 696+ * @param endDate - The end date to consider in the calculation. Must be a reference to a cell containing a
 697+ * date, a function returning a date type, or a number.
 698+ * @param methodToUse - [ OPTIONAL - 0 by default ] - An indicator of what day count method to use.
 699+ * 0 indicates the US method - Under the US method, if start_date is the last day of a month, the day of month of
 700+ * start_date is changed to 30 for the purposes of the calculation. Furthermore if end_date is the last day of a month
 701+ * and the day of the month of start_date is earlier than the 30th, end_date is changed to the first day of the month
 702+ * following end_date, otherwise the day of month of end_date is changed to 30.
 703+ * Any other value indicates the European method - Under the European method, any start_date or end_date that falls on
 704+ * the 31st of a month has its day of month changed to 30.
 705+ * @returns {number} of days between two dates
 706+ * @constructor
 707+ */
 708+var DAYS360 = function (startDate, endDate, methodToUse) {
 709+    ArgsChecker_1.ArgsChecker.checkLengthWithin(arguments, 2, 3, "DAYS360");
 710+    startDate = TypeConverter_1.TypeConverter.numberToMoment(TypeConverter_1.TypeConverter.firstValueAsDateNumber(startDate, true)); // tell firstValueAsDateNumber to coerce boolean
 711+    endDate = TypeConverter_1.TypeConverter.numberToMoment(TypeConverter_1.TypeConverter.firstValueAsDateNumber(endDate, true)); // tell firstValueAsDateNumber to coerce boolean
 712+    methodToUse = methodToUse ? TypeConverter_1.TypeConverter.firstValueAsBoolean(methodToUse) : false;
 713+    var smd = 31;
 714+    var emd = 31;
 715+    var sd = startDate.date();
 716+    var ed = endDate.date();
 717+    if (methodToUse) {
 718+        sd = (sd === 31) ? 30 : sd;
 719+        ed = (ed === 31) ? 30 : ed;
 720+    }
 721+    else {
 722+        if (startDate.month() === 1) {
 723+            smd = startDate.daysInMonth();
 724+        }
 725+        if (endDate.month() === 1) {
 726+            emd = endDate.daysInMonth();
 727+        }
 728+        sd = (sd === smd) ? 30 : sd;
 729+        if (sd === 30 || sd === smd) {
 730+            ed = (ed === emd) ? 30 : ed;
 731+        }
 732+    }
 733+    return 360 * (endDate.year() - startDate.year()) + 30 * (endDate.month() - startDate.month()) + (ed - sd);
 734+};
 735+exports.DAYS360 = DAYS360;
 736+/**
 737+ * Returns the month of the year a specific date falls in, in numeric format.
 738+ * @param date - The date from which to extract the month. Must be a reference to a cell containing a date, a
 739+ * function returning a date type, or a number.
 740+ * @returns {number} month of the year that the input date falls on.
 741+ * @constructor
 742+ */
 743+var MONTH = function (date) {
 744+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 1, "MONTH");
 745+    date = TypeConverter_1.TypeConverter.firstValueAsDateNumber(date, true); // tell firstValueAsDateNumber to coerce boolean
 746+    if (date < 0) {
 747+        throw new Errors_1.NumError("Function MONTH parameter 1 value is " + date + ". It should be greater than or equal to 0.");
 748+    }
 749+    return TypeConverter_1.TypeConverter.numberToMoment(date).month() + 1;
 750+};
 751+exports.MONTH = MONTH;
 752+/**
 753+ * Returns the year specified by a given date.
 754+ * @param date - The date from which to calculate the year. Must be a cell reference to a cell containing a
 755+ * date, a function returning a date type, or a number.
 756+ * @returns {number} year of the input date
 757+ * @constructor
 758+ */
 759+var YEAR = function (date) {
 760+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 1, "YEAR");
 761+    date = TypeConverter_1.TypeConverter.firstValueAsDateNumber(date, true); // tell firstValueAsDateNumber to coerce boolean
 762+    if (date < 0) {
 763+        throw new Errors_1.NumError("Function YEAR parameter 1 value is " + date + ". It should be greater than or equal to 0.");
 764+    }
 765+    return TypeConverter_1.TypeConverter.numberToMoment(date).year();
 766+};
 767+exports.YEAR = YEAR;
 768+/**
 769+ * Returns a number representing the day of the week of the date provided.
 770+ * @param date - The date for which to determine the day of the week. Must be a reference to a cell containing
 771+ * a date, a function returning a date type, or a number.
 772+ * @param offsetType - [ OPTIONAL - 1 by default ] - A number indicating which numbering system to use to represent
 773+ * weekdays. By default counts starting with Sunday = 1. If type is 1, days are counted from Sunday and the value of
 774+ * Sunday is 1, therefore the value of Saturday is 7. If type is 2, days are counted from Monday and the value of Monday
 775+ * is 1, therefore the value of Sunday is 7. If type is 3, days are counted from Monday and the value of Monday is 0,
 776+ * therefore the value of Sunday is 6.
 777+ * @returns {number} day of week
 778+ * @constructor
 779+ */
 780+var WEEKDAY = function (date, offsetType) {
 781+    ArgsChecker_1.ArgsChecker.checkLengthWithin(arguments, 1, 2, "WEEKDAY");
 782+    date = TypeConverter_1.TypeConverter.firstValueAsDateNumber(date, true); // tell firstValueAsDateNumber to coerce boolean
 783+    offsetType = offsetType ? TypeConverter_1.TypeConverter.firstValueAsNumber(offsetType) : 1;
 784+    if (date < 0) {
 785+        throw new Errors_1.NumError("Function WEEKDAY parameter 1 value is " + date + ". It should be greater than or equal to 0.");
 786+    }
 787+    var day = TypeConverter_1.TypeConverter.numberToMoment(date).day();
 788+    if (offsetType === 1) {
 789+        return day + 1;
 790+    }
 791+    else if (offsetType === 2) {
 792+        if (day === 0) {
 793+            return 7;
 794+        }
 795+        return day;
 796+    }
 797+    else if (offsetType === 3) {
 798+        if (day === 0) {
 799+            return 6;
 800+        }
 801+        return day - 1;
 802+    }
 803+    else {
 804+        throw new Errors_1.NumError("Function WEEKDAY parameter 2 value " + day + " is out of range.");
 805+    }
 806+};
 807+exports.WEEKDAY = WEEKDAY;
 808+/**
 809+ * Returns a number representing the week of the year where the provided date falls. When inputting the date, it is best
 810+ * to use the DATE function, as text values may return errors.
 811+ *
 812+ * Behind the scenes, there are two week numbering "systems" used for this function: System 1 - The first week of the
 813+ * year is considered to be the week containing January 1, which is numbered week 1. System 2 - The first week of the
 814+ * year is considered to be the week containing the first Thursday of the year, which is numbered as week 1. System 2 is
 815+ * the approach specified in ISO 8601, also known as the European system for numbering weeks.
 816+ *
 817+ * @param date - The date for which to determine the week number. Must be a reference to a cell containing a
 818+ * date, a function returning a date type, or a number.
 819+ * @param shiftType - [ OPTIONAL - default is 1 ] - A number representing the day that a week starts on as well as
 820+ * the system used for determining the first week of the year (1=Sunday, 2=Monday).
 821+ * @returns {number} representing week number of year.
 822+ * @constructor
 823+ */
 824+var WEEKNUM = function (date, shiftType) {
 825+    // Given a moment, an array of days of the week for shifting, will calculate the week number.
 826+    function calculateWeekNum(dm, shifterArray) {
 827+        var startOfYear = moment.utc(dm).startOf("year");
 828+        var weeksCount = 1;
 829+        var d = moment.utc(dm).startOf("year").add(6 - shifterArray[startOfYear.day()], "days");
 830+        while (d.isBefore(dm)) {
 831+            d.add(7, "days");
 832+            weeksCount++;
 833+        }
 834+        return weeksCount;
 835+    }
 836+    ArgsChecker_1.ArgsChecker.checkLengthWithin(arguments, 1, 2, "WEEKNUM");
 837+    date = TypeConverter_1.TypeConverter.firstValueAsDateNumber(date, true); // tell firstValueAsDateNumber to coerce boolean
 838+    shiftType = shiftType ? TypeConverter_1.TypeConverter.firstValueAsNumber(shiftType) : 1;
 839+    if (date < 0) {
 840+        throw new Errors_1.NumError("Function YEAR parameter 1 value is " + date + ". It should be greater than or equal to 0.");
 841+    }
 842+    var dm = TypeConverter_1.TypeConverter.numberToMoment(date);
 843+    var week = dm.week();
 844+    var dayOfWeek = dm.day(); // between 1 and 7, inclusively
 845+    if (shiftType === 1) {
 846+        // If this weekYear is not the same as the year, then we're technically in "week 53"
 847+        // See https://momentjs.com/docs/#/get-set/week-year/ for more info.
 848+        if (dm.weekYear() !== dm.year()) {
 849+            week = dm.weeksInYear() + 1;
 850+        }
 851+        return week;
 852+    }
 853+    else if (shiftType === 2 || shiftType === 11) {
 854+        if (dm.weekYear() !== dm.year()) {
 855+            week = dm.weeksInYear() + 1;
 856+        }
 857+        if (dayOfWeek === 0) {
 858+            return week - 1;
 859+        }
 860+        return week;
 861+    }
 862+    else if (shiftType === 12) {
 863+        if (dm.weekYear() !== dm.year()) {
 864+            week = dm.weeksInYear() + 1;
 865+        }
 866+        if (dayOfWeek <= 1) {
 867+            return week - 1;
 868+        }
 869+        return week;
 870+    }
 871+    else if (shiftType === 13) {
 872+        if (dm.weekYear() !== dm.year()) {
 873+            week = dm.weeksInYear() + 1;
 874+        }
 875+        if (dayOfWeek <= 2) {
 876+            return week - 1;
 877+        }
 878+        return week;
 879+    }
 880+    else if (shiftType === 14) {
 881+        return calculateWeekNum(dm, [3, 4, 5, 6, 0, 1, 2]);
 882+    }
 883+    else if (shiftType === 15) {
 884+        return calculateWeekNum(dm, [2, 3, 4, 5, 6, 0, 1]);
 885+    }
 886+    else if (shiftType === 16) {
 887+        return calculateWeekNum(dm, [1, 2, 3, 4, 5, 6, 0]);
 888+    }
 889+    else if (shiftType === 17) {
 890+        return calculateWeekNum(dm, [0, 1, 2, 3, 4, 5, 6]);
 891+    }
 892+    else if (shiftType === 21) {
 893+        return dm.isoWeek();
 894+    }
 895+    else {
 896+        throw new Errors_1.NumError("Function WEEKNUM parameter 2 value " + shiftType + " is out of range.");
 897+    }
 898+};
 899+exports.WEEKNUM = WEEKNUM;
 900+/**
 901+ * Calculates the number of days, months, or years between two dates.
 902+ * @param startDate - The start date to consider in the calculation. Must be a reference to a cell containing
 903+ * a DATE, a function returning a DATE type, or a number.
 904+ * @param endDate - The end date to consider in the calculation. Must be a reference to a cell containing a
 905+ * DATE, a function returning a DATE type, or a number.
 906+ * @param unit - A text abbreviation for unit of time. For example,"M" for month. Accepted values are "Y": the
 907+ * number of whole years between start_date and end_date, "M": the number of whole months between start_date and
 908+ * end_date, "D": the number of days between start_date and end_date, "MD": the number of days between start_date and
 909+ * end_date after subtracting whole months, "YM": the number of whole months between start_date and end_date after
 910+ * subtracting whole years, "YD": the number of days between start_date and end_date, assuming start_date and end_date
 911+ * were no more than one year apart.
 912+ * @returns {number} number of days, months, or years between two dates.
 913+ * @constructor
 914+ */
 915+var DATEDIF = function (startDate, endDate, unit) {
 916+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 3, "DATEDIF");
 917+    startDate = TypeConverter_1.TypeConverter.firstValueAsDateNumber(startDate, true);
 918+    endDate = TypeConverter_1.TypeConverter.firstValueAsDateNumber(endDate, true);
 919+    unit = TypeConverter_1.TypeConverter.firstValueAsString(unit);
 920+    var unitClean = unit.toUpperCase();
 921+    var startMoment = TypeConverter_1.TypeConverter.numberToMoment(startDate);
 922+    var endMoment = TypeConverter_1.TypeConverter.numberToMoment(endDate);
 923+    if (startDate > endDate) {
 924+        throw new Errors_1.NumError("Function DATEDIF parameter 1 (" + startDate.toString() +
 925+            ") should be on or before Function DATEDIF parameter 2 (" + endDate.toString() + ").");
 926+    }
 927+    if (unitClean === "Y") {
 928+        return Math.floor(endMoment.diff(startMoment, "years"));
 929+    }
 930+    else if (unitClean === "M") {
 931+        return Math.floor(endMoment.diff(startMoment, "months"));
 932+    }
 933+    else if (unitClean === "D") {
 934+        return endDate - startDate;
 935+    }
 936+    else if (unitClean === "MD") {
 937+        var s = startMoment;
 938+        while (s.isBefore(endMoment)) {
 939+            s.add(1, "month");
 940+        }
 941+        s.subtract(1, "month");
 942+        var days = endMoment.diff(s, "days");
 943+        return s.date() === endMoment.date() ? 0 : days;
 944+    }
 945+    else if (unitClean === "YM") {
 946+        var s = startMoment;
 947+        while (s.isBefore(endMoment)) {
 948+            s.add(1, "year");
 949+        }
 950+        s.subtract(1, "year");
 951+        var months = Math.floor(endMoment.diff(s, "months"));
 952+        return months === 12 ? 0 : months;
 953+    }
 954+    else if (unitClean === "YD") {
 955+        var s = startMoment;
 956+        while (s.isBefore(endMoment)) {
 957+            s.add(1, "year");
 958+        }
 959+        s.subtract(1, "year");
 960+        var days = Math.floor(endMoment.diff(s, "days"));
 961+        return days >= 365 ? 0 : days;
 962+    }
 963+    else {
 964+        throw new Errors_1.NumError("Function DATEDIF parameter 3 value is " + unit +
 965+            ". It should be one of: 'Y', 'M', 'D', 'MD', 'YM', 'YD'.");
 966+    }
 967+};
 968+exports.DATEDIF = DATEDIF;
 969+/**
 970+ * Returns the number of years, including fractional years, between two dates using a specified day count convention.
 971+ *
 972+ * Further reading:
 973+ *
 974+ * * http://christian-fries.de/blog/files/2013-yearfrac.html
 975+ *
 976+ * * http://finmath.net/finmath-lib/
 977+ *
 978+ * @param startDate - The start date to consider in the calculation. Must be a reference to a cell
 979+ * containing a date, a function returning a date type, or a number.
 980+ * @param endDate - The end date to consider in the calculation. Must be a reference to a cell containing
 981+ * a date, a function returning a date type, or a number.
 982+ * @param dayCountConvention - [ OPTIONAL - 0 by default ] - An indicator of what day count method to
 983+ * use.
 984+ * @returns {number}the number of years, including fractional years, between two dates
 985+ * @constructor
 986+ */
 987+var YEARFRAC = function (startDate, endDate, dayCountConvention) {
 988+    ArgsChecker_1.ArgsChecker.checkLengthWithin(arguments, 2, 3, "YEARFRAC");
 989+    startDate = TypeConverter_1.TypeConverter.firstValueAsDateNumber(startDate, true);
 990+    endDate = TypeConverter_1.TypeConverter.firstValueAsDateNumber(endDate, true);
 991+    dayCountConvention = dayCountConvention ? TypeConverter_1.TypeConverter.firstValueAsNumber(dayCountConvention) : 0;
 992+    var s = TypeConverter_1.TypeConverter.numberToMoment(startDate);
 993+    var e = TypeConverter_1.TypeConverter.numberToMoment(endDate);
 994+    if (e.isBefore(s)) {
 995+        var me = moment.utc(e);
 996+        e = moment.utc(s);
 997+        s = me;
 998+    }
 999+    var syear = s.year();
1000+    var smonth = s.month();
1001+    var sday = s.date();
1002+    var eyear = e.year();
1003+    var emonth = e.month();
1004+    var eday = e.date();
1005+    var feb29Between = function (date1, date2) {
1006+        // Requires year2 == (year1 + 1) or year2 == year1
1007+        // Returns TRUE if February 29 is between the two dates (date1 may be February 29), with two possibilities:
1008+        // year1 is a leap year and date1 <= February 29 of year1
1009+        // year2 is a leap year and date2 > February 29 of year2
1010+        var mar1year1 = moment.utc(new Date(date1.year(), 2, 1));
1011+        if (moment.utc([date1.year()]).isLeapYear() && date1.diff(mar1year1) < 0 && date2.diff(mar1year1) >= 0) {
1012+            return true;
1013+        }
1014+        var mar1year2 = moment.utc(new Date(date2.year(), 2, 1));
1015+        if (moment.utc([date2.year()]).isLeapYear() && date2.diff(mar1year2) >= 0 && date1.diff(mar1year2) < 0) {
1016+            return true;
1017+        }
1018+        return false;
1019+    };
1020+    switch (dayCountConvention) {
1021+        // US (NASD) 30/360
1022+        case 0:
1023+            // Note: if eday == 31, it stays 31 if sday < 30
1024+            if (sday === 31 && eday === 31) {
1025+                sday = 30;
1026+                eday = 30;
1027+            }
1028+            else if (sday === 31) {
1029+                sday = 30;
1030+            }
1031+            else if (sday === 30 && eday === 31) {
1032+                eday = 30;
1033+            }
1034+            else if (smonth === 1 && emonth === 1 && s.daysInMonth() === sday && e.daysInMonth() === eday) {
1035+                sday = 30;
1036+                eday = 30;
1037+            }
1038+            else if (smonth === 1 && s.daysInMonth() === sday) {
1039+                sday = 30;
1040+            }
1041+            return Math.abs(((eday + emonth * 30 + eyear * 360) - (sday + smonth * 30 + syear * 360)) / 360);
1042+        // Actual/actual
1043+        case 1:
1044+            var ylength = 365;
1045+            if (syear === eyear || ((syear + 1) === eyear) && ((smonth > emonth) || ((smonth === emonth) && (sday >= eday)))) {
1046+                if (syear === eyear && moment.utc([syear]).isLeapYear()) {
1047+                    ylength = 366;
1048+                }
1049+                else if (feb29Between(s, e) || (emonth === 1 && eday === 29)) {
1050+                    ylength = 366;
1051+                }
1052+                return Math.abs((endDate - startDate) / ylength);
1053+            }
1054+            else {
1055+                var years = (eyear - syear) + 1;
1056+                var days = moment.utc([eyear + 1]).startOf("year").diff(moment.utc([syear]).startOf("year"), 'days');
1057+                var average = days / years;
1058+                return Math.abs((endDate - startDate) / average);
1059+            }
1060+        // Actual/360
1061+        case 2:
1062+            return Math.abs(e.diff(s, 'days') / 360);
1063+        // Actual/365
1064+        case 3:
1065+            return Math.abs(e.diff(s, 'days') / 365);
1066+        // European 30/360
1067+        case 4:
1068+            sday = sday === 31 ? 30 : sday;
1069+            eday = eday === 31 ? 30 : eday;
1070+            // Remarkably, do NOT change February 28 or February 29 at ALL
1071+            return Math.abs(((eday + emonth * 30 + eyear * 360) - (sday + smonth * 30 + syear * 360)) / 360);
1072+    }
1073+    throw new Errors_1.NumError("Function YEARFRAC parameter 3 value is " + dayCountConvention + ". Valid values are between 0 and 4 inclusive.");
1074+};
1075+exports.YEARFRAC = YEARFRAC;
1076+/**
1077+ * Returns the fraction of a 24-hour day the time represents.
1078+ * @param timeString - The string that holds the time representation. Eg: "10am", "10:10", "10:10am", "10:10:11",
1079+ * or "10:10:11am".
1080+ * @returns {number} representing the fraction of a 24-hour day
1081+ * @constructor
1082+ */
1083+var TIMEVALUE = function (timeString) {
1084+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 1, "TIMEVALUE");
1085+    timeString = TypeConverter_1.TypeConverter.firstValueAsString(timeString);
1086+    try {
1087+        return TypeConverter_1.TypeConverter.stringToTimeNumber(timeString);
1088+    }
1089+    catch (e) {
1090+        throw new Errors_1.ValueError("TIMEVALUE parameter '" + timeString + "' cannot be parsed to date/time.");
1091+    }
1092+};
1093+exports.TIMEVALUE = TIMEVALUE;
1094+var MILLISECONDS_IN_DAY = 86400000;
1095+/**
1096+ * Returns the hour component of a specific time, in numeric format.
1097+ * @param time - The time from which to calculate the hour component. Must be a reference to a cell containing
1098+ * a date/time, a function returning a date/time type, or a number.
1099+ * @returns {number}
1100+ * @constructor
1101+ */
1102+var HOUR = function (time) {
1103+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 1, "HOUR");
1104+    time = TypeConverter_1.TypeConverter.firstValueAsTimestampNumber(time);
1105+    if (time % 1 === 0) {
1106+        return 0;
1107+    }
1108+    var m = moment.utc([1900]).add(time * MILLISECONDS_IN_DAY, "milliseconds");
1109+    return m.hour();
1110+};
1111+exports.HOUR = HOUR;
1112+/**
1113+ * Returns the minute component of a specific time, in numeric format.
1114+ * @param time - The time from which to calculate the minute component. Must be a reference to a cell
1115+ * containing a date/time, a function returning a date/time type, or a number.
1116+ * @returns {number} minute of the time passed in.
1117+ * @constructor
1118+ */
1119+var MINUTE = function (time) {
1120+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 1, "MINUTE");
1121+    time = TypeConverter_1.TypeConverter.firstValueAsTimestampNumber(time);
1122+    if (time % 1 === 0) {
1123+        return 0;
1124+    }
1125+    var m = moment.utc([1900]).add(time * MILLISECONDS_IN_DAY, "milliseconds");
1126+    return m.minute();
1127+};
1128+exports.MINUTE = MINUTE;
1129+/**
1130+ * Returns the second component of a specific time, in numeric format.
1131+ * @param time - The time from which to calculate the second component. Must be a reference to a cell
1132+ * containing a date/time, a function returning a date/time type, or a number.
1133+ * @returns {number} second component of a specific time.
1134+ * @constructor
1135+ */
1136+var SECOND = function (time) {
1137+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 1, "SECOND");
1138+    time = TypeConverter_1.TypeConverter.firstValueAsTimestampNumber(time);
1139+    if (time % 1 === 0) {
1140+        return 0;
1141+    }
1142+    var m = moment.utc([1900]).add(time * MILLISECONDS_IN_DAY, "milliseconds");
1143+    return m.second();
1144+};
1145+exports.SECOND = SECOND;
1146+/**
1147+ * Returns the number of net working days between two provided days.
1148+ * @param startDate - The start date of the period from which to calculate the number of net working days.
1149+ * @param endDate - The end date of the period from which to calculate the number of net working days.
1150+ * @param holidays - [ OPTIONAL ] - A range or array constant containing the date serial numbers to consider
1151+ * holidays. The values provided within an array for holidays must be date serial number values, as returned by N or
1152+ * date values, as returned by DATE, DATEVALUE or TO_DATE. Values specified by a range should be standard date values or
1153+ * date serial numbers.
1154+ * @returns {number} the number of net working days between two provided dates.
1155+ * @constructor
1156+ */
1157+var NETWORKDAYS = function (startDate, endDate, holidays) {
1158+    ArgsChecker_1.ArgsChecker.checkLengthWithin(arguments, 2, 3, "NETWORKDAYS");
1159+    startDate = TypeConverter_1.TypeConverter.firstValueAsDateNumber(startDate, true);
1160+    endDate = TypeConverter_1.TypeConverter.firstValueAsDateNumber(endDate, true);
1161+    var hasHolidays = (holidays !== undefined);
1162+    var cleanHolidays = [];
1163+    if (hasHolidays) {
1164+        holidays = (holidays instanceof Array) ? holidays : [holidays];
1165+        if (holidays.length === 0) {
1166+            throw new Errors_1.RefError("Reference does not exist.");
1167+        }
1168+        for (var _i = 0, holidays_1 = holidays; _i < holidays_1.length; _i++) {
1169+            var holidayDateValue = holidays_1[_i];
1170+            if (typeof holidayDateValue === "number") {
1171+                cleanHolidays.push(holidayDateValue);
1172+            }
1173+            else {
1174+                throw new Errors_1.ValueError("NETWORKDAYS expects number values. But '" + holidayDateValue + "' is a " +
1175+                    (typeof holidayDateValue) + " and cannot be coerced to a number.");
1176+            }
1177+        }
1178+    }
1179+    // Handle cases in which the start date is not before the end date.
1180+    var didSwap = startDate > endDate;
1181+    if (didSwap) {
1182+        var swap = endDate;
1183+        endDate = startDate;
1184+        startDate = swap;
1185+    }
1186+    var countMoment = moment.utc(TypeConverter_1.TypeConverter.numberToMoment(startDate));
1187+    var weekendDays = [6, 0]; // Default weekend_days.
1188+    var days = endDate - startDate + 1;
1189+    var networkDays = days;
1190+    var j = 0;
1191+    while (j < days) {
1192+        if (weekendDays.indexOf(countMoment.day()) >= 0) {
1193+            networkDays--;
1194+        }
1195+        else if (hasHolidays && cleanHolidays.indexOf(TypeConverter_1.TypeConverter.momentToDayNumber(countMoment)) > -1) {
1196+            networkDays--;
1197+        }
1198+        countMoment.add(1, 'days');
1199+        j++;
1200+    }
1201+    // If the we swapped the start and end date, the result should be a negative number of network days.
1202+    if (didSwap) {
1203+        return networkDays * -1;
1204+    }
1205+    return networkDays;
1206+};
1207+exports.NETWORKDAYS = NETWORKDAYS;
1208+/**
1209+ * Returns the number of networking days between two provided days excluding specified weekend days and holidays.
1210+ * @param startDate - The start date of the period from which to calculate the number of net working days.
1211+ * @param endDate - The end date of the period from which to calculate the number of net working days.
1212+ * @param weekend - [ OPTIONAL - 1 by default ] - A number or string representing which days of the week are
1213+ * considered weekends. String method: weekends can be specified using seven 0’s and 1’s, where the first number in the
1214+ * set represents Monday and the last number is for Sunday. A zero means that the day is a work day, a 1 means that the
1215+ * day is a weekend. For example, “0000011” would mean Saturday and Sunday are weekends. Number method: instead of using
1216+ * the string method above, a single number can be used. 1 = Saturday/Sunday are weekends, 2 = Sunday/Monday, and this
1217+ * pattern repeats until 7 = Friday/Saturday. 11 = Sunday is the only weekend, 12 = Monday is the only weekend, and this
1218+ * pattern repeats until 17 = Saturday is the only weekend.
1219+ * @param holidays - [ OPTIONAL ] - A range or array constant containing the dates to consider as holidays.
1220+ * The values provided within an array for holidays must be date serial number values, as returned by N or date values,
1221+ * as returned by DATE, DATEVALUE or TO_DATE. Values specified by a range should be standard date values or date serial
1222+ * numbers.
1223+ * @returns {number} of networking days between two provided days
1224+ * @constructor
1225+ */
1226+var NETWORKDAYS$INTL = function (startDate, endDate, weekend, holidays) {
1227+    ArgsChecker_1.ArgsChecker.checkLengthWithin(arguments, 2, 4, "NETWORKDAYS$INTL");
1228+    startDate = TypeConverter_1.TypeConverter.firstValueAsDateNumber(startDate, true);
1229+    endDate = TypeConverter_1.TypeConverter.firstValueAsDateNumber(endDate, true);
1230+    var weekendDays = [];
1231+    if (weekend !== undefined) {
1232+        weekend = TypeConverter_1.TypeConverter.firstValue(weekend);
1233+        if (typeof weekend === "string") {
1234+            if (!/^[0-1]{6,}$/.test(weekend)) {
1235+                throw new Errors_1.NumError("Function NETWORKDAYS.INTL parameter 3 requires a number in the format '0000011'. "
1236+                    + "Actual value is '" + weekend + "'");
1237+            }
1238+            var ws = weekend.split("");
1239+            for (var i = 0; i < ws.length; i++) {
1240+                if (ws[i] === "1") {
1241+                    weekendDays.push(i === 6 ? 0 : i + 1);
1242+                }
1243+            }
1244+        }
1245+        else if (typeof weekend === "number") {
1246+            switch (weekend) {
1247+                case 1:
1248+                    weekendDays = [0, 6];
1249+                    break;
1250+                case 2:
1251+                case 3:
1252+                case 4:
1253+                case 5:
1254+                case 6:
1255+                case 7:
1256+                    weekendDays = [weekend, weekend - 1];
1257+                    break;
1258+                case 11:
1259+                case 12:
1260+                case 13:
1261+                case 14:
1262+                case 15:
1263+                case 16:
1264+                case 17:
1265+                    weekendDays = [weekend - 10];
1266+                    break;
1267+                default:
1268+                    throw new Errors_1.NumError("Function NETWORKDAYS.INTL parameter 3 requires a number in the range 1-7 or 11-17. "
1269+                        + "Actual number is " + weekend + ".");
1270+            }
1271+        }
1272+        else {
1273+            throw new Errors_1.ValueError("Function NETWORKDAYS.INTL parameter 4 expects number values. But '" + weekend
1274+                + "' cannot be coerced to a number.");
1275+        }
1276+    }
1277+    else {
1278+        weekendDays = [0, 6];
1279+    }
1280+    var hasHolidays = holidays !== undefined;
1281+    var cleanHolidays = [];
1282+    if (hasHolidays) {
1283+        if (holidays === 0) {
1284+            throw new Errors_1.RefError("Reference does not exist.");
1285+        }
1286+        for (var _i = 0, holidays_2 = holidays; _i < holidays_2.length; _i++) {
1287+            var holidayDateValue = holidays_2[_i];
1288+            if (typeof holidayDateValue === "number") {
1289+                cleanHolidays.push(holidayDateValue);
1290+            }
1291+            else {
1292+                throw new Errors_1.ValueError("NETWORKDAYS.INTL expects number values. But '" + holidayDateValue + "' is a " +
1293+                    (typeof holidayDateValue) + " and cannot be coerced to a number.");
1294+            }
1295+        }
1296+    }
1297+    // Handle cases in which the start date is not before the end date.
1298+    var didSwap = startDate > endDate;
1299+    if (didSwap) {
1300+        var swap = endDate;
1301+        endDate = startDate;
1302+        startDate = swap;
1303+    }
1304+    var countMoment = moment.utc(TypeConverter_1.TypeConverter.numberToMoment(startDate));
1305+    var days = endDate - startDate + 1;
1306+    var networkDays = days;
1307+    var j = 0;
1308+    while (j < days) {
1309+        if (weekendDays.indexOf(countMoment.day()) >= 0) {
1310+            networkDays--;
1311+        }
1312+        else if (hasHolidays && cleanHolidays.indexOf(TypeConverter_1.TypeConverter.momentToDayNumber(countMoment)) > -1) {
1313+            networkDays--;
1314+        }
1315+        countMoment.add(1, 'days');
1316+        j++;
1317+    }
1318+    // If the we swapped the start and end date, the result should be a negative number of network days.
1319+    if (didSwap) {
1320+        return networkDays * -1;
1321+    }
1322+    return networkDays;
1323+};
1324+exports.NETWORKDAYS$INTL = NETWORKDAYS$INTL;
1325+/**
1326+ * Returns the current date and time as a date value.
1327+ * @returns {number} representing the current date and time.
1328+ * @constructor
1329+ */
1330+var NOW = function () {
1331+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 0, "NOW");
1332+    return TypeConverter_1.TypeConverter.momentToNumber(moment.utc());
1333+};
1334+exports.NOW = NOW;
1335+/**
1336+ * Returns the current date as a date value.
1337+ * @returns {number} today
1338+ * @constructor
1339+ */
1340+var TODAY = function () {
1341+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 0, "TODAY");
1342+    return TypeConverter_1.TypeConverter.momentToNumber(moment.utc().startOf("day"));
1343+};
1344+exports.TODAY = TODAY;
1345+/**
1346+ * Converts a provided hour, minute, and second into a time. Will silently recalculate numeric time values which fall
1347+ * outside of valid ranges. Eg: TIME(24, 0, 0) is the same as TIME(0, 0, 0).
1348+ * @param hours - The hour component of the time.
1349+ * @param minutes - The minute component of the time.
1350+ * @param seconds - The second component of the time.
1351+ * @returns {number} time of day
1352+ * @constructor
1353+ */
1354+var TIME = function (hours, minutes, seconds) {
1355+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 3, "TIME");
1356+    hours = Math.floor(TypeConverter_1.TypeConverter.firstValueAsNumber(hours));
1357+    minutes = Math.floor(TypeConverter_1.TypeConverter.firstValueAsNumber(minutes));
1358+    seconds = Math.floor(TypeConverter_1.TypeConverter.firstValueAsNumber(seconds));
1359+    var e = TypeConverter_1.TypeConverter.unitsToTimeNumber(hours, minutes, seconds);
1360+    if (e < 0) {
1361+        throw new Errors_1.NumError("TIME evaluates to an out of range value " + e + ". It should be greater than or equal to 0.");
1362+    }
1363+    return e;
1364+};
1365+exports.TIME = TIME;
1366+/**
1367+ * Calculates the end date after a specified number of working days.
1368+ * @param startDate - The date from which to begin counting.
1369+ * @param numberOfDays - The number of working days to advance from start_date. If negative, counts backwards. If
1370+ * not an integer, truncate.
1371+ * @param holidays - [ OPTIONAL ] - A range or array constant containing the dates to consider holidays. The
1372+ * values provided within an array for holidays must be date serial number values, as returned by N or date values, as
1373+ * returned by DATE, DATEVALUE or TO_DATE. Values specified by a range should be standard date values or date serial
1374+ * numbers.
1375+ * @returns {number} end date after a specified number of working days.
1376+ * @constructor
1377+ */
1378+var WORKDAY = function (startDate, numberOfDays, holidays) {
1379+    ArgsChecker_1.ArgsChecker.checkLengthWithin(arguments, 2, 3, "WORKDAY");
1380+    startDate = TypeConverter_1.TypeConverter.firstValueAsDateNumber(startDate, true);
1381+    numberOfDays = TypeConverter_1.TypeConverter.firstValueAsNumber(numberOfDays);
1382+    var hasHolidays = (cleanHolidays !== undefined);
1383+    var cleanHolidays = [];
1384+    if (hasHolidays !== undefined) {
1385+        if (holidays instanceof Array) {
1386+            if (holidays.length === 0) {
1387+                throw new Errors_1.RefError("Reference does not exist.");
1388+            }
1389+            for (var _i = 0, holidays_3 = holidays; _i < holidays_3.length; _i++) {
1390+                var holidayDateValue = holidays_3[_i];
1391+                if (typeof holidayDateValue === "number") {
1392+                    cleanHolidays.push(holidayDateValue);
1393+                }
1394+                else {
1395+                    throw new Errors_1.ValueError("WORKDAY expects number values. But '" + holidayDateValue + "' is a " +
1396+                        (typeof holidayDateValue) + " and cannot be coerced to a number.");
1397+                }
1398+            }
1399+        }
1400+        else {
1401+            cleanHolidays.push(TypeConverter_1.TypeConverter.valueToNumber(holidays));
1402+        }
1403+    }
1404+    var weekendDays = [0, 6];
1405+    var countMoment = moment.utc(TypeConverter_1.TypeConverter.numberToMoment(startDate));
1406+    var j = 0;
1407+    while (j < numberOfDays) {
1408+        countMoment.add(1, 'days');
1409+        if (weekendDays.indexOf(countMoment.day()) < 0 && cleanHolidays.indexOf(TypeConverter_1.TypeConverter.momentToDayNumber(countMoment)) < 0) {
1410+            j++;
1411+        }
1412+    }
1413+    return TypeConverter_1.TypeConverter.momentToDayNumber(countMoment);
1414+};
1415+exports.WORKDAY = WORKDAY;
1416+/**
1417+ * Calculates the date after a specified number of workdays excluding specified weekend days and holidays.
1418+ * @param startDate - The date from which to begin counting.
1419+ * @param numberOfDays - The number of working days to advance from start_date. If negative, counts backwards.
1420+ * @param weekend - [ OPTIONAL - 1 by default ] - A number or string representing which days of the week are
1421+ * considered weekends. String method: weekends can be specified using seven 0’s and 1’s, where the first number in the
1422+ * set represents Monday and the last number is for Sunday. A zero means that the day is a work day, a 1 means that the
1423+ * day is a weekend. For example, “0000011” would mean Saturday and Sunday are weekends. Number method: instead of using
1424+ * the string method above, a single number can be used. 1 = Saturday/Sunday are weekends, 2 = Sunday/Monday, and this
1425+ * pattern repeats until 7 = Friday/Saturday. 11 = Sunday is the only weekend, 12 = Monday is the only weekend, and this
1426+ * pattern repeats until 17 = Saturday is the only weekend.
1427+ * @param holidays - [ OPTIONAL ] - A range or array constant containing the dates to consider holidays.
1428+ * @returns {number}
1429+ * @constructor
1430+ */
1431+var WORKDAY$INTL = function (startDate, numberOfDays, weekend, holidays) {
1432+    ArgsChecker_1.ArgsChecker.checkLengthWithin(arguments, 2, 3, "WORKDAY$INTL");
1433+    startDate = TypeConverter_1.TypeConverter.firstValueAsDateNumber(startDate, true);
1434+    numberOfDays = TypeConverter_1.TypeConverter.firstValueAsNumber(numberOfDays);
1435+    var weekendDays = [];
1436+    if (weekend !== undefined) {
1437+        weekend = TypeConverter_1.TypeConverter.firstValue(weekend);
1438+        if (typeof weekend === "string") {
1439+            if (!/^[0-1]{6,}$/.test(weekend)) {
1440+                throw new Errors_1.NumError("Function WORKDAY.INTL parameter 3 requires a number in the format '0000011'. "
1441+                    + "Actual value is '" + weekend + "'");
1442+            }
1443+            var ws = weekend.split("");
1444+            for (var i = 0; i < ws.length; i++) {
1445+                if (ws[i] === "1") {
1446+                    weekendDays.push(i === 6 ? 0 : i + 1);
1447+                }
1448+            }
1449+        }
1450+        else if (typeof weekend === "number") {
1451+            switch (weekend) {
1452+                case 1:
1453+                    weekendDays = [0, 6];
1454+                    break;
1455+                case 2:
1456+                case 3:
1457+                case 4:
1458+                case 5:
1459+                case 6:
1460+                case 7:
1461+                    weekendDays = [weekend, weekend - 1];
1462+                    break;
1463+                case 11:
1464+                case 12:
1465+                case 13:
1466+                case 14:
1467+                case 15:
1468+                case 16:
1469+                case 17:
1470+                    weekendDays = [weekend - 10];
1471+                    break;
1472+                default:
1473+                    throw new Errors_1.NumError("Function WORKDAY.INTL parameter 3 requires a number in the range 1-7 or 11-17. "
1474+                        + "Actual number is " + weekend + ".");
1475+            }
1476+        }
1477+        else {
1478+            throw new Errors_1.ValueError("Function WORKDAY.INTL parameter 4 expects number values. But '" + weekend
1479+                + "' cannot be coerced to a number.");
1480+        }
1481+    }
1482+    else {
1483+        weekendDays = [0, 6];
1484+    }
1485+    var hasHolidays = (holidays !== undefined);
1486+    var cleanHolidays = [];
1487+    if (hasHolidays) {
1488+        if (holidays instanceof Array) {
1489+            if (holidays.length === 0) {
1490+                throw new Errors_1.RefError("Reference does not exist.");
1491+            }
1492+            for (var _i = 0, holidays_4 = holidays; _i < holidays_4.length; _i++) {
1493+                var holidayDateValue = holidays_4[_i];
1494+                if (typeof holidayDateValue === "number") {
1495+                    cleanHolidays.push(holidayDateValue);
1496+                }
1497+                else {
1498+                    throw new Errors_1.ValueError("WORKDAY expects number values. But '" + holidayDateValue + "' is a " +
1499+                        (typeof holidayDateValue) + " and cannot be coerced to a number.");
1500+                }
1501+            }
1502+        }
1503+        else {
1504+            cleanHolidays.push(TypeConverter_1.TypeConverter.valueToNumber(holidays));
1505+        }
1506+    }
1507+    var countMoment = moment.utc(TypeConverter_1.TypeConverter.numberToMoment(startDate));
1508+    var j = 0;
1509+    while (j < numberOfDays) {
1510+        countMoment.add(1, 'days');
1511+        if (weekendDays.indexOf(countMoment.day()) < 0 && cleanHolidays.indexOf(TypeConverter_1.TypeConverter.momentToDayNumber(countMoment)) < 0) {
1512+            j++;
1513+        }
1514+    }
1515+    return TypeConverter_1.TypeConverter.momentToDayNumber(countMoment);
1516+};
1517+exports.WORKDAY$INTL = WORKDAY$INTL;
1518diff --git a/dist/Formulas/Engineering.js b/dist/Formulas/Engineering.js
1519new file mode 100644
1520index 0000000..32706c1
1521--- /dev/null
1522+++ b/dist/Formulas/Engineering.js
1523@@ -0,0 +1,288 @@
1524+"use strict";
1525+exports.__esModule = true;
1526+var ArgsChecker_1 = require("../Utilities/ArgsChecker");
1527+var TypeConverter_1 = require("../Utilities/TypeConverter");
1528+var Errors_1 = require("../Errors");
1529+/**
1530+ * Converts a signed binary number to decimal format.
1531+ * @param signedBinaryNumber - The signed 10-bit binary value to be converted to decimal, provided as a
1532+ * string. The most significant bit of signed_binary_number is the sign bit; that is, negative numbers are represented
1533+ * in two's complement format.
1534+ * @returns {number}
1535+ * @constructor
1536+ */
1537+var BIN2DEC = function (signedBinaryNumber) {
1538+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 1, "BIN2DEC");
1539+    if (typeof TypeConverter_1.TypeConverter.firstValue(signedBinaryNumber) === "boolean") {
1540+        throw new Errors_1.ValueError("Function BIN2DEC parameter 1 expects text values. But '" + signedBinaryNumber + "' is a boolean and cannot be coerced to a text.");
1541+    }
1542+    var n = TypeConverter_1.TypeConverter.firstValueAsString(signedBinaryNumber);
1543+    if (!(/^[01]{1,10}$/).test(n)) {
1544+        throw new Errors_1.NumError("Input for BIN2DEC ('" + n + "') is not a valid binary representation.");
1545+    }
1546+    if (n.length === 10 && n.substring(0, 1) === '1') {
1547+        return parseInt(n.substring(1), 2) - 512;
1548+    }
1549+    return parseInt(n, 2);
1550+};
1551+exports.BIN2DEC = BIN2DEC;
1552+/**
1553+ * Converts a signed binary number to signed hexadecimal format.
1554+ * @param signedBinaryNumber - The signed 10-bit binary value to be converted to signed hexadecimal,
1555+ * provided as a string. The most significant bit of signed_binary_number is the sign bit; that is, negative numbers are
1556+ * represented in two's complement format.
1557+ * @param significantDigits - [ OPTIONAL ] - The number of significant digits to ensure in the result.
1558+ * @returns {string} string representation of a signed hexadecimal
1559+ * @constructor
1560+ */
1561+var BIN2HEX = function (signedBinaryNumber, significantDigits) {
1562+    ArgsChecker_1.ArgsChecker.checkLengthWithin(arguments, 1, 2, "BIN2HEX");
1563+    if (typeof TypeConverter_1.TypeConverter.firstValue(signedBinaryNumber) === "boolean") {
1564+        throw new Errors_1.ValueError("Function BIN2HEX parameter 1 expects text values. But '" + signedBinaryNumber + "' is a boolean and cannot be coerced to a text.");
1565+    }
1566+    var n = TypeConverter_1.TypeConverter.firstValueAsString(signedBinaryNumber);
1567+    var p = 10;
1568+    if (significantDigits !== undefined) {
1569+        p = TypeConverter_1.TypeConverter.firstValueAsNumber(significantDigits);
1570+    }
1571+    if (!(/^[01]{1,10}$/).test(n)) {
1572+        throw new Errors_1.NumError("Input for BIN2HEX ('" + n + "') is not a valid binary representation.");
1573+    }
1574+    if (n.length === 10 && n.substring(0, 1) === '1') {
1575+        return (1099511627264 + parseInt(n.substring(1), 2)).toString(16).toUpperCase();
1576+    }
1577+    if (p < 1 || p > 10) {
1578+        throw new Errors_1.NumError("Function BIN2HEX parameter 2 value is " + p + ". Valid values are between 1 and 10 inclusive.");
1579+    }
1580+    p = Math.floor(p);
1581+    // Convert decimal number to hexadecimal
1582+    var result = parseInt(n.toString(), 2).toString(16).toUpperCase();
1583+    if (p === 10) {
1584+        return result;
1585+    }
1586+    var str = "";
1587+    for (var i = 0; i < p - result.length; i++) {
1588+        str += "0";
1589+    }
1590+    return str + result;
1591+};
1592+exports.BIN2HEX = BIN2HEX;
1593+/**
1594+ * Converts a signed binary number to signed octal format.
1595+ * @param signedBinaryNumber - The signed 10-bit binary value to be converted to signed octal, provided as a
1596+ * string. The most significant bit of signed_binary_number is the sign bit; that is, negative numbers are represented
1597+ * in two's complement format.
1598+ * @param significantDigits - [ OPTIONAL ] - The number of significant digits to ensure in the result. If
1599+ * this is greater than the number of significant digits in the result, the result is left-padded with zeros until the
1600+ * total number of digits reaches significant_digits.
1601+ * @returns {string} number in octal format
1602+ * @constructor
1603+ */
1604+var BIN2OCT = function (signedBinaryNumber, significantDigits) {
1605+    ArgsChecker_1.ArgsChecker.checkLengthWithin(arguments, 1, 2, "BIN2OCT");
1606+    if (typeof TypeConverter_1.TypeConverter.firstValue(signedBinaryNumber) === "boolean") {
1607+        throw new Errors_1.ValueError("Function BIN2OCT parameter 1 expects text values. But '" + signedBinaryNumber + "' is a boolean and cannot be coerced to a text.");
1608+    }
1609+    var n = TypeConverter_1.TypeConverter.firstValueAsString(signedBinaryNumber);
1610+    var p = 10;
1611+    if (significantDigits !== undefined) {
1612+        p = TypeConverter_1.TypeConverter.firstValueAsNumber(significantDigits);
1613+    }
1614+    if (!(/^[01]{1,10}$/).test(n)) {
1615+        throw new Errors_1.NumError("Input for BIN2OCT ('" + n + "') is not a valid binary representation.");
1616+    }
1617+    if (n.length === 10 && n.substring(0, 1) === '1') {
1618+        return (1073741312 + parseInt(n.substring(1), 2)).toString(8);
1619+    }
1620+    if (p < 1 || p > 10) {
1621+        throw new Errors_1.NumError("Function BIN2OCT parameter 2 value is " + p + ". Valid values are between 1 and 10 inclusive.");
1622+    }
1623+    p = Math.floor(p);
1624+    var result = parseInt(n.toString(), 2).toString(8);
1625+    if (p === 10) {
1626+        return result;
1627+    }
1628+    if (p >= result.length) {
1629+        var str = "";
1630+        for (var i = 0; i < p - result.length - 1; i++) {
1631+            str += "0";
1632+        }
1633+        return str + result;
1634+    }
1635+};
1636+exports.BIN2OCT = BIN2OCT;
1637+/**
1638+ * Converts a decimal number to signed octal format.
1639+ * @param decimalDumber - The decimal value to be converted to signed octal,provided as a string. For this
1640+ * function, this value has a maximum of 536870911 if positive, and a minimum of -53687092 if negative.
1641+ * @param significantDigits - [ OPTIONAL ] The number of significant digits to ensure in the result. If this
1642+ * is greater than the number of significant digits in the result, the result is left-padded with zeros until the total
1643+ * number of digits reaches significant_digits.
1644+ * @returns {string} octal string representation of the decimal number
1645+ * @constructor
1646+ */
1647+var DEC2OCT = function (decimalDumber, significantDigits) {
1648+    ArgsChecker_1.ArgsChecker.checkLengthWithin(arguments, 1, 2, "DEC2OCT");
1649+    var n = TypeConverter_1.TypeConverter.firstValueAsNumber(decimalDumber);
1650+    if (n < 0) {
1651+        n = Math.ceil(n);
1652+    }
1653+    if (n > 0) {
1654+        n = Math.floor(n);
1655+    }
1656+    var p = 10;
1657+    var placesPresent = false;
1658+    if (significantDigits !== undefined) {
1659+        p = Math.floor(TypeConverter_1.TypeConverter.firstValueAsNumber(significantDigits));
1660+        placesPresent = true;
1661+    }
1662+    if (n < -53687092 || n > 536870911) {
1663+        throw new Errors_1.NumError("Function DEC2OCT parameter 1 value is " + n + ". Valid values are between -53687092 and 536870911 inclusive.");
1664+    }
1665+    if (p < 1 || p > 10) {
1666+        throw new Errors_1.NumError("Function DEC2OCT parameter 2 value is " + p + ". Valid values are between 1 and 10 inclusive.");
1667+    }
1668+    if (n < 0) {
1669+        return (1073741824 + n).toString(8).toUpperCase();
1670+    }
1671+    // Convert decimal number to hexadecimal
1672+    var result = parseInt(n.toString(), 10).toString(8).toUpperCase();
1673+    if (!placesPresent) {
1674+        return result;
1675+    }
1676+    var str = "";
1677+    for (var i = 0; i < p - result.length; i++) {
1678+        str += "0";
1679+    }
1680+    return str + result.toUpperCase();
1681+};
1682+exports.DEC2OCT = DEC2OCT;
1683+/**
1684+ * Converts a decimal number to signed hexadecimal format.
1685+ * @param decimalDumber - The decimal value to be converted to signed hexadecimal, provided as a string. This
1686+ * value has a maximum of 549755813887 if positive, and a minimum of -549755814888 if negative.
1687+ * @param significantDigits - [ OPTIONAL ] - The number of significant digits to ensure in the result. If
1688+ * this is greater than the number of significant digits in the result, the result is left-padded with zeros until the
1689+ * total number of digits reaches significant_digits. This value is ignored if decimal_number is negative.
1690+ * @returns {string} hexadecimal string representation of the decimal number
1691+ * @constructor
1692+ */
1693+var DEC2HEX = function (decimalDumber, significantDigits) {
1694+    ArgsChecker_1.ArgsChecker.checkLengthWithin(arguments, 1, 2, "DEC2HEX");
1695+    var n = TypeConverter_1.TypeConverter.firstValueAsNumber(decimalDumber);
1696+    if (n < 0) {
1697+        n = Math.ceil(n);
1698+    }
1699+    if (n > 0) {
1700+        n = Math.floor(n);
1701+    }
1702+    var p = 10;
1703+    var placesPresent = false;
1704+    if (significantDigits !== undefined) {
1705+        p = Math.floor(TypeConverter_1.TypeConverter.firstValueAsNumber(significantDigits));
1706+        placesPresent = true;
1707+    }
1708+    if (n < -549755813888 || n > 549755813887) {
1709+        throw new Errors_1.NumError("Function DEC2HEX parameter 1 value is " + n + ". Valid values are between -549755813888 and 549755813887 inclusive.");
1710+    }
1711+    if (p < 1 || p > 10) {
1712+        throw new Errors_1.NumError("Function DEC2HEX parameter 2 value is " + p + ". Valid values are between 1 and 10 inclusive.");
1713+    }
1714+    // Ignore places and return a 10-character hexadecimal number if number is negative
1715+    if (n < 0) {
1716+        return (1099511627776 + n).toString(16).toUpperCase();
1717+    }
1718+    // Convert decimal number to hexadecimal
1719+    var result = parseInt(n.toString(), 10).toString(16).toUpperCase();
1720+    if (!placesPresent) {
1721+        return result;
1722+    }
1723+    var str = "";
1724+    for (var i = 0; i < p - result.length; i++) {
1725+        str += "0";
1726+    }
1727+    return str + result;
1728+};
1729+exports.DEC2HEX = DEC2HEX;
1730+/**
1731+ * Converts a decimal number to signed binary format.
1732+ * @param decimalDumber - The decimal value to be converted to signed binary, provided as a string. For this
1733+ * function, this value has a maximum of 511 if positive, and a minimum of -512 if negative.
1734+ * @param significantDigits - [ OPTIONAL ] The number of significant digits to ensure in the result. If this
1735+ * is greater than the number of significant digits in the result, the result is left-padded with zeros until the total
1736+ * number of digits reaches significant_digits.
1737+ * @returns {string} signed binary string representation of the input decimal number.
1738+ * @constructor
1739+ */
1740+var DEC2BIN = function (decimalDumber, significantDigits) {
1741+    ArgsChecker_1.ArgsChecker.checkLengthWithin(arguments, 1, 2, "DEC2BIN");
1742+    var n = TypeConverter_1.TypeConverter.firstValueAsNumber(decimalDumber);
1743+    if (n < 0) {
1744+        n = Math.ceil(n);
1745+    }
1746+    if (n > 0) {
1747+        n = Math.floor(n);
1748+    }
1749+    if (n === 0 || n === 1) {
1750+        return n.toString();
1751+    }
1752+    var p = 10;
1753+    var placesPresent = false;
1754+    if (significantDigits !== undefined) {
1755+        p = Math.floor(TypeConverter_1.TypeConverter.firstValueAsNumber(significantDigits));
1756+        placesPresent = true;
1757+    }
1758+    if (n < -512 || n > 511) {
1759+        throw new Errors_1.NumError("Function DEC2BIN parameter 1 value is " + n + ". Valid values are between -512 and 511 inclusive.");
1760+    }
1761+    if (p < 1 || p > 10) {
1762+        throw new Errors_1.NumError("Function DEC2BIN parameter 2 value is " + p + ". Valid values are between 1 and 10 inclusive.");
1763+    }
1764+    // Ignore places and return a 10-character binary number if number is negative
1765+    if (n < 0) {
1766+        var count = (9 - (512 + n).toString(2).length);
1767+        var st = "";
1768+        for (var i = 0; i < count; i++) {
1769+            st += "0";
1770+        }
1771+        return "1" + st + (512 + n).toString(2);
1772+    }
1773+    // Convert decimal number to binary
1774+    var result = parseInt(n.toString(), 10).toString(2);
1775+    // Pad return value with leading 0s (zeros) if necessary
1776+    if (p >= result.length) {
1777+        var str = "";
1778+        for (var i = 0; i < (p - result.length); i++) {
1779+            str += "0";
1780+        }
1781+        var workingString = str + result;
1782+        if (!placesPresent) {
1783+            var returnString = "";
1784+            for (var i = 0; i < workingString.length; i++) {
1785+                var char = workingString[i];
1786+                if (char === "1") {
1787+                    break;
1788+                }
1789+                returnString = workingString.slice(i + 1);
1790+            }
1791+            return returnString;
1792+        }
1793+        return workingString;
1794+    }
1795+};
1796+exports.DEC2BIN = DEC2BIN;
1797+/**
1798+ * Compare two numeric values, returning 1 if they're equal.
1799+ * @param one - The first number to compare.
1800+ * @param two - The second number to compare.
1801+ * @returns {number} 1 if they're equal, 0 if they're not equal.
1802+ * @constructor
1803+ */
1804+var DELTA = function (one, two) {
1805+    ArgsChecker_1.ArgsChecker.checkLengthWithin(arguments, 1, 2, "DELTA");
1806+    if (two === undefined) {
1807+        return TypeConverter_1.TypeConverter.valueToNumber(one) === 0 ? 1 : 0;
1808+    }
1809+    return TypeConverter_1.TypeConverter.valueToNumber(one) === TypeConverter_1.TypeConverter.valueToNumber(two) ? 1 : 0;
1810+};
1811+exports.DELTA = DELTA;
1812diff --git a/dist/Formulas/Financial.js b/dist/Formulas/Financial.js
1813new file mode 100644
1814index 0000000..c633f5c
1815--- /dev/null
1816+++ b/dist/Formulas/Financial.js
1817@@ -0,0 +1,416 @@
1818+"use strict";
1819+exports.__esModule = true;
1820+var ArgsChecker_1 = require("../Utilities/ArgsChecker");
1821+var TypeConverter_1 = require("../Utilities/TypeConverter");
1822+var Errors_1 = require("../Errors");
1823+var Date_1 = require("./Date");
1824+/**
1825+ * Calculates the depreciation of an asset for a specified period using the double-declining balance method.
1826+ * @param cost - The initial cost of the asset.
1827+ * @param salvage - The value of the asset at the end of depreciation.
1828+ * @param life - The number of periods over which the asset is depreciated.
1829+ * @param period - The single period within life for which to calculate depreciation.
1830+ * @param factor - [ OPTIONAL - 2 by default ] - The factor by which depreciation decreases.
1831+ * @returns {number} depreciation of an asset for a specified period
1832+ * @constructor
1833+ */
1834+var DDB = function (cost, salvage, life, period, factor) {
1835+    ArgsChecker_1.ArgsChecker.checkLengthWithin(arguments, 4, 5, "DDB");
1836+    cost = TypeConverter_1.TypeConverter.firstValueAsNumber(cost);
1837+    salvage = TypeConverter_1.TypeConverter.firstValueAsNumber(salvage);
1838+    life = TypeConverter_1.TypeConverter.firstValueAsNumber(life);
1839+    period = TypeConverter_1.TypeConverter.firstValueAsNumber(period);
1840+    factor = factor === undefined ? 2 : TypeConverter_1.TypeConverter.firstValueAsNumber(factor);
1841+    if (cost < 0) {
1842+        throw new Errors_1.NumError("Function DDB parameter 1 value is "
1843+            + cost + ". It should be greater than or equal to 0.");
1844+    }
1845+    if (salvage < 0) {
1846+        throw new Errors_1.NumError("Function DDB parameter 2 value is "
1847+            + salvage + ". It should be greater than or equal to 0.");
1848+    }
1849+    if (life < 0) {
1850+        throw new Errors_1.NumError("Function DDB parameter 3 value is "
1851+            + life + ". It should be greater than or equal to 0.");
1852+    }
1853+    if (period < 0) {
1854+        throw new Errors_1.NumError("Function DDB parameter 4 value is "
1855+            + period + ". It should be greater than or equal to 0.");
1856+    }
1857+    if (period > life) {
1858+        throw new Errors_1.NumError("Function DDB parameter 4 value is "
1859+            + life + ". It should be less than or equal to value of Function DB parameter 3 with " + period + ".");
1860+    }
1861+    if (salvage >= cost) {
1862+        return 0;
1863+    }
1864+    var total = 0;
1865+    var current = 0;
1866+    for (var i = 1; i <= period; i++) {
1867+        current = Math.min((cost - total) * (factor / TypeConverter_1.checkForDevideByZero(life)), (cost - salvage - total));
1868+        total += current;
1869+    }
1870+    return current;
1871+};
1872+exports.DDB = DDB;
1873+/**
1874+ * Calculates the depreciation of an asset for a specified period using the arithmetic declining balance method.
1875+ * @param cost - The initial cost of the asset.
1876+ * @param salvage - The value of the asset at the end of depreciation.
1877+ * @param life - The number of periods over which the asset is depreciated.
1878+ * @param period - The single period within life for which to calculate depreciation.
1879+ * @param month - [ OPTIONAL - 12 by default ] - The number of months in the first year of depreciation.
1880+ * @returns {number} depreciated value
1881+ * @constructor
1882+ */
1883+var DB = function (cost, salvage, life, period, month) {
1884+    ArgsChecker_1.ArgsChecker.checkLengthWithin(arguments, 4, 5, "DB");
1885+    cost = TypeConverter_1.TypeConverter.firstValueAsNumber(cost);
1886+    salvage = TypeConverter_1.TypeConverter.firstValueAsNumber(salvage);
1887+    life = TypeConverter_1.TypeConverter.firstValueAsNumber(life);
1888+    period = TypeConverter_1.TypeConverter.firstValueAsNumber(period);
1889+    month = month !== undefined ? Math.floor(TypeConverter_1.TypeConverter.firstValueAsNumber(month)) : 12;
1890+    if (cost < 0) {
1891+        throw new Errors_1.NumError("Function DB parameter 1 value is "
1892+            + cost + ". It should be greater than or equal to 0.");
1893+    }
1894+    if (salvage < 0) {
1895+        throw new Errors_1.NumError("Function DB parameter 2 value is "
1896+            + salvage + ". It should be greater than or equal to 0.");
1897+    }
1898+    if (life < 0) {
1899+        throw new Errors_1.NumError("Function DB parameter 3 value is "
1900+            + life + ". It should be greater than or equal to 0.");
1901+    }
1902+    if (period < 0) {
1903+        throw new Errors_1.NumError("Function DB parameter 4 value is "
1904+            + period + ". It should be greater than or equal to 0.");
1905+    }
1906+    if (month > 12 || month < 1) {
1907+        throw new Errors_1.NumError("Function DB parameter 5 value is "
1908+            + month + ". Valid values are between 1 and 12 inclusive.");
1909+    }
1910+    if (period > life) {
1911+        throw new Errors_1.NumError("Function DB parameter 4 value is "
1912+            + life + ". It should be less than or equal to value of Function DB parameter 3 with " + period + ".");
1913+    }
1914+    if (salvage >= cost) {
1915+        return 0;
1916+    }
1917+    if (cost === 0 && salvage !== 0) {
1918+        throw new Errors_1.DivZeroError("Evaluation of function DB cause a divide by zero error.");
1919+    }
1920+    var rate = (1 - Math.pow(salvage / cost, 1 / life));
1921+    var initial = cost * rate * month / 12;
1922+    var total = initial;
1923+    var current = 0;
1924+    var ceiling = (period === life) ? life - 1 : period;
1925+    for (var i = 2; i <= ceiling; i++) {
1926+        current = (cost - total) * rate;
1927+        total += current;
1928+    }
1929+    if (period === 1) {
1930+        return initial;
1931+    }
1932+    else if (period === life) {
1933+        return (cost - total) * rate;
1934+    }
1935+    else {
1936+        return current;
1937+    }
1938+};
1939+exports.DB = DB;
1940+/**
1941+ * Formats a number into the locale-specific currency format. WARNING: Currently the equivalent of TRUNC, since this
1942+ * returns numbers
1943+ * @param number - The value to be formatted.
1944+ * @param places - [ OPTIONAL - 2 by default ] - The number of decimal places to display.
1945+ * @returns {number} dollars
1946+ * @constructor
1947+ */
1948+var DOLLAR = function (number, places) {
1949+    ArgsChecker_1.ArgsChecker.checkLengthWithin(arguments, 1, 2, "DOLLAR");
1950+    var v = TypeConverter_1.TypeConverter.firstValueAsNumber(number);
1951+    places = places !== undefined ? TypeConverter_1.TypeConverter.firstValueAsNumber(places) : 2;
1952+    var sign = (v > 0) ? 1 : -1;
1953+    var divisor = sign * (Math.floor(Math.abs(v) * Math.pow(10, places)));
1954+    var pow = Math.pow(10, places);
1955+    if (pow === 0 && divisor !== 0) {
1956+        throw new Errors_1.DivZeroError("Evaluation of function DOLLAR cause a divide by zero error.");
1957+    }
1958+    return divisor / pow;
1959+};
1960+exports.DOLLAR = DOLLAR;
1961+/**
1962+ * Converts a price quotation given as a decimal fraction into a decimal value.
1963+ * @param fractionalPrice - The price quotation given using fractional decimal conventions.
1964+ * @param unit - The units of the fraction, e.g. 8 for 1/8ths or 32 for 1/32nds.
1965+ * @returns {number} decimal value.
1966+ * @constructor
1967+ */
1968+var DOLLARDE = function (fractionalPrice, unit) {
1969+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 2, "DOLLARDE");
1970+    var dollar = TypeConverter_1.TypeConverter.firstValueAsNumber(fractionalPrice);
1971+    var fraction = Math.floor(TypeConverter_1.TypeConverter.firstValueAsNumber(unit));
1972+    if (fraction === 0) {
1973+        throw new Errors_1.DivZeroError("Function DOLLARDE parameter 2 cannot be zero.");
1974+    }
1975+    var result = parseInt(dollar.toString(), 10);
1976+    result += (dollar % 1) * Math.pow(10, Math.ceil(Math.log(fraction) / Math.LN10)) / fraction;
1977+    var power = Math.pow(10, Math.ceil(Math.log(fraction) / Math.LN2) + 1);
1978+    if (power === 0) {
1979+        throw new Errors_1.DivZeroError("Evaluation of function DOLLARDE cause a divide by zero error.");
1980+    }
1981+    result = Math.round(result * power) / power;
1982+    return result;
1983+};
1984+exports.DOLLARDE = DOLLARDE;
1985+/**
1986+ * Converts a price quotation given as a decimal value into a decimal fraction.
1987+ * @param decimalPrice - The price quotation given as a decimal value.
1988+ * @param unit - The units of the desired fraction, e.g. 8 for 1/8ths or 32 for 1/32nds
1989+ * @returns {number} price quotation as decimal fraction.
1990+ * @constructor
1991+ */
1992+var DOLLARFR = function (decimalPrice, unit) {
1993+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 2, "DOLLARFR");
1994+    decimalPrice = TypeConverter_1.TypeConverter.firstValueAsNumber(decimalPrice);
1995+    unit = Math.floor(TypeConverter_1.TypeConverter.firstValueAsNumber(unit));
1996+    if (unit === 0) {
1997+        throw new Errors_1.DivZeroError("Function DOLLARFR parameter 2 cannot be zero.");
1998+    }
1999+    var result = parseInt(decimalPrice.toString(), 10);
2000+    result += (decimalPrice % 1) * Math.pow(10, -Math.ceil(Math.log(unit) / Math.LN10)) * unit;
2001+    return result;
2002+};
2003+exports.DOLLARFR = DOLLARFR;
2004+/**
2005+ * Calculates the annual effective interest rate given the nominal rate and number of compounding periods per year.
2006+ * @param nominalRate - The nominal interest rate per year.
2007+ * @param periodsPerYear - The number of compounding periods per year.
2008+ * @returns {number} annual effective interest rate
2009+ * @constructor
2010+ */
2011+var EFFECT = function (nominalRate, periodsPerYear) {
2012+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 2, "EFFECT");
2013+    var rate = TypeConverter_1.TypeConverter.firstValueAsNumber(nominalRate);
2014+    var periods = TypeConverter_1.TypeConverter.firstValueAsNumber(periodsPerYear);
2015+    if (rate <= 0) {
2016+        throw new Errors_1.NumError("Function EFFECT parameter 1 value is " + rate + ". It should be greater than to 0");
2017+    }
2018+    if (periods < 1) {
2019+        throw new Errors_1.NumError("Function EFFECT parameter 2 value is " + periods + ". It should be greater than or equal to 1");
2020+    }
2021+    periods = Math.floor(periods);
2022+    return Math.pow(1 + rate / periods, periods) - 1;
2023+};
2024+exports.EFFECT = EFFECT;
2025+/**
2026+ * Calculates the periodic payment for an annuity investment based on constant-amount periodic payments and a constant
2027+ * interest rate.
2028+ * @param rate - The interest rate.
2029+ * @param periods - The number of payments to be made.
2030+ * @param presentValue - The current value of the annuity.
2031+ * @param futureValue [ OPTIONAL ] - The future value remaining after the final payment has been made.
2032+ * @param endOrBeginning [ OPTIONAL - 0 by default ] - Whether payments are due at the end (0) or beginning (1) of each
2033+ * period.
2034+ * @returns {number}
2035+ * @constructor
2036+ */
2037+var PMT = function (rate, periods, presentValue, futureValue, endOrBeginning) {
2038+    ArgsChecker_1.ArgsChecker.checkLengthWithin(arguments, 3, 5, "PMT");
2039+    rate = TypeConverter_1.TypeConverter.firstValueAsNumber(rate);
2040+    periods = TypeConverter_1.TypeConverter.firstValueAsNumber(periods);
2041+    presentValue = TypeConverter_1.TypeConverter.firstValueAsNumber(presentValue);
2042+    futureValue = futureValue ? TypeConverter_1.TypeConverter.firstValueAsNumber(futureValue) : 0;
2043+    endOrBeginning = endOrBeginning ? TypeConverter_1.TypeConverter.firstValueAsNumber(endOrBeginning) : 0;
2044+    var result;
2045+    if (rate === 0) {
2046+        result = (presentValue + futureValue) / periods;
2047+    }
2048+    else {
2049+        var term = Math.pow(1 + rate, periods);
2050+        if (endOrBeginning) {
2051+            result = (futureValue * rate / (term - 1) + presentValue * rate / (1 - 1 / term)) / (1 + rate);
2052+        }
2053+        else {
2054+            result = futureValue * rate / (term - 1) + presentValue * rate / (1 - 1 / term);
2055+        }
2056+    }
2057+    return -result;
2058+};
2059+exports.PMT = PMT;
2060+// TODO: Convert to real formula FV
2061+function fv(rate, periods, payment, value, type) {
2062+    var result;
2063+    if (rate === 0) {
2064+        result = value + payment * periods;
2065+    }
2066+    else {
2067+        var term = Math.pow(1 + rate, periods);
2068+        if (type) {
2069+            result = value * term + payment * (1 + rate) * (term - 1.0) / rate;
2070+        }
2071+        else {
2072+            result = value * term + payment * (term - 1) / rate;
2073+        }
2074+    }
2075+    return -result;
2076+}
2077+/**
2078+ * Calculates the cumulative principal paid over a range of payment periods for an investment based on constant-amount
2079+ * periodic payments and a constant interest rate.
2080+ * @param rate - The interest rate.
2081+ * @param numberOfPeriods - The number of payments to be made.
2082+ * @param presentValue - The current value of the annuity.
2083+ * @param firstPeriod - The number of the payment period to begin the cumulative calculation. must be greater
2084+ * than or equal to 1.
2085+ * @param lastPeriod - The number of the payment period to end the cumulative calculation, must be greater
2086+ * than first_period.
2087+ * @param endOrBeginning - Whether payments are due at the end (0) or beginning (1) of each period
2088+ * @returns {number} cumulative principal
2089+ * @constructor
2090+ */
2091+var CUMPRINC = function (rate, numberOfPeriods, presentValue, firstPeriod, lastPeriod, endOrBeginning) {
2092+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 6, "CUMPRINC");
2093+    rate = TypeConverter_1.TypeConverter.firstValueAsNumber(rate);
2094+    var periods = TypeConverter_1.TypeConverter.firstValueAsNumber(numberOfPeriods);
2095+    var value = TypeConverter_1.TypeConverter.firstValueAsNumber(presentValue);
2096+    var start = TypeConverter_1.TypeConverter.firstValueAsNumber(firstPeriod);
2097+    if (start < 1) {
2098+        throw new Errors_1.NumError("Function CUMPRINC parameter 4 value is " + start + ". It should be greater than or equal to 1.");
2099+    }
2100+    var end = TypeConverter_1.TypeConverter.firstValueAsNumber(lastPeriod);
2101+    if (end < 1) {
2102+        throw new Errors_1.NumError("Function CUMPRINC parameter 5 value is " + end + ". It should be greater than or equal to 1.");
2103+    }
2104+    if (end < start) {
2105+        throw new Errors_1.NumError("Function CUMPRINC parameter 5 value is " + end + ". It should be greater than or equal to " + start + ".");
2106+    }
2107+    var type = TypeConverter_1.TypeConverter.firstValueAsBoolean(endOrBeginning);
2108+    var payment = PMT(rate, periods, value, 0, type);
2109+    var principal = 0;
2110+    if (start === 1) {
2111+        if (type) {
2112+            principal = payment;
2113+        }
2114+        else {
2115+            principal = payment + value * rate;
2116+        }
2117+        start++;
2118+    }
2119+    for (var i = start; i <= end; i++) {
2120+        if (type) {
2121+            principal += payment - (fv(rate, i - 2, payment, value, 1) - payment) * rate;
2122+        }
2123+        else {
2124+            principal += payment - fv(rate, i - 1, payment, value, 0) * rate;
2125+        }
2126+    }
2127+    return principal;
2128+};
2129+exports.CUMPRINC = CUMPRINC;
2130+/**
2131+ * Calculates the cumulative interest over a range of payment periods for an investment based on constant-amount
2132+ * periodic payments and a constant interest rate.
2133+ * @param rate - The interest rate.
2134+ * @param numberOfPeriods - The number of payments to be made.
2135+ * @param presentValue - The current value of the annuity.
2136+ * @param firstPeriod - The number of the payment period to begin the cumulative calculation, must be greater
2137+ * than or equal to 1.
2138+ * @param lastPeriod - The number of the payment period to end the cumulative calculation, must be greater
2139+ * than first_period.
2140+ * @param endOrBeginning - Whether payments are due at the end (0) or beginning (1) of each period.
2141+ * @returns {number} cumulative interest
2142+ * @constructor
2143+ */
2144+var CUMIPMT = function (rate, numberOfPeriods, presentValue, firstPeriod, lastPeriod, endOrBeginning) {
2145+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 6, "CUMIPMT");
2146+    rate = TypeConverter_1.TypeConverter.firstValueAsNumber(rate);
2147+    var periods = TypeConverter_1.TypeConverter.firstValueAsNumber(numberOfPeriods);
2148+    var value = TypeConverter_1.TypeConverter.firstValueAsNumber(presentValue);
2149+    var start = TypeConverter_1.TypeConverter.firstValueAsNumber(firstPeriod);
2150+    if (start < 1) {
2151+        throw new Errors_1.NumError("Function CUMPRINC parameter 4 value is " + start + ". It should be greater than or equal to 1.");
2152+    }
2153+    var end = TypeConverter_1.TypeConverter.firstValueAsNumber(lastPeriod);
2154+    if (end < 1) {
2155+        throw new Errors_1.NumError("Function CUMPRINC parameter 5 value is " + end + ". It should be greater than or equal to 1.");
2156+    }
2157+    if (end < start) {
2158+        throw new Errors_1.NumError("Function CUMPRINC parameter 5 value is " + end + ". It should be greater than or equal to " + start + ".");
2159+    }
2160+    var type = TypeConverter_1.TypeConverter.firstValueAsBoolean(endOrBeginning);
2161+    var payment = PMT(rate, periods, value, 0, type);
2162+    var interest = 0;
2163+    if (start === 1) {
2164+        if (!type) {
2165+            interest = -value;
2166+            start++;
2167+        }
2168+        else {
2169+            start++;
2170+        }
2171+    }
2172+    for (var i = start; i <= end; i++) {
2173+        if (type) {
2174+            interest += fv(rate, i - 2, payment, value, 1) - payment;
2175+        }
2176+        else {
2177+            interest += fv(rate, i - 1, payment, value, 0);
2178+        }
2179+    }
2180+    interest *= rate;
2181+    return interest;
2182+};
2183+exports.CUMIPMT = CUMIPMT;
2184+/**
2185+ * Calculates the accrued interest of a security that has periodic payments.
2186+ * WARNING: This function has been implemented to specifications as outlined in Google Spreadsheets, LibreOffice, and
2187+ * OpenOffice. It functions much the same as MSExcel's ACCRINT, but there are several key differences. Below are links
2188+ * to illustrate the differences. Please see the source code for more information on differences. Links: https://quant.stackexchange.com/questions/7040/whats-the-algorithm-behind-excels-accrint, https://support.office.com/en-us/article/ACCRINT-function-fe45d089-6722-4fb3-9379-e1f911d8dc74, https://quant.stackexchange.com/questions/7040/whats-the-algorithm-behind-excels-accrint, https://support.google.com/docs/answer/3093200 .
2189+ * @param issue - The date the security was initially issued.
2190+ * @param firstPayment - The first date interest will be paid.
2191+ * @param settlement - The settlement date of the security, the date after issuance when the security is
2192+ * delivered to the buyer. Is the maturity date of the security if it is held until maturity rather than sold.
2193+ * @param rate - The annualized rate of interest.
2194+ * @param redemption - The redemption amount per 100 face value, or par.
2195+ * @param frequency - The number of coupon payments per year. For annual payments, frequency = 1; for
2196+ * semiannual, frequency = 2; for quarterly, frequency = 4.
2197+ * @param dayCountConvention - [ OPTIONAL - 0 by default ] - An indicator of what day count method to use.
2198+ * 0 or omitted = US (NASD) 30/360, 1 = Actual/actual, 2 = Actual/360, 3 = Actual/365, 4 = European 30/360.
2199+ * @returns {number}
2200+ * @constructor
2201+ * TODO: This function is based off of the open-source versions I was able to dig up online. We should implement a
2202+ * TODO:     second version that is closer to what MSExcel does and is named something like `ACCRINT.MS`.
2203+ */
2204+var ACCRINT = function (issue, firstPayment, settlement, rate, redemption, frequency, dayCountConvention) {
2205+    ArgsChecker_1.ArgsChecker.checkLengthWithin(arguments, 6, 7, "ACCRINT");
2206+    issue = TypeConverter_1.TypeConverter.firstValueAsDateNumber(issue);
2207+    // "firstPayment" param is only here to check for errors for GS implementation.
2208+    // In MSE, there is a 7th (zero-indexed-6th) param that indicates the calculation-method to use, which indicates
2209+    // weather the total accrued interest starting at the first_intrest date, instead of the issue date.
2210+    firstPayment = TypeConverter_1.TypeConverter.firstValueAsDateNumber(firstPayment);
2211+    if (firstPayment < 0) {
2212+        throw new Errors_1.NumError("Function ACCRINT parameter 2 value is " + firstPayment
2213+            + ". It should be greater than 0.");
2214+    }
2215+    settlement = TypeConverter_1.TypeConverter.firstValueAsDateNumber(settlement);
2216+    if (issue > settlement) {
2217+        throw new Errors_1.NumError("Function ACCRINT parameter 1 (" + issue.toString()
2218+            + ") should be on or before Function ACCRINT parameter 3 (" + settlement.toString() + ").");
2219+    }
2220+    rate = TypeConverter_1.TypeConverter.firstValueAsNumber(rate);
2221+    // redemption, aka "par"
2222+    redemption = TypeConverter_1.TypeConverter.firstValueAsNumber(redemption);
2223+    // The frequency parameter also does not affect the resulting value of the formula in the GS implementation.
2224+    // In MSE, frequency is used to calculate a more accurate value, by breaking apart the year, and computing interest
2225+    // on an on-going basis. In this implementation, we use YEARFRAC to get a numerical value that encompasses the
2226+    // functionality of "frequency".
2227+    frequency = TypeConverter_1.TypeConverter.firstValueAsNumber(frequency);
2228+    // dayCountConvention, aka "basis"
2229+    dayCountConvention = dayCountConvention !== undefined ? TypeConverter_1.TypeConverter.firstValueAsNumber(dayCountConvention) : 1;
2230+    var factor = Date_1.YEARFRAC(issue, settlement, dayCountConvention);
2231+    return redemption * rate * factor;
2232+};
2233+exports.ACCRINT = ACCRINT;
2234diff --git a/dist/Formulas/Logical.js b/dist/Formulas/Logical.js
2235new file mode 100644
2236index 0000000..e09f10a
2237--- /dev/null
2238+++ b/dist/Formulas/Logical.js
2239@@ -0,0 +1,167 @@
2240+"use strict";
2241+exports.__esModule = true;
2242+var ArgsChecker_1 = require("../Utilities/ArgsChecker");
2243+var TypeConverter_1 = require("../Utilities/TypeConverter");
2244+var Errors_1 = require("../Errors");
2245+/**
2246+ * Returns true if all of the provided arguments are logically true, and false if any of the provided arguments are
2247+ * logically false.
2248+ * @param values At least one expression or reference to a cell containing an expression that represents some logical
2249+ * value, i.e. TRUE or FALSE, or an expression that can be coerced to a logical value.
2250+ * @returns {boolean} if all values are logically true.
2251+ * @constructor
2252+ */
2253+var AND = function () {
2254+    var values = [];
2255+    for (var _i = 0; _i < arguments.length; _i++) {
2256+        values[_i] = arguments[_i];
2257+    }
2258+    ArgsChecker_1.ArgsChecker.checkAtLeastLength(values, 1, "AND");
2259+    var result = true;
2260+    for (var i = 0; i < values.length; i++) {
2261+        if (typeof values[i] === "string") {
2262+            throw new Errors_1.ValueError("AND expects boolean values. But '" + values[i]
2263+                + "' is a text and cannot be coerced to a boolean.");
2264+        }
2265+        else if (values[i] instanceof Array) {
2266+            if (!AND.apply(this, values[i])) {
2267+                result = false;
2268+                break;
2269+            }
2270+        }
2271+        else if (!values[i]) {
2272+            result = false;
2273+            break;
2274+        }
2275+    }
2276+    return result;
2277+};
2278+exports.AND = AND;
2279+/**
2280+ * Tests whether two strings are identical, returning true if they are.
2281+ * @param one - The first string to compare
2282+ * @param two - The second string to compare
2283+ * @returns {boolean}
2284+ * @constructor
2285+ */
2286+var EXACT = function (one, two) {
2287+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 2, "EXACT");
2288+    one = TypeConverter_1.TypeConverter.firstValue(one);
2289+    two = TypeConverter_1.TypeConverter.firstValue(two);
2290+    return one.toString() === two.toString();
2291+};
2292+exports.EXACT = EXACT;
2293+/**
2294+ * Returns true.
2295+ * @returns {boolean} true boolean
2296+ * @constructor
2297+ */
2298+var TRUE = function () {
2299+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 0, "TRUE");
2300+    return true;
2301+};
2302+exports.TRUE = TRUE;
2303+/**
2304+ * Returns false.
2305+ * @returns {boolean} false boolean
2306+ * @constructor
2307+ */
2308+var FALSE = function () {
2309+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 0, "FALSE");
2310+    return false;
2311+};
2312+exports.FALSE = FALSE;
2313+/**
2314+ * Returns the opposite of a logical value - NOT(TRUE) returns FALSE; NOT(FALSE) returns TRUE.
2315+ * @param value - An expression or reference to a cell holding an expression that represents some logical value.
2316+ * @returns {boolean} opposite of a logical value input
2317+ * @constructor
2318+ */
2319+var NOT = function (value) {
2320+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 1, "NOT");
2321+    if (typeof (value) === "boolean") {
2322+        return !value;
2323+    }
2324+    if (typeof (value) === "string") {
2325+        if (value === "") {
2326+            return true;
2327+        }
2328+        throw new Errors_1.ValueError("Function NOT parameter 1 expects boolean values. But '" + value
2329+            + "' is a text and cannot be coerced to a boolean.");
2330+    }
2331+    if (typeof (value) === "number") {
2332+        return value === 0;
2333+    }
2334+    if (value instanceof Array) {
2335+        if (value.length === 0) {
2336+            throw new Errors_1.RefError("Reference does not exist.");
2337+        }
2338+        return NOT(value[0]);
2339+    }
2340+};
2341+exports.NOT = NOT;
2342+/**
2343+ * Returns true if any of the provided arguments are logically true, and false if all of the provided arguments are
2344+ * logically false.
2345+ * @param values An expression or reference to a cell containing an expression that represents some logical value, i.e.
2346+ * TRUE or FALSE, or an expression that can be coerced to a logical value.
2347+ * @returns {boolean}
2348+ * @constructor
2349+ */
2350+var OR = function () {
2351+    var values = [];
2352+    for (var _i = 0; _i < arguments.length; _i++) {
2353+        values[_i] = arguments[_i];
2354+    }
2355+    ArgsChecker_1.ArgsChecker.checkAtLeastLength(values, 1, "OR");
2356+    for (var i = 0; i < values.length; i++) {
2357+        if (values[i] instanceof Array) {
2358+            if (values[i].length === 0) {
2359+                throw new Errors_1.RefError("Reference does not exist.");
2360+            }
2361+            if (OR.apply(this, values[i])) {
2362+                return true;
2363+            }
2364+        }
2365+        else if (TypeConverter_1.TypeConverter.valueToBoolean(values[i])) {
2366+            return true;
2367+        }
2368+    }
2369+    return false;
2370+};
2371+exports.OR = OR;
2372+/**
2373+ * Exclusive or or exclusive disjunction is a logical operation that outputs true only when inputs differ.
2374+ * @param values to check for exclusivity.
2375+ * @returns {boolean} returns true if only one input is considered logically true.
2376+ * @constructor
2377+ */
2378+var XOR = function () {
2379+    var values = [];
2380+    for (var _i = 0; _i < arguments.length; _i++) {
2381+        values[_i] = arguments[_i];
2382+    }
2383+    ArgsChecker_1.ArgsChecker.checkAtLeastLength(values, 1, "XOR");
2384+    var alreadyTruthy = false;
2385+    for (var i = 0; i < values.length; i++) {
2386+        if (values[i] instanceof Array) {
2387+            if (values[i].length === 0) {
2388+                throw new Errors_1.RefError("Reference does not exist.");
2389+            }
2390+            if (XOR.apply(this, values[i])) {
2391+                if (alreadyTruthy) {
2392+                    return false;
2393+                }
2394+                alreadyTruthy = true;
2395+            }
2396+        }
2397+        else if (TypeConverter_1.TypeConverter.valueToBoolean(values[i])) {
2398+            if (alreadyTruthy) {
2399+                return false;
2400+            }
2401+            alreadyTruthy = true;
2402+        }
2403+    }
2404+    return alreadyTruthy;
2405+};
2406+exports.XOR = XOR;
2407diff --git a/dist/Formulas/Math.js b/dist/Formulas/Math.js
2408new file mode 100644
2409index 0000000..0603a00
2410--- /dev/null
2411+++ b/dist/Formulas/Math.js
2412@@ -0,0 +1,983 @@
2413+"use strict";
2414+exports.__esModule = true;
2415+var ArgsChecker_1 = require("../Utilities/ArgsChecker");
2416+var TypeConverter_1 = require("../Utilities/TypeConverter");
2417+var Filter_1 = require("../Utilities/Filter");
2418+var Serializer_1 = require("../Utilities/Serializer");
2419+var CriteriaFunctionFactory_1 = require("../Utilities/CriteriaFunctionFactory");
2420+var Errors_1 = require("../Errors");
2421+var MathHelpers_1 = require("../Utilities/MathHelpers");
2422+/**
2423+ * Returns the absolute value of a number.
2424+ * @param value to get the absolute value of.
2425+ * @returns {number} absolute value
2426+ * @constructor
2427+ */
2428+var ABS = function (value) {
2429+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 1, "ABS");
2430+    var v = TypeConverter_1.TypeConverter.valueToNumber(value);
2431+    return Math.abs(v);
2432+};
2433+exports.ABS = ABS;
2434+/**
2435+ * Returns the inverse cosine of a value, in radians.
2436+ * @param value The value for which to calculate the inverse cosine. Must be between -1 and 1, inclusive.
2437+ * @returns {number} inverse cosine of value
2438+ * @constructor
2439+ */
2440+var ACOS = function (value) {
2441+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 1, "ACOS");
2442+    value = TypeConverter_1.TypeConverter.valueToNumber(value);
2443+    if (value === -1) {
2444+        return Math.PI;
2445+    }
2446+    else if (value > 1 || value < -1) {
2447+        throw new Errors_1.NumError("Function ACOS parameter 1 value is " + value + ". Valid values are between -1 and 1 inclusive.");
2448+    }
2449+    return Math.acos(value);
2450+};
2451+exports.ACOS = ACOS;
2452+/**
2453+ * Returns the inverse hyperbolic cosine of a number.
2454+ * @param value The value for which to calculate the inverse hyperbolic cosine. Must be greater than or equal to 1.
2455+ * @returns {number} to find the inverse hyperbolic cosine for.
2456+ * @constructor
2457+ */
2458+var ACOSH = function (value) {
2459+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 1, "ACOSH");
2460+    value = TypeConverter_1.TypeConverter.valueToNumber(value);
2461+    if (value < 1) {
2462+        throw new Errors_1.NumError("Function ACOSH parameter 1 value is " + value + ". It should be greater than or equal to 1.");
2463+    }
2464+    return Math.log(value + Math.sqrt(value * value - 1));
2465+};
2466+exports.ACOSH = ACOSH;
2467+/**
2468+ * Calculate the hyperbolic arc-cotangent of a value
2469+ * @param value number not between -1 and 1 inclusively.
2470+ * @returns {number} hyperbolic arc-cotangent
2471+ * @constructor
2472+ */
2473+var ACOTH = function (value) {
2474+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 1, "ACOTH");
2475+    value = TypeConverter_1.TypeConverter.valueToNumber(value);
2476+    if (value <= 1 && value >= -1) {
2477+        throw new Errors_1.NumError("Function ACOTH parameter 1 value is " + value + ". Valid values cannot be between -1 and 1 inclusive.");
2478+    }
2479+    return 0.5 * Math.log((value + 1) / (value - 1));
2480+};
2481+exports.ACOTH = ACOTH;
2482+/**
2483+ * Returns the inverse sine of a value, in radians.
2484+ * @param value The value for which to calculate the inverse sine. Must be between -1 and 1, inclusive.
2485+ * @returns {number} inverse sine of input value
2486+ * @constructor
2487+ */
2488+var ASIN = function (value) {
2489+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 1, "ASIN");
2490+    value = TypeConverter_1.TypeConverter.valueToNumber(value);
2491+    if (value === -1) {
2492+        return Math.PI;
2493+    }
2494+    else if (value > 1 || value < -1) {
2495+        throw new Errors_1.NumError("Function ASIN parameter 1 value is " + value + ". Valid values are between -1 and 1 inclusive.");
2496+    }
2497+    return Math.asin(value);
2498+};
2499+exports.ASIN = ASIN;
2500+/**
2501+ * Returns the inverse hyperbolic sine of a number.
2502+ * @param value The value for which to calculate the inverse hyperbolic sine.
2503+ * @returns {number} inverse hyperbolic sine of input
2504+ * @constructor
2505+ */
2506+var ASINH = function (value) {
2507+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 1, "ASINH");
2508+    value = TypeConverter_1.TypeConverter.valueToNumber(value);
2509+    return Math.log(value + Math.sqrt(value * value + 1));
2510+};
2511+exports.ASINH = ASINH;
2512+/**
2513+ * Returns the inverse tangent of a value, in radians.
2514+ * @param value The value for which to calculate the inverse tangent.
2515+ * @returns {number} inverse tangent of input value
2516+ * @constructor
2517+ */
2518+var ATAN = function (value) {
2519+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 1, "ATAN");
2520+    value = TypeConverter_1.TypeConverter.valueToNumber(value);
2521+    if (value === -1) {
2522+        return Math.PI;
2523+    }
2524+    else if (value > 1 || value < -1) {
2525+        throw new Errors_1.NumError("Function ATAN parameter 1 value is " + value + ". Valid values are between -1 and 1 inclusive.");
2526+    }
2527+    return Math.atan(value);
2528+};
2529+exports.ATAN = ATAN;
2530+/**
2531+ * Returns the angle between the x-axis and a line segment from the origin (0,0) to specified coordinate pair (x,y), in radians.
2532+ * @param x The x coordinate of the endpoint of the line segment for which to calculate the angle from the x-axis.
2533+ * @param y The y coordinate of the endpoint of the line segment for which to calculate the angle from the x-axis.
2534+ * @returns {number} angle in radians
2535+ * @constructor
2536+ */
2537+var ATAN2 = function (x, y) {
2538+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 2, "ATAN2");
2539+    x = TypeConverter_1.TypeConverter.valueToNumber(x);
2540+    y = TypeConverter_1.TypeConverter.valueToNumber(y);
2541+    if (x === 0 && y === 0) {
2542+        throw new Errors_1.DivZeroError("Evaluation of function ATAN2 caused a divide by zero error.");
2543+    }
2544+    return Math.atan2(y, x);
2545+};
2546+exports.ATAN2 = ATAN2;
2547+/**
2548+ * Returns the inverse hyperbolic tangent of a number.
2549+ * @param value The value for which to calculate the inverse hyperbolic tangent. Must be between -1 and 1, exclusive.
2550+ * @returns {number} inverse hyperbolic tangent of input
2551+ * @constructor
2552+ */
2553+var ATANH = function (value) {
2554+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 1, "ATANH");
2555+    value = TypeConverter_1.TypeConverter.valueToNumber(value);
2556+    if (value >= 1 || value <= -1) {
2557+        throw new Errors_1.NumError("Function ATANH parameter 1 value is " + value + ". Valid values are between -1 and 1 exclusive.");
2558+    }
2559+    if (Math.abs(value) < 1) {
2560+    }
2561+    return Math["atanh"](value);
2562+};
2563+exports.ATANH = ATANH;
2564+/**
2565+ * Rounds a number up to the nearest even integer.
2566+ * @param value The value to round to the next greatest even number.
2567+ * @returns {number} next greatest even number
2568+ * @constructor
2569+ */
2570+var EVEN = function (value) {
2571+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 1, "EVEN");
2572+    var X = TypeConverter_1.TypeConverter.firstValueAsNumber(value);
2573+    return X % 2 === 1 ? X + 1 : X;
2574+};
2575+exports.EVEN = EVEN;
2576+/**
2577+ * Returns the result of the modulo operator, the remainder after a division operation.
2578+ * @param dividend The number to be divided to find the remainder.
2579+ * @param divisor The number to divide by.
2580+ * @returns {number}
2581+ * @constructor
2582+ */
2583+var MOD = function (dividend, divisor) {
2584+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 2, "MOD");
2585+    var oneN = TypeConverter_1.TypeConverter.valueToNumber(dividend);
2586+    var twoN = TypeConverter_1.TypeConverter.valueToNumber(divisor);
2587+    if (twoN === 0) {
2588+        throw new Errors_1.DivZeroError("Function MOD parameter 2 cannot be zero.");
2589+    }
2590+    return oneN % twoN;
2591+};
2592+exports.MOD = MOD;
2593+/**
2594+ * Rounds a number up to the nearest odd integer.
2595+ * @param value The value to round to the next greatest odd number.
2596+ * @returns {number} value to round up to next greatest odd number.
2597+ * @constructor
2598+ */
2599+var ODD = function (value) {
2600+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 1, "ODD");
2601+    var X = TypeConverter_1.TypeConverter.firstValueAsNumber(value);
2602+    return X % 2 === 1 ? X : X + 1;
2603+};
2604+exports.ODD = ODD;
2605+/**
2606+ * Returns a number raised to a power.
2607+ * @param base - The number to raise to the exponent power.
2608+ * @param exponent - The exponent to raise base to.
2609+ * @returns {number} resulting number
2610+ * @constructor
2611+ */
2612+var POWER = function (base, exponent) {
2613+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 2, "POWER");
2614+    var n = TypeConverter_1.TypeConverter.firstValueAsNumber(base);
2615+    var p = TypeConverter_1.TypeConverter.firstValueAsNumber(exponent);
2616+    return Math.pow(n, p);
2617+};
2618+exports.POWER = POWER;
2619+/**
2620+ * Returns the sum of a series of numbers and/or cells.
2621+ * @param values The first number or range to add together.
2622+ * @returns {number} The sum of the series
2623+ * @constructor
2624+ */
2625+var SUM = function () {
2626+    var values = [];
2627+    for (var _i = 0; _i < arguments.length; _i++) {
2628+        values[_i] = arguments[_i];
2629+    }
2630+    ArgsChecker_1.ArgsChecker.checkAtLeastLength(values, 1, "SUM");
2631+    var result = 0;
2632+    for (var i = 0; i < values.length; i++) {
2633+        if (values[i] instanceof Array) {
2634+            result = result + SUM.apply(this, values[i]);
2635+        }
2636+        else {
2637+            if (values[i] === "") {
2638+                throw new Errors_1.ValueError("Function SUM parameter " + i + " expects number values. But '" + values[i] + "' is a text and cannot be coerced to a number.");
2639+            }
2640+            result = result + TypeConverter_1.TypeConverter.valueToNumber(values[i]);
2641+        }
2642+    }
2643+    return result;
2644+};
2645+exports.SUM = SUM;
2646+/**
2647+ * Returns the positive square root of a positive number.
2648+ * @param value - The number for which to calculate the positive square root.
2649+ * @returns {number} square root
2650+ * @constructor
2651+ */
2652+var SQRT = function (value) {
2653+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 1, "SQRT");
2654+    var x = TypeConverter_1.TypeConverter.firstValueAsNumber(value);
2655+    if (x < 0) {
2656+        throw new Errors_1.ValueError("Function SQRT parameter 1 value is " + x + ". It should be greater than or equal to 0.");
2657+    }
2658+    return Math.sqrt(x);
2659+};
2660+exports.SQRT = SQRT;
2661+/**
2662+ * Returns the positive square root of the product of Pi and the given positive number.
2663+ * @param value - The number which will be multiplied by Pi and have the product's square root returned
2664+ * @returns {number} the positive square root of the product of Pi and the given positive number.
2665+ * @constructor
2666+ */
2667+var SQRTPI = function (value) {
2668+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 1, "SQRTPI");
2669+    var n = TypeConverter_1.TypeConverter.firstValueAsNumber(value);
2670+    if (n < 0) {
2671+        throw new Errors_1.NumError("Function SQRTPI parameter 1 value is " + n + ". It should be greater than or equal to 0.");
2672+    }
2673+    return Math.sqrt(n * Math.PI);
2674+};
2675+exports.SQRTPI = SQRTPI;
2676+/**
2677+ * Returns the cosine of an angle provided in radians.
2678+ * @param value - The angle to find the cosine of, in radians.
2679+ * @returns {number} cosine of angle
2680+ * @constructor
2681+ */
2682+var COS = function (value) {
2683+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 1, "COS");
2684+    var r = TypeConverter_1.TypeConverter.firstValueAsNumber(value);
2685+    return Math.cos(r);
2686+};
2687+exports.COS = COS;
2688+/**
2689+ * Returns the hyperbolic cosine of any real number.
2690+ * @param value - Any real value to calculate the hyperbolic cosine of.
2691+ * @returns {number} the hyperbolic cosine of the input
2692+ * @constructor
2693+ */
2694+var COSH = function (value) {
2695+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 1, "COSH");
2696+    var r = TypeConverter_1.TypeConverter.firstValueAsNumber(value);
2697+    return Math["cosh"](r);
2698+};
2699+exports.COSH = COSH;
2700+/**
2701+ * Returns the cotangent of any real number. Defined as cot(x) = 1 / tan(x).
2702+ * @param value - number to calculate the cotangent for
2703+ * @returns {number} cotangent
2704+ * @constructor
2705+ */
2706+var COT = function (value) {
2707+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 1, "COT");
2708+    var x = TypeConverter_1.TypeConverter.firstValueAsNumber(value);
2709+    if (x === 0) {
2710+        throw new Errors_1.DivZeroError("Evaluation of function COT caused a divide by zero error.");
2711+    }
2712+    return 1 / Math.tan(x);
2713+};
2714+exports.COT = COT;
2715+/**
2716+ * Return the hyperbolic cotangent of a value, defined as coth(x) = 1 / tanh(x).
2717+ * @param value - value to calculate the hyperbolic cotangent value of
2718+ * @returns {number} hyperbolic cotangent
2719+ * @constructor
2720+ */
2721+var COTH = function (value) {
2722+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 1, "COTH");
2723+    var x = TypeConverter_1.TypeConverter.firstValueAsNumber(value);
2724+    if (x === 0) {
2725+        throw new Errors_1.DivZeroError("Evaluation of function COTH caused a divide by zero error.");
2726+    }
2727+    return 1 / Math["tanh"](x);
2728+};
2729+exports.COTH = COTH;
2730+/**
2731+ * Rounds a number down to the nearest integer that is less than or equal to it.
2732+ * @param value -  The value to round down to the nearest integer.
2733+ * @returns {number} Rounded number
2734+ * @constructor
2735+ */
2736+var INT = function (value) {
2737+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 1, "INT");
2738+    var x = TypeConverter_1.TypeConverter.firstValueAsNumber(value);
2739+    return Math.floor(x);
2740+};
2741+exports.INT = INT;
2742+/**
2743+ * Checks whether the provided value is even.
2744+ * @param value - The value to be verified as even.
2745+ * @returns {boolean} whether this value is even or not
2746+ * @constructor
2747+ */
2748+var ISEVEN = function (value) {
2749+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 1, "ISEVEN");
2750+    if (value === "") {
2751+        throw new Errors_1.ValueError("Function ISEVEN parameter 1 expects boolean values. But '" + value + "' is a text and cannot be coerced to a boolean.");
2752+    }
2753+    var x = TypeConverter_1.TypeConverter.firstValueAsNumber(value);
2754+    return Math.floor(x) % 2 === 0;
2755+};
2756+exports.ISEVEN = ISEVEN;
2757+/**
2758+ * Checks whether the provided value is odd.
2759+ * @param value - The value to be verified as odd.
2760+ * @returns {boolean} whether this value is odd or not
2761+ * @constructor
2762+ */
2763+var ISODD = function (value) {
2764+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 1, "ISODD");
2765+    if (value === "") {
2766+        throw new Errors_1.ValueError("Function ISODD parameter 1 expects boolean values. But '" + value + "' is a text and cannot be coerced to a boolean.");
2767+    }
2768+    var x = TypeConverter_1.TypeConverter.firstValueAsNumber(value);
2769+    return Math.floor(x) % 2 === 1;
2770+};
2771+exports.ISODD = ISODD;
2772+/**
2773+ * Returns the sine of an angle provided in radians.
2774+ * @param value - The angle to find the sine of, in radians.
2775+ * @returns {number} Sine of angle.
2776+ * @constructor
2777+ */
2778+var SIN = function (value) {
2779+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 1, "SIN");
2780+    var rad = TypeConverter_1.TypeConverter.firstValueAsNumber(value);
2781+    return rad === Math.PI ? 0 : Math.sin(rad);
2782+};
2783+exports.SIN = SIN;
2784+/**
2785+ * Returns the hyperbolic sine of any real number.
2786+ * @param value - real number to find the hyperbolic sine of
2787+ * @returns {number} hyperbolic sine
2788+ * @constructor
2789+ */
2790+var SINH = function (value) {
2791+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 1, "SINH");
2792+    var rad = TypeConverter_1.TypeConverter.firstValueAsNumber(value);
2793+    return Math["sinh"](rad);
2794+};
2795+exports.SINH = SINH;
2796+/**
2797+ * The value Pi.
2798+ * @returns {number} Pi.
2799+ * @constructor
2800+ */
2801+var PI = function () {
2802+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 0, "SINH");
2803+    return Math.PI;
2804+};
2805+exports.PI = PI;
2806+/**
2807+ * Returns the the logarithm of a number, base 10.
2808+ * @param value - The value for which to calculate the logarithm, base 10.
2809+ * @returns {number} logarithm of the number, in base 10.
2810+ * @constructor
2811+ */
2812+var LOG10 = function (value) {
2813+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 1, "LOG10");
2814+    var n = TypeConverter_1.TypeConverter.firstValueAsNumber(value);
2815+    if (n < 1) {
2816+        throw new Errors_1.NumError("Function LOG10 parameter 1 value is " + n + ". It should be greater than 0.");
2817+    }
2818+    var ln = Math.log(n);
2819+    var lb = Math.log(10);
2820+    return ln / lb;
2821+};
2822+exports.LOG10 = LOG10;
2823+/**
2824+ * Returns the the logarithm of a number given a base.
2825+ * @param value - The value for which to calculate the logarithm given base.
2826+ * @param base - The base to use for calculation of the logarithm. Defaults to 10.
2827+ * @returns {number}
2828+ * @constructor
2829+ */
2830+var LOG = function (value, base) {
2831+    ArgsChecker_1.ArgsChecker.checkAtLeastLength(arguments, 2, "LOG");
2832+    var n = TypeConverter_1.TypeConverter.firstValueAsNumber(value);
2833+    var b = TypeConverter_1.TypeConverter.firstValueAsNumber(base);
2834+    if (b < 1) {
2835+        throw new Errors_1.NumError("Function LOG parameter 2 value is " + b + ". It should be greater than 0.");
2836+    }
2837+    if (b < 2) {
2838+        throw new Errors_1.DivZeroError("Evaluation of function LOG caused a divide by zero error.");
2839+    }
2840+    var ln = Math.log(n);
2841+    var lb = Math.log(b);
2842+    if (lb === 0) {
2843+        throw new Errors_1.DivZeroError("Evaluation of function LOG caused a divide by zero error.");
2844+    }
2845+    return ln / lb;
2846+};
2847+exports.LOG = LOG;
2848+/**
2849+ * Returns the logarithm of a number, base e (Euler's number).
2850+ * @param value - The value for which to calculate the logarithm, base e.
2851+ * @returns {number} logarithm calculated
2852+ * @constructor
2853+ */
2854+var LN = function (value) {
2855+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 1, "LN");
2856+    var n = TypeConverter_1.TypeConverter.firstValueAsNumber(value);
2857+    if (n < 1) {
2858+        throw new Errors_1.NumError("Function LN parameter 1 value is " + n + ". It should be greater than 0.");
2859+    }
2860+    return Math.log(n);
2861+};
2862+exports.LN = LN;
2863+/**
2864+ * Returns the tangent of an angle provided in radians.
2865+ * @param value - The angle to find the tangent of, in radians.
2866+ * @returns {number} tangent in radians
2867+ * @constructor
2868+ */
2869+var TAN = function (value) {
2870+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 1, "TAN");
2871+    var rad = TypeConverter_1.TypeConverter.firstValueAsNumber(value);
2872+    return rad === Math.PI ? 0 : Math.tan(rad);
2873+};
2874+exports.TAN = TAN;
2875+/**
2876+ * Returns the hyperbolic tangent of any real number.
2877+ * @param value - Any real value to calculate the hyperbolic tangent of.
2878+ * @returns {number} hyperbolic tangent
2879+ * @constructor
2880+ */
2881+var TANH = function (value) {
2882+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 1, "TANH");
2883+    var rad = TypeConverter_1.TypeConverter.firstValueAsNumber(value);
2884+    return Math["tanh"](rad);
2885+};
2886+exports.TANH = TANH;
2887+/**
2888+ * Rounds a number up to the nearest integer multiple of specified significance.
2889+ * @param value The value to round up to the nearest integer multiple of factor.
2890+ * @param factor - [ OPTIONAL ] The number to whose multiples value will be rounded.
2891+ * @returns {number}
2892+ * @constructor
2893+ */
2894+var CEILING = function (value, factor) {
2895+    ArgsChecker_1.ArgsChecker.checkLengthWithin(arguments, 1, 2, "CEILING");
2896+    var num = TypeConverter_1.TypeConverter.firstValueAsNumber(value);
2897+    if (factor === undefined) {
2898+        return Math.ceil(num);
2899+    }
2900+    var significance = TypeConverter_1.TypeConverter.firstValueAsNumber(factor);
2901+    if (significance === 0) {
2902+        throw new Errors_1.DivZeroError("Function CEILING parameter 2 cannot be zero.");
2903+    }
2904+    var precision = -Math.floor(Math.log(significance) / Math.log(10));
2905+    if (num >= 0) {
2906+        return ROUND(Math.ceil(num / significance) * significance, precision);
2907+    }
2908+    else {
2909+        return -ROUND(Math.floor(Math.abs(num) / significance) * significance, precision);
2910+    }
2911+};
2912+exports.CEILING = CEILING;
2913+/**
2914+ * Rounds a number down to the nearest integer multiple of specified significance.
2915+ * @param value - The value to round down to the nearest integer multiple of factor.
2916+ * @param factor - The number to whose multiples value will be rounded.
2917+ * @returns {number}
2918+ * @constructor
2919+ */
2920+var FLOOR = function (value, factor) {
2921+    ArgsChecker_1.ArgsChecker.checkLengthWithin(arguments, 1, 2, "FLOOR");
2922+    var num = TypeConverter_1.TypeConverter.firstValueAsNumber(value);
2923+    if (factor === undefined) {
2924+        return Math.floor(num);
2925+    }
2926+    var significance = TypeConverter_1.TypeConverter.firstValueAsNumber(factor);
2927+    if (significance === 0) {
2928+        throw new Errors_1.DivZeroError("Function FLOOR parameter 2 cannot be zero.");
2929+    }
2930+    significance = significance ? Math.abs(significance) : 1;
2931+    var precision = -Math.floor(Math.log(significance) / Math.log(10));
2932+    if (num >= 0) {
2933+        return ROUND(Math.floor(num / significance) * significance, precision);
2934+    }
2935+    return -ROUND(Math.floor(Math.abs(num) / significance) * significance, precision);
2936+};
2937+exports.FLOOR = FLOOR;
2938+/**
2939+ * Returns one value if a logical expression is TRUE and another if it is FALSE.
2940+ * @param logicalExpression - An expression or reference to a cell containing an expression that represents some logical value, i.e. TRUE or FALSE.
2941+ * @param valueIfTrue - The value the function returns if logical_expression is TRUE
2942+ * @param valueIfFalse - The value the function returns if logical_expression is FALSE.
2943+ * @returns one value if a logical expression is TRUE and another if it is FALSE.
2944+ * @constructor
2945+ */
2946+var IF = function (logicalExpression, valueIfTrue, valueIfFalse) {
2947+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 3, "IF");
2948+    if (logicalExpression instanceof Array) {
2949+        if (logicalExpression.length === 0) {
2950+            throw new Errors_1.RefError("Reference does not exist.");
2951+        }
2952+        return IF(logicalExpression[0], valueIfTrue, valueIfFalse);
2953+    }
2954+    else if (logicalExpression === "") {
2955+        return valueIfFalse;
2956+    }
2957+    return (TypeConverter_1.TypeConverter.valueToBoolean(logicalExpression)) ? valueIfTrue : valueIfFalse;
2958+};
2959+exports.IF = IF;
2960+/**
2961+ * Returns a conditional count across a range.
2962+ * @param range - The range that is tested against criterion., value[1];
2963+ * @param criteria - The pattern or test to apply to range. If the range to check against contains text,
2964+ * this must be a string. It can be a comparison based string (e.g. "=1", "<1", ">=1") or it can be a wild-card string,
2965+ * in which * matches any number of characters, and ? matches the next character. Both ? and * can be escaped by placing
2966+ * a ~ in front of them. If it is neither, it will compared with values in the range using equality comparison.
2967+ * @returns {number}
2968+ * @constructor
2969+ */
2970+var COUNTIF = function (range, criteria) {
2971+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 2, "COUNTIF");
2972+    if (!(range instanceof Array)) {
2973+        range = [range];
2974+    }
2975+    var criteriaEvaluation = CriteriaFunctionFactory_1.CriteriaFunctionFactory.createCriteriaFunction(criteria);
2976+    var count = 0;
2977+    for (var i = 0; i < range.length; i++) {
2978+        var x = range[i];
2979+        if (x instanceof Array) {
2980+            count = count + COUNTIF.apply(this, [x, criteria]);
2981+        }
2982+        else if (criteriaEvaluation(x)) {
2983+            count++;
2984+        }
2985+    }
2986+    return count;
2987+};
2988+exports.COUNTIF = COUNTIF;
2989+/**
2990+ * Returns the count of a range depending on multiple criteria.
2991+ * @param values[0] criteria_range1 - The range to check against criterion1.
2992+ * @param values[1] criterion1 - The pattern or test to apply to criteria_range1.
2993+ * @param values[2...N] Repeated sets of ranges and criterion to check.
2994+ * @returns {number} count
2995+ * @constructor
2996+ */
2997+var COUNTIFS = function () {
2998+    var values = [];
2999+    for (var _i = 0; _i < arguments.length; _i++) {
3000+        values[_i] = arguments[_i];
3001+    }
3002+    ArgsChecker_1.ArgsChecker.checkAtLeastLength(values, 2, "COUNTIFS");
3003+    var criteriaEvaluationFunctions = values.map(function (criteria, index) {
3004+        if (index % 2 === 1) {
3005+            return CriteriaFunctionFactory_1.CriteriaFunctionFactory.createCriteriaFunction(criteria);
3006+        }
3007+        else {
3008+            return function () { return false; };
3009+        }
3010+    });
3011+    var filteredValues = [];
3012+    // Flatten arrays/ranges
3013+    for (var x = 0; x < values.length; x++) {
3014+        // If this is an array/range parameter
3015+        if (x % 2 === 0) {
3016+            filteredValues.push(Filter_1.Filter.flatten(values[x]));
3017+        }
3018+        else {
3019+            filteredValues.push(values[x]);
3020+        }
3021+    }
3022+    var count = 0;
3023+    // For every value in the range
3024+    for (var i = 0; i < filteredValues[0].length; i++) {
3025+        // Check for criteria eval for other ranges and other criteria pairs.
3026+        var otherCriteriaEvaluationSuccessfulSoFar = true;
3027+        for (var x = 0; x < filteredValues.length; x += 2) {
3028+            if (filteredValues[x].length < filteredValues[0].length) {
3029+                throw new Errors_1.ValueError("Array arguments to COUNTIFS are of different size.");
3030+            }
3031+            var criteriaEvaluation = criteriaEvaluationFunctions[x + 1];
3032+            if (otherCriteriaEvaluationSuccessfulSoFar) {
3033+                if (!criteriaEvaluation(filteredValues[x][i])) {
3034+                    otherCriteriaEvaluationSuccessfulSoFar = false;
3035+                }
3036+            }
3037+        }
3038+        if (otherCriteriaEvaluationSuccessfulSoFar) {
3039+            count++;
3040+        }
3041+    }
3042+    return count;
3043+};
3044+exports.COUNTIFS = COUNTIFS;
3045+/**
3046+ * Rounds a number to a certain number of decimal places according to standard rules.
3047+ * @param value - The value to round to places number of places.
3048+ * @param places - The number of decimal places to which to round.
3049+ * @returns {number}
3050+ * @constructor
3051+ */
3052+var ROUND = function (value, places) {
3053+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 2, "ROUND");
3054+    var n = TypeConverter_1.TypeConverter.firstValueAsNumber(value);
3055+    var d = TypeConverter_1.TypeConverter.firstValueAsNumber(places);
3056+    return Math.round(n * Math.pow(10, d)) / Math.pow(10, d);
3057+};
3058+exports.ROUND = ROUND;
3059+/**
3060+ * Rounds a number to a certain number of decimal places, always rounding down to the next valid increment.
3061+ * @param value - The value to round to places number of places, always rounding down.
3062+ * @param places - (optional) The number of decimal places to which to round.
3063+ * @returns {number}
3064+ * @constructor
3065+ */
3066+var ROUNDDOWN = function (value, places) {
3067+    ArgsChecker_1.ArgsChecker.checkLengthWithin(arguments, 1, 2, "ROUNDDOWN");
3068+    var n = TypeConverter_1.TypeConverter.firstValueAsNumber(value);
3069+    if (places === undefined) {
3070+        return Math.floor(n);
3071+    }
3072+    var d = TypeConverter_1.TypeConverter.firstValueAsNumber(places);
3073+    return Math.floor(n * Math.pow(10, d)) / Math.pow(10, d);
3074+};
3075+exports.ROUNDDOWN = ROUNDDOWN;
3076+/**
3077+ * Rounds a number to a certain number of decimal places, always rounding up to the next valid increment.
3078+ * @param value - The value to round to places number of places, always rounding up.
3079+ * @param places - (optional) The number of decimal places to which to round.
3080+ * @returns {number}
3081+ * @constructor
3082+ */
3083+var ROUNDUP = function (value, places) {
3084+    ArgsChecker_1.ArgsChecker.checkLengthWithin(arguments, 1, 2, "ROUNDUP");
3085+    var n = TypeConverter_1.TypeConverter.firstValueAsNumber(value);
3086+    if (places === undefined) {
3087+        return Math.ceil(n);
3088+    }
3089+    var d = TypeConverter_1.TypeConverter.firstValueAsNumber(places);
3090+    return Math.ceil(n * Math.pow(10, d)) / Math.pow(10, d);
3091+};
3092+exports.ROUNDUP = ROUNDUP;
3093+/**
3094+ * Returns a conditional sum across a range.
3095+ * @param range -  The range which is tested against criterion.
3096+ * @param criteria - The pattern or test to apply to range. If the range to check against contains text, this must be a
3097+ * string. It can be a comparison based string (e.g. "=1", "<1", ">=1") or it can be a wild-card string, in which *
3098+ * matches any number of characters, and ? matches the next character. Both ? and * can be escaped by placing a ~ in
3099+ * front of them.
3100+ * @param sumRange - (optional) The range to be summed, if different from range.
3101+ * @returns {number}
3102+ * @constructor
3103+ */
3104+var SUMIF = function (range, criteria, sumRange) {
3105+    ArgsChecker_1.ArgsChecker.checkLengthWithin(arguments, 2, 3, "SUMIF");
3106+    var criteriaEvaluation = CriteriaFunctionFactory_1.CriteriaFunctionFactory.createCriteriaFunction(criteria);
3107+    var sum = 0;
3108+    for (var i = 0; i < range.length; i++) {
3109+        var x = range[i];
3110+        if (x instanceof Array) {
3111+            sum += SUMIF.apply(this, [x, criteria]);
3112+        }
3113+        else {
3114+            if (sumRange && i > sumRange.length - 1) {
3115+                continue;
3116+            }
3117+            if (arguments.length === 2 && TypeConverter_1.TypeConverter.canCoerceToNumber(x) && criteriaEvaluation(x)) {
3118+                sum = sum + TypeConverter_1.TypeConverter.valueToNumber(x);
3119+            }
3120+            else if (arguments.length === 3 && TypeConverter_1.TypeConverter.canCoerceToNumber(sumRange[i]) && criteriaEvaluation(x)) {
3121+                sum = sum + TypeConverter_1.TypeConverter.valueToNumber(sumRange[i]);
3122+            }
3123+        }
3124+    }
3125+    return sum;
3126+};
3127+exports.SUMIF = SUMIF;
3128+/**
3129+ * Returns the sum of the squares of a series of numbers and/or cells.
3130+ * @param values  The values or range(s) whose squares to add together.
3131+ * @returns {number} the sum of the squares if the input.
3132+ * @constructor
3133+ */
3134+var SUMSQ = function () {
3135+    var values = [];
3136+    for (var _i = 0; _i < arguments.length; _i++) {
3137+        values[_i] = arguments[_i];
3138+    }
3139+    ArgsChecker_1.ArgsChecker.checkAtLeastLength(values, 1, "SUMSQ");
3140+    var result = 0;
3141+    for (var i = 0; i < values.length; i++) {
3142+        if (values[i] instanceof Array) {
3143+            if (values[i].length === 0) {
3144+                throw new Errors_1.RefError("Reference does not exist.");
3145+            }
3146+            result = result + SUMSQ.apply(this, Filter_1.Filter.filterOutNonNumberValues(values[i]));
3147+        }
3148+        else {
3149+            var n = TypeConverter_1.TypeConverter.valueToNumber(values[i]);
3150+            result = result + (n * n);
3151+        }
3152+    }
3153+    return result;
3154+};
3155+exports.SUMSQ = SUMSQ;
3156+/**
3157+ * Truncates a number to a certain number of significant digits by omitting less significant digits.
3158+ * @param value - The value to be truncated.
3159+ * @param places - [ OPTIONAL - 0 by default ] - The number of significant digits to the right of the decimal point to
3160+ * retain. If places is greater than the number of significant digits in value, value is returned without modification.
3161+ * places may be negative, in which case the specified number of digits to the left of the decimal place are changed to
3162+ * zero. All digits to the right of the decimal place are discarded. If all digits of value are changed to zero, TRUNC
3163+ * simply returns 0.
3164+ * @returns {number} after truncation
3165+ * @constructor
3166+ */
3167+var TRUNC = function (value, places) {
3168+    ArgsChecker_1.ArgsChecker.checkLengthWithin(arguments, 1, 2, "TRUNC");
3169+    var n = TypeConverter_1.TypeConverter.firstValueAsNumber(value);
3170+    var digits = 0;
3171+    if (places !== undefined) {
3172+        digits = TypeConverter_1.TypeConverter.firstValueAsNumber(places);
3173+    }
3174+    var sign = (n > 0) ? 1 : -1;
3175+    return sign * (Math.floor(Math.abs(n) * Math.pow(10, digits))) / Math.pow(10, digits);
3176+};
3177+exports.TRUNC = TRUNC;
3178+/**
3179+ * Converts an angle value in degrees to radians.
3180+ * @param angle - The angle to convert from degrees to radians.
3181+ * @returns {number} radians
3182+ * @constructor
3183+ */
3184+var RADIANS = function (angle) {
3185+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 1, "RADIANS");
3186+    var d = TypeConverter_1.TypeConverter.firstValueAsNumber(angle);
3187+    return d * Math.PI / 180;
3188+};
3189+exports.RADIANS = RADIANS;
3190+/**
3191+ * Converts an angle value in radians to degrees.
3192+ * @param angle - The angle to convert from radians to degrees.
3193+ * @returns {number} degrees
3194+ * @constructor
3195+ */
3196+var DEGREES = function (angle) {
3197+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 1, "DEGREES");
3198+    var r = TypeConverter_1.TypeConverter.firstValueAsNumber(angle);
3199+    return r * 180 / Math.PI;
3200+};
3201+exports.DEGREES = DEGREES;
3202+/**
3203+ * Returns the complementary Gauss error function of a value.
3204+ * @param value - The number for which to calculate the complementary Gauss error function.
3205+ * @returns {number} complementary Gauss error function of a value
3206+ * @constructor
3207+ */
3208+var ERFC = function (value) {
3209+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 1, "ERFC");
3210+    var v = TypeConverter_1.TypeConverter.firstValueAsNumber(value);
3211+    return v === 0 ? 1 : 1 - MathHelpers_1.erf(v);
3212+};
3213+exports.ERFC = ERFC;
3214+/**
3215+ * Returns the error function integrated between lower_limit and upper_limit.
3216+ * @param lowerLimit - The lower bound for integrating ERF.
3217+ * @param upperLimit - [Optional]. The upper bound for integrating ERF. If omitted, ERF integrates between
3218+ * zero and lower_limit.
3219+ * @returns {number} error function integrated between lower_limit and upper_limit
3220+ * @constructor
3221+ */
3222+var ERF = function (lowerLimit, upperLimit) {
3223+    ArgsChecker_1.ArgsChecker.checkLengthWithin(arguments, 1, 2, "ERF");
3224+    var lower = TypeConverter_1.TypeConverter.firstValueAsNumber(lowerLimit);
3225+    var upper = upperLimit !== undefined ? TypeConverter_1.TypeConverter.firstValueAsNumber(upperLimit) : 0;
3226+    return upperLimit === undefined ? MathHelpers_1.erf(lower) : MathHelpers_1.erf(upper) - MathHelpers_1.erf(lower);
3227+};
3228+exports.ERF = ERF;
3229+/**
3230+ * Calculates the sum of the sums of the squares of values in two arrays.
3231+ * @param arrayX - The array or range of values whose squares will be added to the squares of corresponding
3232+ * entries in arrayY and added together.
3233+ * @param arrayY - The array or range of values whose squares will be added to the squares of corresponding
3234+ * entries in arrayX and added together.
3235+ * @returns {number} sum of the sums of the squares
3236+ * @constructor
3237+ */
3238+var SUMX2PY2 = function (arrayX, arrayY) {
3239+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 2, "SUMX2PY2");
3240+    var arrOne = Filter_1.Filter.flattenAndThrow(arrayX);
3241+    var arrTwo = Filter_1.Filter.flattenAndThrow(arrayY);
3242+    if (arrOne.length !== arrTwo.length) {
3243+        throw new Errors_1.NAError("Array arguments to SUMX2PY2 are of different size.");
3244+    }
3245+    var result = 0;
3246+    for (var i = 0; i < arrOne.length; i++) {
3247+        // If either values at this index are anything but numbers, skip them. This is the behavior in GS at least.
3248+        if (typeof arrOne[i] === "number" && typeof arrTwo[i] === "number") {
3249+            result += arrOne[i] * arrOne[i] + arrTwo[i] * arrTwo[i];
3250+        }
3251+    }
3252+    return result;
3253+};
3254+exports.SUMX2PY2 = SUMX2PY2;
3255+/**
3256+ * Calculates the sum of the differences of the squares of values in two arrays.
3257+ * @param arrayX - The array or range of values whose squares will be reduced by the squares of corresponding
3258+ * entries in array_y and added together.
3259+ * @param arrayY - The array or range of values whose squares will be subtracted from the squares of
3260+ * corresponding entries in array_x and added together.
3261+ * @returns {number} sum of the differences of the squares
3262+ * @constructor
3263+ */
3264+var SUMX2MY2 = function (arrayX, arrayY) {
3265+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 2, "SUMX2MY2");
3266+    var arrOne = Filter_1.Filter.flattenAndThrow(arrayX);
3267+    var arrTwo = Filter_1.Filter.flattenAndThrow(arrayY);
3268+    if (arrOne.length !== arrTwo.length) {
3269+        throw new Errors_1.NAError("Array arguments to SUMX2MY2 are of different size.");
3270+    }
3271+    var result = 0;
3272+    for (var i = 0; i < arrOne.length; i++) {
3273+        // If either values at this index are anything but numbers, skip them. This is the behavior in GS at least.
3274+        if (typeof arrOne[i] === "number" && typeof arrTwo[i] === "number") {
3275+            result += arrOne[i] * arrOne[i] - arrTwo[i] * arrTwo[i];
3276+        }
3277+    }
3278+    return result;
3279+};
3280+exports.SUMX2MY2 = SUMX2MY2;
3281+// Private function that will recursively generate an array of the unique primitives
3282+var _countUnique = function (values) {
3283+    var uniques = {};
3284+    for (var i = 0; i < values.length; i++) {
3285+        if (Array.isArray(values[i])) {
3286+            // For some reasons an empty range is converted to a range with a single empty string in it.
3287+            if (values[i].length === 0) {
3288+                values[i] = [""];
3289+            }
3290+            var uniquesOfArray = _countUnique(values[i]);
3291+            for (var key in uniquesOfArray) {
3292+                uniques[key] = true;
3293+            }
3294+        }
3295+        else {
3296+            uniques[Serializer_1.Serializer.serialize(values[i])] = true;
3297+        }
3298+    }
3299+    return uniques;
3300+};
3301+/**
3302+ * Counts the number of unique values in a list of specified values and ranges.
3303+ * @param values The values or ranges to consider for uniqueness. Supports an arbitrary number of arguments for this
3304+ * function.
3305+ * @returns {number} of unique values passed in.
3306+ * @constructor
3307+ */
3308+var COUNTUNIQUE = function () {
3309+    var values = [];
3310+    for (var _i = 0; _i < arguments.length; _i++) {
3311+        values[_i] = arguments[_i];
3312+    }
3313+    ArgsChecker_1.ArgsChecker.checkAtLeastLength(values, 1, "COUNTUNIQUE");
3314+    var uniques = _countUnique(values);
3315+    return Object.keys(uniques).length;
3316+};
3317+exports.COUNTUNIQUE = COUNTUNIQUE;
3318+/**
3319+ * Calculates the sum of the products of corresponding entries in two equal-sized arrays or ranges.
3320+ * @param values Arrays or ranges whose entries will be multiplied with corresponding entries in the second such array
3321+ * or range.
3322+ * @returns {number} sum of the products
3323+ * @constructor
3324+ */
3325+var SUMPRODUCT = function () {
3326+    var values = [];
3327+    for (var _i = 0; _i < arguments.length; _i++) {
3328+        values[_i] = arguments[_i];
3329+    }
3330+    ArgsChecker_1.ArgsChecker.checkAtLeastLength(values, 1, "SUMPRODUCT");
3331+    // Ensuring that all values are array values
3332+    for (var x = 0; x < values.length; x++) {
3333+        if (!Array.isArray(values[x])) {
3334+            values[x] = [values[x]];
3335+        }
3336+    }
3337+    // Flatten any nested ranges (arrays) and check for mismatched range sizes
3338+    var flattenedValues = [Filter_1.Filter.flattenAndThrow(values[0])];
3339+    for (var x = 1; x < values.length; x++) {
3340+        flattenedValues.push(Filter_1.Filter.flattenAndThrow(values[x]));
3341+        if (flattenedValues[x].length !== flattenedValues[0].length) {
3342+            throw new Errors_1.ValueError("SUMPRODUCT has mismatched range sizes. Expected count: "
3343+                + flattenedValues[0].length + ". Actual count: " + flattenedValues[0].length + ".");
3344+        }
3345+    }
3346+    // Do the actual math
3347+    var result = 0;
3348+    for (var i = 0; i < flattenedValues[0].length; i++) {
3349+        var product = 1;
3350+        for (var x = 0; x < flattenedValues.length; x++) {
3351+            product *= TypeConverter_1.TypeConverter.valueToNumberGracefully(flattenedValues[x][i]);
3352+        }
3353+        result += product;
3354+    }
3355+    return result;
3356+};
3357+exports.SUMPRODUCT = SUMPRODUCT;
3358+/**
3359+ * Returns the number of ways to choose some number of objects from a pool of a given size of objects.
3360+ * @param m - The size of the pool of objects to choose from.
3361+ * @param k - The number of objects to choose.
3362+ * @returns {number} number of ways
3363+ * @constructor
3364+ */
3365+var COMBIN = function (m, k) {
3366+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 2, "COMBIN");
3367+    var MEMOIZED_FACT = [];
3368+    function fact(number) {
3369+        var n = Math.floor(number);
3370+        if (n === 0 || n === 1) {
3371+            return 1;
3372+        }
3373+        else if (MEMOIZED_FACT[n] > 0) {
3374+            return MEMOIZED_FACT[n];
3375+        }
3376+        else {
3377+            MEMOIZED_FACT[n] = fact(n - 1) * n;
3378+            return MEMOIZED_FACT[n];
3379+        }
3380+    }
3381+    var n = TypeConverter_1.TypeConverter.firstValueAsNumber(m);
3382+    var c = TypeConverter_1.TypeConverter.firstValueAsNumber(k);
3383+    if (n < c) {
3384+        throw new Errors_1.NumError("Function COMBIN parameter 2 value is "
3385+            + c + ". It should be less than or equal to value of Function COMBIN parameter 1 with " + n + ".");
3386+    }
3387+    n = Math.floor(n);
3388+    c = Math.floor(c);
3389+    var div = fact(c) * fact(n - c);
3390+    if (div === 0) {
3391+        throw new Errors_1.DivZeroError("Evaluation of function COMBIN caused a divide by zero error.");
3392+    }
3393+    return fact(n) / div;
3394+};
3395+exports.COMBIN = COMBIN;
3396diff --git a/dist/Formulas/Statistical.js b/dist/Formulas/Statistical.js
3397new file mode 100644
3398index 0000000..8ea559e
3399--- /dev/null
3400+++ b/dist/Formulas/Statistical.js
3401@@ -0,0 +1,529 @@
3402+"use strict";
3403+exports.__esModule = true;
3404+var ArgsChecker_1 = require("../Utilities/ArgsChecker");
3405+var CriteriaFunctionFactory_1 = require("../Utilities/CriteriaFunctionFactory");
3406+var Filter_1 = require("../Utilities/Filter");
3407+var TypeConverter_1 = require("../Utilities/TypeConverter");
3408+var Errors_1 = require("../Errors");
3409+var Math_1 = require("./Math");
3410+var MathHelpers_1 = require("../Utilities/MathHelpers");
3411+/**
3412+ * Calculates the sum of squares of deviations based on a sample.
3413+ * @param values - The values or ranges of the sample.
3414+ * @returns {number} sum of squares of deviations
3415+ * @constructor
3416+ */
3417+var DEVSQ = function () {
3418+    var values = [];
3419+    for (var _i = 0; _i < arguments.length; _i++) {
3420+        values[_i] = arguments[_i];
3421+    }
3422+    ArgsChecker_1.ArgsChecker.checkAtLeastLength(values, 1, "DEVSQ");
3423+    var range = Filter_1.Filter.flattenAndThrow(values);
3424+    var result = 0;
3425+    var count = 0;
3426+    for (var i = 0; i < range.length; i++) {
3427+        result = result + TypeConverter_1.TypeConverter.valueToNumber(range[i]);
3428+        count++;
3429+    }
3430+    var mean = result / count;
3431+    var result = 0;
3432+    for (var i = 0; i < range.length; i++) {
3433+        result += Math.pow((TypeConverter_1.TypeConverter.valueToNumber(range[i]) - mean), 2);
3434+    }
3435+    return result;
3436+};
3437+exports.DEVSQ = DEVSQ;
3438+/**
3439+ * Returns the median value in a numeric dataset.
3440+ * @param values - The value(s) or range(s) to consider when calculating the median value.
3441+ * @returns {number} the median value of the dataset
3442+ * @constructor
3443+ */
3444+var MEDIAN = function () {
3445+    var values = [];
3446+    for (var _i = 0; _i < arguments.length; _i++) {
3447+        values[_i] = arguments[_i];
3448+    }
3449+    ArgsChecker_1.ArgsChecker.checkAtLeastLength(values, 1, "MEDIAN");
3450+    var sortedArray = [];
3451+    values.forEach(function (currentValue) {
3452+        if (currentValue instanceof Array) {
3453+            if (currentValue.length === 0) {
3454+                throw new Errors_1.RefError("Reference does not exist.");
3455+            }
3456+            var filtered = Filter_1.Filter.filterOutStringValues(currentValue);
3457+            sortedArray = sortedArray.concat(filtered);
3458+        }
3459+        else {
3460+            sortedArray.push(TypeConverter_1.TypeConverter.valueToNumber(currentValue));
3461+        }
3462+    });
3463+    sortedArray = sortedArray.sort(function (a, b) {
3464+        var aN = TypeConverter_1.TypeConverter.valueToNumber(a);
3465+        var bN = TypeConverter_1.TypeConverter.valueToNumber(b);
3466+        return aN - bN;
3467+    });
3468+    if (sortedArray.length === 1) {
3469+        return TypeConverter_1.TypeConverter.valueToNumber(sortedArray[0]);
3470+    }
3471+    if (sortedArray.length === 0) {
3472+        throw new Errors_1.NumError("MEDIAN has no valid input data.");
3473+    }
3474+    // even number of values
3475+    if (sortedArray.length % 2 === 0) {
3476+        if (sortedArray.length === 2) {
3477+            return AVERAGE(sortedArray[0], sortedArray[1]);
3478+        }
3479+        var top = sortedArray[sortedArray.length / 2];
3480+        var bottom = sortedArray[(sortedArray.length / 2) - 1];
3481+        return AVERAGE(top, bottom);
3482+    }
3483+    else {
3484+        // odd number of values
3485+        return sortedArray[Math.round(sortedArray.length / 2) - 1];
3486+    }
3487+};
3488+exports.MEDIAN = MEDIAN;
3489+/**
3490+ * Returns the numerical average value in a dataset, ignoring text.
3491+ * @param values - The values or ranges to consider when calculating the average value.
3492+ * @returns {number} the average value of this dataset.
3493+ * @constructor
3494+ */
3495+var AVERAGE = function () {
3496+    var values = [];
3497+    for (var _i = 0; _i < arguments.length; _i++) {
3498+        values[_i] = arguments[_i];
3499+    }
3500+    ArgsChecker_1.ArgsChecker.checkAtLeastLength(values, 1, "AVERAGE");
3501+    var result = 0;
3502+    var count = 0;
3503+    for (var i = 0; i < values.length; i++) {
3504+        if (values[i] instanceof Array) {
3505+            if (values[i].length === 0) {
3506+                throw new Errors_1.RefError("Reference does not exist.");
3507+            }
3508+            var filtered = Filter_1.Filter.filterOutStringValues(values[i]);
3509+            result = result + Math_1.SUM.apply(this, filtered);
3510+            count += filtered.length;
3511+        }
3512+        else {
3513+            result = result + TypeConverter_1.TypeConverter.valueToNumber(values[i]);
3514+            count++;
3515+        }
3516+    }
3517+    return result / count;
3518+};
3519+exports.AVERAGE = AVERAGE;
3520+/**
3521+ * Calculates the average of the magnitudes of deviations of data from a dataset's mean.
3522+ * @param values - The value(s) or range(s)
3523+ * @returns {number} average of the magnitudes of deviations of data from a dataset's mean
3524+ * @constructor
3525+ */
3526+var AVEDEV = function () {
3527+    var values = [];
3528+    for (var _i = 0; _i < arguments.length; _i++) {
3529+        values[_i] = arguments[_i];
3530+    }
3531+    ArgsChecker_1.ArgsChecker.checkAtLeastLength(values, 1, "AVEDEV");
3532+    // Sort to array-values, and non-array-values
3533+    var arrayValues = [];
3534+    var nonArrayValues = [];
3535+    for (var i = 0; i < values.length; i++) {
3536+        var X = values[i];
3537+        if (X instanceof Array) {
3538+            if (X.length === 0) {
3539+                throw new Errors_1.RefError("Reference does not exist.");
3540+            }
3541+            arrayValues.push(X);
3542+        }
3543+        else {
3544+            nonArrayValues.push(TypeConverter_1.TypeConverter.valueToNumber(X));
3545+        }
3546+    }
3547+    // Remove string values from array-values, but not from non-array-values, and concat.
3548+    var flatValues = Filter_1.Filter.filterOutStringValues(Filter_1.Filter.flatten(arrayValues)).map(function (value) {
3549+        return TypeConverter_1.TypeConverter.valueToNumber(value);
3550+    }).concat(nonArrayValues);
3551+    // Calculating mean
3552+    var result = 0;
3553+    var count = 0;
3554+    for (var i = 0; i < flatValues.length; i++) {
3555+        result = result + TypeConverter_1.TypeConverter.valueToNumber(flatValues[i]);
3556+        count++;
3557+    }
3558+    if (count === 0) {
3559+        throw new Errors_1.DivZeroError("Evaluation of function AVEDEV caused a devide by zero error.");
3560+    }
3561+    var mean = result / count;
3562+    for (var i = 0; i < flatValues.length; i++) {
3563+        flatValues[i] = Math_1.ABS(TypeConverter_1.TypeConverter.valueToNumber(flatValues[i]) - mean);
3564+    }
3565+    return Math_1.SUM(flatValues) / flatValues.length;
3566+};
3567+exports.AVEDEV = AVEDEV;
3568+/**
3569+ * Returns the numerical average value in a dataset, coercing text values in ranges to 0 values.
3570+ * @param values - value(s) or range(s) to consider when calculating the average value.
3571+ * @returns {number} the numerical average value in a dataset
3572+ * @constructor
3573+ */
3574+var AVERAGEA = function () {
3575+    var values = [];
3576+    for (var _i = 0; _i < arguments.length; _i++) {
3577+        values[_i] = arguments[_i];
3578+    }
3579+    ArgsChecker_1.ArgsChecker.checkAtLeastLength(values, 1, "AVERAGEA");
3580+    var result = 0;
3581+    var count = 0;
3582+    for (var i = 0; i < values.length; i++) {
3583+        if (values[i] instanceof Array) {
3584+            if (values[i].length === 0) {
3585+                throw new Errors_1.RefError("Reference does not exist.");
3586+            }
3587+            var filtered = Filter_1.Filter.stringValuesToZeros(values[i]);
3588+            result = result + Math_1.SUM.apply(this, filtered);
3589+            count += filtered.length;
3590+        }
3591+        else {
3592+            result = result + TypeConverter_1.TypeConverter.valueToNumber(values[i]);
3593+            count++;
3594+        }
3595+    }
3596+    if (count === 0) {
3597+        throw new Errors_1.DivZeroError("Evaluation of function AVEDEV caused a devide by zero error.");
3598+    }
3599+    return result / count;
3600+};
3601+exports.AVERAGEA = AVERAGEA;
3602+/**
3603+ * Calculates r, the Pearson product-moment correlation coefficient of a dataset. Any text encountered in the arguments
3604+ * will be ignored. CORREL is synonymous with PEARSON.
3605+ * @param dataY - The range representing the array or matrix of dependent data.
3606+ * @param dataX - The range representing the array or matrix of independent data.
3607+ * @returns {number} the Pearson product-moment correlation coefficient.
3608+ * @constructor
3609+ */
3610+var CORREL = function (dataY, dataX) {
3611+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 2, "CORREL");
3612+    if (!Array.isArray(dataY)) {
3613+        dataY = [dataY];
3614+    }
3615+    if (!Array.isArray(dataX)) {
3616+        dataX = [dataX];
3617+    }
3618+    if (dataY.length !== dataX.length) {
3619+        throw new Errors_1.NAError("CORREL has mismatched argument count " + dataY + " vs " + dataX + ".");
3620+    }
3621+    var arr1 = Filter_1.Filter.filterOutNonNumberValues(Filter_1.Filter.flattenAndThrow(dataY));
3622+    var arr2 = Filter_1.Filter.filterOutNonNumberValues(Filter_1.Filter.flattenAndThrow(dataX));
3623+    var stdevArr1 = MathHelpers_1.stdev(arr1, 1);
3624+    var stdevArr2 = MathHelpers_1.stdev(arr2, 1);
3625+    if (stdevArr1 === 0 || stdevArr2 === 0) {
3626+        throw new Errors_1.DivZeroError("Evaluation of function CORREL caused a divide by zero error.");
3627+    }
3628+    return MathHelpers_1.covariance(arr1, arr2) / stdevArr1 / stdevArr2;
3629+};
3630+exports.CORREL = CORREL;
3631+/**
3632+ * Calculates r, the Pearson product-moment correlation coefficient of a dataset. Any text encountered in the arguments
3633+ * will be ignored. PEARSON is synonymous with CORREL.
3634+ * @param dataY - The range representing the array or matrix of dependent data.
3635+ * @param dataX - The range representing the array or matrix of independent data.
3636+ * @returns {number} the Pearson product-moment correlation coefficient.
3637+ * @constructor
3638+ */
3639+var PEARSON = function (dataY, dataX) {
3640+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 2, "PEARSON");
3641+    return CORREL.apply(this, [dataY, dataX]);
3642+};
3643+exports.PEARSON = PEARSON;
3644+/**
3645+ * Returns the value of the exponential distribution function with a specified lambda at a specified value.
3646+ * @param x - The input to the exponential distribution function. If cumulative is TRUE then EXPONDIST returns
3647+ * the cumulative probability of all values up to x.
3648+ * @param lambda - The lambda to specify the exponential distribution function.
3649+ * @param cumulative - Whether to use the exponential cumulative distribution.
3650+ * @returns {number} value of the exponential distribution function.
3651+ * @constructor
3652+ */
3653+var EXPONDIST = function (x, lambda, cumulative) {
3654+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 3, "EXPONDIST");
3655+    function cdf(x, rate) {
3656+        return x < 0 ? 0 : 1 - Math.exp(-rate * x);
3657+    }
3658+    function pdf(x, rate) {
3659+        return x < 0 ? 0 : rate * Math.exp(-rate * x);
3660+    }
3661+    x = TypeConverter_1.TypeConverter.firstValueAsNumber(x);
3662+    lambda = TypeConverter_1.TypeConverter.firstValueAsNumber(lambda);
3663+    cumulative = TypeConverter_1.TypeConverter.firstValueAsBoolean(cumulative);
3664+    return (cumulative) ? cdf(x, lambda) : pdf(x, lambda);
3665+};
3666+exports.EXPONDIST = EXPONDIST;
3667+/**
3668+ * Calculates the left-tailed F probability distribution (degree of diversity) for two data sets with given input x.
3669+ * Alternately called Fisher-Snedecor distribution or Snecdor's F distribution.
3670+ * @param x - The input to the F probability distribution function. The value at which to evaluate the function.
3671+ * Must be a positive number.
3672+ * @param degreesFreedom1 - The numerator degrees of freedom.
3673+ * @param degreesFreedom2 - The denominator degrees of freedom.
3674+ * @param cumulative - Logical value that determines the form of the function. If true returns the cumulative
3675+ * distribution function. If false returns the probability density function.
3676+ * @returns {number|boolean} left-tailed F probability distribution
3677+ * @constructor
3678+ * TODO: This function should be stricter in its return type.
3679+ */
3680+var FDIST$LEFTTAILED = function (x, degreesFreedom1, degreesFreedom2, cumulative) {
3681+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 4, "FDIST$LEFTTAILED");
3682+    x = TypeConverter_1.TypeConverter.firstValueAsNumber(x);
3683+    if (x < 0) {
3684+        throw new Errors_1.NumError("Function F.DIST parameter 1 value is " + x + ". It should be greater than or equal to 0.");
3685+    }
3686+    var d1 = TypeConverter_1.TypeConverter.firstValueAsNumber(degreesFreedom1);
3687+    var d2 = TypeConverter_1.TypeConverter.firstValueAsNumber(degreesFreedom2);
3688+    var cum = TypeConverter_1.TypeConverter.firstValueAsBoolean(cumulative);
3689+    return (cum) ? MathHelpers_1.cdf(x, d1, d2) : MathHelpers_1.pdf(x, d1, d2);
3690+};
3691+exports.FDIST$LEFTTAILED = FDIST$LEFTTAILED;
3692+/**
3693+ * Returns the inverse of the (right-tailed) F probability distribution. If p = FDIST(x,...), then FINV(p,...) = x. The
3694+ * F distribution can be used in an F-test that compares the degree of variability in two data sets.
3695+ * @param probability - A probability associated with the F cumulative distribution.
3696+ * @param degFreedom1 - Required. The numerator degrees of freedom.
3697+ * @param degFreedom2 - Required. The denominator degrees of freedom.
3698+ * @returns {number} inverse of the (right-tailed) F probability distribution
3699+ * @constructor
3700+ */
3701+var FINV = function (probability, degFreedom1, degFreedom2) {
3702+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 3, "FINV");
3703+    probability = TypeConverter_1.TypeConverter.firstValueAsNumber(probability);
3704+    if (probability <= 0.0 || probability > 1.0) {
3705+        throw new Errors_1.NumError("Function FINV parameter 1 value is " + probability
3706+            + ". It should be greater than or equal to 0, and less than 1.");
3707+    }
3708+    var d1 = TypeConverter_1.TypeConverter.firstValueAsNumber(degFreedom1);
3709+    var d2 = TypeConverter_1.TypeConverter.firstValueAsNumber(degFreedom2);
3710+    return MathHelpers_1.inv(1.0 - probability, d1, d2);
3711+};
3712+exports.FINV = FINV;
3713+/**
3714+ * Returns the Fisher transformation of a specified value.
3715+ * @param value - The value for which to calculate the Fisher transformation.
3716+ * @returns {number} Fisher transformation
3717+ * @constructor
3718+ */
3719+var FISHER = function (value) {
3720+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 1, "FISHER");
3721+    var x = TypeConverter_1.TypeConverter.firstValueAsNumber(value);
3722+    if (x <= -1 || x >= 1) {
3723+        throw new Errors_1.NumError("Function FISHER parameter 1 value is " + x + ". Valid values are between -1 and 1 exclusive.");
3724+    }
3725+    return Math.log((1 + x) / (1 - x)) / 2;
3726+};
3727+exports.FISHER = FISHER;
3728+/**
3729+ * Returns the inverse Fisher transformation of a specified value.
3730+ * @param value - The value for which to calculate the inverse Fisher transformation.
3731+ * @returns {number} inverse Fisher transformation
3732+ * @constructor
3733+ */
3734+var FISHERINV = function (value) {
3735+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 1, "FISHERINV");
3736+    var y = TypeConverter_1.TypeConverter.firstValueAsNumber(value);
3737+    var e2y = Math.exp(2 * y);
3738+    return (e2y - 1) / (e2y + 1);
3739+};
3740+exports.FISHERINV = FISHERINV;
3741+/**
3742+ * Returns the maximum value in a numeric dataset.
3743+ * @param values - The values or range(s) to consider when calculating the maximum value.
3744+ * @returns {number} the maximum value of the dataset
3745+ * @constructor
3746+ */
3747+var MAX = function () {
3748+    var values = [];
3749+    for (var _i = 0; _i < arguments.length; _i++) {
3750+        values[_i] = arguments[_i];
3751+    }
3752+    ArgsChecker_1.ArgsChecker.checkAtLeastLength(values, 1, "MAX");
3753+    var maxSoFar = -Infinity;
3754+    for (var i = 0; i < values.length; i++) {
3755+        if (values[i] instanceof Array) {
3756+            if (values[i].length === 0) {
3757+                throw new Errors_1.RefError("Reference does not exist.");
3758+            }
3759+            var filtered = Filter_1.Filter.filterOutStringValues(values[i]);
3760+            if (filtered.length !== 0) {
3761+                maxSoFar = Math.max(MAX.apply(this, filtered), maxSoFar);
3762+            }
3763+        }
3764+        else {
3765+            maxSoFar = Math.max(TypeConverter_1.TypeConverter.valueToNumber(values[i]), maxSoFar);
3766+        }
3767+    }
3768+    return maxSoFar;
3769+};
3770+exports.MAX = MAX;
3771+/**
3772+ * Returns the maximum numeric value in a dataset.
3773+ * @param values - The value(s) or range(s) to consider when calculating the maximum value.
3774+ * @returns {number} maximum value of the dataset
3775+ * @constructor
3776+ */
3777+var MAXA = function () {
3778+    var values = [];
3779+    for (var _i = 0; _i < arguments.length; _i++) {
3780+        values[_i] = arguments[_i];
3781+    }
3782+    ArgsChecker_1.ArgsChecker.checkAtLeastLength(values, 1, "MAXA");
3783+    var maxSoFar = -Infinity;
3784+    var filteredValues = Filter_1.Filter.stringValuesToZeros(values);
3785+    for (var i = 0; i < filteredValues.length; i++) {
3786+        if (filteredValues[i] instanceof Array) {
3787+            if (values[i].length === 0) {
3788+                throw new Errors_1.RefError("Reference does not exist.");
3789+            }
3790+            var filtered = Filter_1.Filter.stringValuesToZeros(filteredValues[i]);
3791+            if (filtered.length !== 0) {
3792+                maxSoFar = Math.max(MAXA.apply(this, filtered), maxSoFar);
3793+            }
3794+        }
3795+        else {
3796+            maxSoFar = Math.max(TypeConverter_1.TypeConverter.valueToNumber(filteredValues[i]), maxSoFar);
3797+        }
3798+    }
3799+    return maxSoFar;
3800+};
3801+exports.MAXA = MAXA;
3802+/**
3803+ * Returns the minimum value in a numeric dataset.
3804+ * @param values - The value(s) or range(s) to consider when calculating the minimum value.
3805+ * @returns {number} the minimum value of the dataset
3806+ * @constructor
3807+ */
3808+var MIN = function () {
3809+    var values = [];
3810+    for (var _i = 0; _i < arguments.length; _i++) {
3811+        values[_i] = arguments[_i];
3812+    }
3813+    ArgsChecker_1.ArgsChecker.checkAtLeastLength(values, 1, "MIN");
3814+    var minSoFar = Infinity;
3815+    for (var i = 0; i < values.length; i++) {
3816+        if (values[i] instanceof Array) {
3817+            if (values[i].length === 0) {
3818+                throw new Errors_1.RefError("Reference does not exist.");
3819+            }
3820+            var filtered = Filter_1.Filter.filterOutStringValues(values[i]);
3821+            if (filtered.length !== 0) {
3822+                minSoFar = Math.min(MIN.apply(this, filtered), minSoFar);
3823+            }
3824+        }
3825+        else {
3826+            minSoFar = Math.min(TypeConverter_1.TypeConverter.valueToNumber(values[i]), minSoFar);
3827+        }
3828+    }
3829+    return minSoFar;
3830+};
3831+exports.MIN = MIN;
3832+/**
3833+ * Returns the minimum numeric value in a dataset.
3834+ * @param values - The value(s) or range(s) to consider when calculating the minimum value.
3835+ * @returns {number} the minimum value in the dataset
3836+ * @constructor
3837+ */
3838+var MINA = function () {
3839+    var values = [];
3840+    for (var _i = 0; _i < arguments.length; _i++) {
3841+        values[_i] = arguments[_i];
3842+    }
3843+    ArgsChecker_1.ArgsChecker.checkAtLeastLength(values, 1, "MINA");
3844+    return MIN.apply(this, values);
3845+};
3846+exports.MINA = MINA;
3847+/**
3848+ * Returns the average of a range depending on criteria.
3849+ * @param criteriaRange - The range to check against criterion.
3850+ * @param criterion - The pattern or test to apply to criteria_range.
3851+ * @param averageRange - [optional] The range to average. If not included, criteria_range is used for the
3852+ * average instead.
3853+ * @returns {number}
3854+ * @constructor
3855+ * TODO: This needs to also accept a third parameter "average_range"
3856+ */
3857+var AVERAGEIF = function (criteriaRange, criterion, averageRange) {
3858+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 2, "AVERAGEIF");
3859+    var range = Filter_1.Filter.flatten(criteriaRange);
3860+    var criteriaEvaluation = CriteriaFunctionFactory_1.CriteriaFunctionFactory.createCriteriaFunction(criterion);
3861+    var result = 0;
3862+    var count = 0;
3863+    for (var i = 0; i < range.length; i++) {
3864+        var val = TypeConverter_1.TypeConverter.valueToNumber(range[i]);
3865+        if (criteriaEvaluation(val)) {
3866+            result = result + val;
3867+            count++;
3868+        }
3869+    }
3870+    if (count === 0) {
3871+        throw new Errors_1.DivZeroError("Evaluation of function AVERAGEIF caused a divide by zero error.");
3872+    }
3873+    return result / count;
3874+};
3875+exports.AVERAGEIF = AVERAGEIF;
3876+/**
3877+ * Returns the a count of the number of numeric values in a dataset.
3878+ * @param values - The values or ranges to consider when counting.
3879+ * @returns {number} number of numeric values in a dataset.
3880+ * @constructor
3881+ */
3882+var COUNT = function () {
3883+    var values = [];
3884+    for (var _i = 0; _i < arguments.length; _i++) {
3885+        values[_i] = arguments[_i];
3886+    }
3887+    ArgsChecker_1.ArgsChecker.checkAtLeastLength(values, 1, "COUNT");
3888+    var count = 0;
3889+    for (var i = 0; i < values.length; i++) {
3890+        if (values[i] instanceof Array) {
3891+            if (values[i].length > 0) {
3892+                count += COUNT.apply(this, values[i]);
3893+            }
3894+        }
3895+        else if (TypeConverter_1.TypeConverter.canCoerceToNumber(values[i])) {
3896+            count++;
3897+        }
3898+    }
3899+    return count;
3900+};
3901+exports.COUNT = COUNT;
3902+/**
3903+ * Returns the a count of the number of values in a dataset.
3904+ * @param values - The values or ranges to consider when counting.
3905+ * @returns {number} number of values in a dataset.
3906+ * @constructor
3907+ */
3908+var COUNTA = function () {
3909+    var values = [];
3910+    for (var _i = 0; _i < arguments.length; _i++) {
3911+        values[_i] = arguments[_i];
3912+    }
3913+    ArgsChecker_1.ArgsChecker.checkAtLeastLength(values, 1, "COUNTA");
3914+    var count = 0;
3915+    for (var i = 0; i < values.length; i++) {
3916+        if (values[i] instanceof Array) {
3917+            if (values[i].length > 0) {
3918+                count += COUNTA.apply(this, values[i]);
3919+            }
3920+            else {
3921+                count++;
3922+            }
3923+        }
3924+        else {
3925+            count++;
3926+        }
3927+    }
3928+    return count;
3929+};
3930+exports.COUNTA = COUNTA;
3931diff --git a/dist/Formulas/Text.js b/dist/Formulas/Text.js
3932new file mode 100644
3933index 0000000..2f3bdef
3934--- /dev/null
3935+++ b/dist/Formulas/Text.js
3936@@ -0,0 +1,410 @@
3937+"use strict";
3938+exports.__esModule = true;
3939+var ArgsChecker_1 = require("../Utilities/ArgsChecker");
3940+var TypeConverter_1 = require("../Utilities/TypeConverter");
3941+var Errors_1 = require("../Errors");
3942+/**
3943+ * Computes the value of a Roman numeral.
3944+ * @param text - The Roman numeral to format, whose value must be between 1 and 3999, inclusive.
3945+ * @returns {number} value in integer format
3946+ * @constructor
3947+ */
3948+var ARABIC = function (text) {
3949+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 1, "ARABIC");
3950+    if (typeof text !== "string") {
3951+        throw new Errors_1.ValueError('Invalid roman numeral in ARABIC evaluation.');
3952+    }
3953+    var negative = false;
3954+    if (text[0] === "-") {
3955+        negative = true;
3956+        text = text.substr(1);
3957+    }
3958+    // Credits: Rafa? Kukawski
3959+    if (!/^M*(?:D?C{0,3}|C[MD])(?:L?X{0,3}|X[CL])(?:V?I{0,3}|I[XV])$/.test(text)) {
3960+        throw new Errors_1.ValueError('Invalid roman numeral in ARABIC evaluation.');
3961+    }
3962+    var r = 0;
3963+    text.replace(/[MDLV]|C[MD]?|X[CL]?|I[XV]?/g, function (i) {
3964+        r += { M: 1000, CM: 900, D: 500, CD: 400, C: 100, XC: 90, L: 50, XL: 40, X: 10, IX: 9, V: 5, IV: 4, I: 1 }[i];
3965+    });
3966+    if (negative) {
3967+        return r * -1;
3968+    }
3969+    return r;
3970+};
3971+exports.ARABIC = ARABIC;
3972+/**
3973+ * Convert a number into a character according to the current Unicode table.
3974+ * @param value - The number of the character to look up from the current Unicode table in decimal format.
3975+ * @returns {string} character corresponding to Unicode number
3976+ * @constructor
3977+ */
3978+var CHAR = function (value) {
3979+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 1, "CHAR");
3980+    var n = TypeConverter_1.TypeConverter.firstValueAsNumber(value);
3981+    if (n < 1 || n > 1114112) {
3982+        throw new Errors_1.NumError("Function CHAR parameter 1 value " + n + " is out of range.");
3983+    }
3984+    return String.fromCharCode(n);
3985+};
3986+exports.CHAR = CHAR;
3987+/**
3988+ * Returns the numeric Unicode map value of the first character in the string provided.
3989+ * @param value - The string whose first character's Unicode map value will be returned.
3990+ * @returns {number} number of the first character's Unicode value
3991+ * @constructor
3992+ */
3993+var CODE = function (value) {
3994+    ArgsChecker_1.ArgsChecker.checkLength(arguments, 1, "CODE");
3995+    var text = TypeConverter_1.TypeConverter.firstValueAsString(value);
3996+    if (text === "") {
3997+        throw new Errors_1.ValueError("Function CODE parameter 1 value should be non-empty.");
3998+    }
3999+    return text.charCodeAt(0);
4000+};
4001+exports.CODE = CODE;
4002+/**
4003+ * Divides text around a specified character or string, and puts each fragment into a separate cell in the row.
4004+ * @param text - The text to divide.
4005+ * @param delimiter - The character or characters to use to split text.
4006+ * @param splitByEach - [optional] Whether or not to divide text around each character contained in
4007+ * delimiter.
4008+ * @returns {Array<string>} containing the split
4009+ * @constructor
4010+ * TODO: At some point this needs to return a more complex type than Array. Needs to return a type that has a dimension.
4011+ */
4012+var SPLIT = function (text, delimiter, splitByEach) {
4013+    ArgsChecker_1.ArgsChecker.checkLengthWithin(arguments, 2, 3, "SPLIT");
4014+    text = TypeConverter_1.TypeConverter.firstValueAsString(text);
4015+    delimiter = TypeConverter_1.TypeConverter.firstValueAsString(delimiter);
4016+    splitByEach = splitByEach === undefined ? false : TypeConverter_1.TypeConverter.firstValueAsBoolean(splitByEach);
4017+    if (splitByEach) {
4018+        var result = [text];
4019+        for (var i = 0; i < delimiter.length; i++) {
4020+            var char = delimiter[i];
4021+            var subResult = [];
4022+            for (var x = 0; x < result.length; x++) {
4023+                subResult = subResult.concat(result[x].split(char));
4024+            }
4025+            result = subResult;
4026+        }
4027+        return result.filter(function (val) {
4028+            return val.trim() !== "";
4029+        });
4030+    }
4031+    else {
4032+        return text.split(delimiter);
4033+    }
4034+};
4035+exports.SPLIT = SPLIT;
4036+/**
4037+ * Appends strings to one another.
4038+ * @param values - to append to one another. Must contain at least one value
4039+ * @returns {string} concatenated string
4040+ * @constructor
4041+ */
4042+var CONCATENATE = function () {
4043+    var values = [];
4044+    for (var _i = 0; _i < arguments.length; _i++) {
4045+        values[_i] = arguments[_i];
4046+    }
4047+    ArgsChecker_1.ArgsChecker.checkAtLeastLength(values, 1, "CONCATENATE");
4048+    var string = '';
4049+    for (var i = 0; i < values.length; i++) {
4050+        if (values[i] instanceof Array) {
4051+            if (values[i].length === 0) {
4052+                throw new Errors_1.RefError("Reference does not exist.");
4053+            }
4054+            string += CONCATENATE.apply(this, arguments[i]);
4055+        }