@progress/kendo-ui
Version:
This package is part of the [Kendo UI for jQuery](http://www.telerik.com/kendo-ui) suite.
1,570 lines (1,391 loc) • 83.6 kB
JavaScript
module.exports =
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId])
/******/ return installedModules[moduleId].exports;
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ exports: {},
/******/ id: moduleId,
/******/ loaded: false
/******/ };
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/ // Flag the module as loaded
/******/ module.loaded = true;
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/ // Load entry module and return exports
/******/ return __webpack_require__(0);
/******/ })
/************************************************************************/
/******/ ({
/***/ 0:
/***/ (function(module, exports, __webpack_require__) {
__webpack_require__(1531);
module.exports = __webpack_require__(1531);
/***/ }),
/***/ 3:
/***/ (function(module, exports) {
module.exports = function() { throw new Error("define cannot be used indirect"); };
/***/ }),
/***/ 1485:
/***/ (function(module, exports) {
module.exports = require("./runtime");
/***/ }),
/***/ 1522:
/***/ (function(module, exports) {
module.exports = require("../util/main");
/***/ }),
/***/ 1531:
/***/ (function(module, exports, __webpack_require__) {
var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// -*- fill-column: 100 -*-
(function(f, define){
!(__WEBPACK_AMD_DEFINE_ARRAY__ = [ __webpack_require__(1485), __webpack_require__(1522) ], __WEBPACK_AMD_DEFINE_FACTORY__ = (f), __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
})(function(){
"use strict";
if (kendo.support.browser.msie && kendo.support.browser.version < 9) {
return;
}
// WARNING: removing the following jshint declaration and turning
// == into === to make JSHint happy will break functionality.
/* jshint eqnull:true, newcap:false, laxbreak:true, validthis:true */
/* jshint latedef:false */
var util = kendo.util;
var spreadsheet = kendo.spreadsheet;
var calc = spreadsheet.calc;
var runtime = calc.runtime;
var defineFunction = runtime.defineFunction;
var defineAlias = runtime.defineAlias;
var CalcError = runtime.CalcError;
var RangeRef = spreadsheet.RangeRef;
var CellRef = spreadsheet.CellRef;
var UnionRef = spreadsheet.UnionRef;
var Matrix = runtime.Matrix;
var Ref = spreadsheet.Ref;
var daysInMonth = runtime.daysInMonth;
var packDate = runtime.packDate;
var unpackDate = runtime.unpackDate;
var daysInYear = runtime.daysInYear;
/* -----[ Math functions ]----- */
[ "abs", "cos", "sin", "acos", "asin", "tan", "atan", "exp", "sqrt" ].forEach(function(name){
defineFunction(name, Math[name]).args([
[ "*n", "number" ]
]);
});
defineFunction("ln", Math.log).args([
[ "*n", "number" ]
]);
defineFunction("log", function(num, base){
return Math.log(num) / Math.log(base);
}).args([
[ "*num", "number++" ],
[ "*base", [ "or", "number++", [ "null", 10 ] ] ],
[ "?", [ "assert", "$base != 1", "DIV/0" ] ]
]);
defineFunction("log10", function(num){
return Math.log(num) / Math.log(10);
}).args([
[ "*num", "number++" ]
]);
defineFunction("pi", function(){
return Math.PI;
}).args([]);
defineFunction("sqrtpi", function(n){
return Math.sqrt(n * Math.PI);
}).args([
[ "*num", "number+" ]
]);
defineFunction("degrees", function(rad){
return ((180 * rad) / Math.PI) % 360;
}).args([
[ "*radians", "number" ]
]);
defineFunction("radians", function(deg){
return Math.PI * deg / 180;
}).args([
[ "*degrees", "number" ]
]);
function _cosh(n){
return (Math.exp(n) + Math.exp(-n)) / 2;
}
defineFunction("cosh", _cosh).args([
[ "*num", "number" ]
]);
defineFunction("acosh", function(n){
return Math.log(n + Math.sqrt(n - 1) * Math.sqrt(n + 1));
}).args([
[ "*num", "number" ],
[ "?", [ "assert", "$num >= 1" ] ]
]);
function _sinh(n){
return (Math.exp(n) - Math.exp(-n)) / 2;
}
defineFunction("sinh", _sinh).args([
[ "*num", "number" ]
]);
defineFunction("asinh", function(n){
return Math.log(n + Math.sqrt(n * n + 1));
}).args([
[ "*num", "number" ]
]);
defineFunction("sec", function(n){
return 1 / Math.cos(n);
}).args([
[ "*num", "number" ]
]);
defineFunction("sech", function(n){
return 1 / _cosh(n);
}).args([
[ "*num", "number" ]
]);
defineFunction("csc", function(n){
return 1 / Math.sin(n);
}).args([
[ "*num", "number" ]
]);
defineFunction("csch", function(n){
return 1 / _sinh(n);
}).args([
[ "*num", "number" ]
]);
defineFunction("atan2", function(x, y){
return Math.atan(y / x);
}).args([
[ "*x", "divisor" ],
[ "*y", "number" ]
]);
function _tanh(n) {
return _sinh(n) / _cosh(n);
}
defineFunction("tanh", _tanh).args([
[ "*num", "number" ]
]);
defineFunction("atanh", function(n){
return Math.log(Math.sqrt(1 - n*n) / (1 - n));
}).args([
[ "*num", [ "and", "number", [ "(between)", -1, 1 ] ] ]
]);
defineFunction("cot", function(n){
return 1 / Math.tan(n);
}).args([
[ "*num", "divisor" ]
]);
defineFunction("coth", function(n){
return 1 / _tanh(n);
}).args([
[ "*num", "divisor" ]
]);
defineFunction("acot", function(n){
return Math.PI / 2 - Math.atan(n);
}).args([
[ "*num", "number" ]
]);
defineFunction("acoth", function(n){
return Math.log((n + 1) / (n - 1)) / 2;
}).args([
[ "*num", "number" ],
[ "?", [ "or",
[ "assert", "$num < -1"],
[ "assert", "$num > 1" ] ] ]
]);
defineFunction("power", function(a, b){
return Math.pow(a, b);
}).args([
[ "*a", "number" ],
[ "*b", "number" ]
]);
defineFunction("mod", function(a, b){
return a % b;
}).args([
[ "*a", "number" ],
[ "*b", "divisor" ]
]);
defineFunction("quotient", function(a, b){
return Math.floor(a / b);
}).args([
[ "*a", "number" ],
[ "*b", "divisor" ]
]);
defineFunction("ceiling", function(num, s){
return s ? s * Math.ceil(num / s) : 0;
}).args([
[ "*number", "number" ],
[ "*significance", "number" ],
[ "?", [ "assert", "$significance >= 0 || $number < 0" ] ]
]);
defineFunction("ceiling.precise", function(num, s){
s = Math.abs(s);
return s ? s * Math.ceil(num / s) : 0;
}).args([
[ "*number", "number" ],
[ "*significance", [ "or", "number", [ "null", 1 ] ] ]
]);
defineAlias("iso.ceiling", "ceiling.precise");
// XXX: how do we know if this function is correct?
//
// https://support.office.com/en-gb/article/CEILING-MATH-function-80f95d2f-b499-4eee-9f16-f795a8e306c8
//
// “There are many combinations of Significance and Mode values that affect rounding of negative
// numbers in different ways.” — right, thanks for the info. :-\
defineFunction("ceiling.math", function(num, s, mode){
if (!s || !num) {
return 0;
}
if (num < 0 && ((!mode && s < 0) || (mode && s > 0))) {
s = -s;
}
return s ? s * Math.ceil(num / s) : 0;
}).args([
[ "*number", "number" ],
[ "*significance", [ "or", "number", [ "null", "$number < 0 ? -1 : 1" ] ] ],
[ "*mode", [ "or", "logical", [ "null", 0 ] ] ]
]);
defineFunction("floor", function(num, s){
return s ? s * Math.floor(num / s) : 0;
}).args([
[ "*number", "number" ],
[ "*significance", "number" ],
[ "?", [ "assert", "$significance >= 0 || $number < 0" ] ]
]);
defineFunction("floor.precise", function(num, s){
s = Math.abs(s);
return s ? s * Math.floor(num / s) : 0;
}).args([
[ "*number", "number" ],
[ "*significance", [ "or", "number", [ "null", 1 ] ] ]
]);
// XXX: check this
defineFunction("floor.math", function(num, s, mode){
if (!s || !num) {
return 0;
}
if (num < 0 && ((!mode && s < 0) || (mode && s > 0))) {
s = -s;
}
return s ? s * Math.floor(num / s) : 0;
}).args([
[ "*number", "number" ],
[ "*significance", [ "or", "number", [ "null", "$number < 0 ? -1 : 1" ] ] ],
[ "*mode", [ "or", "logical", [ "null", 0 ] ] ]
]);
defineFunction("int", Math.floor).args([
[ "*number", "number" ]
]);
defineFunction("mround", function(num, mult){
return mult ? mult * Math.round(num / mult) : 0;
}).args([
[ "*number", "number" ],
[ "*multiple", "number" ]
]);
defineFunction("round", function(num, digits){
var sign = num < 0 ? -1 : 1;
if (sign < 0) { num = -num; }
digits = Math.pow(10, digits);
num *= digits;
num = Math.round(num);
return sign * num / digits;
}).args([
[ "*number", "number" ],
[ "*digits", "number" ]
]);
defineFunction("roundup", function(num, digits){
digits = Math.pow(10, digits);
num *= digits;
num = num < 0 ? Math.floor(num) : Math.ceil(num);
return num / digits;
}).args([
[ "*number", "number" ],
[ "*digits", "number" ]
]);
defineFunction("rounddown", function(num, digits){
digits = Math.pow(10, digits);
num *= digits;
num = num < 0 ? Math.ceil(num) : Math.floor(num);
return num / digits;
}).args([
[ "*number", "number" ],
[ "*digits", "number" ]
]);
defineFunction("even", function(num){
var n = num < 0 ? Math.floor(num) : Math.ceil(num);
return n % 2 ? n + (n < 0 ? -1 : 1) : n;
}).args([
[ "*number", "number" ]
]);
defineFunction("odd", function(num){
var n = num < 0 ? Math.floor(num) : Math.ceil(num);
return n % 2 ? n : n + (n < 0 ? -1 : 1);
}).args([
[ "*number", "number" ]
]);
defineFunction("sign", function(num){
return num < 0 ? -1 : num > 0 ? 1 : 0;
}).args([
[ "*number", "number" ]
]);
function _gcd(a, b) {
while (b) {
var r = a % b;
a = b;
b = r;
}
return a;
}
function _lcm(a, b) {
return Math.abs(a * b) / _gcd(a, b);
}
defineFunction("gcd", function(args){
var a = args[0];
for (var i = 1; i < args.length; ++i) {
a = _gcd(a, args[i]);
}
return a;
}).args([
[ "numbers", [ "collect", "number" ] ]
]);
defineFunction("lcm", function(args){
var a = args[0];
for (var i = 1; i < args.length; ++i) {
a = _lcm(a, args[i]);
}
return a;
}).args([
[ "numbers", [ "collect", "number" ] ]
]);
defineFunction("sum", function(numbers){
return numbers.reduce(function(sum, num){
return sum + num;
}, 0);
}).args([
[ "numbers", [ "collect", "number" ] ]
]);
defineFunction("product", function(numbers){
return numbers.reduce(function(prod, num){
return prod * num;
}, 1);
}).args([
[ "numbers", [ "collect", "number" ] ]
]);
defineFunction("sumproduct", function(first, rest) {
var sum = 0;
first.each(function(p, row, col){
if (typeof p == "number") {
for (var i = 0; i < rest.length; ++i) {
var v = rest[i].get(row, col);
if (typeof v != "number") {
return;
}
p *= v;
}
sum += p;
}
});
return sum;
}).args([
[ "a1", "matrix" ],
[ "+",
[ "a2", [ "and", "matrix",
[ "assert", "$a2.width == $a1.width" ],
[ "assert", "$a2.height == $a1.height" ] ] ] ]
]);
defineFunction("sumsq", function(numbers){
return numbers.reduce(function(sum, num){
return sum + num * num;
}, 0);
}).args([
[ "numbers", [ "collect", "number" ] ]
]);
defineFunction("sumx2my2", function(a, b){
var sum = 0;
a.each(function(x, row, col){
var y = b.get(row, col);
if (typeof x == "number" && typeof y == "number") {
sum += x*x - y*y;
}
});
return sum;
}).args([
[ "a", "matrix" ],
[ "b", [ "and", "matrix",
[ "assert", "$b.width == $a.width" ],
[ "assert", "$b.height == $a.height" ] ] ]
]);
defineFunction("sumx2py2", function(a, b){
var sum = 0;
a.each(function(x, row, col){
var y = b.get(row, col);
if (typeof x == "number" && typeof y == "number") {
sum += x*x + y*y;
}
});
return sum;
}).args([
[ "a", "matrix" ],
[ "b", [ "and", "matrix",
[ "assert", "$b.width == $a.width" ],
[ "assert", "$b.height == $a.height" ] ] ]
]);
defineFunction("sumxmy2", function(a, b){
var sum = 0;
a.each(function(x, row, col){
var y = b.get(row, col);
if (typeof x == "number" && typeof y == "number") {
sum += (x - y) * (x - y);
}
});
return sum;
}).args([
[ "a", "matrix" ],
[ "b", [ "and", "matrix",
[ "assert", "$b.width == $a.width" ],
[ "assert", "$b.height == $a.height" ] ] ]
]);
defineFunction("seriessum", function(x, n, m, a){
var sum = 0;
a.each(function(coef){
if (typeof coef != "number") {
throw new CalcError("VALUE");
}
sum += coef * Math.pow(x, n);
n += m;
});
return sum;
}).args([
[ "x", "number" ],
[ "y", "number" ],
[ "m", "number" ],
[ "a", "matrix" ]
]);
defineFunction("min", function(numbers){
return numbers.length ? Math.min.apply(Math, numbers) : 0;
}).args([
[ "numbers", [ "collect", "number" ] ]
]);
defineFunction("max", function(numbers){
return numbers.length ? Math.max.apply(Math, numbers) : 0;
}).args([
[ "numbers", [ "collect", "number" ] ]
]);
defineFunction("counta", function(values){
return values.length;
}).args([
[ "values", [ "#collect", "anyvalue" ] ]
]);
defineFunction("count", function(numbers){
return numbers.length;
}).args([
[ "numbers", [ "#collect", "number" ] ]
]);
defineFunction("countunique", function(values){
var count = 0, seen = [];
values.forEach(function(val){
if (seen.indexOf(val) < 0) {
count++;
seen.push(val);
}
});
return count;
}).args([
[ "values", [ "#collect", "anyvalue" ] ]
]);
defineFunction("countblank", function(a){
var count = 0;
function add(val) {
if (val == null || val === "") {
count++;
}
}
function loop(args){
for (var i = 0; i < args.length; ++i) {
var x = args[i];
if (x instanceof Matrix) {
x.each(add, true);
} else {
add(x);
}
}
}
loop(a);
return count;
}).args([
[ "+", [ "args", [ "or", "matrix", "anyvalue" ] ] ]
]);
defineFunction("iseven", function(num){
return num % 2 === 0;
}).args([
[ "*number", "number" ]
]);
defineFunction("isodd", function(num){
return num % 2 !== 0;
}).args([
[ "*number", "number" ]
]);
defineFunction("n", function(val){
if (typeof val == "boolean") {
return val ? 1 : 0;
}
if (typeof val == "number") {
return val;
}
return 0;
}).args([
[ "*value", "anyvalue" ]
]);
defineFunction("na", function(){
return new CalcError("N/A");
}).args([]);
/* -----[ the "*IFS" functions ]----- */
// helper function: take `args` like COUNTIFS (see Excel docs) and
// calls `f` for each cell matching all criteria. `f` receives
// `chunks` (parsed args containing matrix and predicate) and
// row,col of matching cells.
function forIFS(args, f) {
var chunks = [], i = 0, matrix = args[0];
while (i < args.length) {
chunks.push({
matrix: args[i++],
pred: parseCriteria(args[i++])
});
}
ROW: for (var row = 0; row < matrix.height; ++row) {
COL: for (var col = 0; col < matrix.width; ++col) {
for (i = 0; i < chunks.length; ++i) {
var val = chunks[i].matrix.get(row, col);
if (!chunks[i].pred(val == null || val === "" ? 0 : val)) {
continue COL;
}
}
f(row, col);
}
}
}
var ARGS_COUNTIFS = [
[ "m1", "matrix" ],
[ "c1", "anyvalue" ],
[ [ "m2", "matrix" ],
[ "c2", "anyvalue" ] ]
];
defineFunction("countifs", function(m1, c1, rest){
var count = 0;
rest.unshift(m1, c1);
forIFS(rest, function(){ count++; });
return count;
}).args(ARGS_COUNTIFS);
var ARGS_SUMIFS = [
[ "range", "matrix" ]
].concat(ARGS_COUNTIFS);
defineFunction("sumifs", function(range, m1, c1, args){
// hack: insert a predicate that filters out non-numeric
// values; should also accept blank cells. it's safe to
// modify args.
args.unshift(range, numericPredicate, m1, c1);
var sum = 0;
forIFS(args, function(row, col){
var val = range.get(row, col);
if (val) {
sum += val;
}
});
return sum;
}).args(ARGS_SUMIFS);
// similar to sumifs, but compute average of matching cells
defineFunction("averageifs", function(range, m1, c1, args){
args.unshift(range, numericPredicate, m1, c1);
var sum = 0, count = 0;
forIFS(args, function(row, col){
var val = range.get(row, col);
if (val == null || val === "") {
val = 0;
}
sum += val;
count++;
});
return count ? sum / count : new CalcError("DIV/0");
}).args(ARGS_SUMIFS);
defineFunction("countif", function(matrix, criteria){
criteria = parseCriteria(criteria);
var count = 0;
matrix.each(function(val){
if (criteria(val)) {
count++;
}
});
return count;
}).args([
[ "range", "matrix" ],
[ "*criteria", "anyvalue" ]
]);
var ARGS_SUMIF = [
[ "range", "matrix" ],
[ "*criteria", "anyvalue" ],
[ "sumRange", [ "or",
[ "and", "matrix",
[ "assert", "$sumRange.width == $range.width" ],
[ "assert", "$sumRange.height == $range.height" ] ],
[ "null", "$range" ] ] ]
];
defineFunction("sumif", function(range, criteria, sumRange){
var sum = 0;
criteria = parseCriteria(criteria);
range.each(function(val, row, col){
if (criteria(val)) {
var v = sumRange.get(row, col);
if (numericPredicate(v)) {
sum += v || 0;
}
}
});
return sum;
}).args(ARGS_SUMIF);
defineFunction("averageif", function(range, criteria, sumRange){
var sum = 0, count = 0;
criteria = parseCriteria(criteria);
range.each(function(val, row, col){
if (criteria(val)) {
var v = sumRange.get(row, col);
if (numericPredicate(v)) {
sum += v || 0;
count++;
}
}
});
return count ? sum / count : new CalcError("DIV/0");
}).args(ARGS_SUMIF);
(function(def){
def("large", function(numbers, nth){
return numbers.sort(descending)[nth];
});
def("small", function(numbers, nth){
return numbers.sort(ascending)[nth];
});
})(function(name, handler){
defineFunction(name, function(matrix, nth){
var numbers = [];
var error = matrix.each(function(val){
if (val instanceof CalcError) {
return val;
}
if (typeof val == "number") {
numbers.push(val);
}
});
if (error) {
return error;
}
if (nth > numbers.length) {
return new CalcError("NUM");
}
return handler(numbers, nth - 1);
}).args([
[ "array", "matrix" ],
[ "*nth", "number++" ]
]);
});
function _avg(numbers) {
return numbers.reduce(function(sum, num){
return sum + num;
}, 0) / numbers.length;
}
function _var_sp(numbers, divisor, avg) {
if (avg == null) {
avg = _avg(numbers);
}
return numbers.reduce(function(sum, num){
return sum + Math.pow(num - avg, 2);
}, 0) / divisor;
}
function _stdev_sp(numbers, divisor) {
return Math.sqrt(_var_sp(numbers, divisor));
}
// https://support.office.com/en-sg/article/STDEV-S-function-7d69cf97-0c1f-4acf-be27-f3e83904cc23
defineFunction("stdev.s", function(numbers){
return _stdev_sp(numbers, numbers.length - 1);
}).args([
[ "numbers", [ "collect", "number" ] ],
[ "?", [ "assert", "$numbers.length >= 2", "NUM" ] ]
]);
// https://support.office.com/en-sg/article/STDEV-P-function-6e917c05-31a0-496f-ade7-4f4e7462f285
defineFunction("stdev.p", function(numbers){
return _stdev_sp(numbers, numbers.length);
}).args([
[ "numbers", [ "collect", "number" ] ],
[ "?", [ "assert", "$numbers.length >= 2", "NUM" ] ]
]);
defineFunction("var.s", function(numbers){
return _var_sp(numbers, numbers.length - 1);
}).args([
[ "numbers", [ "collect", "number" ] ],
[ "?", [ "assert", "$numbers.length >= 2", "NUM" ] ]
]);
defineFunction("var.p", function(numbers){
return _var_sp(numbers, numbers.length);
}).args([
[ "numbers", [ "collect", "number" ] ],
[ "?", [ "assert", "$numbers.length >= 2", "NUM" ] ]
]);
defineFunction("median", function(numbers){
var n = numbers.length;
numbers.sort(ascending);
if (n % 2) {
// when length is odd, the median is the number in the middle
return numbers[n >> 1];
}
// that's the average of the two middle numbers, written in in a fancy way
return (numbers[n >>= 1] + numbers[n - 1]) / 2;
}).args([
[ "numbers", [ "collect", "number" ] ],
[ "?", [ "assert", "$numbers.length > 0", "N/A" ] ]
]);
defineFunction("mode.sngl", function(numbers){
numbers.sort(ascending);
var prev = null, count = 0, max = 1, mode = null;
for (var i = 0; i < numbers.length; ++i) {
var n = numbers[i];
if (n != prev) {
count = 1;
prev = n;
} else {
count++;
}
if (count > max) {
max = count;
mode = n;
}
}
return mode == null ? new CalcError("N/A") : mode;
}).args([
[ "numbers", [ "collect", "number" ] ]
]);
defineFunction("mode.mult", function(numbers){
var seen = Object.create(null), max = 2, res = [];
numbers.forEach(function(num){
var s = seen[num] || 0;
seen[num] = ++s;
if (s == max) {
res.push(num);
} else if (s > max) {
max = s;
res = [ num ];
}
});
var m = new Matrix(this);
res.forEach(function(num, i){
m.set(i, 0, num);
});
return m;
}).args([
[ "numbers", [ "collect", "number" ] ]
]);
defineFunction("geomean", function(numbers){
var n = numbers.length;
var p = numbers.reduce(function(p, num){
if (num < 0) {
throw new CalcError("NUM");
}
return p * num;
}, 1);
return Math.pow(p, 1/n);
}).args([
[ "numbers", [ "collect", "number" ] ],
[ "?", [ "assert", "$numbers.length > 0", "NUM" ] ]
]);
defineFunction("harmean", function(numbers){
var n = numbers.length;
var s = numbers.reduce(function(s, num){
if (!num) {
throw new CalcError("DIV/0");
}
return s + 1 / num;
}, 0);
return n / s;
}).args([
[ "numbers", [ "collect", "number" ] ],
[ "?", [ "assert", "$numbers.length > 0", "NUM" ] ]
]);
defineFunction("trimmean", function(numbers, p){
var n = numbers.length;
numbers.sort(ascending);
var discard = Math.floor(n * p);
if (discard % 2) {
--discard;
}
discard /= 2;
var sum = 0;
for (var i = discard; i < n-discard; ++i) {
sum += numbers[i];
}
return sum / (n - discard * 2);
}).args([
[ "numbers", [ "collect", "number", 1 ] ],
[ "percent", [ "and", "number", [ "[between)", 0, 1 ] ] ],
[ "?", [ "assert", "$numbers.length > 0", "NUM" ] ]
]);
defineFunction("frequency", function(data, bins){
// apparently this always returns a vertical matrix in Excel, so we collect all numbers in
// bins instead of receiving it as a Matrix and try to mimic its shape.
data.sort(ascending);
bins.sort(ascending);
var prev = -Infinity;
var i = 0;
function count(max) {
var n = 0;
while (i < data.length && data[i] > prev && data[i] <= max) {
++n; ++i;
}
return n;
}
var m = new Matrix(this);
bins.forEach(function(val, i){
var n = count(val);
prev = val;
m.set(i, 0, n);
});
m.set(m.height, 0, data.length - i);
return m;
}).args([
[ "data", [ "collect", "number", 1 ] ],
[ "bins", [ "collect", "number", 1 ] ]
]);
defineFunction("rank.eq", function(val, numbers, asc) {
numbers.sort(asc ? ascending : descending);
var pos = numbers.indexOf(val);
return pos < 0 ? new CalcError("N/A") : pos + 1;
}).args([
[ "value", "number" ],
[ "numbers", [ "collect", "number" ] ],
[ "order", [ "or", "logical", [ "null", false ] ] ]
]);
defineAlias("rank", "rank.eq");
defineFunction("rank.avg", function(val, numbers, asc) {
numbers.sort(asc ? ascending : descending);
var pos = numbers.indexOf(val);
if (pos < 0) {
return new CalcError("N/A");
}
for (var i = pos; numbers[i] == val; ++i){}
return (pos + i + 1) / 2;
}).args([
[ "value", "number" ],
[ "numbers", [ "collect", "number" ] ],
[ "order", [ "or", "logical", [ "null", false ] ] ]
]);
// formula available at https://support.office.microsoft.com/en-us/article/KURT-function-cbbc2312-dfa6-4cc4-b5c0-1b3c59cc9377
defineFunction("kurt", function(numbers){
var n = numbers.length;
var avg = _avg(numbers);
var variance = _var_sp(numbers, n-1, avg);
var stddev = Math.sqrt(variance);
var sum = numbers.reduce(function(sum, num){
return sum + Math.pow((num - avg) / stddev, 4);
}, 0);
return n*(n+1)/((n-1)*(n-2)*(n-3)) * sum
- 3*Math.pow(n-1, 2)/((n-2)*(n-3));
}).args([
[ "numbers", [ "collect", "number" ] ],
[ "?", [ "assert", "$numbers.length >= 4", "NUM" ] ]
]);
function _percentrank(numbers, x, exc) {
var nlt = 0, ngt = 0, left = null, right = null, found = false;
numbers.forEach(function(num){
if (num < x) {
nlt++;
left = left == null ? num : Math.max(left, num);
} else if (num > x) {
ngt++;
right = right == null ? num : Math.min(right, num);
} else {
found = true;
}
});
if (!nlt && !ngt) {
return new CalcError("N/A");
}
if (found) {
if (exc) {
return (nlt + 1) / (numbers.length + 1);
}
return nlt / (nlt + ngt);
}
return ((right - x) * _percentrank(numbers, left, exc) +
(x - left) * _percentrank(numbers, right, exc)) / (right - left);
}
var ARGS_PERCENTRANK = [
[ "array", [ "collect", "number", 1 ] ],
[ "x", "number" ],
[ "significance", [ "or", [ "null", 3 ], "integer++" ] ],
[ "?", [ "assert", "$array.length > 0", "NUM" ] ]
];
defineFunction("percentrank.inc", function(numbers, x, significance) {
var p = _percentrank(numbers, x, 0);
p = p.toFixed(significance + 1);
return parseFloat(p.substr(0, p.length - 1));
}).args(ARGS_PERCENTRANK);
defineFunction("percentrank.exc", function(numbers, x, significance) {
var p = _percentrank(numbers, x, 1);
p = p.toFixed(significance + 1);
return parseFloat(p.substr(0, p.length - 1));
}).args(ARGS_PERCENTRANK);
defineAlias("percentrank", "percentrank.inc");
function _covariance(x, y, divisor) {
var sum = 0;
var ax = _avg(x);
var ay = _avg(y);
var n = x.length;
for (var i = 0; i < n; ++i) {
sum += (x[i] - ax) * (y[i] - ay);
}
return sum / divisor;
}
defineFunction("covariance.p", function(x, y){
return _covariance(x, y, x.length);
}).args([
[ "array1", [ "collect", "number", 1 ] ],
[ "array2", [ "collect", "number", 1 ] ],
[ "?", [ "assert", "$array1.length == $array2.length", "N/A" ] ],
[ "?", [ "assert", "$array1.length > 0", "DIV/0" ] ]
]);
defineFunction("covariance.s", function(x, y){
return _covariance(x, y, x.length - 1);
}).args([
[ "array1", [ "collect", "number", 1 ] ],
[ "array2", [ "collect", "number", 1 ] ],
[ "?", [ "assert", "$array1.length == $array2.length", "N/A" ] ],
[ "?", [ "assert", "$array1.length > 1", "DIV/0" ] ]
]);
defineAlias("covar", "covariance.p");
/* -----[ Factorials ]----- */
var _fact = util.memoize(function(n){
for (var i = 2, fact = 1; i <= n; ++i) {
fact *= i;
}
return fact;
});
defineFunction("fact", _fact).args([
[ "*n", "integer+" ]
]);
defineFunction("factdouble", function(n){
for (var i = 2 + (n&1), fact = 1; i <= n; i += 2) {
fact *= i;
}
return fact;
}).args([
[ "*n", "integer+" ]
]);
defineFunction("multinomial", function(numbers){
var div = 1, sum = 0;
numbers.forEach(function(n){
if (n < 0) {
throw new CalcError("NUM");
}
sum += n;
div *= _fact(n);
});
return _fact(sum) / div;
}).args([
[ "numbers", [ "collect", "number" ] ]
]);
var _combinations = util.memoize(function (n, k){
for (var f1 = k + 1, f2 = 1, p1 = 1, p2 = 1; f2 <= n - k; ++f1, ++f2) {
p1 *= f1;
p2 *= f2;
}
return p1/p2;
});
defineFunction("combin", _combinations).args([
[ "*n", "integer++" ],
[ "*k", [ "and", "integer", [ "[between]", 0, "$n" ] ] ]
]);
defineFunction("combina", function(n, k){
return _combinations(n + k - 1, n - 1);
}).args([
[ "*n", "integer++" ],
[ "*k", [ "and", "integer", [ "[between]", 1, "$n" ] ] ]
]);
/* -----[ Statistical functions ]----- */
defineFunction("average", function(numbers){
var sum = numbers.reduce(function(sum, num){
return sum + num;
}, 0);
return sum / numbers.length;
}).args([
// most numeric functions must treat booleans as numbers (1 for TRUE
// and 0 for FALSE), but AVERAGE shouldn't.
[ "numbers", [ "collect", "number!" ] ],
[ "?", [ "assert", "$numbers.length > 0", "DIV/0" ] ]
]);
defineFunction("averagea", function(values){
var sum = 0, count = 0;
values.forEach(function(num){
if (typeof num != "string") {
sum += num;
}
++count;
});
return count ? sum / count : new CalcError("DIV/0");
}).args([
[ "values", [ "collect", "anyvalue" ] ]
]);
function _percentile(numbers, rank) {
numbers.sort(ascending);
var n = numbers.length;
var k = rank | 0, d = rank - k;
if (k === 0) {
return numbers[0];
}
if (k >= n) {
return numbers[n - 1];
}
--k;
return numbers[k] + d * (numbers[k + 1] - numbers[k]);
}
function _percentile_inc(numbers, p){
// algorithm from https://en.wikipedia.org/wiki/Percentile#Microsoft_Excel_method
var rank = p * (numbers.length - 1) + 1;
return _percentile(numbers, rank);
}
function _percentile_exc(numbers, p){
// https://en.wikipedia.org/wiki/Percentile#NIST_method
var rank = p * (numbers.length + 1);
return _percentile(numbers, rank);
}
defineFunction("percentile.inc", _percentile_inc).args([
[ "numbers", [ "collect", "number", 1 ] ],
[ "p", [ "and", "number", [ "[between]", 0, 1 ] ] ]
]);
defineFunction("percentile.exc", _percentile_exc).args([
[ "numbers", [ "collect", "number", 1 ] ],
[ "p", [ "and", "number", [ "(between)", 0, 1 ] ] ]
]);
defineFunction("quartile.inc", function(numbers, quarter){
return _percentile_inc(numbers, quarter / 4);
}).args([
[ "numbers", [ "collect", "number", 1 ] ],
[ "quarter", [ "values", 0, 1, 2, 3, 4 ] ]
]);
defineFunction("quartile.exc", function(numbers, quarter){
return _percentile_exc(numbers, quarter / 4);
}).args([
[ "numbers", [ "collect", "number", 1 ] ],
[ "quarter", [ "values", 0, 1, 2, 3, 4 ] ]
]);
defineAlias("quartile", "quartile.inc");
defineAlias("percentile", "percentile.inc");
var AGGREGATE_FUNCS = [
"AVERAGE", "COUNT", "COUNTA", "MAX", "MIN", "PRODUCT",
"STDEV.S", "STDEV.P", "SUM", "VAR.S", "VAR.P", "MEDIAN",
"MODE.SNGL", "LARGE", "SMALL", "PERCENTILE.INC",
"QUARTILE.INC", "PERCENTILE.EXC", "QUARTILE.EXC"
];
function fetchValuesForAggregate(self, args, options) {
var values = [];
var opt_ignore_hidden_rows = 1;
var opt_ignore_errors = 2;
var opt_use_aggregates = 4;
(function fetchValues(args) {
if (args instanceof Ref) {
self.getRefCells(args, true).forEach(function(cell){
var value = cell.value;
if ((options & opt_ignore_hidden_rows) && cell.hidden) {
return;
}
if (cell.formula) {
// XXX: formula.print is fast, but still, can't we do any better here?
// perhaps access the input string directly somehow?
var str = cell.formula.print(cell.row, cell.col);
if (/^\s*(?:aggregate|subtotal)\s*\(/i.test(str)) {
if (!(options & opt_use_aggregates)) {
return;
}
}
if ("value" in cell.formula) {
value = cell.formula.value;
}
}
if ((options & opt_ignore_errors) && value instanceof CalcError) {
return;
}
if (typeof value == "number" || value instanceof CalcError) {
values.push(value);
}
});
} else if (Array.isArray(args)) {
for (var i = 0; i < args.length; ++i) {
fetchValues(args[i]);
}
} else if (args instanceof Matrix) {
args.each(fetchValues);
} else if (typeof args == "number") {
values.push(args);
} else if (args instanceof CalcError && !(options & opt_ignore_errors)) {
values.push(args);
}
})(args);
return values;
}
// AGGREGATE function
//
// https://support.office.com/en-SG/article/aggregate-function-c8caed56-07df-4aeb-9741-23693ffbe525
//
// we can only partially type-check this function. also, we need to use the async version in
// order to resolve references and delegate values to the function to aggregate.
defineFunction("aggregate", function(callback, funcId, options, args){
// options is a bit field. that makes sense; it's the documentation which doesn't.
var self = this;
self.resolveCells(args, function(){
var values;
if (funcId > 12) {
// "array form"
values = fetchValuesForAggregate(self, args[0], options);
var k = args[1];
if (k instanceof CellRef) {
k = self.getRefData(k);
}
if (typeof k != "number") {
return callback(new CalcError("VALUE"));
}
} else {
values = fetchValuesForAggregate(self, args, options);
}
self.func(AGGREGATE_FUNCS[funcId - 1], callback, values);
});
}).argsAsync([
[ "funcId", [ "values", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 15, 16, 17, 18, 19 ] ],
[ "options", [ "or",
[ "null", 0 ],
[ "values", 0, 1, 2, 3, 4, 5, 6, 7 ] ] ],
[ "args", "rest" ]
]);
defineFunction("subtotal", function(callback, funcId){
var self = this;
var ignoreHidden = funcId > 100;
if (ignoreHidden) {
funcId -= 100;
}
var args = [];
for (var i = 2; i < arguments.length; ++i) {
args.push(arguments[i]);
}
self.resolveCells(args, function(){
var values = fetchValuesForAggregate(self, args, ignoreHidden ? 1 : 0);
self.func(AGGREGATE_FUNCS[funcId - 1], callback, values);
});
}).argsAsync([
[ "funcId", [ "values", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111 ] ],
[ "+", [ "ref", [ "or", "ref", "#matrix" ] ] ]
]);
// https://support.office.com/en-sg/article/AVEDEV-function-ec78fa01-4755-466c-9a2b-0c4f9eacaf6d
defineFunction("avedev", function(numbers){
var avg = numbers.reduce(function(sum, num){
return sum + num;
}, 0) / numbers.length;
return numbers.reduce(function(sum, num){
return sum + Math.abs(num - avg);
}, 0) / numbers.length;
}).args([
[ "numbers", [ "collect", "number" ] ],
[ "?", [ "assert", "$numbers.length >= 2", "NUM" ] ]
]);
function _binom_dist(x, n, p, cumulative) {
if (!cumulative) {
return _combinations(n, x) * Math.pow(p, x) * Math.pow(1-p, n-x);
} else {
var sum = 0;
for (var j = 0; j <= x; ++j) {
sum += _combinations(n, j) * Math.pow(p, j) * Math.pow(1-p, n-j);
}
return sum;
}
}
defineFunction("binom.dist", _binom_dist).args([
[ "successes", "integer+" ],
[ "trials", [ "and", "integer", [ "assert", "$trials >= $successes" ] ] ],
[ "probability", [ "and", "number", [ "[between]", 0, 1 ] ] ],
[ "cumulative", "logical" ]
]);
defineAlias("binomdist", "binom.dist");
defineFunction("binom.inv", function(n, p, alpha){
// XXX: could a binary search be faster?
for (var x = 0; x <= n; ++x) {
if (_binom_dist(x, n, p, true) >= alpha) {
return x;
}
}
return new CalcError("N/A"); // XXX: is this right?
}).args([
[ "trials", "integer+" ],
[ "probability", [ "and", "number", [ "[between]", 0, 1 ] ] ],
[ "alpha", [ "and", "number", [ "[between]", 0, 1 ] ] ]
]);
defineAlias("critbinom", "binom.inv");
defineFunction("binom.dist.range", function(n, p, s, s2){
var sum = 0;
for (var k = s; k <= s2; ++k) {
sum += _combinations(n, k) * Math.pow(p, k) * Math.pow(1-p, n-k);
}
return sum;
}).args([
[ "trials", "integer+" ],
[ "probability", [ "and", "number", [ "[between]", 0, 1 ] ] ],
[ "successes_min", [ "and", "integer", [ "[between]", 0, "$trials" ] ] ],
[ "successes_max", [ "or",
[ "and", "integer",
[ "[between]", "$successes_min", "$trials" ] ],
[ "null", "$successes_min" ] ] ]
]);
defineFunction("negbinom.dist", function(x, k, p, cumulative){
if (cumulative) {
var sum = 0;
while (x >= 0) {
sum += _combinations(x+k-1, x) * Math.pow(p, k) * Math.pow(1-p, x);
x--;
}
return sum;
}
return _combinations(x+k-1, x) * Math.pow(p, k) * Math.pow(1-p, x);
}).args([
[ "number_f", "integer+" ],
[ "number_s", "integer+" ],
[ "probability_s", [ "and", "number", [ "[between]", 0, 1 ] ] ],
[ "cumulative", "logical" ]
]);
defineAlias("negbinomdist", "negbinom.dist");
/* -----[ lookup functions ]----- */
defineFunction("address", function(row, col, abs, a1, sheet){
// by some lucky coincidence, we get the corret `rel` value by just subtracting 1 from the
// abs argument
var cell = new CellRef(row - 1, col - 1, abs - 1);
if (sheet) {
cell.setSheet(sheet, true);
}
return a1 ? cell.print(0, 0) : cell.print();
}).args([
[ "row", "integer++" ],
[ "col", "integer++" ],
[ "abs", [ "or", [ "null", 1 ], [ "values", 1, 2, 3, 4 ]]],
[ "a1", [ "or", [ "null", true ], "logical" ]],
[ "sheet", [ "or", "null", "string" ]]
]);
defineFunction("areas", function(ref){
var count = 0;
(function loop(x){
if (x instanceof CellRef || x instanceof RangeRef) {
count++;
} else if (x instanceof UnionRef) {
x.refs.forEach(loop);
}
// XXX: NameRef if we add support
})(ref);
return count;
}).args([
[ "ref", "ref" ]
]);
defineFunction("choose", function(index, args){
if (index > args.length) {
return new CalcError("N/A");
} else {
return args[index - 1];
}
}).args([
[ "*index", "integer" ],
[ "+", [ "value", "anything" ] ]
]);
defineFunction("column", function(ref){
if (!ref) {
return this.formula.col + 1;
}
if (ref instanceof CellRef) {
return ref.col + 1;
}
return this.asMatrix(ref).mapCol(function(col){
return col + ref.topLeft.col + 1;
});
}).args([
[ "ref", [ "or", "area", "null" ]]
]);
defineFunction("columns", function(m){
return m instanceof Ref ? m.width() : m.width;
}).args([
[ "ref", [ "or", "area", "#matrix" ] ]
]);
defineFunction("formulatext", function(ref){
var cell = this.getRefCells(ref)[0]; // XXX: overkill, but oh well.
if (!cell.formula) {
return new CalcError("N/A");
}
return cell.formula.print(cell.row, cell.col);
}).args([
[ "ref", "ref" ]
]);
defineFunction("hlookup", function(value, m, row, approx){
var resultCol = null;
m.eachCol(function(col){
var data = m.get(0, col);
if (approx) {
if (data > value) {
return true;
}
resultCol = col;
} else if (data === value) {
resultCol = col;
return true;
}
});
if (resultCol == null) {
return new CalcError("N/A");
}
return m.get(row - 1, resultCol);
}).args([
[ "value", "anyvalue" ],
[ "range", "matrix" ],
[ "row", "integer++" ],
[ "approx", [ "or", "logical", [ "null", true ]]]
]);
defineFunction("index", function(callback, ref, row, col, areanum){
var self = this;
if (ref instanceof UnionRef) {
ref = ref.refs[areanum - 1];
}
if ((!row && !col) || !ref) {
return callback(new CalcError("N/A"));
}
if (ref instanceof CellRef) {
ref = ref.toRangeRef();
}
if (ref instanceof RangeRef) {
if (row && col) {
if (col > ref.width() || row > ref.height()) {
return callback(new CalcError("REF"));
}
// fetching a single cell
var cell = ref.toCell(row - 1, col - 1);
self.resolveCells([ cell ], function(){
callback(self.getRefData(cell));
});
return;
}
if (!row) {
// fetch a full column
var colRange = ref.toColumn(col - 1);
self.resolveCells([ colRange ], function(){
callback(self.asMatrix(colRange));
});
return;
}
if (!col) {
// fetch a full row
var rowRange = ref.toRow(row - 1);
self.resolveCells([ rowRange ], function(){