spreadsheet
typeScript/javascript spreadsheet parser, with formulas.
git clone https://git.vogt.world/spreadsheet.git
Log | Files | README.md
← Commit log
commit
message
Switching to npm instead of browser based developement (working!)
author
Ben Vogt <[email protected]>
date
2016-12-27 18:53:29
stats
22 file(s) changed, 44 insertions(+), 26450 deletions(-)
files
README.md
js/mine.js
js/parser.js
js/ruleJS.js
lib/formulajs/formula.js
lib/js-md5/md5.js
lib/jstat/jstat.js
lib/lodash/lodash.js
lib/moment/moment.js
lib/numeral/numeral.js
lib/numericjs/numeric.js
lib/underscore.string/underscore.string.js
mine.html
package.json
rule.html
ts.html
ts/Main.ts
ts/Parser.d.ts
ts/parser.js
lib/parser.js
ts/sheet.js
ts/sheet.ts
ts/Sheet.ts
tsconfig.json
    1diff --git a/README.md b/README.md
    2index 51f909b..5ecc9a2 100644
    3--- a/README.md
    4+++ b/README.md
    5@@ -3,10 +3,3 @@ TypeScript implementation of a spreadsheet.
    6 
    7 ## TODO
    8 Things I should do.
    9-
   10-## THE PLAN FROM NOW ON
   11-Take ruleJS.js and slowly alter it slowly.
   12-Step by step build up to a model that is not DOM-based.
   13-Then introduce types.
   14-Then introduce classes.
   15-
   16diff --git a/js/mine.js b/js/mine.js
   17deleted file mode 100644
   18index da6a654..0000000
   19--- a/js/mine.js
   20+++ /dev/null
   21@@ -1,662 +0,0 @@
   22-var mine = (function () {
   23-  'use strict';
   24-  var instance = this;
   25-
   26-  var parser = {};
   27-
   28-  var FormulaParser = function(handler) {
   29-    var formulaLexer = function () {};
   30-    formulaLexer.prototype = Parser.lexer;
   31-
   32-    var formulaParser = function () {
   33-      this.lexer = new formulaLexer();
   34-      this.yy = {};
   35-    };
   36-
   37-    formulaParser.prototype = Parser;
   38-    var newParser = new formulaParser;
   39-    newParser.setObj = function(obj) {
   40-      newParser.yy.obj = obj;
   41-    };
   42-
   43-    newParser.yy.parseError = function (str, hash) {
   44-      throw {
   45-        name: 'Parser error',
   46-        message: str,
   47-        prop: hash
   48-      }
   49-    };
   50-
   51-    newParser.yy.handler = handler;
   52-
   53-    return newParser;
   54-  };
   55-
   56-  var Exception = {
   57-    errors: [
   58-      {type: 'NULL', output: '#NULL'},
   59-      {type: 'DIV_ZERO', output: '#DIV/0!'},
   60-      {type: 'VALUE', output: '#VALUE!'},
   61-      {type: 'REF', output: '#REF!'},
   62-      {type: 'NAME', output: '#NAME?'},
   63-      {type: 'NUM', output: '#NUM!'},
   64-      {type: 'NOT_AVAILABLE', output: '#N/A!'},
   65-      {type: 'ERROR', output: '#ERROR'}
   66-    ],
   67-    get: function (type) {
   68-      var error = Exception.errors.filter(function (item) {
   69-        return item.type === type || item.output === type;
   70-      })[0];
   71-
   72-      return error ? error.output : null;
   73-    }
   74-  };
   75-
   76-  var Matrix = function () {
   77-
   78-    // var item = {
   79-    //   id: '',
   80-    //   formula: '',
   81-    //   value: '',
   82-    //   error: '',
   83-    //   deps: [],
   84-    //   formulaEdit: false
   85-    // };
   86-
   87-    this.data = [];
   88-
   89-    this.getItem = function (id) {
   90-      return instance.matrix.data.filter(function (item) {
   91-        return item.id === id;
   92-      })[0];
   93-    };
   94-
   95-    this.removeItem = function (id) {
   96-      instance.matrix.data = instance.matrix.data.filter(function (item) {
   97-        return item.id !== id;
   98-      });
   99-    };
  100-
  101-    this.updateItem = function (item, props) {
  102-      if (instance.utils.isString(item)) {
  103-        item = instance.matrix.getItem(item);
  104-      }
  105-
  106-      if (item && props) {
  107-        for (var p in props) {
  108-          if (item[p] && instance.utils.isArray(item[p])) {
  109-            if (instance.utils.isArray(props[p])) {
  110-              props[p].forEach(function (i) {
  111-                if (item[p].indexOf(i) === -1) {
  112-                  item[p].push(i);
  113-                }
  114-              });
  115-            } else {
  116-
  117-              if (item[p].indexOf(props[p]) === -1) {
  118-                item[p].push(props[p]);
  119-              }
  120-            }
  121-          } else {
  122-            item[p] = props[p];
  123-          }
  124-        }
  125-      }
  126-    };
  127-
  128-    this.addItem = function (item) {
  129-      var cellId = item.id,
  130-        coords = instance.utils.cellCoords(cellId);
  131-
  132-      item.row = coords.row;
  133-      item.col = coords.col;
  134-
  135-      var cellExist = instance.matrix.data.filter(function (cell) {
  136-        return cell.id === cellId;
  137-      })[0];
  138-
  139-      if (!cellExist) {
  140-        instance.matrix.data.push(item);
  141-      } else {
  142-        instance.matrix.updateItem(cellExist, item);
  143-      }
  144-
  145-      return instance.matrix.getItem(cellId);
  146-    };
  147-
  148-
  149-    this.updateCellItem = function (id, props) {
  150-      var item = instance.matrix.getItem(id);
  151-
  152-      instance.matrix.updateItem(item, props);
  153-    };
  154-
  155-    this.getDependencies = function (id) {
  156-      var getDependencies = function (id) {
  157-        var filtered = instance.matrix.data.filter(function (cell) {
  158-          if (cell.deps) {
  159-            return cell.deps.indexOf(id) > -1;
  160-          }
  161-        });
  162-
  163-        var deps = [];
  164-        filtered.forEach(function (cell) {
  165-          if (deps.indexOf(cell.id) === -1) {
  166-            deps.push(cell.id);
  167-          }
  168-        });
  169-
  170-        return deps;
  171-      };
  172-
  173-      var allDependencies = [];
  174-
  175-      var getTotalDependencies = function (id) {
  176-        var deps = getDependencies(id);
  177-
  178-        if (deps.length) {
  179-          deps.forEach(function (refId) {
  180-            if (allDependencies.indexOf(refId) === -1) {
  181-              allDependencies.push(refId);
  182-
  183-              var item = instance.matrix.getItem(refId);
  184-              if (item.deps.length) {
  185-                getTotalDependencies(refId);
  186-              }
  187-            }
  188-          });
  189-        }
  190-      };
  191-
  192-      getTotalDependencies(id);
  193-
  194-      return allDependencies;
  195-    };
  196-
  197-    this.getCellDependencies = function (cell) {
  198-      return instance.matrix.getDependencies(cell.id);
  199-    };
  200-
  201-    var recalculateCellDependencies = function (cell) {
  202-      var allDependencies = instance.matrix.getCellDependencies(cell);
  203-
  204-      allDependencies.forEach(function (refId) {
  205-        var currentCell = instance.matrix.getItem(refId);
  206-        if (currentCell && currentCell.formula) {
  207-          calculateCellFormula(currentCell.formula, currentCell.id);
  208-        }
  209-      });
  210-    };
  211-
  212-    var calculateCellFormula = function (formula, id) {
  213-      // to avoid double translate formulas, update item data in parser
  214-      var parsed = parse(formula, id),
  215-        value = parsed.result,
  216-        error = parsed.error;
  217-
  218-      instance.matrix.updateCellItem(id, {value: value, error: error});
  219-
  220-      return parsed;
  221-    };
  222-
  223-    var registerCellInMatrix = function (cell) {
  224-      instance.matrix.addItem(cell);
  225-      calculateCellFormula(cell.formula, cell.id);
  226-    };
  227-
  228-    this.scan = function () {
  229-      var input = [
  230-        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, "SUM(A1:D1, H1)"],
  231-        [-1, -10, 2, 4, 100, 1, 50, 20, 200, -100, "MAX(A2:J2)"],
  232-        [-1, -40, -53, 1, 10, 30, 10, 301, -1, -20, "MIN(A3:J3)"],
  233-        [20, 50, 100, 20, 1, 5, 15, 25, 45, 23, "AVERAGE(A4:J4)"],
  234-        [0, 10, 1, 10, 2, 10, 3, 10, 4, 10, "SUMIF(A5:J5,'>5')"]
  235-      ];
  236-      for (var y = 0; y < input.length; y++) {
  237-        for (var x = 0; x < input[0].length; x++) {
  238-          // set the cell here
  239-          var id = utils.XYtoA1(x, y);
  240-          var cell = {
  241-            id: id,
  242-            formula: input[y][x].toString()
  243-          };
  244-          registerCellInMatrix(cell);
  245-          recalculateCellDependencies(cell);
  246-        }
  247-      }
  248-      console.log(this.data);
  249-    };
  250-  };
  251-
  252-  var utils = {
  253-    isArray: function (value) {
  254-      return Object.prototype.toString.call(value) === '[object Array]';
  255-    },
  256-
  257-    isNumber: function (value) {
  258-      return Object.prototype.toString.call(value) === '[object Number]';
  259-    },
  260-
  261-    isString: function (value) {
  262-      return Object.prototype.toString.call(value) === '[object String]';
  263-    },
  264-
  265-    isFunction: function (value) {
  266-      return Object.prototype.toString.call(value) === '[object Function]';
  267-    },
  268-
  269-    isUndefined: function (value) {
  270-      return Object.prototype.toString.call(value) === '[object Undefined]';
  271-    },
  272-
  273-    isNull: function (value) {
  274-      return Object.prototype.toString.call(value) === '[object Null]';
  275-    },
  276-
  277-    isSet: function (value) {
  278-      return !instance.utils.isUndefined(value) && !instance.utils.isNull(value);
  279-    },
  280-
  281-    getCellAlphaNum: function (cell) {
  282-      var num = cell.match(/\d+$/),
  283-        alpha = cell.replace(num, '');
  284-
  285-      return {
  286-        alpha: alpha,
  287-        num: parseInt(num[0], 10)
  288-      }
  289-    },
  290-
  291-    toNum: function (chr) {
  292-      chr = instance.utils.clearFormula(chr);
  293-      var base = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', i, j, result = 0;
  294-
  295-      for (i = 0, j = chr.length - 1; i < chr.length; i += 1, j -= 1) {
  296-        result += Math.pow(base.length, j) * (base.indexOf(chr[i]) + 1);
  297-      }
  298-
  299-      if (result) {
  300-        --result;
  301-      }
  302-
  303-      return result;
  304-    },
  305-
  306-    toChar: function (num) {
  307-      var s = '';
  308-
  309-      while (num >= 0) {
  310-        s = String.fromCharCode(num % 26 + 97) + s;
  311-        num = Math.floor(num / 26) - 1;
  312-      }
  313-
  314-      return s.toUpperCase();
  315-    },
  316-    XYtoA1: function (x, y) {
  317-      function numberToLetters(num) {
  318-        var mod = num % 26,
  319-          pow = num / 26 | 0,
  320-          out = mod ? String.fromCharCode(64 + mod) : (--pow, 'Z');
  321-        return pow ? numberToLetters(pow) + out : out;
  322-      }
  323-      return numberToLetters(x+1) + (y+1).toString();
  324-    },
  325-    cellCoords: function (cell) {
  326-      var num = cell.match(/\d+$/),
  327-        alpha = cell.replace(num, '');
  328-
  329-      return {
  330-        row: parseInt(num[0], 10) - 1,
  331-        col: instance.utils.toNum(alpha)
  332-      };
  333-    },
  334-
  335-    clearFormula: function (formula) {
  336-      return formula.replace(/\$/g, '');
  337-    },
  338-
  339-    translateCellCoords: function (coords) {
  340-      return instance.utils.toChar(coords.col) + '' + parseInt(coords.row + 1, 10);
  341-    },
  342-
  343-    iterateCells: function (startCell, endCell, callback) {
  344-      var result = {
  345-        index: [], // list of cell index: A1, A2, A3
  346-        value: []  // list of cell value
  347-      };
  348-
  349-      var cols = {
  350-        start: 0,
  351-        end: 0
  352-      };
  353-
  354-      if (endCell.col >= startCell.col) {
  355-        cols = {
  356-          start: startCell.col,
  357-          end: endCell.col
  358-        };
  359-      } else {
  360-        cols = {
  361-          start: endCell.col,
  362-          end: startCell.col
  363-        };
  364-      }
  365-
  366-      var rows = {
  367-        start: 0,
  368-        end: 0
  369-      };
  370-
  371-      if (endCell.row >= startCell.row) {
  372-        rows = {
  373-          start: startCell.row,
  374-          end: endCell.row
  375-        };
  376-      } else {
  377-        rows = {
  378-          start: endCell.row,
  379-          end: startCell.row
  380-        };
  381-      }
  382-
  383-      for (var column = cols.start; column <= cols.end; column++) {
  384-        for (var row = rows.start; row <= rows.end; row++) {
  385-          var cellIndex = instance.utils.toChar(column) + (row + 1),
  386-            cellValue = instance.helper.cellValue.call(this, cellIndex);
  387-
  388-          result.index.push(cellIndex);
  389-          result.value.push(cellValue);
  390-        }
  391-      }
  392-
  393-      if (instance.utils.isFunction(callback)) {
  394-        return callback.apply(callback, [result]);
  395-      } else {
  396-        return result;
  397-      }
  398-    },
  399-
  400-    sort: function (rev) {
  401-      return function (a, b) {
  402-        return ((a < b) ? -1 : ((a > b) ? 1 : 0)) * (rev ? -1 : 1);
  403-      }
  404-    }
  405-  };
  406-
  407-  var helper = {
  408-    SUPPORTED_FORMULAS: [
  409-      'ABS', 'ACCRINT', 'ACOS', 'ACOSH', 'ACOTH', 'AND', 'ARABIC', 'ASIN', 'ASINH', 'ATAN', 'ATAN2', 'ATANH', 'AVEDEV', 'AVERAGE', 'AVERAGEA', 'AVERAGEIF',
  410-      'BASE', 'BESSELI', 'BESSELJ', 'BESSELK', 'BESSELY', 'BETADIST', 'BETAINV', 'BIN2DEC', 'BIN2HEX', 'BIN2OCT', 'BINOMDIST', 'BINOMDISTRANGE', 'BINOMINV', 'BITAND', 'BITLSHIFT', 'BITOR', 'BITRSHIFT', 'BITXOR',
  411-      'CEILING', 'CEILINGMATH', 'CEILINGPRECISE', 'CHAR', 'CHISQDIST', 'CHISQINV', 'CODE', 'COMBIN', 'COMBINA', 'COMPLEX', 'CONCATENATE', 'CONFIDENCENORM', 'CONFIDENCET', 'CONVERT', 'CORREL', 'COS', 'COSH', 'COT', 'COTH', 'COUNT', 'COUNTA', 'COUNTBLANK', 'COUNTIF', 'COUNTIFS', 'COUNTIN', 'COUNTUNIQUE', 'COVARIANCEP', 'COVARIANCES', 'CSC', 'CSCH', 'CUMIPMT', 'CUMPRINC',
  412-      'DATE', 'DATEVALUE', 'DAY', 'DAYS', 'DAYS360', 'DB', 'DDB', 'DEC2BIN', 'DEC2HEX', 'DEC2OCT', 'DECIMAL', 'DEGREES', 'DELTA', 'DEVSQ', 'DOLLAR', 'DOLLARDE', 'DOLLARFR',
  413-      'E', 'EDATE', 'EFFECT', 'EOMONTH', 'ERF', 'ERFC', 'EVEN', 'EXACT', 'EXPONDIST',
  414-      'FALSE', 'FDIST', 'FINV', 'FISHER', 'FISHERINV',
  415-      'IF', 'INT', 'ISEVEN', 'ISODD',
  416-      'LN', 'LOG', 'LOG10',
  417-      'MAX', 'MAXA', 'MEDIAN', 'MIN', 'MINA', 'MOD',
  418-      'NOT',
  419-      'ODD', 'OR',
  420-      'PI', 'POWER',
  421-      'ROUND', 'ROUNDDOWN', 'ROUNDUP',
  422-      'SIN', 'SINH', 'SPLIT', 'SQRT', 'SQRTPI', 'SUM', 'SUMIF', 'SUMIFS', 'SUMPRODUCT', 'SUMSQ', 'SUMX2MY2', 'SUMX2PY2', 'SUMXMY2',
  423-      'TAN', 'TANH', 'TRUE', 'TRUNC',
  424-      'XOR'
  425-    ],
  426-
  427-    number: function (num) {
  428-      switch (typeof num) {
  429-        case 'number':
  430-          return num;
  431-        case 'string':
  432-          if (!isNaN(num)) {
  433-            return num.indexOf('.') > -1 ? parseFloat(num) : parseInt(num, 10);
  434-          }
  435-      }
  436-
  437-      return num;
  438-    },
  439-
  440-    string: function (str) {
  441-      return str.substring(1, str.length - 1);
  442-    },
  443-
  444-    numberInverted: function (num) {
  445-      return this.number(num) * (-1);
  446-    },
  447-
  448-    specialMatch: function (type, exp1, exp2) {
  449-      var result;
  450-
  451-      switch (type) {
  452-        case '&':
  453-          result = exp1.toString() + exp2.toString();
  454-          break;
  455-      }
  456-      return result;
  457-    },
  458-
  459-    logicMatch: function (type, exp1, exp2) {
  460-      var result;
  461-
  462-      switch (type) {
  463-        case '=':
  464-          result = (exp1 === exp2);
  465-          break;
  466-
  467-        case '>':
  468-          result = (exp1 > exp2);
  469-          break;
  470-
  471-        case '<':
  472-          result = (exp1 < exp2);
  473-          break;
  474-
  475-        case '>=':
  476-          result = (exp1 >= exp2);
  477-          break;
  478-
  479-        case '<=':
  480-          result = (exp1 === exp2);
  481-          break;
  482-
  483-        case '<>':
  484-          result = (exp1 != exp2);
  485-          break;
  486-
  487-        case 'NOT':
  488-          result = (exp1 != exp2);
  489-          break;
  490-      }
  491-
  492-      return result;
  493-    },
  494-
  495-    mathMatch: function (type, number1, number2) {
  496-      var result;
  497-
  498-      number1 = helper.number(number1);
  499-      number2 = helper.number(number2);
  500-
  501-      if (isNaN(number1) || isNaN(number2)) {
  502-        throw Error('VALUE');
  503-      }
  504-
  505-      switch (type) {
  506-        case '+':
  507-          result = number1 + number2;
  508-          break;
  509-        case '-':
  510-          result = number1 - number2;
  511-          break;
  512-        case '/':
  513-          result = number1 / number2;
  514-          if (result == Infinity) {
  515-            throw Error('DIV_ZERO');
  516-          } else if (isNaN(result)) {
  517-            throw Error('VALUE');
  518-          }
  519-          break;
  520-        case '*':
  521-          result = number1 * number2;
  522-          break;
  523-        case '^':
  524-          result = Math.pow(number1, number2);
  525-          break;
  526-      }
  527-
  528-      return result;
  529-    },
  530-
  531-    callFunction: function (fn, args) {
  532-      fn = fn.toUpperCase();
  533-      args = args || [];
  534-
  535-      if (instance.helper.SUPPORTED_FORMULAS.indexOf(fn) > -1) {
  536-        if (instance.formulas[fn]) {
  537-          return instance.formulas[fn].apply(this, args);
  538-        }
  539-      }
  540-
  541-      throw Error('NAME');
  542-    },
  543-
  544-    callVariable: function (args) {
  545-      args = args || [];
  546-      var str = args[0];
  547-
  548-      if (str) {
  549-        str = str.toUpperCase();
  550-        if (instance.formulas[str]) {
  551-          return ((typeof instance.formulas[str] === 'function') ? instance.formulas[str].apply(this, args) : instance.formulas[str]);
  552-        }
  553-      }
  554-
  555-      throw Error('NAME');
  556-    },
  557-
  558-    cellValue: function (cell) {
  559-      var value,
  560-        element = this,
  561-        item = instance.matrix.getItem(cell);
  562-      // get value
  563-      value = item ? item.value : "0"; // TODO: fix this, it's sloppy.
  564-      //update dependencies
  565-      instance.matrix.updateCellItem(element, {deps: [cell]});
  566-      // check references error
  567-      if (item && item.deps) {
  568-        if (item.deps.indexOf(cellId) !== -1) {
  569-          throw Error('REF');
  570-        }
  571-      }
  572-
  573-      // check if any error occurs
  574-      if (item && item.error) {
  575-        throw Error(item.error);
  576-      }
  577-
  578-      // return value if is set
  579-      if (instance.utils.isSet(value)) {
  580-        var result = instance.helper.number(value);
  581-
  582-        return !isNaN(result) ? result : value;
  583-      }
  584-
  585-      // cell is not available
  586-      throw Error('NOT_AVAILABLE');
  587-    },
  588-
  589-    cellRangeValue: function (start, end) {
  590-      var coordsStart = instance.utils.cellCoords(start),
  591-        coordsEnd = instance.utils.cellCoords(end),
  592-        element = this;
  593-
  594-      // iterate cells to get values and indexes
  595-      var cells = instance.utils.iterateCells.call(this, coordsStart, coordsEnd),
  596-        result = [];
  597-      //update dependencies
  598-      instance.matrix.updateCellItem(element, {deps: cells.index});
  599-
  600-      result.push(cells.value);
  601-      return result;
  602-    },
  603-
  604-    fixedCellValue: function (id) {
  605-      id = id.replace(/\$/g, '');
  606-      return instance.helper.cellValue.call(this, id);
  607-    },
  608-
  609-    fixedCellRangeValue: function (start, end) {
  610-      start = start.replace(/\$/g, '');
  611-      end = end.replace(/\$/g, '');
  612-
  613-      return instance.helper.cellRangeValue.call(this, start, end);
  614-    }
  615-  };
  616-
  617-  var parse = function (formula, element) {
  618-    var result = null,
  619-      error = null;
  620-
  621-    try {
  622-
  623-      parser.setObj(element);
  624-      result = parser.parse(formula);
  625-
  626-      var id;
  627-
  628-      if (element instanceof HTMLElement) {
  629-        id = element.getAttribute('id');
  630-      } else if (element && element.id) {
  631-        id = element.id;
  632-      }
  633-
  634-      var deps = instance.matrix.getDependencies(id);
  635-
  636-      if (deps.indexOf(id) !== -1) {
  637-        result = null;
  638-
  639-        deps.forEach(function (id) {
  640-          instance.matrix.updateItem(id, {value: null, error: Exception.get('REF')});
  641-        });
  642-
  643-        throw Error('REF');
  644-      }
  645-
  646-    } catch (ex) {
  647-
  648-      var message = Exception.get(ex.message);
  649-
  650-      if (message) {
  651-        error = message;
  652-      } else {
  653-        error = Exception.get('ERROR');
  654-      }
  655-    }
  656-
  657-    return {
  658-      error: error,
  659-      result: result
  660-    }
  661-  };
  662-
  663-  var init = function () {
  664-    instance = this;
  665-
  666-    parser = new FormulaParser(instance);
  667-
  668-    instance.formulas = Formula;
  669-    instance.matrix = new Matrix();
  670-
  671-    instance.custom = {};
  672-
  673-    instance.matrix.scan();
  674-  };
  675-
  676-  return {
  677-    init: init,
  678-    utils: utils,
  679-    helper: helper,
  680-    parse: parse
  681-  };
  682-
  683-});
  684diff --git a/js/ruleJS.js b/js/ruleJS.js
  685deleted file mode 100644
  686index c3a6fed..0000000
  687--- a/js/ruleJS.js
  688+++ /dev/null
  689@@ -1,777 +0,0 @@
  690-var ruleJS = (function (root) {
  691-  'use strict';
  692-  var instance = this;
  693-
  694-  var rootElement = document.getElementById(root) || null;
  695-
  696-  var parser = {};
  697-
  698-  var FormulaParser = function(handler) {
  699-    var formulaLexer = function () {};
  700-    formulaLexer.prototype = Parser.lexer;
  701-
  702-    var formulaParser = function () {
  703-      this.lexer = new formulaLexer();
  704-      this.yy = {};
  705-    };
  706-
  707-    formulaParser.prototype = Parser;
  708-    var newParser = new formulaParser;
  709-    newParser.setObj = function(obj) {
  710-      newParser.yy.obj = obj;
  711-    };
  712-
  713-    newParser.yy.parseError = function (str, hash) {
  714-      throw {
  715-        name: 'Parser error',
  716-        message: str,
  717-        prop: hash
  718-      }
  719-    };
  720-
  721-    newParser.yy.handler = handler;
  722-
  723-    return newParser;
  724-  };
  725-
  726-  var Exception = {
  727-    errors: [
  728-      {type: 'NULL', output: '#NULL'},
  729-      {type: 'DIV_ZERO', output: '#DIV/0!'},
  730-      {type: 'VALUE', output: '#VALUE!'},
  731-      {type: 'REF', output: '#REF!'},
  732-      {type: 'NAME', output: '#NAME?'},
  733-      {type: 'NUM', output: '#NUM!'},
  734-      {type: 'NOT_AVAILABLE', output: '#N/A!'},
  735-      {type: 'ERROR', output: '#ERROR'}
  736-    ],
  737-    get: function (type) {
  738-      var error = Exception.errors.filter(function (item) {
  739-        return item.type === type || item.output === type;
  740-      })[0];
  741-
  742-      return error ? error.output : null;
  743-    }
  744-  };
  745-
  746-  var Matrix = function () {
  747-
  748-    // var item = {
  749-    //   id: '',
  750-    //   formula: '',
  751-    //   value: '',
  752-    //   error: '',
  753-    //   deps: [],
  754-    //   formulaEdit: false
  755-    // };
  756-
  757-    this.data = [];
  758-
  759-    var formElements = ['input[type=text]', '[data-formula]'];
  760-
  761-    var listen = function () {
  762-      if (document.activeElement && document.activeElement !== document.body) {
  763-        document.activeElement.blur();
  764-      }
  765-      else if (!document.activeElement) { //IE
  766-        document.body.focus();
  767-      }
  768-    };
  769-
  770-    this.getItem = function (id) {
  771-      return instance.matrix.data.filter(function (item) {
  772-        return item.id === id;
  773-      })[0];
  774-    };
  775-
  776-    this.removeItem = function (id) {
  777-      instance.matrix.data = instance.matrix.data.filter(function (item) {
  778-        return item.id !== id;
  779-      });
  780-    };
  781-
  782-    this.updateItem = function (item, props) {
  783-      if (instance.utils.isString(item)) {
  784-        item = instance.matrix.getItem(item);
  785-      }
  786-
  787-      if (item && props) {
  788-        for (var p in props) {
  789-          if (item[p] && instance.utils.isArray(item[p])) {
  790-            if (instance.utils.isArray(props[p])) {
  791-              props[p].forEach(function (i) {
  792-                if (item[p].indexOf(i) === -1) {
  793-                  item[p].push(i);
  794-                }
  795-              });
  796-            } else {
  797-
  798-              if (item[p].indexOf(props[p]) === -1) {
  799-                item[p].push(props[p]);
  800-              }
  801-            }
  802-          } else {
  803-            item[p] = props[p];
  804-          }
  805-        }
  806-      }
  807-    };
  808-
  809-    this.addItem = function (item) {
  810-      var cellId = item.id,
  811-        coords = instance.utils.cellCoords(cellId);
  812-
  813-      item.row = coords.row;
  814-      item.col = coords.col;
  815-
  816-      var cellExist = instance.matrix.data.filter(function (cell) {
  817-        return cell.id === cellId;
  818-      })[0];
  819-
  820-      if (!cellExist) {
  821-        instance.matrix.data.push(item);
  822-      } else {
  823-        instance.matrix.updateItem(cellExist, item);
  824-      }
  825-
  826-      return instance.matrix.getItem(cellId);
  827-    };
  828-
  829-
  830-    this.updateElementItem = function (element, props) {
  831-      var id = element.getAttribute('id'),
  832-        item = instance.matrix.getItem(id);
  833-
  834-      instance.matrix.updateItem(item, props);
  835-    };
  836-
  837-    this.getDependencies = function (id) {
  838-      var getDependencies = function (id) {
  839-        var filtered = instance.matrix.data.filter(function (cell) {
  840-          if (cell.deps) {
  841-            return cell.deps.indexOf(id) > -1;
  842-          }
  843-        });
  844-
  845-        var deps = [];
  846-        filtered.forEach(function (cell) {
  847-          if (deps.indexOf(cell.id) === -1) {
  848-            deps.push(cell.id);
  849-          }
  850-        });
  851-
  852-        return deps;
  853-      };
  854-
  855-      var allDependencies = [];
  856-
  857-      var getTotalDependencies = function (id) {
  858-        var deps = getDependencies(id);
  859-
  860-        if (deps.length) {
  861-          deps.forEach(function (refId) {
  862-            if (allDependencies.indexOf(refId) === -1) {
  863-              allDependencies.push(refId);
  864-
  865-              var item = instance.matrix.getItem(refId);
  866-              if (item.deps.length) {
  867-                getTotalDependencies(refId);
  868-              }
  869-            }
  870-          });
  871-        }
  872-      };
  873-
  874-      getTotalDependencies(id);
  875-
  876-      return allDependencies;
  877-    };
  878-
  879-    this.getElementDependencies = function (element) {
  880-      return instance.matrix.getDependencies(element.getAttribute('id'));
  881-    };
  882-
  883-    var recalculateElementDependencies = function (element) {
  884-      var allDependencies = instance.matrix.getElementDependencies(element),
  885-        id = element.getAttribute('id');
  886-
  887-      allDependencies.forEach(function (refId) {
  888-        var item = instance.matrix.getItem(refId);
  889-        if (item && item.formula) {
  890-          var refElement = document.getElementById(refId);
  891-          calculateElementFormula(item.formula, refElement);
  892-        }
  893-      });
  894-    };
  895-
  896-    var calculateElementFormula = function (formula, element) {
  897-      // to avoid double translate formulas, update item data in parser
  898-      var parsed = parse(formula, element),
  899-        value = parsed.result,
  900-        error = parsed.error,
  901-        nodeName = element.nodeName.toUpperCase();
  902-
  903-      instance.matrix.updateElementItem(element, {value: value, error: error});
  904-
  905-      if (['INPUT'].indexOf(nodeName) === -1) {
  906-        element.innerText = value || error;
  907-      }
  908-
  909-      element.value = value || error;
  910-
  911-      return parsed;
  912-    };
  913-
  914-    /**
  915-     * register new found element to matrix
  916-     * @param {Element} element
  917-     * @returns {Object}
  918-     */
  919-    var registerElementInMatrix = function (element) {
  920-
  921-      var id = element.getAttribute('id'),
  922-        formula = element.getAttribute('data-formula');
  923-
  924-      if (formula) {
  925-        // add item with basic properties to data array
  926-        instance.matrix.addItem({
  927-          id: id,
  928-          formula: formula
  929-        });
  930-
  931-        calculateElementFormula(formula, element);
  932-      }
  933-
  934-    };
  935-
  936-    /**
  937-     * register events for elements
  938-     * @param element
  939-     */
  940-    var registerElementEvents = function (element) {
  941-      var id = element.getAttribute('id');
  942-
  943-      // on db click show formula
  944-      element.addEventListener('dblclick', function () {
  945-        var item = instance.matrix.getItem(id);
  946-
  947-        if (item && item.formula) {
  948-          item.formulaEdit = true;
  949-          element.value = '=' + item.formula;
  950-        }
  951-      });
  952-
  953-      element.addEventListener('blur', function () {
  954-        var item = instance.matrix.getItem(id);
  955-
  956-        if (item) {
  957-          if (item.formulaEdit) {
  958-            element.value = item.value || item.error;
  959-          }
  960-
  961-          item.formulaEdit = false;
  962-        }
  963-      });
  964-
  965-      // if pressed ESC restore original value
  966-      element.addEventListener('keyup', function (event) {
  967-        switch (event.keyCode) {
  968-          case 13: // ENTER
  969-          case 27: // ESC
  970-            // leave cell
  971-            listen();
  972-            break;
  973-        }
  974-      });
  975-
  976-      // re-calculate formula if ref cells value changed
  977-      element.addEventListener('change', function () {
  978-        // reset and remove item
  979-        instance.matrix.removeItem(id);
  980-
  981-        // check if inserted text could be the formula
  982-        var value = element.value;
  983-
  984-        if (value[0] === '=') {
  985-          element.setAttribute('data-formula', value.substr(1));
  986-          registerElementInMatrix(element);
  987-        }
  988-
  989-        // get ref cells and re-calculate formulas
  990-        recalculateElementDependencies(element);
  991-      });
  992-    };
  993-
  994-    this.scan = function () {
  995-      var $totalElements = rootElement.querySelectorAll(formElements);
  996-      [].slice.call($totalElements).forEach(function ($item) {
  997-        registerElementInMatrix($item);
  998-        registerElementEvents($item);
  999-      });
 1000-    };
 1001-  };
 1002-
 1003-  var utils = {
 1004-    isArray: function (value) {
 1005-      return Object.prototype.toString.call(value) === '[object Array]';
 1006-    },
 1007-
 1008-    isNumber: function (value) {
 1009-      return Object.prototype.toString.call(value) === '[object Number]';
 1010-    },
 1011-
 1012-    isString: function (value) {
 1013-      return Object.prototype.toString.call(value) === '[object String]';
 1014-    },
 1015-
 1016-    isFunction: function (value) {
 1017-      return Object.prototype.toString.call(value) === '[object Function]';
 1018-    },
 1019-
 1020-    isUndefined: function (value) {
 1021-      return Object.prototype.toString.call(value) === '[object Undefined]';
 1022-    },
 1023-
 1024-    isNull: function (value) {
 1025-      return Object.prototype.toString.call(value) === '[object Null]';
 1026-    },
 1027-
 1028-    isSet: function (value) {
 1029-      return !instance.utils.isUndefined(value) && !instance.utils.isNull(value);
 1030-    },
 1031-
 1032-    getCellAlphaNum: function (cell) {
 1033-      var num = cell.match(/\d+$/),
 1034-        alpha = cell.replace(num, '');
 1035-
 1036-      return {
 1037-        alpha: alpha,
 1038-        num: parseInt(num[0], 10)
 1039-      }
 1040-    },
 1041-
 1042-    toNum: function (chr) {
 1043-      chr = instance.utils.clearFormula(chr);
 1044-      var base = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', i, j, result = 0;
 1045-
 1046-      for (i = 0, j = chr.length - 1; i < chr.length; i += 1, j -= 1) {
 1047-        result += Math.pow(base.length, j) * (base.indexOf(chr[i]) + 1);
 1048-      }
 1049-
 1050-      if (result) {
 1051-        --result;
 1052-      }
 1053-
 1054-      return result;
 1055-    },
 1056-
 1057-    toChar: function (num) {
 1058-      var s = '';
 1059-
 1060-      while (num >= 0) {
 1061-        s = String.fromCharCode(num % 26 + 97) + s;
 1062-        num = Math.floor(num / 26) - 1;
 1063-      }
 1064-
 1065-      return s.toUpperCase();
 1066-    },
 1067-
 1068-    cellCoords: function (cell) {
 1069-      var num = cell.match(/\d+$/),
 1070-        alpha = cell.replace(num, '');
 1071-
 1072-      return {
 1073-        row: parseInt(num[0], 10) - 1,
 1074-        col: instance.utils.toNum(alpha)
 1075-      };
 1076-    },
 1077-
 1078-    clearFormula: function (formula) {
 1079-      return formula.replace(/\$/g, '');
 1080-    },
 1081-
 1082-    translateCellCoords: function (coords) {
 1083-      return instance.utils.toChar(coords.col) + '' + parseInt(coords.row + 1, 10);
 1084-    },
 1085-
 1086-    iterateCells: function (startCell, endCell, callback) {
 1087-      var result = {
 1088-        index: [], // list of cell index: A1, A2, A3
 1089-        value: []  // list of cell value
 1090-      };
 1091-
 1092-      var cols = {
 1093-        start: 0,
 1094-        end: 0
 1095-      };
 1096-
 1097-      if (endCell.col >= startCell.col) {
 1098-        cols = {
 1099-          start: startCell.col,
 1100-          end: endCell.col
 1101-        };
 1102-      } else {
 1103-        cols = {
 1104-          start: endCell.col,
 1105-          end: startCell.col
 1106-        };
 1107-      }
 1108-
 1109-      var rows = {
 1110-        start: 0,
 1111-        end: 0
 1112-      };
 1113-
 1114-      if (endCell.row >= startCell.row) {
 1115-        rows = {
 1116-          start: startCell.row,
 1117-          end: endCell.row
 1118-        };
 1119-      } else {
 1120-        rows = {
 1121-          start: endCell.row,
 1122-          end: startCell.row
 1123-        };
 1124-      }
 1125-
 1126-      for (var column = cols.start; column <= cols.end; column++) {
 1127-        for (var row = rows.start; row <= rows.end; row++) {
 1128-          var cellIndex = instance.utils.toChar(column) + (row + 1),
 1129-            cellValue = instance.helper.cellValue.call(this, cellIndex);
 1130-
 1131-          result.index.push(cellIndex);
 1132-          result.value.push(cellValue);
 1133-        }
 1134-      }
 1135-
 1136-      if (instance.utils.isFunction(callback)) {
 1137-        return callback.apply(callback, [result]);
 1138-      } else {
 1139-        return result;
 1140-      }
 1141-    },
 1142-
 1143-    sort: function (rev) {
 1144-      return function (a, b) {
 1145-        return ((a < b) ? -1 : ((a > b) ? 1 : 0)) * (rev ? -1 : 1);
 1146-      }
 1147-    }
 1148-  };
 1149-
 1150-  var helper = {
 1151-    SUPPORTED_FORMULAS: [
 1152-      'ABS', 'ACCRINT', 'ACOS', 'ACOSH', 'ACOTH', 'AND', 'ARABIC', 'ASIN', 'ASINH', 'ATAN', 'ATAN2', 'ATANH', 'AVEDEV', 'AVERAGE', 'AVERAGEA', 'AVERAGEIF',
 1153-      'BASE', 'BESSELI', 'BESSELJ', 'BESSELK', 'BESSELY', 'BETADIST', 'BETAINV', 'BIN2DEC', 'BIN2HEX', 'BIN2OCT', 'BINOMDIST', 'BINOMDISTRANGE', 'BINOMINV', 'BITAND', 'BITLSHIFT', 'BITOR', 'BITRSHIFT', 'BITXOR',
 1154-      'CEILING', 'CEILINGMATH', 'CEILINGPRECISE', 'CHAR', 'CHISQDIST', 'CHISQINV', 'CODE', 'COMBIN', 'COMBINA', 'COMPLEX', 'CONCATENATE', 'CONFIDENCENORM', 'CONFIDENCET', 'CONVERT', 'CORREL', 'COS', 'COSH', 'COT', 'COTH', 'COUNT', 'COUNTA', 'COUNTBLANK', 'COUNTIF', 'COUNTIFS', 'COUNTIN', 'COUNTUNIQUE', 'COVARIANCEP', 'COVARIANCES', 'CSC', 'CSCH', 'CUMIPMT', 'CUMPRINC',
 1155-      'DATE', 'DATEVALUE', 'DAY', 'DAYS', 'DAYS360', 'DB', 'DDB', 'DEC2BIN', 'DEC2HEX', 'DEC2OCT', 'DECIMAL', 'DEGREES', 'DELTA', 'DEVSQ', 'DOLLAR', 'DOLLARDE', 'DOLLARFR',
 1156-      'E', 'EDATE', 'EFFECT', 'EOMONTH', 'ERF', 'ERFC', 'EVEN', 'EXACT', 'EXPONDIST',
 1157-      'FALSE', 'FDIST', 'FINV', 'FISHER', 'FISHERINV',
 1158-      'IF', 'INT', 'ISEVEN', 'ISODD',
 1159-      'LN', 'LOG', 'LOG10',
 1160-      'MAX', 'MAXA', 'MEDIAN', 'MIN', 'MINA', 'MOD',
 1161-      'NOT',
 1162-      'ODD', 'OR',
 1163-      'PI', 'POWER',
 1164-      'ROUND', 'ROUNDDOWN', 'ROUNDUP',
 1165-      'SIN', 'SINH', 'SPLIT', 'SQRT', 'SQRTPI', 'SUM', 'SUMIF', 'SUMIFS', 'SUMPRODUCT', 'SUMSQ', 'SUMX2MY2', 'SUMX2PY2', 'SUMXMY2',
 1166-      'TAN', 'TANH', 'TRUE', 'TRUNC',
 1167-      'XOR'
 1168-    ],
 1169-
 1170-    number: function (num) {
 1171-      switch (typeof num) {
 1172-        case 'number':
 1173-          return num;
 1174-        case 'string':
 1175-          if (!isNaN(num)) {
 1176-            return num.indexOf('.') > -1 ? parseFloat(num) : parseInt(num, 10);
 1177-          }
 1178-      }
 1179-
 1180-      return num;
 1181-    },
 1182-
 1183-    string: function (str) {
 1184-      return str.substring(1, str.length - 1);
 1185-    },
 1186-
 1187-    numberInverted: function (num) {
 1188-      return this.number(num) * (-1);
 1189-    },
 1190-
 1191-    specialMatch: function (type, exp1, exp2) {
 1192-      var result;
 1193-
 1194-      switch (type) {
 1195-        case '&':
 1196-          result = exp1.toString() + exp2.toString();
 1197-          break;
 1198-      }
 1199-      return result;
 1200-    },
 1201-
 1202-    logicMatch: function (type, exp1, exp2) {
 1203-      var result;
 1204-
 1205-      switch (type) {
 1206-        case '=':
 1207-          result = (exp1 === exp2);
 1208-          break;
 1209-
 1210-        case '>':
 1211-          result = (exp1 > exp2);
 1212-          break;
 1213-
 1214-        case '<':
 1215-          result = (exp1 < exp2);
 1216-          break;
 1217-
 1218-        case '>=':
 1219-          result = (exp1 >= exp2);
 1220-          break;
 1221-
 1222-        case '<=':
 1223-          result = (exp1 === exp2);
 1224-          break;
 1225-
 1226-        case '<>':
 1227-          result = (exp1 != exp2);
 1228-          break;
 1229-
 1230-        case 'NOT':
 1231-          result = (exp1 != exp2);
 1232-          break;
 1233-      }
 1234-
 1235-      return result;
 1236-    },
 1237-
 1238-    mathMatch: function (type, number1, number2) {
 1239-      var result;
 1240-
 1241-      number1 = helper.number(number1);
 1242-      number2 = helper.number(number2);
 1243-
 1244-      if (isNaN(number1) || isNaN(number2)) {
 1245-        throw Error('VALUE');
 1246-      }
 1247-
 1248-      switch (type) {
 1249-        case '+':
 1250-          result = number1 + number2;
 1251-          break;
 1252-        case '-':
 1253-          result = number1 - number2;
 1254-          break;
 1255-        case '/':
 1256-          result = number1 / number2;
 1257-          if (result == Infinity) {
 1258-            throw Error('DIV_ZERO');
 1259-          } else if (isNaN(result)) {
 1260-            throw Error('VALUE');
 1261-          }
 1262-          break;
 1263-        case '*':
 1264-          result = number1 * number2;
 1265-          break;
 1266-        case '^':
 1267-          result = Math.pow(number1, number2);
 1268-          break;
 1269-      }
 1270-
 1271-      return result;
 1272-    },
 1273-
 1274-    callFunction: function (fn, args) {
 1275-      fn = fn.toUpperCase();
 1276-      args = args || [];
 1277-
 1278-      if (instance.helper.SUPPORTED_FORMULAS.indexOf(fn) > -1) {
 1279-        if (instance.formulas[fn]) {
 1280-          return instance.formulas[fn].apply(this, args);
 1281-        }
 1282-      }
 1283-
 1284-      throw Error('NAME');
 1285-    },
 1286-
 1287-    callVariable: function (args) {
 1288-      args = args || [];
 1289-      var str = args[0];
 1290-
 1291-      if (str) {
 1292-        str = str.toUpperCase();
 1293-        if (instance.formulas[str]) {
 1294-          return ((typeof instance.formulas[str] === 'function') ? instance.formulas[str].apply(this, args) : instance.formulas[str]);
 1295-        }
 1296-      }
 1297-
 1298-      throw Error('NAME');
 1299-    },
 1300-
 1301-    cellValue: function (cell) {
 1302-      var value,
 1303-        fnCellValue = instance.custom.cellValue,
 1304-        element = this,
 1305-        item = instance.matrix.getItem(cell);
 1306-
 1307-      // check if custom cellValue fn exists
 1308-      if (instance.utils.isFunction(fnCellValue)) {
 1309-
 1310-        var cellCoords = instance.utils.cellCoords(cell),
 1311-          cellId = instance.utils.translateCellCoords({row: element.row, col: element.col});
 1312-
 1313-        // get value
 1314-        value = item ? item.value : fnCellValue(cellCoords.row, cellCoords.col);
 1315-
 1316-        if (instance.utils.isNull(value)) {
 1317-          value = 0;
 1318-        }
 1319-
 1320-        if (cellId) {
 1321-          //update dependencies
 1322-          instance.matrix.updateItem(cellId, {deps: [cell]});
 1323-        }
 1324-
 1325-      } else {
 1326-
 1327-        // get value
 1328-        value = item ? item.value : document.getElementById(cell).value;
 1329-
 1330-        //update dependencies
 1331-        instance.matrix.updateElementItem(element, {deps: [cell]});
 1332-      }
 1333-
 1334-      // check references error
 1335-      if (item && item.deps) {
 1336-        if (item.deps.indexOf(cellId) !== -1) {
 1337-          throw Error('REF');
 1338-        }
 1339-      }
 1340-
 1341-      // check if any error occurs
 1342-      if (item && item.error) {
 1343-        throw Error(item.error);
 1344-      }
 1345-
 1346-      // return value if is set
 1347-      if (instance.utils.isSet(value)) {
 1348-        var result = instance.helper.number(value);
 1349-
 1350-        return !isNaN(result) ? result : value;
 1351-      }
 1352-
 1353-      // cell is not available
 1354-      throw Error('NOT_AVAILABLE');
 1355-    },
 1356-
 1357-    cellRangeValue: function (start, end) {
 1358-      var fnCellValue = instance.custom.cellValue,
 1359-        coordsStart = instance.utils.cellCoords(start),
 1360-        coordsEnd = instance.utils.cellCoords(end),
 1361-        element = this;
 1362-
 1363-      // iterate cells to get values and indexes
 1364-      var cells = instance.utils.iterateCells.call(this, coordsStart, coordsEnd),
 1365-        result = [];
 1366-
 1367-      // check if custom cellValue fn exists
 1368-      if (instance.utils.isFunction(fnCellValue)) {
 1369-
 1370-        var cellId = instance.utils.translateCellCoords({row: element.row, col: element.col});
 1371-
 1372-        //update dependencies
 1373-        instance.matrix.updateItem(cellId, {deps: cells.index});
 1374-
 1375-      } else {
 1376-
 1377-        //update dependencies
 1378-        instance.matrix.updateElementItem(element, {deps: cells.index});
 1379-      }
 1380-
 1381-      result.push(cells.value);
 1382-      return result;
 1383-    },
 1384-
 1385-    fixedCellValue: function (id) {
 1386-      id = id.replace(/\$/g, '');
 1387-      return instance.helper.cellValue.call(this, id);
 1388-    },
 1389-
 1390-    fixedCellRangeValue: function (start, end) {
 1391-      start = start.replace(/\$/g, '');
 1392-      end = end.replace(/\$/g, '');
 1393-
 1394-      return instance.helper.cellRangeValue.call(this, start, end);
 1395-    }
 1396-  };
 1397-
 1398-  var parse = function (formula, element) {
 1399-    var result = null,
 1400-      error = null;
 1401-
 1402-    try {
 1403-
 1404-      parser.setObj(element);
 1405-      result = parser.parse(formula);
 1406-
 1407-      var id;
 1408-
 1409-      if (element instanceof HTMLElement) {
 1410-        id = element.getAttribute('id');
 1411-      } else if (element && element.id) {
 1412-        id = element.id;
 1413-      }
 1414-
 1415-      var deps = instance.matrix.getDependencies(id);
 1416-
 1417-      if (deps.indexOf(id) !== -1) {
 1418-        result = null;
 1419-
 1420-        deps.forEach(function (id) {
 1421-          instance.matrix.updateItem(id, {value: null, error: Exception.get('REF')});
 1422-        });
 1423-
 1424-        throw Error('REF');
 1425-      }
 1426-
 1427-    } catch (ex) {
 1428-
 1429-      var message = Exception.get(ex.message);
 1430-
 1431-      if (message) {
 1432-        error = message;
 1433-      } else {
 1434-        error = Exception.get('ERROR');
 1435-      }
 1436-    }
 1437-
 1438-    return {
 1439-      error: error,
 1440-      result: result
 1441-    }
 1442-  };
 1443-
 1444-  var init = function () {
 1445-    instance = this;
 1446-
 1447-    parser = new FormulaParser(instance);
 1448-
 1449-    instance.formulas = Formula;
 1450-    instance.matrix = new Matrix();
 1451-
 1452-    instance.custom = {};
 1453-
 1454-    if (rootElement) {
 1455-      instance.matrix.scan();
 1456-    }
 1457-  };
 1458-
 1459-  return {
 1460-    init: init,
 1461-    utils: utils,
 1462-    helper: helper,
 1463-    parse: parse
 1464-  };
 1465-
 1466-});
 1467diff --git a/lib/formulajs/formula.js b/lib/formulajs/formula.js
 1468deleted file mode 100644
 1469index 53ba1cc..0000000
 1470--- a/lib/formulajs/formula.js
 1471+++ /dev/null
 1472@@ -1,5828 +0,0 @@
 1473-// Copyright (c) 2012 Sutoiku, Inc.
 1474-
 1475-// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 1476-
 1477-// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 1478-
 1479-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 1480-
 1481-// Some algorithms have been ported from Apache OpenOffice:
 1482-
 1483-/**************************************************************
 1484- *
 1485- * Licensed to the Apache Software Foundation (ASF) under one
 1486- * or more contributor license agreements.  See the NOTICE file
 1487- * distributed with this work for additional information
 1488- * regarding copyright ownership.  The ASF licenses this file
 1489- * to you under the Apache License, Version 2.0 (the
 1490- * "License"); you may not use this file except in compliance
 1491- * with the License.  You may obtain a copy of the License at
 1492- *
 1493- *   http://www.apache.org/licenses/LICENSE-2.0
 1494- *
 1495- * Unless required by applicable law or agreed to in writing,
 1496- * software distributed under the License is distributed on an
 1497- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 1498- * KIND, either express or implied.  See the License for the
 1499- * specific language governing permissions and limitations
 1500- * under the License.
 1501- *
 1502- *************************************************************/
 1503-/*jslint evil: true*/
 1504-/*global define */
 1505-
 1506-(function () {
 1507-  var root = this;
 1508-
 1509-  var Formula = root.Formula = {};
 1510-  var _ = root._;
 1511-  var numeric = root.numeric;
 1512-  var numeral = root.numeral;
 1513-  var jStat = root.jStat;
 1514-  var moment = root.moment;
 1515-  var lodash = _;
 1516-  var md5 = root.md5;
 1517-  var _s = _.str;
 1518-
 1519-  if (typeof exports !== "undefined") {
 1520-    module.exports = exportModule(
 1521-      require('numeric'),
 1522-      require('numeral'),
 1523-      require('jStat'),
 1524-      require('moment'),
 1525-      require('lodash'),
 1526-      require('underscore.string'),
 1527-      require('blueimp-md5')
 1528-    );
 1529-  } else if (typeof define === "function" && define.amd) {
 1530-    define(
 1531-      'formula',
 1532-      ['numeric', 'numeral', 'jstat', 'moment', 'lodash', 'underscore.string', 'md5'],
 1533-      exportModule
 1534-    );
 1535-  } else {
 1536-    Formula = exportModule(numeric, numeral, jStat, moment, lodash, _s, md5);
 1537-    return Formula;
 1538-  }
 1539-
 1540-  function exportModule(numeric, numeral, jStat, moment, _, _s, md5) {
 1541-    var MEMOIZED_FACT = [];
 1542-
 1543-    var SQRT2PI = 2.5066282746310002;
 1544-
 1545-    var WEEK_STARTS = [
 1546-      undefined,
 1547-      0,
 1548-      1,
 1549-      undefined,
 1550-      undefined,
 1551-      undefined,
 1552-      undefined,
 1553-      undefined,
 1554-      undefined,
 1555-      undefined,
 1556-      undefined,
 1557-      undefined,
 1558-      1,
 1559-      2,
 1560-      3,
 1561-      4,
 1562-      5,
 1563-      6,
 1564-      0
 1565-    ];
 1566-
 1567-    var WEEK_TYPES = [
 1568-      [],
 1569-      [1, 2, 3, 4, 5, 6, 7],
 1570-      [7, 1, 2, 3, 4, 5, 6],
 1571-      [6, 0, 1, 2, 3, 4, 5],
 1572-      [],
 1573-      [],
 1574-      [],
 1575-      [],
 1576-      [],
 1577-      [],
 1578-      [],
 1579-      [7, 1, 2, 3, 4, 5, 6],
 1580-      [6, 7, 1, 2, 3, 4, 5],
 1581-      [5, 6, 7, 1, 2, 3, 4],
 1582-      [4, 5, 6, 7, 1, 2, 3],
 1583-      [3, 4, 5, 6, 7, 1, 2],
 1584-      [2, 3, 4, 5, 6, 7, 1],
 1585-      [1, 2, 3, 4, 5, 6, 7]
 1586-    ];
 1587-
 1588-    var WEEKEND_TYPES = [
 1589-      [],
 1590-      [6, 0],
 1591-      [0, 1],
 1592-      [1, 2],
 1593-      [2, 3],
 1594-      [3, 4],
 1595-      [4, 5],
 1596-      [5, 6],
 1597-      undefined,
 1598-      undefined,
 1599-      undefined,
 1600-      [0],
 1601-      [1],
 1602-      [2],
 1603-      [3],
 1604-      [4],
 1605-      [5],
 1606-      [6]
 1607-    ];
 1608-
 1609-    var simplifyArguments = function (arguments) {
 1610-      for (var prop in arguments) {
 1611-        if (_.isArray(arguments[prop])) {
 1612-          arguments[prop] = Formula.FLATTEN(arguments[prop]);
 1613-        }
 1614-      }
 1615-      return arguments;
 1616-    };
 1617-
 1618-    // Override some functions
 1619-    Formula.UNIQUE = function () {
 1620-      return _.unique(arguments);
 1621-    };
 1622-
 1623-    Formula.FLATTEN = function () {
 1624-      return _.flatten(arguments);
 1625-    };
 1626-
 1627-    // Generate a callback function
 1628-    Formula.FUNCTION = function () {
 1629-      var args = Array.prototype.slice.call(arguments);
 1630-      var expression = args[args.length - 1];
 1631-      var regexp = /(\w+)\(/g;
 1632-      var newExpression = expression.replace(regexp, function () {
 1633-        return "Formulae." + arguments[0];
 1634-      });
 1635-
 1636-      args[args.length - 1] = "return " + newExpression + ";";
 1637-      if (newExpression !== expression) {
 1638-        args.unshift('Formulae');
 1639-      }
 1640-
 1641-      return  Function.apply(null, args);
 1642-    };
 1643-
 1644-    // Moment functions
 1645-    Formula.MOMENT = function (timestamp, format) {
 1646-      return moment(timestamp).format(format);
 1647-    };
 1648-
 1649-    Formula.MOMENTADD = function (start_date, period, number) {
 1650-      return moment(start_date).add(period, number);
 1651-    };
 1652-
 1653-    Formula.MOMENTDIFF = function (start_date, end_date, period) {
 1654-      return moment(end_date).diff(moment.utc(start_date), period);
 1655-    };
 1656-
 1657-    Formula.MOMENTSUB = function (start_date, period, number) {
 1658-      return moment(start_date).subtract(period, number);
 1659-    };
 1660-
 1661-    Formula.MOMENTUTC = function (timestamp, format) {
 1662-      return moment.utc(timestamp).format(format);
 1663-    };
 1664-
 1665-    Formula.MOMENTUTCADD = function (start_date, period, number) {
 1666-      return moment.utc(start_date).add(period, number);
 1667-    };
 1668-
 1669-    Formula.MOMENTUTCDIFF = function (start_date, end_date, period) {
 1670-      return moment.utc(end_date).diff(moment.utc(start_date), period);
 1671-    };
 1672-
 1673-    Formula.MOMENTUTCSUB = function (start_date, period, number) {
 1674-      return moment.utc(start_date).subtract(period, number);
 1675-    };
 1676-
 1677-    Formula.MOMENTUNIX = function (unixTime) {
 1678-      return moment.unix(unixTime).toDate();
 1679-    };
 1680-
 1681-    Formula.MOMENTFORMAT = function (date, format) {
 1682-      return moment(date).format(format);
 1683-    };
 1684-
 1685-    Formula.MOMENTISLEAPYEAR = function (date, format) {
 1686-      return moment(date, format).isLeapYear();
 1687-    };
 1688-
 1689-    Formula.MOMENTISDST = function (date, format) {
 1690-      return moment(date, format).isDST();
 1691-    };
 1692-
 1693-    Formula.MOMENTSTARTOF = function (date, units, format) {
 1694-      return moment(date, format).startOf(units).toDate();
 1695-    };
 1696-
 1697-    Formula.MOMENTENDOF = function (date, units, format) {
 1698-      return moment(date, format).endOf(units).toDate();
 1699-    };
 1700-
 1701-    Formula.MOMENTISAFTER = function (date1, date2, format) {
 1702-      return moment(date1, format).isAfter(moment(date2, format));
 1703-    };
 1704-
 1705-    Formula.MOMENTISBEFORE = function (date1, date2, format) {
 1706-      return moment(date1, format).isBefore(moment(date2, format));
 1707-    };
 1708-
 1709-    Formula.INTERVAL = function (second) {
 1710-      var year  = Math.floor(second/946080000);
 1711-      second    = second%946080000;
 1712-      var month = Math.floor(second/2592000);
 1713-      second    = second%2592000;
 1714-      var day   = Math.floor(second/86400);
 1715-      second    = second%86400;
 1716-
 1717-      var hour  = Math.floor(second/3600);
 1718-      second    = second%3600;
 1719-      var min   = Math.floor(second/60);
 1720-      second    = second%60;
 1721-      var sec   = second;
 1722-
 1723-      year  = (year  > 0) ? year  + 'Y' : '';
 1724-      month = (month > 0) ? month + 'M' : '';
 1725-      day   = (day   > 0) ? day   + 'D' : '';
 1726-      hour  = (hour  > 0) ? hour  + 'H' : '';
 1727-      min   = (min   > 0) ? min   + 'M' : '';
 1728-      sec   = (sec   > 0) ? sec   + 'S' : '';
 1729-
 1730-      return 'P' + year + month + day +
 1731-        'T' + hour + min + sec;
 1732-    };
 1733-
 1734-    // Custom Functions
 1735-    Formula.ARGSCONCAT = function (args) {
 1736-      var result = [];
 1737-      for (var i = 0; i < args.length; i++) {
 1738-        result = result.concat(args[i]);
 1739-      }
 1740-      return result;
 1741-    };
 1742-
 1743-    Formula.ARGSTOARRAY = function (args) {
 1744-      return Array.prototype.slice.call(args, 0);
 1745-    };
 1746-
 1747-    Formula.CLEANFLOAT = function (number) {
 1748-      var power = Math.pow(10, 14);
 1749-      return Math.round(number * power) / power;
 1750-    };
 1751-
 1752-    Formula.COUNTIN = function (range, value) {
 1753-      var result = 0;
 1754-      for (var i = 0; i < range.length; i++) {
 1755-        if (range[i] === value) {
 1756-          result++;
 1757-        }
 1758-      }
 1759-      return result;
 1760-    };
 1761-
 1762-    Formula.FINDFIELD = function(database, title) {
 1763-      var index = null;
 1764-      for (var i = 0; i < database.length; i++) {
 1765-        if (database[i][0] === title) {
 1766-          index = i;
 1767-          break;
 1768-        }
 1769-      }
 1770-
 1771-      // Return error if the input field title is incorrect
 1772-      if (index == null) {
 1773-        return '#VALUE!';
 1774-      }
 1775-      return index;
 1776-    };
 1777-
 1778-    Formula.FINDRESULTINDEX = function(database, criteria) {
 1779-      var maxCriteriaLength = criteria[0].length;
 1780-      for (var i = 1; i < criteria.length; i++) {
 1781-        if (criteria[i].length > maxCriteriaLength) {
 1782-          maxCriteriaLength = criteria[i].length;
 1783-        }
 1784-      }
 1785-      var columnResultIndexes = [];
 1786-      for (i = 1; i < maxCriteriaLength; i++) {
 1787-        var rowResultIndexes = [];
 1788-        for (var j = 0; j < criteria.length; j++) {
 1789-          if (criteria[j].length < maxCriteriaLength) {
 1790-            continue;
 1791-          }
 1792-          var criteriaTitle = criteria[j][0];
 1793-          var criteriaIndex = Formula.FINDFIELD(database, criteriaTitle);
 1794-          var criteriaValues = _.rest(database[criteriaIndex]);
 1795-          var count = 0;
 1796-          var singleResultIndexes = [];
 1797-          for (var k = 0; k < criteriaValues.length; k++) {
 1798-            if (eval(criteriaValues[k] + criteria[j][i])) {
 1799-              singleResultIndexes[count++] = k;
 1800-            }
 1801-          }
 1802-          rowResultIndexes[j] = singleResultIndexes;
 1803-        }
 1804-        columnResultIndexes[i - 1] = _.intersection.apply(_, rowResultIndexes);
 1805-      }
 1806-
 1807-      var resultIndexes = _.union.apply(_, columnResultIndexes);
 1808-      return resultIndexes;
 1809-    };
 1810-
 1811-    // Database functions
 1812-    Formula.DAVERAGE = function(database, field, criteria) {
 1813-      // Return error if field is not a number and not a string
 1814-      if (isNaN(field) && (typeof field !== "string")) {
 1815-        return '#VALUE!';
 1816-      }
 1817-
 1818-      var resultIndexes = Formula.FINDRESULTINDEX(database, criteria);
 1819-      var targetFields = [];
 1820-      if (typeof field === "string") {
 1821-        var index = Formula.FINDFIELD(database, field);
 1822-        targetFields = _.rest(database[index]);
 1823-      } else {
 1824-        targetFields = _.rest(database[field]);
 1825-      }
 1826-      var sum = 0;
 1827-      for (var i = 0; i < resultIndexes.length; i++) {
 1828-        sum += targetFields[resultIndexes[i]];
 1829-      }
 1830-      var average = Formula.IF(resultIndexes.length === 0, "#DIV/0!", sum / resultIndexes.length);
 1831-      return average;
 1832-    };
 1833-
 1834-    Formula.DCOUNT = function(database, field, criteria) {
 1835-      // Return error if field is not a number and not a string
 1836-      if (isNaN(field) && (typeof field !== "string")) {
 1837-        return '#VALUE!';
 1838-      }
 1839-      var resultIndexes = Formula.FINDRESULTINDEX(database, criteria);
 1840-      var targetFields = [];
 1841-      if (typeof field === "string") {
 1842-        var index = Formula.FINDFIELD(database, field);
 1843-        targetFields = _.rest(database[index]);
 1844-      } else {
 1845-        targetFields = _.rest(database[field]);
 1846-      }
 1847-      var targetValues = [];
 1848-      for (var i = 0; i < resultIndexes.length; i++) {
 1849-        targetValues[i] = targetFields[resultIndexes[i]];
 1850-      }
 1851-      return Formula.COUNT(targetValues);
 1852-    };
 1853-
 1854-    Formula.DCOUNTA = function(database, field, criteria) {
 1855-      // Return error if field is not a number and not a string
 1856-      if (isNaN(field) && (typeof field !== "string")) {
 1857-        return '#VALUE!';
 1858-      }
 1859-      var resultIndexes = Formula.FINDRESULTINDEX(database, criteria);
 1860-      var targetFields = [];
 1861-      if (typeof field === "string") {
 1862-        var index = Formula.FINDFIELD(database, field);
 1863-        targetFields = _.rest(database[index]);
 1864-      } else {
 1865-        targetFields = _.rest(database[field]);
 1866-      }
 1867-      var targetValues = [];
 1868-      for (var i = 0; i < resultIndexes.length; i++) {
 1869-        targetValues[i] = targetFields[resultIndexes[i]];
 1870-      }
 1871-      return Formula.COUNTA(targetValues);
 1872-    };
 1873-
 1874-    Formula.DGET = function(database, field, criteria) {
 1875-      // Return error if field is not a number and not a string
 1876-      if (isNaN(field) && (typeof field !== "string")) {
 1877-        return '#VALUE!';
 1878-      }
 1879-      var resultIndexes = Formula.FINDRESULTINDEX(database, criteria);
 1880-      var targetFields = [];
 1881-      if (typeof field === "string") {
 1882-        var index = Formula.FINDFIELD(database, field);
 1883-        targetFields = _.rest(database[index]);
 1884-      } else {
 1885-        targetFields = _.rest(database[field]);
 1886-      }
 1887-      // Return error if no record meets the criteria
 1888-      if (resultIndexes.length === 0) {
 1889-        return '#VALUE!';
 1890-      }
 1891-      // Returns the #NUM! error value because more than one record meets the
 1892-      // criteria
 1893-      if (resultIndexes.length > 1) {
 1894-        return '#NUM!';
 1895-      }
 1896-
 1897-      return targetFields[resultIndexes[0]];
 1898-    };
 1899-
 1900-    Formula.DMAX = function(database, field, criteria) {
 1901-      // Return error if field is not a number and not a string
 1902-      if (isNaN(field) && (typeof field !== "string")) {
 1903-        return '#VALUE!';
 1904-      }
 1905-      var resultIndexes = Formula.FINDRESULTINDEX(database, criteria);
 1906-      var targetFields = [];
 1907-      if (typeof field === "string") {
 1908-        var index = Formula.FINDFIELD(database, field);
 1909-        targetFields = _.rest(database[index]);
 1910-      } else {
 1911-        targetFields = _.rest(database[field]);
 1912-      }
 1913-      var maxValue = targetFields[resultIndexes[0]];
 1914-      for (var i = 1; i < resultIndexes.length; i++) {
 1915-        if (maxValue < targetFields[resultIndexes[i]]) {
 1916-          maxValue = targetFields[resultIndexes[i]];
 1917-        }
 1918-      }
 1919-      return maxValue;
 1920-    };
 1921-
 1922-    Formula.DMIN = function(database, field, criteria) {
 1923-      // Return error if field is not a number and not a string
 1924-      if (isNaN(field) && (typeof field !== "string")) {
 1925-        return '#VALUE!';
 1926-      }
 1927-      var resultIndexes = Formula.FINDRESULTINDEX(database, criteria);
 1928-      var targetFields = [];
 1929-      if (typeof field === "string") {
 1930-        var index = Formula.FINDFIELD(database, field);
 1931-        targetFields = _.rest(database[index]);
 1932-      } else {
 1933-        targetFields = _.rest(database[field]);
 1934-      }
 1935-      var minValue = targetFields[resultIndexes[0]];
 1936-      for (var i = 1; i < resultIndexes.length; i++) {
 1937-        if (minValue > targetFields[resultIndexes[i]]) {
 1938-          minValue = targetFields[resultIndexes[i]];
 1939-        }
 1940-      }
 1941-      return minValue;
 1942-    };
 1943-
 1944-    Formula.DPRODUCT = function(database, field, criteria) {
 1945-      // Return error if field is not a number and not a string
 1946-      if (isNaN(field) && (typeof field !== "string")) {
 1947-        return '#VALUE!';
 1948-      }
 1949-      var resultIndexes = Formula.FINDRESULTINDEX(database, criteria);
 1950-      var targetFields = [];
 1951-      if (typeof field === "string") {
 1952-        var index = Formula.FINDFIELD(database, field);
 1953-        targetFields = _.rest(database[index]);
 1954-      } else {
 1955-        targetFields = _.rest(database[field]);
 1956-      }
 1957-      var targetValues = [];
 1958-      for (var i = 0; i < resultIndexes.length; i++) {
 1959-        targetValues[i] = targetFields[resultIndexes[i]];
 1960-      }
 1961-      targetValues = _.compact(targetValues);
 1962-      var result = 1;
 1963-      for (i = 0; i < targetValues.length; i++) {
 1964-        result *= targetValues[i];
 1965-      }
 1966-      return result;
 1967-    };
 1968-
 1969-    Formula.DSTDEV = function(database, field, criteria) {
 1970-      // Return error if field is not a number and not a string
 1971-      if (isNaN(field) && (typeof field !== "string")) {
 1972-        return '#VALUE!';
 1973-      }
 1974-      var resultIndexes = Formula.FINDRESULTINDEX(database, criteria);
 1975-      var targetFields = [];
 1976-      if (typeof field === "string") {
 1977-        var index = Formula.FINDFIELD(database, field);
 1978-        targetFields = _.rest(database[index]);
 1979-      } else {
 1980-        targetFields = _.rest(database[field]);
 1981-      }
 1982-      var targetValues = [];
 1983-      for (var i = 0; i < resultIndexes.length; i++) {
 1984-        targetValues[i] = targetFields[resultIndexes[i]];
 1985-      }
 1986-      targetValues = _.compact(targetValues);
 1987-      return Formula.STDEVS(targetValues);
 1988-    };
 1989-
 1990-    Formula.DSTDEVP = function(database, field, criteria) {
 1991-      // Return error if field is not a number and not a string
 1992-      if (isNaN(field) && (typeof field !== "string")) {
 1993-        return '#VALUE!';
 1994-      }
 1995-      var resultIndexes = Formula.FINDRESULTINDEX(database, criteria);
 1996-      var targetFields = [];
 1997-      if (typeof field === "string") {
 1998-        var index = Formula.FINDFIELD(database, field);
 1999-        targetFields = _.rest(database[index]);
 2000-      } else {
 2001-        targetFields = _.rest(database[field]);
 2002-      }
 2003-      var targetValues = [];
 2004-      for (var i = 0; i < resultIndexes.length; i++) {
 2005-        targetValues[i] = targetFields[resultIndexes[i]];
 2006-      }
 2007-      targetValues = _.compact(targetValues);
 2008-      return Formula.STDEVP(targetValues);
 2009-    };
 2010-
 2011-    Formula.DSUM = function(database, field, criteria) {
 2012-      // Return error if field is not a number and not a string
 2013-      if (isNaN(field) && (typeof field !== "string")) {
 2014-        return '#VALUE!';
 2015-      }
 2016-      var resultIndexes = Formula.FINDRESULTINDEX(database, criteria);
 2017-      var targetFields = [];
 2018-      if (typeof field === "string") {
 2019-        var index = Formula.FINDFIELD(database, field);
 2020-        targetFields = _.rest(database[index]);
 2021-      } else {
 2022-        targetFields = _.rest(database[field]);
 2023-      }
 2024-      var targetValues = [];
 2025-      for (var i = 0; i < resultIndexes.length; i++) {
 2026-        targetValues[i] = targetFields[resultIndexes[i]];
 2027-      }
 2028-      return Formula.SUM(targetValues);
 2029-    };
 2030-
 2031-    Formula.DVAR = function(database, field, criteria) {
 2032-      // Return error if field is not a number and not a string
 2033-      if (isNaN(field) && (typeof field !== "string")) {
 2034-        return '#VALUE!';
 2035-      }
 2036-      var resultIndexes = Formula.FINDRESULTINDEX(database, criteria);
 2037-      var targetFields = [];
 2038-      if (typeof field === "string") {
 2039-        var index = Formula.FINDFIELD(database, field);
 2040-        targetFields = _.rest(database[index]);
 2041-      } else {
 2042-        targetFields = _.rest(database[field]);
 2043-      }
 2044-      var targetValues = [];
 2045-      for (var i = 0; i < resultIndexes.length; i++) {
 2046-        targetValues[i] = targetFields[resultIndexes[i]];
 2047-      }
 2048-      return Formula.VARS(targetValues);
 2049-    };
 2050-
 2051-    Formula.DVARP = function(database, field, criteria) {
 2052-      // Return error if field is not a number and not a string
 2053-      if (isNaN(field) && (typeof field !== "string")) {
 2054-        return '#VALUE!';
 2055-      }
 2056-      var resultIndexes = Formula.FINDRESULTINDEX(database, criteria);
 2057-      var targetFields = [];
 2058-      if (typeof field === "string") {
 2059-        var index = Formula.FINDFIELD(database, field);
 2060-        targetFields = _.rest(database[index]);
 2061-      } else {
 2062-        targetFields = _.rest(database[field]);
 2063-      }
 2064-      var targetValues = [];
 2065-      for (var i = 0; i < resultIndexes.length; i++) {
 2066-        targetValues[i] = targetFields[resultIndexes[i]];
 2067-      }
 2068-      return Formula.VARP(targetValues);
 2069-    };
 2070-
 2071-    Formula.GETJSON = function (file) {
 2072-      var request = new XMLHttpRequest();
 2073-      request.open('GET', file, false);
 2074-      request.send(null);
 2075-      if (request.status === 200) {
 2076-        return JSON.parse(request.responseText);
 2077-      }
 2078-    };
 2079-
 2080-
 2081-    // Date functions
 2082-    Formula.DATE = function () {
 2083-      if (!arguments.length) {
 2084-        return new Date();
 2085-      }
 2086-
 2087-      if (arguments.length === 1) {
 2088-        return new Date(arguments[0]);
 2089-      }
 2090-
 2091-      var args = arguments;
 2092-      args[1] = args[1] - 1; // Monthes are between 0 and 11.
 2093-      return new (Date.bind.apply(Date, [Date].concat([].splice.call(args, 0))))();
 2094-    };
 2095-
 2096-    Formula.DATEVALUE = function (date_text) {
 2097-      return Math.ceil((moment(date_text) - moment('1900-1-1')) / 86400000) + 2;
 2098-    };
 2099-
 2100-    Formula.DAY = function (date) {
 2101-      return new Date(date).getDate();
 2102-    };
 2103-
 2104-    Formula.DAYS = function (end_date, start_date) {
 2105-      return moment(new Date(end_date)).diff(moment(new Date(start_date)), 'days');
 2106-    };
 2107-
 2108-    Formula.DAYS360 = function (start_date, end_date, method) {
 2109-      var start = moment(new Date(start_date));
 2110-      var end = moment(new Date(end_date));
 2111-      var smd = 31;
 2112-      var emd = 31;
 2113-      var sd = start.date();
 2114-      var ed = end.date();
 2115-      if (method) {
 2116-        sd = (sd === 31) ? 30 : sd;
 2117-        ed = (ed === 31) ? 30 : ed;
 2118-      }
 2119-      else {
 2120-        if (start.month() === 1) {
 2121-          smd = start.daysInMonth();
 2122-        }
 2123-        if (end.month() === 1) {
 2124-          emd = end.daysInMonth();
 2125-        }
 2126-        sd = (sd === smd) ? 30 : sd;
 2127-        if (sd === 30 || sd === smd) {
 2128-          ed = (ed === emd) ? 30 : ed;
 2129-        }
 2130-      }
 2131-      return 360 * (end.year() - start.year()) + 30 * (end.month() - start.month()) + (ed - sd);
 2132-    };
 2133-
 2134-    Formula.EDATE = function (start_date, months) {
 2135-      return moment(new Date(start_date)).add('months', months).toDate();
 2136-    };
 2137-
 2138-    Formula.EOMONTH = function (start_date, months) {
 2139-      var edate = moment(new Date(start_date)).add('months', months);
 2140-      return new Date(edate.year(), edate.month(), edate.daysInMonth());
 2141-    };
 2142-
 2143-    Formula.FROMNOW = function (timestamp, nosuffix) {
 2144-      return moment(new Date(timestamp)).fromNow(nosuffix);
 2145-    };
 2146-
 2147-    Formula.HOUR = function (timestamp) {
 2148-      return (timestamp <= 1) ? Math.floor(24 * timestamp) : new Date(timestamp).getHours();
 2149-    };
 2150-
 2151-    Formula.MINUTE = function (timestamp) {
 2152-      return (timestamp <= 1) ? Math.floor(24 * 60 * timestamp) - 60 * Math.floor(24 * timestamp) : new Date(timestamp).getMinutes();
 2153-    };
 2154-
 2155-    Formula.ISOWEEKNUM = function (date) {
 2156-      return moment(new Date(date)).format('w');
 2157-    };
 2158-
 2159-    Formula.MONTH = function (timestamp) {
 2160-      return new Date(timestamp).getMonth() + 1;
 2161-    };
 2162-
 2163-    Formula.NETWORKDAYS = function (start_date, end_date, holidays) {
 2164-      return Formula.NETWORKDAYSINTL(start_date, end_date, 1, holidays);
 2165-    };
 2166-
 2167-    Formula.NETWORKDAYSINTL = function (start_date, end_date, weekend, holidays) {
 2168-      var weekend_type = (typeof weekend === 'undefined') ? 1 : weekend;
 2169-      var weekend_days = WEEKEND_TYPES[weekend_type];
 2170-      var sd = moment(start_date);
 2171-      var ed = moment(end_date);
 2172-      var net_days = ed.diff(sd, 'days') + 1;
 2173-      var net_work_days = net_days;
 2174-      var cd = sd;
 2175-      var holiday_dates = [];
 2176-      if (typeof holidays !== 'undefined') {
 2177-        for (var i = 0; i < holidays.length; i++) {
 2178-          holiday_dates[i] = moment(new Date(holidays[i])).format('MM-DD-YYYY');
 2179-        }
 2180-      }
 2181-
 2182-      if (!weekend_days.length && !holiday_dates.length) {
 2183-        // No need to loop here.
 2184-        return net_work_days;
 2185-      }
 2186-      var j = 0;
 2187-      while (j < net_days) {
 2188-        if (weekend_days.indexOf(parseInt(cd.format('d'), 10)) >= 0) {
 2189-          net_work_days--;
 2190-        } else if (holiday_dates.indexOf(cd.format('MM-DD-YYYY')) >= 0) {
 2191-          net_work_days--;
 2192-        }
 2193-        cd = cd.add('days', 1);
 2194-        j++;
 2195-      }
 2196-      return net_work_days;
 2197-    };
 2198-
 2199-    Formula.NOW = function () {
 2200-      return new Date();
 2201-    };
 2202-
 2203-    Formula.SECOND = function (timestamp) {
 2204-      return new Date(timestamp).getSeconds();
 2205-    };
 2206-
 2207-    Formula.TIME = function (hour, minute, second) {
 2208-      return (3600 * hour + 60 * minute + second) / 86400;
 2209-    };
 2210-
 2211-    Formula.TIMEVALUE = function (time_text) {
 2212-      var timestamp = new Date(time_text);
 2213-      return (3600 * timestamp.getHours() + 60 * timestamp.getMinutes() + timestamp.getSeconds()) / 86400;
 2214-    };
 2215-
 2216-    Formula.TODAY = Formula.NOW;
 2217-
 2218-    Formula.WEEKDAY = function (date, type) {
 2219-      var week_day = moment(new Date(date)).format('d');
 2220-      var week_type = (typeof type === 'undefined') ? 1 : type;
 2221-      return WEEK_TYPES[week_type][week_day];
 2222-    };
 2223-
 2224-    Formula.WEEKNUM = function (date, type) {
 2225-      var current_date = moment(new Date(date));
 2226-      var january_first = moment(new Date(current_date.year(), 0, 1));
 2227-      var week_type = (typeof type === 'undefined') ? 1 : type;
 2228-      var week_start = WEEK_STARTS[week_type];
 2229-      var first_day = january_first.format('d');
 2230-      var offset = (first_day < week_start) ? week_start - first_day + 1 : first_day - week_start;
 2231-      if (week_type === 21) {
 2232-        return Formula.ISOWEEKNUM(date);
 2233-      } else {
 2234-        return Math.floor(current_date.diff(january_first.subtract('days', offset), 'days') / 7) + 1;
 2235-      }
 2236-    };
 2237-
 2238-    Formula.WORKDAY = function (start_date, days, holidays) {
 2239-      return Formula.WORKDAYINTL(start_date, days, 1, holidays);
 2240-    };
 2241-
 2242-    Formula.WORKDAYINTL = function (start_date, days, weekend, holidays) {
 2243-      var weekend_type = (typeof weekend === 'undefined') ? 1 : weekend;
 2244-      var weekend_days = WEEKEND_TYPES[weekend_type];
 2245-      var sd = moment(new Date(start_date));
 2246-      var cd = sd;
 2247-      var day_of_week = '';
 2248-      var holiday_dates = [];
 2249-      if (typeof holidays !== 'undefined') {
 2250-        for (var i = 0; i < holidays.length; i++) {
 2251-          holiday_dates[i] = moment(new Date(holidays[i])).format('MM-DD-YYYY');
 2252-        }
 2253-      }
 2254-      var j = 0;
 2255-      while (j < days) {
 2256-        cd = cd.add('days', 1);
 2257-        day_of_week = cd.format('d');
 2258-        if (weekend_days.indexOf(parseInt(day_of_week, 10)) < 0 && holiday_dates.indexOf(cd.format('MM-DD-YYYY')) < 0) {
 2259-          j++;
 2260-        }
 2261-      }
 2262-      return cd.toDate();
 2263-    };
 2264-
 2265-    Formula.YEAR = function (date) {
 2266-      return new Date(date).getFullYear();
 2267-    };
 2268-
 2269-    Formula.YEARFRAC = function (start_date, end_date, basis) {
 2270-      // Credits: David A. Wheeler [http://www.dwheeler.com/]
 2271-
 2272-      // Initialize parameters
 2273-      basis = (typeof basis === 'undefined') ? 0 : basis;
 2274-      var sdate = moment(new Date(start_date));
 2275-      var edate = moment(new Date(end_date));
 2276-
 2277-      // Return error if either date is invalid
 2278-      if (!sdate.isValid() || !edate.isValid()) {
 2279-        return '#VALUE!';
 2280-      }
 2281-
 2282-      // Return error if basis is neither 0, 1, 2, 3, or 4
 2283-      if ([0, 1, 2, 3, 4].indexOf(basis) === -1) {
 2284-        return '#NUM!';
 2285-      }
 2286-
 2287-      // Return zero if start_date and end_date are the same
 2288-      if (sdate === edate) {
 2289-        return 0;
 2290-      }
 2291-
 2292-      // Swap dates if start_date is later than end_date
 2293-      if (sdate.diff(edate) > 0) {
 2294-        edate = moment(new Date(start_date));
 2295-        sdate = moment(new Date(end_date));
 2296-      }
 2297-
 2298-      // Lookup years, months, and days
 2299-      var syear = sdate.year();
 2300-      var smonth = sdate.month();
 2301-      var sday = sdate.date();
 2302-      var eyear = edate.year();
 2303-      var emonth = edate.month();
 2304-      var eday = edate.date();
 2305-
 2306-      switch (basis) {
 2307-        case 0:
 2308-          // US (NASD) 30/360
 2309-          // Note: if eday == 31, it stays 31 if sday < 30
 2310-          if (sday === 31 && eday === 31) {
 2311-            sday = 30;
 2312-            eday = 30;
 2313-          } else if (sday === 31) {
 2314-            sday = 30;
 2315-          } else if (sday === 30 && eday === 31) {
 2316-            eday = 30;
 2317-          } else if (smonth === 1 && emonth === 1 && sdate.daysInMonth() === sday && edate.daysInMonth() === eday) {
 2318-            sday = 30;
 2319-            eday = 30;
 2320-          } else if (smonth === 1 && sdate.daysInMonth() === sday) {
 2321-            sday = 30;
 2322-          }
 2323-          return ((eday + emonth * 30 + eyear * 360) - (sday + smonth * 30 + syear * 360)) / 360;
 2324-
 2325-        case 1:
 2326-          // Actual/actual
 2327-          var feb29Between = function (date1, date2) {
 2328-            // Requires year2 == (year1 + 1) or year2 == year1
 2329-            // Returns TRUE if February 29 is between the two dates (date1 may be February 29), with two possibilities:
 2330-            // year1 is a leap year and date1 <= Februay 29 of year1
 2331-            // year2 is a leap year and date2 > Februay 29 of year2
 2332-
 2333-            var mar1year1 = moment(new Date(date1.year(), 2, 1));
 2334-            if (moment([date1.year()]).isLeapYear() && date1.diff(mar1year1) < 0 && date2.diff(mar1year1) >= 0) {
 2335-              return true;
 2336-            }
 2337-            var mar1year2 = moment(new Date(date2.year(), 2, 1));
 2338-            if (moment([date2.year()]).isLeapYear() && date2.diff(mar1year2) >= 0 && date1.diff(mar1year2) < 0) {
 2339-              return true;
 2340-            }
 2341-            return false;
 2342-          };
 2343-          var ylength = 365;
 2344-          if (syear === eyear || ((syear + 1) === eyear) && ((smonth > emonth) || ((smonth === emonth) && (sday >= eday)))) {
 2345-            if (syear === eyear && moment([syear]).isLeapYear()) {
 2346-              ylength = 366;
 2347-            } else if (feb29Between(sdate, edate) || (emonth === 1 && eday === 29)) {
 2348-              ylength = 366;
 2349-            }
 2350-            return edate.diff(sdate, 'days') / ylength;
 2351-          } else {
 2352-            var years = (eyear - syear) + 1;
 2353-            var days = moment(new Date(eyear + 1, 0, 1)).diff(moment(new Date(syear, 0, 1)), 'days');
 2354-            var average = days / years;
 2355-            return edate.diff(sdate, 'days') / average;
 2356-          }
 2357-          break;
 2358-
 2359-        case 2:
 2360-          // Actual/360
 2361-          return edate.diff(sdate, 'days') / 360;
 2362-
 2363-        case 3:
 2364-          // Actual/365
 2365-          return edate.diff(sdate, 'days') / 365;
 2366-
 2367-        case 4:
 2368-          // European 30/360
 2369-          if (sday === 31) {
 2370-            sday = 30;
 2371-          }
 2372-
 2373-          if (eday === 31) {
 2374-            eday = 30;
 2375-          }
 2376-          // Remarkably, do NOT change February 28 or February 29 at ALL
 2377-          return ((eday + emonth * 30 + eyear * 360) - (sday + smonth * 30 + syear * 360)) / 360;
 2378-      }
 2379-    };
 2380-
 2381-    // Engineering functions
 2382-
 2383-    // This function is extracted from the source code of SheetJS/bessel:
 2384-    // https://github.com/SheetJS/bessel/blob/master/bessel.js#L144
 2385-    Formula.BESSELI = (function() {
 2386-      function horner(arr, v) {
 2387-        return arr.reduce(function(z, w) {
 2388-          return v*z + w;
 2389-        }, 0);
 2390-      }
 2391-      var b0_a = [1.0, 3.5156229, 3.0899424, 1.2067492, 0.2659732, 0.360768e-1, 0.45813e-2].reverse();
 2392-      var b0_b = [0.39894228, 0.1328592e-1, 0.225319e-2, -0.157565e-2, 0.916281e-2, -0.2057706e-1, 0.2635537e-1, -0.1647633e-1, 0.392377e-2].reverse();
 2393-      function bessel0(x) {
 2394-        if(x <= 3.75) {
 2395-          return horner(b0_a, x*x/(3.75*3.75));
 2396-        }
 2397-        return Math.exp(Math.abs(x))/Math.sqrt(Math.abs(x))*horner(b0_b, 3.75/Math.abs(x));
 2398-      }
 2399-
 2400-      var b1_a = [0.5, 0.87890594, 0.51498869, 0.15084934, 0.2658733e-1, 0.301532e-2, 0.32411e-3].reverse();
 2401-      var b1_b = [0.39894228, -0.3988024e-1, -0.362018e-2, 0.163801e-2, -0.1031555e-1, 0.2282967e-1, -0.2895312e-1, 0.1787654e-1, -0.420059e-2].reverse();
 2402-      function bessel1(x) {
 2403-        if(x < 3.75) {
 2404-          return x * horner(b1_a, x*x/(3.75*3.75));
 2405-        }
 2406-        return (x < 0 ? -1 : 1) * Math.exp(Math.abs(x))/Math.sqrt(Math.abs(x))*horner(b1_b, 3.75/Math.abs(x));
 2407-      }
 2408-
 2409-      return function besseli(x, n) {
 2410-        n = Math.round(n);
 2411-        if(n === 0) {
 2412-          return bessel0(x);
 2413-        }
 2414-        if(n === 1) {
 2415-          return bessel1(x);
 2416-        }
 2417-        if(n < 0) {
 2418-          throw 'BESSELI Order (' + n + ') must be nonnegative';
 2419-        }
 2420-        if(Math.abs(x) === 0) {
 2421-          return 0;
 2422-        }
 2423-
 2424-        var ret, j, tox = 2 / Math.abs(x), m, bip, bi, bim;
 2425-        m=2*Math.round((n+Math.round(Math.sqrt(40*n)))/2);
 2426-        bip=ret=0.0;
 2427-        bi=1.0;
 2428-        for (j=m;j>0;j--) {
 2429-          bim=j*tox*bi + bip;
 2430-          bip=bi; bi=bim;
 2431-          if (Math.abs(bi) > 1E10) {
 2432-            bi *= 1E-10;
 2433-            bip *= 1E-10;
 2434-            ret *= 1E-10;
 2435-          }
 2436-          if(j === n) {
 2437-            ret = bip;
 2438-          }
 2439-        }
 2440-        ret *= besseli(x, 0) / bi;
 2441-        return x < 0 && (n%2) ? -ret : ret;
 2442-      };
 2443-
 2444-    })();
 2445-
 2446-    // This function is extracted from the source code of SheetJS/bessel:
 2447-    // https://github.com/SheetJS/bessel/blob/master/bessel.js#L25
 2448-    Formula.BESSELJ = (function() {
 2449-      function horner(arr, v) {
 2450-        return arr.reduce(function(z, w) {
 2451-          return v*z + w;
 2452-        }, 0);
 2453-      }
 2454-      var b0_a1a = [57568490574.0,-13362590354.0,651619640.7,-11214424.18,77392.33017,-184.9052456].reverse();
 2455-      var b0_a2a = [57568490411.0,1029532985.0,9494680.718,59272.64853,267.8532712,1.0].reverse();
 2456-      var b0_a1b = [1.0, -0.1098628627e-2, 0.2734510407e-4, -0.2073370639e-5, 0.2093887211e-6].reverse();
 2457-      var b0_a2b = [-0.1562499995e-1, 0.1430488765e-3, -0.6911147651e-5, 0.7621095161e-6, -0.934935152e-7].reverse();
 2458-      var W = 0.636619772; // 2 / Math.PI
 2459-
 2460-      function bessel0(x) {
 2461-        var a, a1, a2, y = x * x, xx = Math.abs(x) - 0.785398164;
 2462-        if(Math.abs(x) < 8) {
 2463-          a1 = horner(b0_a1a, y);
 2464-          a2 = horner(b0_a2a, y);
 2465-          a = a1/a2;
 2466-        }
 2467-        else {
 2468-          y = 64 / y;
 2469-          a1 = horner(b0_a1b, y);
 2470-          a2 = horner(b0_a2b, y);
 2471-          a = Math.sqrt(W/Math.abs(x))*(Math.cos(xx)*a1-Math.sin(xx)*a2*8/Math.abs(x));
 2472-        }
 2473-        return a;
 2474-      }
 2475-      var b1_a1a = [72362614232.0,-7895059235.0,242396853.1,-2972611.439, 15704.48260, -30.16036606].reverse();
 2476-      var b1_a2a = [144725228442.0, 2300535178.0, 18583304.74, 99447.43394, 376.9991397, 1.0].reverse();
 2477-      var b1_a1b = [1.0, 0.183105e-2, -0.3516396496e-4, 0.2457520174e-5, -0.240337019e-6].reverse();
 2478-      var b1_a2b = [0.04687499995, -0.2002690873e-3, 0.8449199096e-5, -0.88228987e-6, 0.105787412e-6].reverse();
 2479-      function bessel1(x) {
 2480-        var a, a1, a2, y = x*x, xx = Math.abs(x) - 2.356194491;
 2481-        if(Math.abs(x)< 8) {
 2482-          a1 = x*horner(b1_a1a, y);
 2483-          a2 = horner(b1_a2a, y);
 2484-          a = a1 / a2;
 2485-        } else {
 2486-          y = 64 / y;
 2487-          a1=horner(b1_a1b, y);
 2488-          a2=horner(b1_a2b, y);
 2489-          a=Math.sqrt(W/Math.abs(x))*(Math.cos(xx)*a1-Math.sin(xx)*a2*8/Math.abs(x));
 2490-          if(x < 0) {
 2491-            a = -a;
 2492-          }
 2493-        }
 2494-        return a;
 2495-      }
 2496-
 2497-      function _bessel_iter(x, n, f0, f1, sign) {
 2498-        if(!sign) {
 2499-          sign = -1;
 2500-        }
 2501-        var tdx = 2 / x, f2;
 2502-        if(n === 0) {
 2503-          return f0;
 2504-        }
 2505-        if(n === 1) {
 2506-          return f1;
 2507-        }
 2508-        for(var o = 1; o !== n; ++o) {
 2509-          f2 = f1 * o * tdx + sign * f0;
 2510-          f0 = f1; f1 = f2;
 2511-        }
 2512-        return f1;
 2513-      }
 2514-
 2515-      return function besselj(x, n) {
 2516-        n = Math.round(n);
 2517-        if(n === 0) {
 2518-          return bessel0(Math.abs(x));
 2519-        }
 2520-        if(n === 1) {
 2521-          return bessel1(Math.abs(x));
 2522-        }
 2523-        if(n < 0) {
 2524-          throw 'BESSELJ: Order (' + n + ') must be nonnegative';
 2525-        }
 2526-        if(Math.abs(x) === 0) {
 2527-          return 0;
 2528-        }
 2529-
 2530-        var ret, j, tox = 2 / Math.abs(x), m, jsum, sum, bjp, bj, bjm;
 2531-        if(Math.abs(x) > n) {
 2532-          ret = _bessel_iter(x, n, bessel0(Math.abs(x)), bessel1(Math.abs(x)),-1);
 2533-        } else {
 2534-          m=2*Math.floor((n+Math.floor(Math.sqrt(40*n)))/2);
 2535-          jsum=0;
 2536-          bjp=ret=sum=0.0;
 2537-          bj=1.0;
 2538-          for (j=m;j>0;j--) {
 2539-            bjm=j*tox*bj-bjp;
 2540-            bjp=bj;
 2541-            bj=bjm;
 2542-            if (Math.abs(bj) > 1E10) {
 2543-              bj *= 1E-10;
 2544-              bjp *= 1E-10;
 2545-              ret *= 1E-10;
 2546-              sum *= 1E-10;
 2547-            }
 2548-            if (jsum) {
 2549-              sum += bj;
 2550-            }
 2551-            jsum=!jsum;
 2552-            if (j === n) {
 2553-              ret=bjp;
 2554-            }
 2555-          }
 2556-          sum=2.0*sum-bj;
 2557-          ret /= sum;
 2558-        }
 2559-        return x < 0 && (n%2) ? -ret : ret;
 2560-      };
 2561-    })();
 2562-
 2563-    // This function is extracted from the source code of SheetJS/bessel:
 2564-    // https://github.com/SheetJS/bessel/blob/master/bessel.js#L186
 2565-    Formula.BESSELK = (function() {
 2566-      function horner(arr, v) {
 2567-        return arr.reduce(function(z, w) {
 2568-          return v*z + w;
 2569-        }, 0);
 2570-      }
 2571-      var b0_a = [-0.57721566, 0.42278420, 0.23069756, 0.3488590e-1, 0.262698e-2, 0.10750e-3, 0.74e-5].reverse();
 2572-      var b0_b = [1.25331414, -0.7832358e-1, 0.2189568e-1, -0.1062446e-1, 0.587872e-2, -0.251540e-2, 0.53208e-3].reverse();
 2573-      function bessel0(x) {
 2574-        if(x <= 2) {
 2575-          return -Math.log(x/2)*Formula.BESSELI(x,0) + horner(b0_a,x*x/4);
 2576-        }
 2577-        return Math.exp(-x)/Math.sqrt(x)*horner(b0_b,2/x);
 2578-      }
 2579-
 2580-      var b1_a = [1.0, 0.15443144, -0.67278579, -0.18156897, -0.1919402e-1, -0.110404e-2, -0.4686e-4].reverse();
 2581-      var b1_b = [1.25331414, 0.23498619, -0.3655620e-1, 0.1504268e-1, -0.780353e-2, 0.325614e-2, -0.68245e-3].reverse();
 2582-      function bessel1(x) {
 2583-        if(x <= 2) {
 2584-          return Math.log(x/2)*Formula.BESSELI(x,1) + (1/x)*horner(b1_a,x*x/4);
 2585-        }
 2586-        return Math.exp(-x)/Math.sqrt(x)*horner(b1_b,2/x);
 2587-      }
 2588-
 2589-      function _bessel_iter(x, n, f0, f1, sign) {
 2590-        if(!sign) {
 2591-          sign = -1;
 2592-        }
 2593-        var tdx = 2 / x, f2;
 2594-        if(n === 0) {
 2595-          return f0;
 2596-        }
 2597-        if(n === 1) {
 2598-          return f1;
 2599-        }
 2600-        for(var o = 1; o !== n; ++o) {
 2601-          f2 = f1 * o * tdx + sign * f0;
 2602-          f0 = f1; f1 = f2;
 2603-        }
 2604-        return f1;
 2605-      }
 2606-
 2607-      function _bessel_wrap(bessel0, bessel1, name, nonzero, sign) {
 2608-        return function bessel(x,n) {
 2609-          if(n === 0) {
 2610-            return bessel0(x);
 2611-          }
 2612-          if(n === 1) {
 2613-            return bessel1(x);
 2614-          }
 2615-          if(n < 0) {
 2616-            throw name + ': Order (' + n + ') must be nonnegative';
 2617-          }
 2618-          if(nonzero === 1 && x === 0) {
 2619-            throw name + ': Undefined when x == 0';
 2620-          }
 2621-          if(nonzero === 2 && x <= 0) {
 2622-            throw name + ': Undefined when x <= 0';
 2623-          }
 2624-          var b0 = bessel0(x), b1 = bessel1(x);
 2625-          return _bessel_iter(x, n, b0, b1, sign);
 2626-        };
 2627-      }
 2628-
 2629-      return _bessel_wrap(bessel0, bessel1, 'BESSELK', 2, 1);
 2630-    })();
 2631-
 2632-    // This function is extracted from the source code of SheetJS/bessel:
 2633-    // https://github.com/SheetJS/bessel/blob/master/bessel.js#L101
 2634-    Formula.BESSELY = (function() {
 2635-      function horner(arr, v) {
 2636-        return arr.reduce(function(z, w) {
 2637-          return v*z + w;
 2638-        }, 0);
 2639-      }
 2640-      var b0_a1a = [-2957821389.0, 7062834065.0, -512359803.6, 10879881.29, -86327.92757, 228.4622733].reverse();
 2641-      var b0_a2a = [40076544269.0, 745249964.8, 7189466.438, 47447.26470, 226.1030244, 1.0].reverse();
 2642-      var b0_a1b = [1.0, -0.1098628627e-2, 0.2734510407e-4, -0.2073370639e-5, 0.2093887211e-6].reverse();
 2643-      var b0_a2b = [-0.1562499995e-1, 0.1430488765e-3, -0.6911147651e-5, 0.7621095161e-6, -0.934945152e-7].reverse();
 2644-
 2645-      var W = 0.636619772;
 2646-      function bessel0(x) {
 2647-        var a, a1, a2, y = x * x, xx = x - 0.785398164;
 2648-        if(x < 8) {
 2649-          a1 = horner(b0_a1a, y);
 2650-          a2 = horner(b0_a2a, y);
 2651-          a = a1/a2 + W * Formula.BESSELJ(x,0) * Math.log(x);
 2652-        } else {
 2653-          y = 64 / y;
 2654-          a1 = horner(b0_a1b, y);
 2655-          a2 = horner(b0_a2b, y);
 2656-          a = Math.sqrt(W/x)*(Math.sin(xx)*a1+Math.cos(xx)*a2*8/x);
 2657-        }
 2658-        return a;
 2659-      }
 2660-
 2661-      var b1_a1a = [-0.4900604943e13, 0.1275274390e13, -0.5153438139e11, 0.7349264551e9, -0.4237922726e7, 0.8511937935e4].reverse();
 2662-      var b1_a2a = [0.2499580570e14, 0.4244419664e12, 0.3733650367e10, 0.2245904002e8, 0.1020426050e6, 0.3549632885e3, 1].reverse();
 2663-      var b1_a1b = [1.0, 0.183105e-2, -0.3516396496e-4, 0.2457520174e-5, -0.240337019e-6].reverse();
 2664-      var b1_a2b = [0.04687499995, -0.2002690873e-3, 0.8449199096e-5, -0.88228987e-6, 0.105787412e-6].reverse();
 2665-      function bessel1(x) {
 2666-        var a, a1, a2, y = x*x, xx = x - 2.356194491;
 2667-        if(x < 8) {
 2668-          a1 = x*horner(b1_a1a, y);
 2669-          a2 = horner(b1_a2a, y);
 2670-          a = a1/a2 + W * (Formula.BESSELJ(x,1) * Math.log(x) - 1 / x);
 2671-        } else {
 2672-          y = 64 / y;
 2673-          a1=horner(b1_a1b, y);
 2674-          a2=horner(b1_a2b, y);
 2675-          a=Math.sqrt(W/x)*(Math.sin(xx)*a1+Math.cos(xx)*a2*8/x);
 2676-        }
 2677-        return a;
 2678-      }
 2679-
 2680-      function _bessel_iter(x, n, f0, f1, sign) {
 2681-        if(!sign) {
 2682-          sign = -1;
 2683-        }
 2684-        var tdx = 2 / x, f2;
 2685-        if(n === 0) {
 2686-          return f0;
 2687-        }
 2688-        if(n === 1) {
 2689-          return f1;
 2690-        }
 2691-        for(var o = 1; o !== n; ++o) {
 2692-          f2 = f1 * o * tdx + sign * f0;
 2693-          f0 = f1; f1 = f2;
 2694-        }
 2695-        return f1;
 2696-      }
 2697-
 2698-      function _bessel_wrap(bessel0, bessel1, name, nonzero, sign) {
 2699-        return function bessel(x,n) {
 2700-          if(n === 0) {
 2701-            return bessel0(x);
 2702-          }
 2703-          if(n === 1) {
 2704-            return bessel1(x);
 2705-          }
 2706-          if(n < 0) {
 2707-            throw name + ': Order (' + n + ') must be nonnegative';
 2708-          }
 2709-          if(nonzero === 1 && x === 0) {
 2710-            throw name + ': Undefined when x == 0';
 2711-          }
 2712-          if(nonzero === 2 && x <= 0) {
 2713-            throw name + ': Undefined when x <= 0';
 2714-          }
 2715-          var b0 = bessel0(x), b1 = bessel1(x);
 2716-          return _bessel_iter(x, n, b0, b1, sign);
 2717-        };
 2718-      }
 2719-
 2720-      return _bessel_wrap(bessel0, bessel1, 'BESSELY', 1, -1);
 2721-    })();
 2722-
 2723-    Formula.VALIDBIN = function (number) {
 2724-      return (/^[01]{1,10}$/).test(number);
 2725-    };
 2726-
 2727-    Formula.BIN2DEC = function (number) {
 2728-      // Return error if number is not binary or contains more than 10 characters (10 digits)
 2729-      if (!Formula.VALIDBIN(number)) {
 2730-        return '#NUM!';
 2731-      }
 2732-
 2733-      // Convert binary number to decimal
 2734-      var result = parseInt(number, 2);
 2735-
 2736-      // Handle negative numbers
 2737-      var stringified = number.toString();
 2738-      if (stringified.length === 10 && stringified.substring(0, 1) === '1') {
 2739-        return parseInt(stringified.substring(1), 2) - 512;
 2740-      } else {
 2741-        return result;
 2742-      }
 2743-    };
 2744-
 2745-    Formula.BIN2HEX = function (number, places) {
 2746-      // Return error if number is not binary or contains more than 10 characters (10 digits)
 2747-      if (!Formula.VALIDBIN(number)) {
 2748-        return '#NUM!';
 2749-      }
 2750-
 2751-      // Ignore places and return a 10-character hexadecimal number if number is negative
 2752-      var stringified = number.toString();
 2753-      if (stringified.length === 10 && stringified.substring(0, 1) === '1') {
 2754-        return (1099511627264 + parseInt(stringified.substring(1), 2)).toString(16);
 2755-      }
 2756-
 2757-      // Convert binary number to hexadecimal
 2758-      var result = parseInt(number, 2).toString(16);
 2759-
 2760-      // Return hexadecimal number using the minimum number of characters necessary if places is undefined
 2761-      if (typeof places === 'undefined') {
 2762-        return result;
 2763-      } else {
 2764-        // Return error if places is nonnumeric
 2765-        if (isNaN(places)) {
 2766-          return '#VALUE!';
 2767-        }
 2768-
 2769-        // Return error if places is negative
 2770-        if (places < 0) {
 2771-          return '#NUM!';
 2772-        }
 2773-
 2774-        // Truncate places in case it is not an integer
 2775-        places = Math.floor(places);
 2776-
 2777-        // Pad return value with leading 0s (zeros) if necessary (using Underscore.string)
 2778-        return (places >= result.length) ? _s.repeat('0', places - result.length) + result : '#NUM!';
 2779-      }
 2780-    };
 2781-
 2782-    Formula.BIN2OCT = function (number, places) {
 2783-      // Return error if number is not binary or contains more than 10 characters (10 digits)
 2784-      if (!Formula.VALIDBIN(number)) {
 2785-        return '#NUM!';
 2786-      }
 2787-
 2788-      // Ignore places and return a 10-character octal number if number is negative
 2789-      var stringified = number.toString();
 2790-      if (stringified.length === 10 && stringified.substring(0, 1) === '1') {
 2791-        return (1073741312 + parseInt(stringified.substring(1), 2)).toString(8);
 2792-      }
 2793-
 2794-      // Convert binary number to octal
 2795-      var result = parseInt(number, 2).toString(8);
 2796-
 2797-      // Return octal number using the minimum number of characters necessary if places is undefined
 2798-      if (typeof places === 'undefined') {
 2799-        return result;
 2800-      } else {
 2801-        // Return error if places is nonnumeric
 2802-        if (isNaN(places)) {
 2803-          return '#VALUE!';
 2804-        }
 2805-
 2806-        // Return error if places is negative
 2807-        if (places < 0) {
 2808-          return '#NUM!';
 2809-        }
 2810-
 2811-        // Truncate places in case it is not an integer
 2812-        places = Math.floor(places);
 2813-
 2814-        // Pad return value with leading 0s (zeros) if necessary (using Underscore.string)
 2815-        return (places >= result.length) ? _s.repeat('0', places - result.length) + result : '#NUM!';
 2816-      }
 2817-    };
 2818-
 2819-    Formula.BITAND = function (number1, number2) {
 2820-      // Return error if either number is a non-numeric value
 2821-      if (isNaN(number1) || isNaN(number2)) {
 2822-        return '#VALUE!';
 2823-      }
 2824-
 2825-      // Return error if either number is less than 0
 2826-      if (number1 < 0 || number2 < 0) {
 2827-        return '#NUM!';
 2828-      }
 2829-
 2830-      // Return error if either number is a non-integer
 2831-      if (Math.floor(number1) !== number1 || Math.floor(number2) !== number2) {
 2832-        return '#NUM!';
 2833-      }
 2834-
 2835-      // Return error if either number is greater than (2^48)-1
 2836-      if (number1 > 281474976710655 || number2 > 281474976710655) {
 2837-        return '#NUM!';
 2838-      }
 2839-
 2840-      // Return bitwise AND of two numbers
 2841-      return number1 & number2;
 2842-    };
 2843-
 2844-    Formula.BITLSHIFT = function (number, shift) {
 2845-      // Return error if either number is a non-numeric value
 2846-      if (isNaN(number) || isNaN(shift)) {
 2847-        return '#VALUE!';
 2848-      }
 2849-
 2850-      // Return error if number is less than 0
 2851-      if (number < 0) {
 2852-        return '#NUM!';
 2853-      }
 2854-
 2855-      // Return error if number is a non-integer
 2856-      if (Math.floor(number) !== number) {
 2857-        return '#NUM!';
 2858-      }
 2859-
 2860-      // Return error if number is greater than (2^48)-1
 2861-      if (number > 281474976710655) {
 2862-        return '#NUM!';
 2863-      }
 2864-
 2865-      // Return error if the absolute value of shift is greater than 53
 2866-      if (Math.abs(shift) > 53) {
 2867-        return '#NUM!';
 2868-      }
 2869-
 2870-      // Return number shifted by shift bits to the left or to the right if shift is negative
 2871-      return (shift >= 0 ) ? number << shift : number >> -shift;
 2872-    };
 2873-
 2874-    Formula.BITOR = function (number1, number2) {
 2875-      // Return error if either number is a non-numeric value
 2876-      if (isNaN(number1) || isNaN(number2)) {
 2877-        return '#VALUE!';
 2878-      }
 2879-
 2880-      // Return error if either number is less than 0
 2881-      if (number1 < 0 || number2 < 0) {
 2882-        return '#NUM!';
 2883-      }
 2884-
 2885-      // Return error if either number is a non-integer
 2886-      if (Math.floor(number1) !== number1 || Math.floor(number2) !== number2) {
 2887-        return '#NUM!';
 2888-      }
 2889-
 2890-      // Return error if either number is greater than (2^48)-1
 2891-      if (number1 > 281474976710655 || number2 > 281474976710655) {
 2892-        return '#NUM!';
 2893-      }
 2894-
 2895-      // Return bitwise OR of two numbers
 2896-      return number1 | number2;
 2897-    };
 2898-
 2899-    Formula.BITRSHIFT = function (number, shift) {
 2900-      // Return error if either number is a non-numeric value
 2901-      if (isNaN(number) || isNaN(shift)) {
 2902-        return '#VALUE!';
 2903-      }
 2904-
 2905-      // Return error if number is less than 0
 2906-      if (number < 0) {
 2907-        return '#NUM!';
 2908-      }
 2909-
 2910-      // Return error if number is a non-integer
 2911-      if (Math.floor(number) !== number) {
 2912-        return '#NUM!';
 2913-      }
 2914-
 2915-      // Return error if number is greater than (2^48)-1
 2916-      if (number > 281474976710655) {
 2917-        return '#NUM!';
 2918-      }
 2919-
 2920-      // Return error if the absolute value of shift is greater than 53
 2921-      if (Math.abs(shift) > 53) {
 2922-        return '#NUM!';
 2923-      }
 2924-
 2925-      // Return number shifted by shift bits to the right or to the left if shift is negative
 2926-      return (shift >= 0 ) ? number >> shift : number << -shift;
 2927-    };
 2928-
 2929-    Formula.BITXOR = function (number1, number2) {
 2930-      // Return error if either number is a non-numeric value
 2931-      if (isNaN(number1) || isNaN(number2)) {
 2932-        return '#VALUE!';
 2933-      }
 2934-
 2935-      // Return error if either number is less than 0
 2936-      if (number1 < 0 || number2 < 0) {
 2937-        return '#NUM!';
 2938-      }
 2939-
 2940-      // Return error if either number is a non-integer
 2941-      if (Math.floor(number1) !== number1 || Math.floor(number2) !== number2) {
 2942-        return '#NUM!';
 2943-      }
 2944-
 2945-      // Return error if either number is greater than (2^48)-1
 2946-      if (number1 > 281474976710655 || number2 > 281474976710655) {
 2947-        return '#NUM!';
 2948-      }
 2949-
 2950-      // Return bitwise XOR of two numbers
 2951-      return number1 ^ number2;
 2952-    };
 2953-
 2954-    Formula.COMPLEX = function (real, imaginary, suffix) {
 2955-      // Return error if either number is a non-numeric value
 2956-      if (isNaN(real) || isNaN(imaginary)) {
 2957-        return '#VALUE!';
 2958-      }
 2959-
 2960-      // Set suffix
 2961-      suffix = (typeof suffix === 'undefined') ? 'i' : suffix;
 2962-
 2963-      // Return error if suffix is neither "i" nor "j"
 2964-      if (suffix !== 'i' && suffix !== 'j') {
 2965-        return '#VALUE!';
 2966-      }
 2967-
 2968-      // Return complex number
 2969-      if (real === 0 && imaginary === 0) {
 2970-        return 0;
 2971-      } else if (real === 0) {
 2972-        return (imaginary === 1) ? suffix : imaginary.toString() + suffix;
 2973-      } else if (imaginary === 0) {
 2974-        return real.toString();
 2975-      } else {
 2976-        var sign = (imaginary > 0) ? '+' : '';
 2977-        return real.toString() + sign + ((imaginary === 1) ? suffix : imaginary.toString() + suffix);
 2978-      }
 2979-    };
 2980-
 2981-    Formula.CONVERT = function (number, from_unit, to_unit) {
 2982-      // Return error if number is a non-numeric value
 2983-      if (isNaN(number)) {
 2984-        return '#VALUE!';
 2985-      }
 2986-
 2987-      // List of units supported by CONVERT and units defined by the International System of Units
 2988-      // [Name, Symbol, Alternate symbols, Quantity, ISU, CONVERT, Conversion ratio]
 2989-      var units = [
 2990-        ["a.u. of action", "?", null, "action", false, false, 1.05457168181818e-34],
 2991-        ["a.u. of charge", "e", null, "electric_charge", false, false, 1.60217653141414e-19],
 2992-        ["a.u. of energy", "Eh", null, "energy", false, false, 4.35974417757576e-18],
 2993-        ["a.u. of length", "a?", null, "length", false, false, 5.29177210818182e-11],
 2994-        ["a.u. of mass", "m?", null, "mass", false, false, 9.10938261616162e-31],
 2995-        ["a.u. of time", "?/Eh", null, "time", false, false, 2.41888432650516e-17],
 2996-        ["admiralty knot", "admkn", null, "speed", false, true, 0.514773333],
 2997-        ["ampere", "A", null, "electric_current", true, false, 1],
 2998-        ["ampere per meter", "A/m", null, "magnetic_field_intensity", true, false, 1],
 2999-        ["ångström", "Å", ["ang"], "length", false, true, 1e-10],
 3000-        ["are", "ar", null, "area", false, true, 100],
 3001-        ["astronomical unit", "ua", null, "length", false, false, 1.49597870691667e-11],
 3002-        ["bar", "bar", null, "pressure", false, false, 100000],
 3003-        ["barn", "b", null, "area", false, false, 1e-28],
 3004-        ["becquerel", "Bq", null, "radioactivity", true, false, 1],
 3005-        ["bit", "bit", ["b"], "information", false, true, 1],
 3006-        ["btu", "BTU", ["btu"], "energy", false, true, 1055.05585262],
 3007-        ["byte", "byte", null, "information", false, true, 8],
 3008-        ["candela", "cd", null, "luminous_intensity", true, false, 1],
 3009-        ["candela per square metre", "cd/m?", null, "luminance", true, false, 1],
 3010-        ["coulomb", "C", null, "electric_charge", true, false, 1],
 3011-        ["cubic ångström", "ang3", ["ang^3"], "volume", false, true, 1e-30],
 3012-        ["cubic foot", "ft3", ["ft^3"], "volume", false, true, 0.028316846592],
 3013-        ["cubic inch", "in3", ["in^3"], "volume", false, true, 0.000016387064],
 3014-        ["cubic light-year", "ly3", ["ly^3"], "volume", false, true, 8.46786664623715e-47],
 3015-        ["cubic metre", "m?", null, "volume", true, true, 1],
 3016-        ["cubic mile", "mi3", ["mi^3"], "volume", false, true, 4168181825.44058],
 3017-        ["cubic nautical mile", "Nmi3", ["Nmi^3"], "volume", false, true, 6352182208],
 3018-        ["cubic Pica", "Pica3", ["Picapt3", "Pica^3", "Picapt^3"], "volume", false, true, 7.58660370370369e-8],
 3019-        ["cubic yard", "yd3", ["yd^3"], "volume", false, true, 0.764554857984],
 3020-        ["cup", "cup", null, "volume", false, true, 0.0002365882365],
 3021-        ["dalton", "Da", ["u"], "mass", false, false, 1.66053886282828e-27],
 3022-        ["day", "d", ["day"], "time", false, true, 86400],
 3023-        ["degree", "°", null, "angle", false, false, 0.0174532925199433],
 3024-        ["degrees Rankine", "Rank", null, "temperature", false, true, 0.555555555555556],
 3025-        ["dyne", "dyn", ["dy"], "force", false, true, 0.00001],
 3026-        ["electronvolt", "eV", ["ev"], "energy", false, true, 1.60217656514141],
 3027-        ["ell", "ell", null, "length", false, true, 1.143],
 3028-        ["erg", "erg", ["e"], "energy", false, true, 1e-7],
 3029-        ["farad", "F", null, "electric_capacitance", true, false, 1],
 3030-        ["fluid ounce", "oz", null, "volume", false, true, 0.0000295735295625],
 3031-        ["foot", "ft", null, "length", false, true, 0.3048],
 3032-        ["foot-pound", "flb", null, "energy", false, true, 1.3558179483314],
 3033-        ["gal", "Gal", null, "acceleration", false, false, 0.01],
 3034-        ["gallon", "gal", null, "volume", false, true, 0.003785411784],
 3035-        ["gauss", "G", ["ga"], "magnetic_flux_density", false, true, 1],
 3036-        ["grain", "grain", null, "mass", false, true, 0.0000647989],
 3037-        ["gram", "g", null, "mass", false, true, 0.001],
 3038-        ["gray", "Gy", null, "absorbed_dose", true, false, 1],
 3039-        ["gross registered ton", "GRT", ["regton"], "volume", false, true, 2.8316846592],
 3040-        ["hectare", "ha", null, "area", false, true, 10000],
 3041-        ["henry", "H", null, "inductance", true, false, 1],
 3042-        ["hertz", "Hz", null, "frequency", true, false, 1],
 3043-        ["horsepower", "HP", ["h"], "power", false, true, 745.69987158227],
 3044-        ["horsepower-hour", "HPh", ["hh", "hph"], "energy", false, true, 2684519.538],
 3045-        ["hour", "h", ["hr"], "time", false, true, 3600],
 3046-        ["imperial gallon (U.K.)", "uk_gal", null, "volume", false, true, 0.00454609],
 3047-        ["imperial hundredweight", "lcwt", ["uk_cwt", "hweight"], "mass", false, true, 50.802345],
 3048-        ["imperial quart (U.K)", "uk_qt", null, "volume", false, true, 0.0011365225],
 3049-        ["imperial ton", "brton", ["uk_ton", "LTON"], "mass", false, true, 1016.046909],
 3050-        ["inch", "in", null, "length", false, true, 0.0254],
 3051-        ["international acre", "uk_acre", null, "area", false, true, 4046.8564224],
 3052-        ["IT calorie", "cal", null, "energy", false, true, 4.1868],
 3053-        ["joule", "J", null, "energy", true, true, 1],
 3054-        ["katal", "kat", null, "catalytic_activity", true, false, 1],
 3055-        ["kelvin", "K", ["kel"], "temperature", true, true, 1],
 3056-        ["kilogram", "kg", null, "mass", true, true, 1],
 3057-        ["knot", "kn", null, "speed", false, true, 0.514444444444444],
 3058-        ["light-year", "ly", null, "length", false, true, 9460730472580800],
 3059-        ["litre", "L", ["l", "lt"], "volume", false, true, 0.001],
 3060-        ["lumen", "lm", null, "luminous_flux", true, false, 1],
 3061-        ["lux", "lx", null, "illuminance", true, false, 1],
 3062-        ["maxwell", "Mx", null, "magnetic_flux", false, false, 1e-18],
 3063-        ["measurement ton", "MTON", null, "volume", false, true, 1.13267386368],
 3064-        ["meter per hour", "m/h", ["m/hr"], "speed", false, true, 0.00027777777777778],
 3065-        ["meter per second", "m/s", ["m/sec"], "speed", true, true, 1],
 3066-        ["meter per second squared", "m?s??", null, "acceleration", true, false, 1],
 3067-        ["parsec", "pc", ["parsec"], "length", false, true, 30856775814671900],
 3068-        ["meter squared per second", "m?/s", null, "kinematic_viscosity", true, false, 1],
 3069-        ["metre", "m", null, "length", true, true, 1],
 3070-        ["miles per hour", "mph", null, "speed", false, true, 0.44704],
 3071-        ["millimetre of mercury", "mmHg", null, "pressure", false, false, 133.322],
 3072-        ["minute", "?", null, "angle", false, false, 0.000290888208665722],
 3073-        ["minute", "min", ["mn"], "time", false, true, 60],
 3074-        ["modern teaspoon", "tspm", null, "volume", false, true, 0.000005],
 3075-        ["mole", "mol", null, "amount_of_substance", true, false, 1],
 3076-        ["morgen", "Morgen", null, "area", false, true, 2500],
 3077-        ["n.u. of action", "?", null, "action", false, false, 1.05457168181818e-34],
 3078-        ["n.u. of mass", "m?", null, "mass", false, false, 9.10938261616162e-31],
 3079-        ["n.u. of speed", "c?", null, "speed", false, false, 299792458],
 3080-        ["n.u. of time", "?/(me?c??)", null, "time", false, false, 1.28808866778687e-21],
 3081-        ["nautical mile", "M", ["Nmi"], "length", false, true, 1852],
 3082-        ["newton", "N", null, "force", true, true, 1],
 3083-        ["œrsted", "Oe ", null, "magnetic_field_intensity", false, false, 79.5774715459477],
 3084-        ["ohm", "Ω", null, "electric_resistance", true, false, 1],
 3085-        ["ounce mass", "ozm", null, "mass", false, true, 0.028349523125],
 3086-        ["pascal", "Pa", null, "pressure", true, false, 1],
 3087-        ["pascal second", "Pa?s", null, "dynamic_viscosity", true, false, 1],
 3088-        ["pferdestärke", "PS", null, "power", false, true, 735.49875],
 3089-        ["phot", "ph", null, "illuminance", false, false, 0.0001],
 3090-        ["pica (1/6 inch)", "pica", null, "length", false, true, 0.00035277777777778],
 3091-        ["pica (1/72 inch)", "Pica", ["Picapt"], "length", false, true, 0.00423333333333333],
 3092-        ["poise", "P", null, "dynamic_viscosity", false, false, 0.1],
 3093-        ["pond", "pond", null, "force", false, true, 0.00980665],
 3094-        ["pound force", "lbf", null, "force", false, true, 4.4482216152605],
 3095-        ["pound mass", "lbm", null, "mass", false, true, 0.45359237],
 3096-        ["quart", "qt", null, "volume", false, true, 0.000946352946],
 3097-        ["radian", "rad", null, "angle", true, false, 1],
 3098-        ["second", "?", null, "angle", false, false, 0.00000484813681109536],
 3099-        ["second", "s", ["sec"], "time", true, true, 1],
 3100-        ["short hundredweight", "cwt", ["shweight"], "mass", false, true, 45.359237],
 3101-        ["siemens", "S", null, "electrical_conductance", true, false, 1],
 3102-        ["sievert", "Sv", null, "equivalent_dose", true, false, 1],
 3103-        ["slug", "sg", null, "mass", false, true, 14.59390294],
 3104-        ["square ångström", "ang2", ["ang^2"], "area", false, true, 1e-20],
 3105-        ["square foot", "ft2", ["ft^2"], "area", false, true, 0.09290304],
 3106-        ["square inch", "in2", ["in^2"], "area", false, true, 0.00064516],
 3107-        ["square light-year", "ly2", ["ly^2"], "area", false, true, 8.95054210748189e+31],
 3108-        ["square meter", "m?", null, "area", true, true, 1],
 3109-        ["square mile", "mi2", ["mi^2"], "area", false, true, 2589988.110336],
 3110-        ["square nautical mile", "Nmi2", ["Nmi^2"], "area", false, true, 3429904],
 3111-        ["square Pica", "Pica2", ["Picapt2", "Pica^2", "Picapt^2"], "area", false, true, 0.00001792111111111],
 3112-        ["square yard", "yd2", ["yd^2"], "area", false, true, 0.83612736],
 3113-        ["statute mile", "mi", null, "length", false, true, 1609.344],
 3114-        ["steradian", "sr", null, "solid_angle", true, false, 1],
 3115-        ["stilb", "sb", null, "luminance", false, false, 0.0001],
 3116-        ["stokes", "St", null, "kinematic_viscosity", false, false, 0.0001],
 3117-        ["stone", "stone", null, "mass", false, true, 6.35029318],
 3118-        ["tablespoon", "tbs", null, "volume", false, true, 0.0000147868],
 3119-        ["teaspoon", "tsp", null, "volume", false, true, 0.00000492892],
 3120-        ["tesla", "T", null, "magnetic_flux_density", true, true, 1],
 3121-        ["thermodynamic calorie", "c", null, "energy", false, true, 4.184],
 3122-        ["ton", "ton", null, "mass", false, true, 907.18474],
 3123-        ["tonne", "t", null, "mass", false, false, 1000],
 3124-        ["U.K. pint", "uk_pt", null, "volume", false, true, 0.00056826125],
 3125-        ["U.S. bushel", "bushel", null, "volume", false, true, 0.03523907],
 3126-        ["U.S. oil barrel", "barrel", null, "volume", false, true, 0.158987295],
 3127-        ["U.S. pint", "pt", ["us_pt"], "volume", false, true, 0.000473176473],
 3128-        ["U.S. survey mile", "survey_mi", null, "length", false, true, 1609.347219],
 3129-        ["U.S. survey/statute acre", "us_acre", null, "area", false, true, 4046.87261],
 3130-        ["volt", "V", null, "voltage", true, false, 1],
 3131-        ["watt", "W", null, "power", true, true, 1],
 3132-        ["watt-hour", "Wh", ["wh"], "energy", false, true, 3600],
 3133-        ["weber", "Wb", null, "magnetic_flux", true, false, 1],
 3134-        ["yard", "yd", null, "length", false, true, 0.9144],
 3135-        ["year", "yr", null, "time", false, true, 31557600]
 3136-      ];
 3137-
 3138-      // Binary prefixes
 3139-      // [Name, Prefix power of 2 value, Previx value, Abbreviation, Derived from]
 3140-      var binary_prefixes = {
 3141-        Yi: ["yobi", 80, 1208925819614629174706176, "Yi", "yotta"],
 3142-        Zi: ["zebi", 70, 1180591620717411303424, "Zi", "zetta"],
 3143-        Ei: ["exbi", 60, 1152921504606846976, "Ei", "exa"],
 3144-        Pi: ["pebi", 50, 1125899906842624, "Pi", "peta"],
 3145-        Ti: ["tebi", 40, 1099511627776, "Ti", "tera"],
 3146-        Gi: ["gibi", 30, 1073741824, "Gi", "giga"],
 3147-        Mi: ["mebi", 20, 1048576, "Mi", "mega"],
 3148-        ki: ["kibi", 10, 1024, "ki", "kilo"]
 3149-      };
 3150-
 3151-      // Unit prefixes
 3152-      // [Name, Multiplier, Abbreviation]
 3153-      var unit_prefixes = {
 3154-        Y: ["yotta", 1e+24, "Y"],
 3155-        Z: ["zetta", 1e+21, "Z"],
 3156-        E: ["exa", 1e+18, "E"],
 3157-        P: ["peta", 1e+15, "P"],
 3158-        T: ["tera", 1e+12, "T"],
 3159-        G: ["giga", 1e+09, "G"],
 3160-        M: ["mega", 1e+06, "M"],
 3161-        k: ["kilo", 1e+03, "k"],
 3162-        h: ["hecto", 1e+02, "h"],
 3163-        e: ["dekao", 1e+01, "e"],
 3164-        d: ["deci", 1e-01, "d"],
 3165-        c: ["centi", 1e-02, "c"],
 3166-        m: ["milli", 1e-03, "m"],
 3167-        u: ["micro", 1e-06, "u"],
 3168-        n: ["nano", 1e-09, "n"],
 3169-        p: ["pico", 1e-12, "p"],
 3170-        f: ["femto", 1e-15, "f"],
 3171-        a: ["atto", 1e-18, "a"],
 3172-        z: ["zepto", 1e-21, "z"],
 3173-        y: ["yocto", 1e-24, "y"]
 3174-      };
 3175-
 3176-      // Initialize units and multipliers
 3177-      var from = null;
 3178-      var to = null;
 3179-      var base_from_unit = from_unit;
 3180-      var base_to_unit = to_unit;
 3181-      var from_multiplier = 1;
 3182-      var to_multiplier = 1;
 3183-      var alt;
 3184-
 3185-      // Lookup from and to units
 3186-      for (var i = 0; i < units.length; i++) {
 3187-        alt = (units[i][2] === null) ? [] : units[i][2];
 3188-        if (units[i][1] === base_from_unit || alt.indexOf(base_from_unit) >= 0) {
 3189-          from = units[i];
 3190-        }
 3191-        if (units[i][1] === base_to_unit || alt.indexOf(base_to_unit) >= 0) {
 3192-          to = units[i];
 3193-        }
 3194-      }
 3195-
 3196-      // Lookup from prefix
 3197-      if (from === null) {
 3198-        var from_binary_prefix = binary_prefixes[from_unit.substring(0, 2)];
 3199-        var from_unit_prefix = unit_prefixes[from_unit.substring(0, 1)];
 3200-
 3201-        // Handle dekao unit prefix (only unit prefix with two characters)
 3202-        if (from_unit.substring(0, 2) === 'da') {
 3203-          from_unit_prefix = ["dekao", 1e+01, "da"];
 3204-        }
 3205-
 3206-        // Handle binary prefixes first (so that 'Yi' is processed before 'Y')
 3207-        if (from_binary_prefix) {
 3208-          from_multiplier = from_binary_prefix[2];
 3209-          base_from_unit = from_unit.substring(2);
 3210-        } else if (from_unit_prefix) {
 3211-          from_multiplier = from_unit_prefix[1];
 3212-          base_from_unit = from_unit.substring(from_unit_prefix[2].length);
 3213-        }
 3214-
 3215-        // Lookup from unit
 3216-        for (var j = 0; j < units.length; j++) {
 3217-          alt = (units[j][2] === null) ? [] : units[j][2];
 3218-          if (units[j][1] === base_from_unit || alt.indexOf(base_from_unit) >= 0) {
 3219-            from = units[j];
 3220-          }
 3221-        }
 3222-      }
 3223-
 3224-      // Lookup to prefix
 3225-      if (to === null) {
 3226-        var to_binary_prefix = binary_prefixes[to_unit.substring(0, 2)];
 3227-        var to_unit_prefix = unit_prefixes[to_unit.substring(0, 1)];
 3228-
 3229-        // Handle dekao unit prefix (only unit prefix with two characters)
 3230-        if (to_unit.substring(0, 2) === 'da') {
 3231-          to_unit_prefix = ["dekao", 1e+01, "da"];
 3232-        }
 3233-
 3234-        // Handle binary prefixes first (so that 'Yi' is processed before 'Y')
 3235-        if (to_binary_prefix) {
 3236-          to_multiplier = to_binary_prefix[2];
 3237-          base_to_unit = to_unit.substring(2);
 3238-        } else if (to_unit_prefix) {
 3239-          to_multiplier = to_unit_prefix[1];
 3240-          base_to_unit = to_unit.substring(to_unit_prefix[2].length);
 3241-        }
 3242-
 3243-        // Lookup to unit
 3244-        for (var k = 0; k < units.length; k++) {
 3245-          alt = (units[k][2] === null) ? [] : units[k][2];
 3246-          if (units[k][1] === base_to_unit || alt.indexOf(base_to_unit) >= 0) {
 3247-            to = units[k];
 3248-          }
 3249-        }
 3250-      }
 3251-
 3252-      // Return error if a unit does not exist
 3253-      if (from === null || to === null) {
 3254-        return '#N/A';
 3255-      }
 3256-
 3257-      // Return error if units represent different quantities
 3258-      if (from[3] !== to[3]) {
 3259-        return '#N/A';
 3260-      }
 3261-
 3262-      // Return converted number
 3263-      return number * from[6] * from_multiplier / (to[6] * to_multiplier);
 3264-    };
 3265-
 3266-    Formula.DEC2BIN = function (number, places) {
 3267-      // Return error if number is not a number
 3268-      if (isNaN(number)) {
 3269-        return '#VALUE!';
 3270-      }
 3271-
 3272-      // Return error if number is not decimal, is lower than -512, or is greater than 511
 3273-      if (!/^-?[0-9]{1,3}$/.test(number) || number < -512 || number > 511) {
 3274-        return '#NUM!';
 3275-      }
 3276-
 3277-      // Ignore places and return a 10-character binary number if number is negative
 3278-      if (number < 0) {
 3279-        return '1' + _s.repeat('0', 9 - (512 + number).toString(2).length) + (512 + number).toString(2);
 3280-      }
 3281-
 3282-      // Convert decimal number to binary
 3283-      var result = parseInt(number, 10).toString(2);
 3284-
 3285-      // Return binary number using the minimum number of characters necessary if places is undefined
 3286-      if (typeof places === 'undefined') {
 3287-        return result;
 3288-      } else {
 3289-        // Return error if places is nonnumeric
 3290-        if (isNaN(places)) {
 3291-          return '#VALUE!';
 3292-        }
 3293-
 3294-        // Return error if places is negative
 3295-        if (places < 0) {
 3296-          return '#NUM!';
 3297-        }
 3298-
 3299-        // Truncate places in case it is not an integer
 3300-        places = Math.floor(places);
 3301-
 3302-        // Pad return value with leading 0s (zeros) if necessary (using Underscore.string)
 3303-        return (places >= result.length) ? _s.repeat('0', places - result.length) + result : '#NUM!';
 3304-      }
 3305-    };
 3306-
 3307-    Formula.DEC2HEX = function (number, places) {
 3308-      // Return error if number is not a number
 3309-      if (isNaN(number)) {
 3310-        return '#VALUE!';
 3311-      }
 3312-
 3313-      // Return error if number is not decimal, is lower than -549755813888, or is greater than 549755813887
 3314-      if (!/^-?[0-9]{1,12}$/.test(number) || number < -549755813888 || number > 549755813887) {
 3315-        return '#NUM!';
 3316-      }
 3317-
 3318-      // Ignore places and return a 10-character hexadecimal number if number is negative
 3319-      if (number < 0) {
 3320-        return (1099511627776 + number).toString(16);
 3321-      }
 3322-
 3323-      // Convert decimal number to hexadecimal
 3324-      var result = parseInt(number, 10).toString(16);
 3325-
 3326-      // Return hexadecimal number using the minimum number of characters necessary if places is undefined
 3327-      if (typeof places === 'undefined') {
 3328-        return result;
 3329-      } else {
 3330-        // Return error if places is nonnumeric
 3331-        if (isNaN(places)) {
 3332-          return '#VALUE!';
 3333-        }
 3334-
 3335-        // Return error if places is negative
 3336-        if (places < 0) {
 3337-          return '#NUM!';
 3338-        }
 3339-
 3340-        // Truncate places in case it is not an integer
 3341-        places = Math.floor(places);
 3342-
 3343-        // Pad return value with leading 0s (zeros) if necessary (using Underscore.string)
 3344-        return (places >= result.length) ? _s.repeat('0', places - result.length) + result : '#NUM!';
 3345-      }
 3346-    };
 3347-
 3348-    Formula.DEC2OCT = function (number, places) {
 3349-      // Return error if number is not a number
 3350-      if (isNaN(number)) {
 3351-        return '#VALUE!';
 3352-      }
 3353-
 3354-      // Return error if number is not decimal, is lower than -549755813888, or is greater than 549755813887
 3355-      if (!/^-?[0-9]{1,9}$/.test(number) || number < -536870912 || number > 536870911) {
 3356-        return '#NUM!';
 3357-      }
 3358-
 3359-      // Ignore places and return a 10-character octal number if number is negative
 3360-      if (number < 0) {
 3361-        return (1073741824 + number).toString(8);
 3362-      }
 3363-
 3364-      // Convert decimal number to octal
 3365-      var result = parseInt(number, 10).toString(8);
 3366-
 3367-      // Return octal number using the minimum number of characters necessary if places is undefined
 3368-      if (typeof places === 'undefined') {
 3369-        return result;
 3370-      } else {
 3371-        // Return error if places is nonnumeric
 3372-        if (isNaN(places)) {
 3373-          return '#VALUE!';
 3374-        }
 3375-
 3376-        // Return error if places is negative
 3377-        if (places < 0) {
 3378-          return '#NUM!';
 3379-        }
 3380-
 3381-        // Truncate places in case it is not an integer
 3382-        places = Math.floor(places);
 3383-
 3384-        // Pad return value with leading 0s (zeros) if necessary (using Underscore.string)
 3385-        return (places >= result.length) ? _s.repeat('0', places - result.length) + result : '#NUM!';
 3386-      }
 3387-    };
 3388-
 3389-    Formula.DELTA = function (number1, number2) {
 3390-      // Set number2 to zero if undefined
 3391-      number2 = (typeof number2 === 'undefined') ? 0 : number2;
 3392-
 3393-      // Return error if either number is not a number
 3394-      if (isNaN(number1) || isNaN(number2)) {
 3395-        return '#VALUE!';
 3396-      }
 3397-
 3398-      // Return delta
 3399-      return (number1 === number2) ? 1 : 0;
 3400-    };
 3401-
 3402-    Formula.ERF = function (lower_bound, upper_bound) {
 3403-      // Set number2 to zero if undefined
 3404-      upper_bound = (typeof upper_bound === 'undefined') ? 0 : upper_bound;
 3405-
 3406-      // Return error if either number is not a number
 3407-      if (isNaN(lower_bound) || isNaN(upper_bound)) {
 3408-        return '#VALUE!';
 3409-      }
 3410-
 3411-      // Return ERFC using jStat [http://www.jstat.org/]
 3412-      return jStat.erf(lower_bound);
 3413-    };
 3414-
 3415-    Formula.ERFC = function (x) {
 3416-      // Return error if x is not a number
 3417-      if (isNaN(x)) {
 3418-        return '#VALUE!';
 3419-      }
 3420-
 3421-      // Return ERFC using jStat [http://www.jstat.org/]
 3422-      return jStat.erfc(x);
 3423-    };
 3424-
 3425-    Formula.ERFCPRECISE = function () {
 3426-      return;
 3427-    };
 3428-
 3429-    Formula.ERFPRECISE = function () {
 3430-      return;
 3431-    };
 3432-
 3433-    Formula.GESTEP = function (number, step) {
 3434-      // Set step to zero if undefined
 3435-      step = (typeof step === 'undefined') ? 0 : step;
 3436-
 3437-      // Return error if either number is not a number
 3438-      if (isNaN(number) || isNaN(step)) {
 3439-        return '#VALUE!';
 3440-      }
 3441-
 3442-      // Return delta
 3443-      return (number >= step) ? 1 : 0;
 3444-    };
 3445-
 3446-    Formula.HEX2BIN = function (number, places) {
 3447-
 3448-      // Return error if number is not hexadecimal or contains more than ten characters (10 digits)
 3449-      if (!/^[0-9A-Fa-f]{1,10}$/.test(number)) {
 3450-        return '#NUM!';
 3451-      }
 3452-
 3453-      // Check if number is negative
 3454-      var negative = (number.length === 10 && number.substring(0, 1).toLowerCase() === 'f') ? true : false;
 3455-
 3456-      // Convert hexadecimal number to decimal
 3457-      var decimal = (negative) ? parseInt(number, 16) - 1099511627776 : parseInt(number, 16);
 3458-
 3459-      // Return error if number is lower than -512 or greater than 511
 3460-      if (decimal < -512 || decimal > 511) {
 3461-        return '#NUM!';
 3462-      }
 3463-
 3464-      // Ignore places and return a 10-character binary number if number is negative
 3465-      if (negative) {
 3466-        return '1' + _s.repeat('0', 9 - (512 + decimal).toString(2).length) + (512 + decimal).toString(2);
 3467-      }
 3468-
 3469-      // Convert decimal number to binary
 3470-      var result = decimal.toString(2);
 3471-
 3472-      // Return binary number using the minimum number of characters necessary if places is undefined
 3473-      if (typeof places === 'undefined') {
 3474-        return result;
 3475-      } else {
 3476-        // Return error if places is nonnumeric
 3477-        if (isNaN(places)) {
 3478-          return '#VALUE!';
 3479-        }
 3480-
 3481-        // Return error if places is negative
 3482-        if (places < 0) {
 3483-          return '#NUM!';
 3484-        }
 3485-
 3486-        // Truncate places in case it is not an integer
 3487-        places = Math.floor(places);
 3488-
 3489-        // Pad return value with leading 0s (zeros) if necessary (using Underscore.string)
 3490-        return (places >= result.length) ? _s.repeat('0', places - result.length) + result : '#NUM!';
 3491-      }
 3492-    };
 3493-
 3494-    Formula.HEX2DEC = function (number) {
 3495-      // Return error if number is not hexadecimal or contains more than ten characters (10 digits)
 3496-      if (!/^[0-9A-Fa-f]{1,10}$/.test(number)) {
 3497-        return '#NUM!';
 3498-      }
 3499-
 3500-      // Convert hexadecimal number to decimal
 3501-      var decimal = parseInt(number, 16);
 3502-
 3503-      // Return decimal number
 3504-      return (decimal >= 549755813888) ? decimal - 1099511627776 : decimal;
 3505-    };
 3506-
 3507-    Formula.HEX2OCT = function (number, places) {
 3508-      // Return error if number is not hexadecimal or contains more than ten characters (10 digits)
 3509-      if (!/^[0-9A-Fa-f]{1,10}$/.test(number)) {
 3510-        return '#NUM!';
 3511-      }
 3512-
 3513-      // Convert hexadecimal number to decimal
 3514-      var decimal = parseInt(number, 16);
 3515-
 3516-      // Return error if number is positive and greater than 0x1fffffff (536870911)
 3517-      if (decimal > 536870911 && decimal < 1098974756864) {
 3518-        return '#NUM!';
 3519-      }
 3520-
 3521-      // Ignore places and return a 10-character octal number if number is negative
 3522-      if (decimal >= 1098974756864) {
 3523-        return (decimal - 1098437885952).toString(8);
 3524-      }
 3525-
 3526-      // Convert decimal number to octal
 3527-      var result = decimal.toString(8);
 3528-
 3529-      // Return octal number using the minimum number of characters necessary if places is undefined
 3530-      if (typeof places === 'undefined') {
 3531-        return result;
 3532-      } else {
 3533-        // Return error if places is nonnumeric
 3534-        if (isNaN(places)) {
 3535-          return '#VALUE!';
 3536-        }
 3537-
 3538-        // Return error if places is negative
 3539-        if (places < 0) {
 3540-          return '#NUM!';
 3541-        }
 3542-
 3543-        // Truncate places in case it is not an integer
 3544-        places = Math.floor(places);
 3545-
 3546-        // Pad return value with leading 0s (zeros) if necessary (using Underscore.string)
 3547-        return (places >= result.length) ? _s.repeat('0', places - result.length) + result : '#NUM!';
 3548-      }
 3549-    };
 3550-
 3551-    Formula.IMABS = function (inumber) {
 3552-      // Lookup real and imaginary coefficients using Formula.js [http://formulajs.org]
 3553-      var x = Formula.IMREAL(inumber);
 3554-      var y = Formula.IMAGINARY(inumber);
 3555-
 3556-      // Return error if either coefficient is not a number
 3557-      if (x === '#NUM!' || y === '#NUM!') {
 3558-        return '#NUM!';
 3559-      }
 3560-
 3561-      // Return absolute value of complex number
 3562-      return Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
 3563-    };
 3564-
 3565-    Formula.IMAGINARY = function (inumber) {
 3566-      // Return 0 if inumber is equal to 0
 3567-      if (inumber === 0 || inumber === '0') {
 3568-        return 0;
 3569-      }
 3570-
 3571-      // Handle special cases
 3572-      if (['i', 'j'].indexOf(inumber) >= 0) {
 3573-        return 1;
 3574-      }
 3575-
 3576-      // Normalize imaginary coefficient
 3577-      inumber = inumber.replace('+i', '+1i').replace('-i', '-1i').replace('+j', '+1j').replace('-j', '-1j');
 3578-
 3579-      // Lookup sign
 3580-      var plus = inumber.indexOf('+');
 3581-      var minus = inumber.indexOf('-');
 3582-      if (plus === 0) {
 3583-        plus = inumber.indexOf('+', 1);
 3584-      }
 3585-
 3586-      if (minus === 0) {
 3587-        minus = inumber.indexOf('-', 1);
 3588-      }
 3589-
 3590-      // Lookup imaginary unit
 3591-      var last = inumber.substring(inumber.length - 1, inumber.length);
 3592-      var unit = (last === 'i' || last === 'j');
 3593-
 3594-      if (plus >= 0 || minus >= 0) {
 3595-        // Return error if imaginary unit is neither i nor j
 3596-        if (!unit) {
 3597-          return '#NUM!';
 3598-        }
 3599-
 3600-        // Return imaginary coefficient of complex number
 3601-        if (plus >= 0) {
 3602-          return (isNaN(inumber.substring(0, plus)) || isNaN(inumber.substring(plus + 1, inumber.length - 1))) ?
 3603-            '#NUM!' :
 3604-            Number(inumber.substring(plus + 1, inumber.length - 1));
 3605-        } else {
 3606-          return (isNaN(inumber.substring(0, minus)) || isNaN(inumber.substring(minus + 1, inumber.length - 1))) ?
 3607-            '#NUM!' :
 3608-            -Number(inumber.substring(minus + 1, inumber.length - 1));
 3609-        }
 3610-      } else {
 3611-        if (unit) {
 3612-          return (isNaN(inumber.substring(0, inumber.length - 1))) ? '#NUM!' : inumber.substring(0, inumber.length - 1);
 3613-        } else {
 3614-          return (isNaN(inumber)) ? '#NUM!' : 0;
 3615-        }
 3616-      }
 3617-    };
 3618-
 3619-    Formula.IMARGUMENT = function (inumber) {
 3620-      // Lookup real and imaginary coefficients using Formula.js [http://formulajs.org]
 3621-      var x = Formula.IMREAL(inumber);
 3622-      var y = Formula.IMAGINARY(inumber);
 3623-
 3624-      // Return error if either coefficient is not a number
 3625-      if (x === '#NUM!' || y === '#NUM!') {
 3626-        return '#NUM!';
 3627-      }
 3628-
 3629-      // Return error if inumber is equal to zero
 3630-      if (x === 0 && y === 0) {
 3631-        return '#DIV/0!';
 3632-      }
 3633-
 3634-      // Return PI/2 if x is equal to zero and y is positive
 3635-      if (x === 0 && y > 0) {
 3636-        return Math.PI / 2;
 3637-      }
 3638-
 3639-      // Return -PI/2 if x is equal to zero and y is negative
 3640-      if (x === 0 && y < 0) {
 3641-        return -Math.PI / 2;
 3642-      }
 3643-
 3644-      // Return zero if x is negative and y is equal to zero
 3645-      if (y === 0 && x > 0) {
 3646-        return 0;
 3647-      }
 3648-
 3649-      // Return zero if x is negative and y is equal to zero
 3650-      if (y === 0 && x < 0) {
 3651-        return -Math.PI;
 3652-      }
 3653-
 3654-      // Return argument of complex number
 3655-      if (x > 0) {
 3656-        return Math.atan(y / x);
 3657-      } else if (x < 0 && y >= 0) {
 3658-        return Math.atan(y / x) + Math.PI;
 3659-      } else {
 3660-        return Math.atan(y / x) - Math.PI;
 3661-      }
 3662-    };
 3663-
 3664-    Formula.IMCONJUGATE = function (inumber) {
 3665-      // Lookup real and imaginary coefficients using Formula.js [http://formulajs.org]
 3666-      var x = Formula.IMREAL(inumber);
 3667-      var y = Formula.IMAGINARY(inumber);
 3668-
 3669-      // Lookup imaginary unit
 3670-      var unit = inumber.substring(inumber.length - 1);
 3671-      unit = (unit === 'i' || unit === 'j') ? unit : 'i';
 3672-
 3673-      // Return error if either coefficient is not a number
 3674-      if (x === '#NUM!' || y === '#NUM!') {
 3675-        return '#NUM!';
 3676-      }
 3677-
 3678-      // Return conjugate of complex number
 3679-      return (y !== 0) ? Formula.COMPLEX(x, -y, unit) : inumber;
 3680-    };
 3681-
 3682-    Formula.IMCOS = function (inumber) {
 3683-      // Return error if inumber is a logical value
 3684-      if (inumber === true || inumber === false) {
 3685-        return '#VALUE!';
 3686-      }
 3687-
 3688-      // Lookup real and imaginary coefficients using Formula.js [http://formulajs.org]
 3689-      var x = Formula.IMREAL(inumber);
 3690-      var y = Formula.IMAGINARY(inumber);
 3691-
 3692-      // Lookup imaginary unit
 3693-      var unit = inumber.substring(inumber.length - 1);
 3694-      unit = (unit === 'i' || unit === 'j') ? unit : 'i';
 3695-
 3696-      // Return error if either coefficient is not a number
 3697-      if (x === '#NUM!' || y === '#NUM!') {
 3698-        return '#NUM!';
 3699-      }
 3700-
 3701-      // Return cosine of complex number
 3702-      return Formula.COMPLEX(Math.cos(x) * (Math.exp(y) + Math.exp(-y)) / 2, -Math.sin(x) * (Math.exp(y) - Math.exp(-y)) / 2, unit);
 3703-    };
 3704-
 3705-    Formula.IMCOSH = function (inumber) {
 3706-      // Return error if inumber is a logical value
 3707-      if (inumber === true || inumber === false) {
 3708-        return '#VALUE!';
 3709-      }
 3710-
 3711-      // Lookup real and imaginary coefficients using Formula.js [http://formulajs.org]
 3712-      var x = Formula.IMREAL(inumber);
 3713-      var y = Formula.IMAGINARY(inumber);
 3714-
 3715-      // Lookup imaginary unit
 3716-      var unit = inumber.substring(inumber.length - 1);
 3717-      unit = (unit === 'i' || unit === 'j') ? unit : 'i';
 3718-
 3719-      // Return error if either coefficient is not a number
 3720-      if (x === '#NUM!' || y === '#NUM!') {
 3721-        return '#NUM!';
 3722-      }
 3723-
 3724-      // Return hyperbolic cosine of complex number
 3725-      return Formula.COMPLEX(Math.cos(y) * (Math.exp(x) + Math.exp(-x)) / 2, Math.sin(y) * (Math.exp(x) - Math.exp(-x)) / 2, unit);
 3726-    };
 3727-
 3728-    Formula.IMCOT = function (inumber) {
 3729-      // Return error if inumber is a logical value
 3730-      if (inumber === true || inumber === false) {
 3731-        return '#VALUE!';
 3732-      }
 3733-
 3734-      // Lookup real and imaginary coefficients using Formula.js [http://formulajs.org]
 3735-      var x = Formula.IMREAL(inumber);
 3736-      var y = Formula.IMAGINARY(inumber);
 3737-
 3738-      // Return error if either coefficient is not a number
 3739-      if (x === '#NUM!' || y === '#NUM!') {
 3740-        return '#NUM!';
 3741-      }
 3742-
 3743-      // Return cotangent of complex number
 3744-      return Formula.IMDIV(Formula.IMCOS(inumber), Formula.IMSIN(inumber));
 3745-    };
 3746-
 3747-    Formula.IMCSC = function (inumber) {
 3748-      // Return error if inumber is a logical value
 3749-      if (inumber === true || inumber === false) {
 3750-        return '#VALUE!';
 3751-      }
 3752-
 3753-      // Lookup real and imaginary coefficients using Formula.js [http://formulajs.org]
 3754-      var x = Formula.IMREAL(inumber);
 3755-      var y = Formula.IMAGINARY(inumber);
 3756-
 3757-      // Return error if either coefficient is not a number
 3758-      if (x === '#NUM!' || y === '#NUM!') {
 3759-        return '#NUM!';
 3760-      }
 3761-
 3762-      // Return cosecant of complex number
 3763-      return Formula.IMDIV('1', Formula.IMSIN(inumber));
 3764-    };
 3765-
 3766-    Formula.IMCSCH = function (inumber) {
 3767-      // Return error if inumber is a logical value
 3768-      if (inumber === true || inumber === false) {
 3769-        return '#VALUE!';
 3770-      }
 3771-
 3772-      // Lookup real and imaginary coefficients using Formula.js [http://formulajs.org]
 3773-      var x = Formula.IMREAL(inumber);
 3774-      var y = Formula.IMAGINARY(inumber);
 3775-
 3776-      // Return error if either coefficient is not a number
 3777-      if (x === '#NUM!' || y === '#NUM!') {
 3778-        return '#NUM!';
 3779-      }
 3780-
 3781-      // Return hyperbolic cosecant of complex number
 3782-      return Formula.IMDIV('1', Formula.IMSINH(inumber));
 3783-    };
 3784-
 3785-    Formula.IMDIV = function (inumber1, inumber2) {
 3786-      // Lookup real and imaginary coefficients using Formula.js [http://formulajs.org]
 3787-      var a = Formula.IMREAL(inumber1);
 3788-      var b = Formula.IMAGINARY(inumber1);
 3789-      var c = Formula.IMREAL(inumber2);
 3790-      var d = Formula.IMAGINARY(inumber2);
 3791-
 3792-      // Lookup imaginary unit
 3793-      var unit1 = inumber1.substring(inumber1.length - 1);
 3794-      var unit2 = inumber1.substring(inumber1.length - 1);
 3795-      var unit = 'i';
 3796-      if (unit1 === 'j') {
 3797-        unit = 'j';
 3798-      } else if (unit2 === 'j') {
 3799-        unit = 'j';
 3800-      }
 3801-
 3802-      // Return error if either coefficient is not a number
 3803-      if (a === '#NUM!' || b === '#NUM!' || c === '#NUM!' || d === '#NUM!') {
 3804-        return '#NUM!';
 3805-      }
 3806-
 3807-      // Return error if inumber2 is null
 3808-      if (c === 0 && d === 0) {
 3809-        return '#NUM!';
 3810-      }
 3811-
 3812-      // Return exponential of complex number
 3813-      var den = c * c + d * d;
 3814-      return Formula.COMPLEX((a * c + b * d) / den, (b * c - a * d) / den, unit);
 3815-    };
 3816-
 3817-    Formula.IMEXP = function (inumber) {
 3818-      // Lookup real and imaginary coefficients using Formula.js [http://formulajs.org]
 3819-      var x = Formula.IMREAL(inumber);
 3820-      var y = Formula.IMAGINARY(inumber);
 3821-
 3822-      // Lookup imaginary unit
 3823-      var unit = inumber.substring(inumber.length - 1);
 3824-      unit = (unit === 'i' || unit === 'j') ? unit : 'i';
 3825-
 3826-      // Return error if either coefficient is not a number
 3827-      if (x === '#NUM!' || y === '#NUM!') {
 3828-        return '#NUM!';
 3829-      }
 3830-
 3831-      // Return exponential of complex number
 3832-      var e = Math.exp(x);
 3833-      return Formula.COMPLEX(e * Math.cos(y), e * Math.sin(y), unit);
 3834-    };
 3835-
 3836-    Formula.IMLN = function (inumber) {
 3837-      // Lookup real and imaginary coefficients using Formula.js [http://formulajs.org]
 3838-      var x = Formula.IMREAL(inumber);
 3839-      var y = Formula.IMAGINARY(inumber);
 3840-
 3841-      // Lookup imaginary unit
 3842-      var unit = inumber.substring(inumber.length - 1);
 3843-      unit = (unit === 'i' || unit === 'j') ? unit : 'i';
 3844-
 3845-      // Return error if either coefficient is not a number
 3846-      if (x === '#NUM!' || y === '#NUM!') {
 3847-        return '#NUM!';
 3848-      }
 3849-
 3850-      // Return exponential of complex number
 3851-      return Formula.COMPLEX(Math.log(Math.sqrt(x * x + y * y)), Math.atan(y / x), unit);
 3852-    };
 3853-
 3854-    Formula.IMLOG10 = function (inumber) {
 3855-      // Lookup real and imaginary coefficients using Formula.js [http://formulajs.org]
 3856-      var x = Formula.IMREAL(inumber);
 3857-      var y = Formula.IMAGINARY(inumber);
 3858-
 3859-      // Lookup imaginary unit
 3860-      var unit = inumber.substring(inumber.length - 1);
 3861-      unit = (unit === 'i' || unit === 'j') ? unit : 'i';
 3862-
 3863-      // Return error if either coefficient is not a number
 3864-      if (x === '#NUM!' || y === '#NUM!') {
 3865-        return '#NUM!';
 3866-      }
 3867-
 3868-      // Return exponential of complex number
 3869-      return Formula.COMPLEX(Math.log(Math.sqrt(x * x + y * y)) / Math.log(10), Math.atan(y / x) / Math.log(10), unit);
 3870-    };
 3871-
 3872-    Formula.IMLOG2 = function (inumber) {
 3873-      // Lookup real and imaginary coefficients using Formula.js [http://formulajs.org]
 3874-      var x = Formula.IMREAL(inumber);
 3875-      var y = Formula.IMAGINARY(inumber);
 3876-
 3877-      // Lookup imaginary unit
 3878-      var unit = inumber.substring(inumber.length - 1);
 3879-      unit = (unit === 'i' || unit === 'j') ? unit : 'i';
 3880-
 3881-      // Return error if either coefficient is not a number
 3882-      if (x === '#NUM!' || y === '#NUM!') {
 3883-        return '#NUM!';
 3884-      }
 3885-
 3886-      // Return exponential of complex number
 3887-      return Formula.COMPLEX(Math.log(Math.sqrt(x * x + y * y)) / Math.log(2), Math.atan(y / x) / Math.log(2), unit);
 3888-    };
 3889-
 3890-    Formula.IMPOWER = function (inumber, number) {
 3891-      // Return error if number is nonnumeric
 3892-      if (isNaN(number)) {
 3893-        return '#VALUE!';
 3894-      }
 3895-
 3896-      // Lookup real and imaginary coefficients using Formula.js [http://formulajs.org]
 3897-      var x = Formula.IMREAL(inumber);
 3898-      var y = Formula.IMAGINARY(inumber);
 3899-
 3900-      // Lookup imaginary unit
 3901-      var unit = inumber.substring(inumber.length - 1);
 3902-      unit = (unit === 'i' || unit === 'j') ? unit : 'i';
 3903-
 3904-      // Return error if either coefficient is not a number
 3905-      if (x === '#NUM!' || y === '#NUM!') {
 3906-        return '#NUM!';
 3907-      }
 3908-
 3909-      // Calculate power of modulus
 3910-      var p = Math.pow(Formula.IMABS(inumber), number);
 3911-
 3912-      // Calculate argument
 3913-      var t = Formula.IMARGUMENT(inumber);
 3914-
 3915-      // Return exponential of complex number
 3916-      return Formula.COMPLEX(p * Math.cos(number * t), p * Math.sin(number * t), unit);
 3917-    };
 3918-
 3919-    Formula.IMPRODUCT = function () {
 3920-      // Initialize result
 3921-      var result = arguments[0];
 3922-
 3923-      // Loop on all numbers
 3924-      for (var i = 1; i < arguments.length; i++) {
 3925-        // Lookup coefficients of two complex numbers
 3926-        var a = Formula.IMREAL(result);
 3927-        var b = Formula.IMAGINARY(result);
 3928-        var c = Formula.IMREAL(arguments[i]);
 3929-        var d = Formula.IMAGINARY(arguments[i]);
 3930-
 3931-        // Return error if either coefficient is not a number
 3932-        if (a === '#NUM!' || b === '#NUM!' || c === '#NUM!' || d === '#NUM!') {
 3933-          return '#NUM!';
 3934-        }
 3935-
 3936-        // Complute product of two complex numbers
 3937-        result = Formula.COMPLEX(a * c - b * d, a * d + b * c);
 3938-      }
 3939-
 3940-      // Return product of complex numbers
 3941-      return result;
 3942-    };
 3943-
 3944-    Formula.IMREAL = function (inumber) {
 3945-      // Return 0 if inumber is equal to 0
 3946-      if (inumber === 0 || inumber === '0') {
 3947-        return 0;
 3948-      }
 3949-
 3950-      // Handle special cases
 3951-      if (['i', '+i', '1i', '+1i', '-i', '-1i', 'j', '+j', '1j', '+1j', '-j', '-1j'].indexOf(inumber) >= 0) {
 3952-        return 0;
 3953-      }
 3954-
 3955-      // Lookup sign
 3956-      var plus = inumber.indexOf('+');
 3957-      var minus = inumber.indexOf('-');
 3958-      if (plus === 0) {
 3959-        plus = inumber.indexOf('+', 1);
 3960-      }
 3961-      if (minus === 0) {
 3962-        minus = inumber.indexOf('-', 1);
 3963-      }
 3964-
 3965-      // Lookup imaginary unit
 3966-      var last = inumber.substring(inumber.length - 1, inumber.length);
 3967-      var unit = (last === 'i' || last === 'j');
 3968-
 3969-      if (plus >= 0 || minus >= 0) {
 3970-        // Return error if imaginary unit is neither i nor j
 3971-        if (!unit) {
 3972-          return '#NUM!';
 3973-        }
 3974-
 3975-        // Return real coefficient of complex number
 3976-        if (plus >= 0) {
 3977-          return (isNaN(inumber.substring(0, plus)) || isNaN(inumber.substring(plus + 1, inumber.length - 1))) ?
 3978-            '#NUM!' :
 3979-            Number(inumber.substring(0, plus));
 3980-        } else {
 3981-          return (isNaN(inumber.substring(0, minus)) || isNaN(inumber.substring(minus + 1, inumber.length - 1))) ?
 3982-            '#NUM!' :
 3983-            Number(inumber.substring(0, minus));
 3984-        }
 3985-      } else {
 3986-        if (unit) {
 3987-          return (isNaN(inumber.substring(0, inumber.length - 1))) ? '#NUM!' : 0;
 3988-        } else {
 3989-          return (isNaN(inumber)) ? '#NUM!' : inumber;
 3990-        }
 3991-      }
 3992-    };
 3993-
 3994-    Formula.IMSEC = function (inumber) {
 3995-      // Return error if inumber is a logical value
 3996-      if (inumber === true || inumber === false) {
 3997-        return '#VALUE!';
 3998-      }
 3999-
 4000-      // Lookup real and imaginary coefficients using Formula.js [http://formulajs.org]
 4001-      var x = Formula.IMREAL(inumber);
 4002-      var y = Formula.IMAGINARY(inumber);
 4003-
 4004-      // Return error if either coefficient is not a number
 4005-      if (x === '#NUM!' || y === '#NUM!') {
 4006-        return '#NUM!';
 4007-      }
 4008-
 4009-      // Return secant of complex number
 4010-      return Formula.IMDIV('1', Formula.IMCOS(inumber));
 4011-    };
 4012-
 4013-    Formula.IMSECH = function (inumber) {
 4014-      // Return error if inumber is a logical value
 4015-      if (inumber === true || inumber === false) {
 4016-        return '#VALUE!';
 4017-      }
 4018-
 4019-      // Lookup real and imaginary coefficients using Formula.js [http://formulajs.org]
 4020-      var x = Formula.IMREAL(inumber);
 4021-      var y = Formula.IMAGINARY(inumber);
 4022-
 4023-      // Return error if either coefficient is not a number
 4024-      if (x === '#NUM!' || y === '#NUM!') {
 4025-        return '#NUM!';
 4026-      }
 4027-
 4028-      // Return hyperbolic secant of complex number
 4029-      return Formula.IMDIV('1', Formula.IMCOSH(inumber));
 4030-    };
 4031-
 4032-    Formula.IMSIN = function (inumber) {
 4033-      // Return error if inumber is a logical value
 4034-      if (inumber === true || inumber === false) {
 4035-        return '#VALUE!';
 4036-      }
 4037-
 4038-      // Lookup real and imaginary coefficients using Formula.js [http://formulajs.org]
 4039-      var x = Formula.IMREAL(inumber);
 4040-      var y = Formula.IMAGINARY(inumber);
 4041-
 4042-      // Lookup imaginary unit
 4043-      var unit = inumber.substring(inumber.length - 1);
 4044-      unit = (unit === 'i' || unit === 'j') ? unit : 'i';
 4045-
 4046-      // Return error if either coefficient is not a number
 4047-      if (x === '#NUM!' || y === '#NUM!') {
 4048-        return '#NUM!';
 4049-      }
 4050-
 4051-      // Return sine of complex number
 4052-      return Formula.COMPLEX(Math.sin(x) * (Math.exp(y) + Math.exp(-y)) / 2, Math.cos(x) * (Math.exp(y) - Math.exp(-y)) / 2, unit);
 4053-    };
 4054-
 4055-    Formula.IMSINH = function (inumber) {
 4056-      // Return error if inumber is a logical value
 4057-      if (inumber === true || inumber === false) {
 4058-        return '#VALUE!';
 4059-      }
 4060-
 4061-      // Lookup real and imaginary coefficients using Formula.js [http://formulajs.org]
 4062-      var x = Formula.IMREAL(inumber);
 4063-      var y = Formula.IMAGINARY(inumber);
 4064-
 4065-      // Lookup imaginary unit
 4066-      var unit = inumber.substring(inumber.length - 1);
 4067-      unit = (unit === 'i' || unit === 'j') ? unit : 'i';
 4068-
 4069-      // Return error if either coefficient is not a number
 4070-      if (x === '#NUM!' || y === '#NUM!') {
 4071-        return '#NUM!';
 4072-      }
 4073-
 4074-      // Return hyperbolic sine of complex number
 4075-      return Formula.COMPLEX(Math.cos(y) * (Math.exp(x) - Math.exp(-x)) / 2, Math.sin(y) * (Math.exp(x) + Math.exp(-x)) / 2, unit);
 4076-    };
 4077-
 4078-    Formula.IMSQRT = function (inumber) {
 4079-      // Lookup real and imaginary coefficients using Formula.js [http://formulajs.org]
 4080-      var x = Formula.IMREAL(inumber);
 4081-      var y = Formula.IMAGINARY(inumber);
 4082-
 4083-      // Lookup imaginary unit
 4084-      var unit = inumber.substring(inumber.length - 1);
 4085-      unit = (unit === 'i' || unit === 'j') ? unit : 'i';
 4086-
 4087-      // Return error if either coefficient is not a number
 4088-      if (x === '#NUM!' || y === '#NUM!') {
 4089-        return '#NUM!';
 4090-      }
 4091-
 4092-      // Calculate power of modulus
 4093-      var s = Math.sqrt(Formula.IMABS(inumber));
 4094-
 4095-      // Calculate argument
 4096-      var t = Formula.IMARGUMENT(inumber);
 4097-
 4098-      // Return exponential of complex number
 4099-      return Formula.COMPLEX(s * Math.cos(t / 2), s * Math.sin(t / 2), unit);
 4100-    };
 4101-
 4102-    Formula.IMSUB = function (inumber1, inumber2) {
 4103-      // Lookup real and imaginary coefficients using Formula.js [http://formulajs.org]
 4104-      var a = Formula.IMREAL(inumber1);
 4105-      var b = Formula.IMAGINARY(inumber1);
 4106-      var c = Formula.IMREAL(inumber2);
 4107-      var d = Formula.IMAGINARY(inumber2);
 4108-
 4109-      // Lookup imaginary unit
 4110-      var unit1 = inumber1.substring(inumber1.length - 1);
 4111-      var unit2 = inumber1.substring(inumber1.length - 1);
 4112-      var unit = 'i';
 4113-      if (unit1 === 'j') {
 4114-        unit = 'j';
 4115-      } else if (unit2 === 'j') {
 4116-        unit = 'j';
 4117-      }
 4118-
 4119-      // Return error if either coefficient is not a number
 4120-      if (a === '#NUM!' || b === '#NUM!' || c === '#NUM!' || d === '#NUM!') {
 4121-        return '#NUM!';
 4122-      }
 4123-
 4124-      // Return _ of two complex numbers
 4125-      return Formula.COMPLEX(a - c, b - d, unit);
 4126-    };
 4127-
 4128-    Formula.IMSUM = function () {
 4129-      // Initialize result
 4130-      var result = arguments[0];
 4131-
 4132-      // Loop on all numbers
 4133-      for (var i = 1; i < arguments.length; i++) {
 4134-        // Lookup coefficients of two complex numbers
 4135-        var a = Formula.IMREAL(result);
 4136-        var b = Formula.IMAGINARY(result);
 4137-        var c = Formula.IMREAL(arguments[i]);
 4138-        var d = Formula.IMAGINARY(arguments[i]);
 4139-
 4140-        // Return error if either coefficient is not a number
 4141-        if (a === '#NUM!' || b === '#NUM!' || c === '#NUM!' || d === '#NUM!') {
 4142-          return '#NUM!';
 4143-        }
 4144-
 4145-        // Complute product of two complex numbers
 4146-        result = Formula.COMPLEX(a + c, b + d);
 4147-      }
 4148-
 4149-      // Return sum of complex numbers
 4150-      return result;
 4151-    };
 4152-
 4153-    Formula.IMTAN = function (inumber) {
 4154-      // Return error if inumber is a logical value
 4155-      if (inumber === true || inumber === false) {
 4156-        return '#VALUE!';
 4157-      }