name:
src/Formulas/Math.ts
-rw-r--r--
48930
1import {
2 ArgsChecker
3} from "../Utilities/ArgsChecker";
4import {
5 TypeConverter
6} from "../Utilities/TypeConverter";
7import {
8 Filter
9} from "../Utilities/Filter";
10import {
11 Serializer
12} from "../Utilities/Serializer";
13import {
14 CriteriaFunctionFactory
15} from "../Utilities/CriteriaFunctionFactory";
16import {
17 NumError,
18 DivZeroError,
19 RefError,
20 ValueError,
21 NAError
22} from "../Errors";
23import {
24 erf, gammaln
25} from "../Utilities/MathHelpers";
26import {
27 AVERAGE,
28 COUNT,
29 COUNTA,
30 MAX,
31 MIN,
32 STDEV,
33 STDEVP,
34 VAR,
35 VARP
36} from "./Statistical";
37
38
39/**
40 * Returns the greatest common divisor of one or more integers.
41 * @param values - The values or ranges whose factors to consider in a calculation to find the greatest common divisor.
42 * @returns {number} greatest common divisor.
43 * @constructor
44 */
45let GCD = function (...values) {
46 ArgsChecker.checkAtLeastLength(arguments, 1, "ABS");
47 // Credits: Andrew Pociu
48 for (var r, a, i = values.length - 1, result = values[i]; i;) {
49 for (a = values[--i]; (r = a % result); a = result, result = r) {
50 //empty
51 }
52 }
53 return result;
54};
55
56
57/**
58 * Returns the least common multiple of one or more integers.
59 * @param values - The values or range whose factors to consider in a calculation to find the least common multiple.
60 * @returns {number}
61 * @constructor
62 */
63let LCM = function (...values) {
64 ArgsChecker.checkAtLeastLength(arguments, 1, "LCM");
65 // Credits: Jonas Raoni Soares Silva
66 let o = Filter.flatten(values);
67 for (var i, j, n, d, r = 1; (n = o.pop()) !== undefined;) {
68 while (n > 1) {
69 if (n % 2) {
70 for (i = 3, j = Math.floor(Math.sqrt(n)); i <= j && n % i; i += 2) {}
71 d = (i <= j) ? i : n;
72 } else {
73 d = 2;
74 }
75 for (n /= d, r *= d, i = o.length; i; (o[--i] % d) === 0 && (o[i] /= d) === 1 && o.splice(i, 1)) {}
76 }
77 }
78 return r;
79};
80
81/**
82 * Returns the the logarithm of a specified Gamma function, base e (Euler's number).
83 * @param value - The input number. The natural logarithm of Gamma (value) will be returned. Must be positive.
84 * @returns {number}
85 * @constructor
86 */
87let GAMMALN = function (value) {
88 ArgsChecker.checkLength(arguments, 1, "GAMMALN");
89 let x = TypeConverter.firstValueAsNumber(value);
90 if (x <= 0) {
91 throw new NumError("Function GAMMALN parameter 1 value is " + x + ". It should be greater than 0.");
92 }
93 return gammaln(x);
94};
95
96/**
97 * Returns the absolute value of a number.
98 * @param value to get the absolute value of.
99 * @returns {number} absolute value
100 * @constructor
101 */
102let ABS = function (value) {
103 ArgsChecker.checkLength(arguments, 1, "ABS");
104 let v = TypeConverter.valueToNumber(value);
105 return Math.abs(v);
106};
107
108/**
109 * Returns the inverse cosine of a value, in radians.
110 * @param value The value for which to calculate the inverse cosine. Must be between -1 and 1, inclusive.
111 * @returns {number} inverse cosine of value
112 * @constructor
113 */
114let ACOS = function (value) {
115 ArgsChecker.checkLength(arguments, 1, "ACOS");
116 value = TypeConverter.valueToNumber(value);
117 if (value === -1) {
118 return Math.PI;
119 } else if (value > 1 || value < -1) {
120 throw new NumError("Function ACOS parameter 1 value is " + value + ". Valid values are between -1 and 1 inclusive.");
121 }
122 return Math.acos(value);
123};
124
125/**
126 * Returns the inverse hyperbolic cosine of a number.
127 * @param value The value for which to calculate the inverse hyperbolic cosine. Must be greater than or equal to 1.
128 * @returns {number} to find the inverse hyperbolic cosine for.
129 * @constructor
130 */
131let ACOSH = function (value) {
132 ArgsChecker.checkLength(arguments, 1, "ACOSH");
133 value = TypeConverter.valueToNumber(value);
134 if (value < 1) {
135 throw new NumError("Function ACOSH parameter 1 value is " + value + ". It should be greater than or equal to 1.");
136 }
137 return Math.log(value + Math.sqrt(value * value - 1));
138};
139
140/**
141 * Calculate the hyperbolic arc-cotangent of a value
142 * @param value number not between -1 and 1 inclusively.
143 * @returns {number} hyperbolic arc-cotangent
144 * @constructor
145 */
146let ACOTH = function (value) {
147 ArgsChecker.checkLength(arguments, 1, "ACOTH");
148 value = TypeConverter.valueToNumber(value);
149 if (value <= 1 && value >= -1) {
150 throw new NumError("Function ACOTH parameter 1 value is " + value + ". Valid values cannot be between -1 and 1 inclusive.")
151 }
152 return 0.5 * Math.log((value + 1) / (value - 1));
153};
154
155/**
156 * Returns the inverse sine of a value, in radians.
157 * @param value The value for which to calculate the inverse sine. Must be between -1 and 1, inclusive.
158 * @returns {number} inverse sine of input value
159 * @constructor
160 */
161let ASIN = function (value) {
162 ArgsChecker.checkLength(arguments, 1, "ASIN");
163 value = TypeConverter.valueToNumber(value);
164 if (value === -1) {
165 return Math.PI;
166 } else if (value > 1 || value < -1) {
167 throw new NumError("Function ASIN parameter 1 value is " + value + ". Valid values are between -1 and 1 inclusive.");
168 }
169 return Math.asin(value);
170};
171
172/**
173 * Returns the inverse hyperbolic sine of a number.
174 * @param value The value for which to calculate the inverse hyperbolic sine.
175 * @returns {number} inverse hyperbolic sine of input
176 * @constructor
177 */
178let ASINH = function (value) {
179 ArgsChecker.checkLength(arguments, 1, "ASINH");
180 value = TypeConverter.valueToNumber(value);
181 return Math.log(value + Math.sqrt(value * value + 1));
182};
183
184
185/**
186 * Returns the inverse tangent of a value, in radians.
187 * @param value The value for which to calculate the inverse tangent.
188 * @returns {number} inverse tangent of input value
189 * @constructor
190 */
191let ATAN = function (value) {
192 ArgsChecker.checkLength(arguments, 1, "ATAN");
193 value = TypeConverter.valueToNumber(value);
194 if (value === -1) {
195 return Math.PI;
196 } else if (value > 1 || value < -1) {
197 throw new NumError("Function ATAN parameter 1 value is " + value + ". Valid values are between -1 and 1 inclusive.");
198 }
199 return Math.atan(value);
200};
201
202
203/**
204 * Returns the angle between the x-axis and a line segment from the origin (0,0) to specified coordinate pair (x,y), in radians.
205 * @param x The x coordinate of the endpoint of the line segment for which to calculate the angle from the x-axis.
206 * @param y The y coordinate of the endpoint of the line segment for which to calculate the angle from the x-axis.
207 * @returns {number} angle in radians
208 * @constructor
209 */
210let ATAN2 = function (x, y) {
211 ArgsChecker.checkLength(arguments, 2, "ATAN2");
212 x = TypeConverter.valueToNumber(x);
213 y = TypeConverter.valueToNumber(y);
214 if (x === 0 && y === 0) {
215 throw new DivZeroError("Evaluation of function ATAN2 caused a divide by zero error.");
216 }
217 return Math.atan2(y, x);
218};
219
220
221/**
222 * Returns the inverse hyperbolic tangent of a number.
223 * @param value The value for which to calculate the inverse hyperbolic tangent. Must be between -1 and 1, exclusive.
224 * @returns {number} inverse hyperbolic tangent of input
225 * @constructor
226 */
227let ATANH = function (value) : number {
228 ArgsChecker.checkLength(arguments, 1, "ATANH");
229 value = TypeConverter.valueToNumber(value);
230 if (value >= 1 || value <= -1) {
231 throw new NumError("Function ATANH parameter 1 value is " + value + ". Valid values are between -1 and 1 exclusive.");
232 }
233 if (Math.abs(value) < 1) {
234
235 }
236 return Math["atanh"](value);
237};
238
239/**
240 * Rounds a number up to the nearest even integer.
241 * @param value The value to round to the next greatest even number.
242 * @returns {number} next greatest even number
243 * @constructor
244 */
245let EVEN = function (value) : number {
246 ArgsChecker.checkLength(arguments, 1, "EVEN");
247 let X = TypeConverter.firstValueAsNumber(value);
248 return X % 2 === 1 ? X + 1 : X;
249};
250
251/**
252 * Returns the result of the modulo operator, the remainder after a division operation.
253 * @param dividend The number to be divided to find the remainder.
254 * @param divisor The number to divide by.
255 * @returns {number}
256 * @constructor
257 */
258let MOD = function (dividend, divisor) : number {
259 ArgsChecker.checkLength(arguments, 2, "MOD");
260 let oneN = TypeConverter.valueToNumber(dividend);
261 let twoN = TypeConverter.valueToNumber(divisor);
262 if (twoN === 0) {
263 throw new DivZeroError("Function MOD parameter 2 cannot be zero.");
264 }
265 return oneN % twoN;
266};
267
268
269/**
270 * Rounds a number up to the nearest odd integer.
271 * @param value The value to round to the next greatest odd number.
272 * @returns {number} value to round up to next greatest odd number.
273 * @constructor
274 */
275let ODD = function (value) : number {
276 ArgsChecker.checkLength(arguments, 1, "ODD");
277 let X = TypeConverter.firstValueAsNumber(value);
278 return X % 2 === 1 ? X : X + 1;
279};
280
281/**
282 * Returns a number raised to a power.
283 * @param base - The number to raise to the exponent power.
284 * @param exponent - The exponent to raise base to.
285 * @returns {number} resulting number
286 * @constructor
287 */
288let POWER = function (base, exponent) : number {
289 ArgsChecker.checkLength(arguments, 2, "POWER");
290 let n = TypeConverter.firstValueAsNumber(base);
291 let p = TypeConverter.firstValueAsNumber(exponent);
292 return Math.pow(n, p);
293};
294
295/**
296 * Returns the sum of a series of numbers and/or cells.
297 * @param values The first number or range to add together.
298 * @returns {number} The sum of the series
299 * @constructor
300 */
301let SUM = function (...values) : number {
302 ArgsChecker.checkAtLeastLength(values, 1, "SUM");
303 let result = 0;
304 for (let i = 0; i < values.length; i++) {
305 if (values[i] instanceof Array) {
306 result = result + SUM.apply(this, values[i]);
307 } else {
308 if (values[i] === "") {
309 throw new ValueError("Function SUM parameter "+i+" expects number values. But '"+values[i]+"' is a text and cannot be coerced to a number.");
310 }
311 result = result + TypeConverter.valueToNumber(values[i]);
312 }
313 }
314 return result;
315};
316
317/**
318 * Returns the positive square root of a positive number.
319 * @param value - The number for which to calculate the positive square root.
320 * @returns {number} square root
321 * @constructor
322 */
323let SQRT = function (value) : number {
324 ArgsChecker.checkLength(arguments, 1, "SQRT");
325 let x = TypeConverter.firstValueAsNumber(value);
326 if (x < 0) {
327 throw new ValueError("Function SQRT parameter 1 value is " + x + ". It should be greater than or equal to 0.");
328 }
329 return Math.sqrt(x);
330};
331
332/**
333 * Returns the positive square root of the product of Pi and the given positive number.
334 * @param value - The number which will be multiplied by Pi and have the product's square root returned
335 * @returns {number} the positive square root of the product of Pi and the given positive number.
336 * @constructor
337 */
338let SQRTPI = function (value) : number{
339 ArgsChecker.checkLength(arguments, 1, "SQRTPI");
340 let n = TypeConverter.firstValueAsNumber(value);
341 if (n < 0) {
342 throw new NumError("Function SQRTPI parameter 1 value is " + n + ". It should be greater than or equal to 0.");
343 }
344 return Math.sqrt(n * Math.PI);
345};
346
347/**
348 * Returns the cosine of an angle provided in radians.
349 * @param value - The angle to find the cosine of, in radians.
350 * @returns {number} cosine of angle
351 * @constructor
352 */
353let COS = function (value) : number {
354 ArgsChecker.checkLength(arguments, 1, "COS");
355 let r = TypeConverter.firstValueAsNumber(value);
356 return Math.cos(r);
357};
358
359/**
360 * Returns the hyperbolic cosine of any real number.
361 * @param value - Any real value to calculate the hyperbolic cosine of.
362 * @returns {number} the hyperbolic cosine of the input
363 * @constructor
364 */
365let COSH = function (value) : number {
366 ArgsChecker.checkLength(arguments, 1, "COSH");
367 let r = TypeConverter.firstValueAsNumber(value);
368 return Math["cosh"](r);
369};
370
371/**
372 * Returns the cotangent of any real number. Defined as cot(x) = 1 / tan(x).
373 * @param value - number to calculate the cotangent for
374 * @returns {number} cotangent
375 * @constructor
376 */
377let COT = function (value) : number {
378 ArgsChecker.checkLength(arguments, 1, "COT");
379 let x = TypeConverter.firstValueAsNumber(value);
380 if (x === 0) {
381 throw new DivZeroError("Evaluation of function COT caused a divide by zero error.");
382 }
383 return 1 / Math.tan(x);
384};
385
386/**
387 * Return the hyperbolic cotangent of a value, defined as coth(x) = 1 / tanh(x).
388 * @param value - value to calculate the hyperbolic cotangent value of
389 * @returns {number} hyperbolic cotangent
390 * @constructor
391 */
392let COTH = function (value) : number {
393 ArgsChecker.checkLength(arguments, 1, "COTH");
394 let x = TypeConverter.firstValueAsNumber(value);
395 if (x === 0) {
396 throw new DivZeroError("Evaluation of function COTH caused a divide by zero error.");
397 }
398 return 1 / Math["tanh"](x);
399};
400
401/**
402 * Rounds a number down to the nearest integer that is less than or equal to it.
403 * @param value - The value to round down to the nearest integer.
404 * @returns {number} Rounded number
405 * @constructor
406 */
407let INT = function (value) : number {
408 ArgsChecker.checkLength(arguments, 1, "INT");
409 let x = TypeConverter.firstValueAsNumber(value);
410 return Math.floor(x);
411};
412
413
414/**
415 * Checks whether the provided value is even.
416 * @param value - The value to be verified as even.
417 * @returns {boolean} whether this value is even or not
418 * @constructor
419 */
420let ISEVEN = function (value) : boolean {
421 ArgsChecker.checkLength(arguments, 1, "ISEVEN");
422 if (value === "") {
423 throw new ValueError("Function ISEVEN parameter 1 expects boolean values. But '" + value + "' is a text and cannot be coerced to a boolean.");
424 }
425 let x = TypeConverter.firstValueAsNumber(value);
426 return Math.floor(x) % 2 === 0;
427};
428
429
430/**
431 * Checks whether the provided value is odd.
432 * @param value - The value to be verified as odd.
433 * @returns {boolean} whether this value is odd or not
434 * @constructor
435 */
436let ISODD = function (value) : boolean {
437 ArgsChecker.checkLength(arguments, 1, "ISODD");
438 if (value === "") {
439 throw new ValueError("Function ISODD parameter 1 expects boolean values. But '" + value + "' is a text and cannot be coerced to a boolean.");
440 }
441 let x = TypeConverter.firstValueAsNumber(value);
442 return Math.floor(x) % 2 === 1;
443};
444
445/**
446 * Returns the sine of an angle provided in radians.
447 * @param value - The angle to find the sine of, in radians.
448 * @returns {number} Sine of angle.
449 * @constructor
450 */
451let SIN = function (value) {
452 ArgsChecker.checkLength(arguments, 1, "SIN");
453 let rad = TypeConverter.firstValueAsNumber(value);
454 return rad === Math.PI ? 0 : Math.sin(rad);
455};
456
457/**
458 * Returns the hyperbolic sine of any real number.
459 * @param value - real number to find the hyperbolic sine of
460 * @returns {number} hyperbolic sine
461 * @constructor
462 */
463let SINH = function (value) : number {
464 ArgsChecker.checkLength(arguments, 1, "SINH");
465 let rad = TypeConverter.firstValueAsNumber(value);
466 return Math["sinh"](rad);
467};
468
469/**
470 * The value Pi.
471 * @returns {number} Pi.
472 * @constructor
473 */
474let PI = function () {
475 ArgsChecker.checkLength(arguments, 0, "SINH");
476 return Math.PI;
477};
478
479/**
480 * Returns the the logarithm of a number, base 10.
481 * @param value - The value for which to calculate the logarithm, base 10.
482 * @returns {number} logarithm of the number, in base 10.
483 * @constructor
484 */
485let LOG10 = function (value) : number {
486 ArgsChecker.checkLength(arguments, 1, "LOG10");
487 let n = TypeConverter.firstValueAsNumber(value);
488 if (n < 1) {
489 throw new NumError("Function LOG10 parameter 1 value is " + n + ". It should be greater than 0.");
490 }
491 let ln = Math.log(n);
492 let lb = Math.log(10);
493 return ln / lb;
494};
495
496/**
497 * Returns the the logarithm of a number given a base.
498 * @param value - The value for which to calculate the logarithm given base.
499 * @param base - The base to use for calculation of the logarithm. Defaults to 10.
500 * @returns {number}
501 * @constructor
502 */
503let LOG = function (value, base) : number {
504 ArgsChecker.checkAtLeastLength(arguments, 2, "LOG");
505 let n = TypeConverter.firstValueAsNumber(value);
506 let b = TypeConverter.firstValueAsNumber(base);
507 if (b < 1) {
508 throw new NumError("Function LOG parameter 2 value is " + b + ". It should be greater than 0.");
509 }
510 if (b < 2) {
511 throw new DivZeroError("Evaluation of function LOG caused a divide by zero error.");
512 }
513 let ln = Math.log(n);
514 let lb = Math.log(b);
515 if (lb === 0) {
516 throw new DivZeroError("Evaluation of function LOG caused a divide by zero error.");
517 }
518 return ln / lb;
519};
520
521/**
522 * Returns the logarithm of a number, base e (Euler's number).
523 * @param value - The value for which to calculate the logarithm, base e.
524 * @returns {number} logarithm calculated
525 * @constructor
526 */
527let LN = function (value) : number {
528 ArgsChecker.checkLength(arguments, 1, "LN");
529 let n = TypeConverter.firstValueAsNumber(value);
530 if (n < 1) {
531 throw new NumError("Function LN parameter 1 value is " + n + ". It should be greater than 0.");
532 }
533 return Math.log(n);
534};
535
536/**
537 * Returns the tangent of an angle provided in radians.
538 * @param value - The angle to find the tangent of, in radians.
539 * @returns {number} tangent in radians
540 * @constructor
541 */
542let TAN = function (value) : number {
543 ArgsChecker.checkLength(arguments, 1, "TAN");
544 let rad = TypeConverter.firstValueAsNumber(value);
545 return rad === Math.PI ? 0 : Math.tan(rad);
546};
547
548/**
549 * Returns the hyperbolic tangent of any real number.
550 * @param value - Any real value to calculate the hyperbolic tangent of.
551 * @returns {number} hyperbolic tangent
552 * @constructor
553 */
554let TANH = function (value) : number {
555 ArgsChecker.checkLength(arguments, 1, "TANH");
556 let rad = TypeConverter.firstValueAsNumber(value);
557 return Math["tanh"](rad);
558};
559
560/**
561 * Rounds a number up to the nearest integer multiple of specified significance.
562 * @param value The value to round up to the nearest integer multiple of factor.
563 * @param factor - [ OPTIONAL ] The number to whose multiples value will be rounded.
564 * @returns {number}
565 * @constructor
566 */
567let CEILING = function (value, factor?) : number {
568 ArgsChecker.checkLengthWithin(arguments, 1, 2, "CEILING");
569 let num = TypeConverter.firstValueAsNumber(value);
570 if (factor === undefined) {
571 return Math.ceil(num);
572 }
573 let significance = TypeConverter.firstValueAsNumber(factor);
574 if (significance === 0) {
575 throw new DivZeroError("Function CEILING parameter 2 cannot be zero.");
576 }
577 let precision = -Math.floor(Math.log(significance) / Math.log(10));
578 if (num >= 0) {
579 return ROUND(Math.ceil(num / significance) * significance, precision);
580 } else {
581 return -ROUND(Math.floor(Math.abs(num) / significance) * significance, precision);
582 }
583};
584
585/**
586 * Rounds a number down to the nearest integer multiple of specified significance.
587 * @param value - The value to round down to the nearest integer multiple of factor.
588 * @param factor - The number to whose multiples value will be rounded.
589 * @returns {number}
590 * @constructor
591 */
592let FLOOR = function (value, factor?) : number {
593 ArgsChecker.checkLengthWithin(arguments, 1, 2, "FLOOR");
594 let num = TypeConverter.firstValueAsNumber(value);
595 if (factor === undefined) {
596 return Math.floor(num);
597 }
598 let significance = TypeConverter.firstValueAsNumber(factor);
599 if (significance === 0) {
600 throw new DivZeroError("Function FLOOR parameter 2 cannot be zero.");
601 }
602 significance = significance ? Math.abs(significance) : 1;
603 let precision = -Math.floor(Math.log(significance) / Math.log(10));
604 if (num >= 0) {
605 return ROUND(Math.floor(num / significance) * significance, precision);
606 }
607 return -ROUND(Math.floor(Math.abs(num) / significance) * significance, precision);
608};
609
610/**
611 * Returns one value if a logical expression is TRUE and another if it is FALSE.
612 * @param logicalExpression - An expression or reference to a cell containing an expression that represents some logical value, i.e. TRUE or FALSE.
613 * @param valueIfTrue - The value the function returns if logical_expression is TRUE
614 * @param valueIfFalse - The value the function returns if logical_expression is FALSE.
615 * @returns one value if a logical expression is TRUE and another if it is FALSE.
616 * @constructor
617 */
618let IF = function (logicalExpression, valueIfTrue, valueIfFalse) : any {
619 ArgsChecker.checkLength(arguments, 3, "IF");
620 if (logicalExpression instanceof Array) {
621 if (logicalExpression.length === 0) {
622 throw new RefError("Reference does not exist.");
623 }
624 return IF(logicalExpression[0], valueIfTrue, valueIfFalse);
625 } else if (logicalExpression === "") {
626 return valueIfFalse;
627 }
628 return (TypeConverter.valueToBoolean(logicalExpression)) ? valueIfTrue : valueIfFalse;
629};
630
631
632/**
633 * Returns a conditional count across a range.
634 * @param range - The range that is tested against criterion., value[1];
635 * @param criteria - The pattern or test to apply to range. If the range to check against contains text,
636 * this must be a string. It can be a comparison based string (e.g. "=1", "<1", ">=1") or it can be a wild-card string,
637 * in which * matches any number of characters, and ? matches the next character. Both ? and * can be escaped by placing
638 * a ~ in front of them. If it is neither, it will compared with values in the range using equality comparison.
639 * @returns {number}
640 * @constructor
641 */
642let COUNTIF = function (range, criteria) {
643 ArgsChecker.checkLength(arguments, 2, "COUNTIF");
644 if (!(range instanceof Array)) {
645 range = [range];
646 }
647 let criteriaEvaluation = CriteriaFunctionFactory.createCriteriaFunction(criteria);
648
649 let count = 0;
650 for (let i = 0; i < range.length; i++) {
651 let x = range[i];
652 if (x instanceof Array) {
653 count = count + COUNTIF.apply(this, [x, criteria]);
654 } else if (criteriaEvaluation(x)) {
655 count++;
656 }
657 }
658 return count;
659};
660
661/**
662 * Returns the count of a range depending on multiple criteria.
663 * @param values[0] criteria_range1 - The range to check against criterion1.
664 * @param values[1] criterion1 - The pattern or test to apply to criteria_range1.
665 * @param values[2...N] Repeated sets of ranges and criterion to check.
666 * @returns {number} count
667 * @constructor
668 */
669let COUNTIFS = function (...values) {
670 ArgsChecker.checkAtLeastLength(values, 2, "COUNTIFS");
671 let criteriaEvaluationFunctions = values.map(function (criteria, index) {
672 if (index % 2 === 1) {
673 return CriteriaFunctionFactory.createCriteriaFunction(criteria);
674 } else {
675 return function () {return false;}
676 }
677 });
678 let filteredValues = [];
679 // Flatten arrays/ranges
680 for (let x = 0; x < values.length; x++) {
681 // If this is an array/range parameter
682 if (x % 2 === 0) {
683 filteredValues.push(Filter.flatten(values[x]));
684 } else {
685 filteredValues.push(values[x]);
686 }
687 }
688 let count = 0;
689 // For every value in the range
690 for (let i = 0; i < filteredValues[0].length; i++) {
691 // Check for criteria eval for other ranges and other criteria pairs.
692 let otherCriteriaEvaluationSuccessfulSoFar = true;
693 for (let x = 0; x < filteredValues.length; x += 2) {
694 if (filteredValues[x].length < filteredValues[0].length) {
695 throw new ValueError("Array arguments to COUNTIFS are of different size.");
696 }
697 let criteriaEvaluation = criteriaEvaluationFunctions[x+1];
698 if (otherCriteriaEvaluationSuccessfulSoFar) {
699 if (!criteriaEvaluation(filteredValues[x][i])) { // evaluate THIS value with x+1 index, which is criteria.
700 otherCriteriaEvaluationSuccessfulSoFar = false;
701 }
702 }
703 }
704 if (otherCriteriaEvaluationSuccessfulSoFar) {
705 count++;
706 }
707 }
708 return count;
709};
710
711
712/**
713 * Rounds a number to a certain number of decimal places according to standard rules.
714 * @param value - The value to round to places number of places.
715 * @param places - The number of decimal places to which to round.
716 * @returns {number}
717 * @constructor
718 */
719let ROUND = function (value, places) {
720 ArgsChecker.checkLength(arguments, 2, "ROUND");
721 let n = TypeConverter.firstValueAsNumber(value);
722 let d = TypeConverter.firstValueAsNumber(places);
723 return Math.round(n * Math.pow(10, d)) / Math.pow(10, d);
724};
725
726/**
727 * Rounds a number to a certain number of decimal places, always rounding down to the next valid increment.
728 * @param value - The value to round to places number of places, always rounding down.
729 * @param places - (optional) The number of decimal places to which to round.
730 * @returns {number}
731 * @constructor
732 */
733let ROUNDDOWN = function (value, places?) {
734 ArgsChecker.checkLengthWithin(arguments, 1, 2, "ROUNDDOWN");
735 let n = TypeConverter.firstValueAsNumber(value);
736 if (places === undefined) {
737 return Math.floor(n);
738 }
739 let d = TypeConverter.firstValueAsNumber(places);
740 return Math.floor(n * Math.pow(10, d)) / Math.pow(10, d);
741};
742
743/**
744 * Rounds a number to a certain number of decimal places, always rounding up to the next valid increment.
745 * @param value - The value to round to places number of places, always rounding up.
746 * @param places - (optional) The number of decimal places to which to round.
747 * @returns {number}
748 * @constructor
749 */
750let ROUNDUP = function (value, places?) {
751 ArgsChecker.checkLengthWithin(arguments, 1, 2, "ROUNDUP");
752 let n = TypeConverter.firstValueAsNumber(value);
753 if (places === undefined) {
754 return Math.ceil(n);
755 }
756 let d = TypeConverter.firstValueAsNumber(places);
757 return Math.ceil(n * Math.pow(10, d)) / Math.pow(10, d);
758};
759
760/**
761 * Returns a conditional sum across a range.
762 * @param range - The range which is tested against criterion.
763 * @param criteria - The pattern or test to apply to range. If the range to check against contains text, this must be a
764 * string. It can be a comparison based string (e.g. "=1", "<1", ">=1") or it can be a wild-card string, in which *
765 * matches any number of characters, and ? matches the next character. Both ? and * can be escaped by placing a ~ in
766 * front of them.
767 * @param sumRange - (optional) The range to be summed, if different from range.
768 * @returns {number}
769 * @constructor
770 */
771let SUMIF = function (range, criteria, sumRange?) {
772 ArgsChecker.checkLengthWithin(arguments, 2, 3, "SUMIF");
773
774 let criteriaEvaluation = CriteriaFunctionFactory.createCriteriaFunction(criteria);
775
776 let sum = 0;
777 for (let i = 0; i < range.length; i++) {
778 let x = range[i];
779 if (x instanceof Array) {
780 sum += SUMIF.apply(this, [x, criteria]);
781 } else {
782 if (sumRange && i > sumRange.length-1) {
783 continue;
784 }
785 if (arguments.length === 2 && TypeConverter.canCoerceToNumber(x) && criteriaEvaluation(x)) {
786 sum = sum + TypeConverter.valueToNumber(x);
787 } else if (arguments.length === 3 && TypeConverter.canCoerceToNumber(sumRange[i]) && criteriaEvaluation(x)) {
788 sum = sum + TypeConverter.valueToNumber(sumRange[i]);
789 }
790 }
791 }
792 return sum;
793};
794
795/**
796 * Returns the sum of the squares of a series of numbers and/or cells.
797 * @param values The values or range(s) whose squares to add together.
798 * @returns {number} the sum of the squares if the input.
799 * @constructor
800 */
801let SUMSQ = function (...values) {
802 ArgsChecker.checkAtLeastLength(values, 1, "SUMSQ");
803 let result = 0;
804 for (let i = 0; i < values.length; i++) {
805 if (values[i] instanceof Array) {
806 if (values[i].length === 0) {
807 throw new RefError("Reference does not exist.");
808 }
809 result = result + SUMSQ.apply(this, Filter.filterOutNonNumberValues(values[i]));
810 } else {
811 let n = TypeConverter.valueToNumber(values[i]);
812 result = result + (n * n);
813 }
814 }
815 return result;
816};
817
818
819/**
820 * Returns the product of two numbers. Equivalent to the `*` operator.
821 * @param factor1 - The first multiplicand.
822 * @param factor2 - The second multiplicand.
823 * @constructor
824 */
825let MULTIPLY = function (factor1, factor2) {
826 ArgsChecker.checkLength(arguments, 2, "MULTIPLY");
827 let x = TypeConverter.firstValueAsNumber(factor1);
828 let y = TypeConverter.firstValueAsNumber(factor2);
829 return x * y;
830};
831
832
833/**
834 * Returns the result of the first number minus the second number. Equivalent to the `-` operator.
835 * @param one - The first number.
836 * @param two - the second number.
837 * @returns {number}
838 * @constructor
839 */
840let MINUS = function (one, two) {
841 ArgsChecker.checkLength(arguments, 2, "MINUS");
842 let x = TypeConverter.firstValueAsNumber(one);
843 let y = TypeConverter.firstValueAsNumber(two);
844 return x - y;
845};
846
847
848/**
849 * Returns true if two specified values are equal and true otherwise. Equivalent to the "=" operator.
850 * @param one - First value to check.
851 * @param two - Second value to check.
852 * @returns {boolean} true if values are equal, false if they are not equal.
853 * @constructor
854 */
855let EQ = function (one, two) {
856 ArgsChecker.checkLength(arguments, 2, "EQ");
857 let x = TypeConverter.firstValue(one);
858 let y = TypeConverter.firstValue(two);
859 return x === y;
860};
861
862
863/**
864 * Returns true if the first argument is strictly greater than the second, and false otherwise. Equivalent to the `>`
865 * operator.
866 * @param one - The value to test as being greater than `two`.
867 * @param two - The second value.
868 * @returns {boolean}
869 * @constructor
870 */
871let GT = function (one, two) {
872 ArgsChecker.checkLength(arguments, 2, "GT");
873 let x = TypeConverter.firstValue(one);
874 let y = TypeConverter.firstValue(two);
875 return x > y;
876};
877
878
879/**
880 * Returns true if the first argument is greater than or equal to the second, and false otherwise. Equivalent to the
881 * `>=` operator.
882 * @param one - The value to test as being greater than or equal to `two`.
883 * @param two -The second value.
884 * @returns {boolean}
885 * @constructor
886 */
887let GTE = function (one, two) {
888 ArgsChecker.checkLength(arguments, 2, "GTE");
889 let x = TypeConverter.firstValue(one);
890 let y = TypeConverter.firstValue(two);
891 return x >= y;
892};
893
894
895/**
896 * Returns true if the first argument is strictly less than the second, and false otherwise. Equivalent to the `<`
897 * operator.
898 * @param one - The value to test as being less than `two`.
899 * @param two - The second value.
900 * @returns {boolean}
901 * @constructor
902 */
903let LT = function (one, two) {
904 ArgsChecker.checkLength(arguments, 2, "LT");
905 let x = TypeConverter.firstValue(one);
906 let y = TypeConverter.firstValue(two);
907 return x < y;
908};
909
910
911/**
912 * Returns true if the first argument is less than or equal to the second, and true otherwise. Equivalent to the
913 * `<=` operator.
914 * @param one - The value to test as being less than or equal to `two`.
915 * @param two - The second value.
916 * @constructor
917 */
918let LTE = function (one, two) {
919 ArgsChecker.checkLength(arguments, 2, "LTE");
920 let x = TypeConverter.firstValue(one);
921 let y = TypeConverter.firstValue(two);
922 return x <= y;
923};
924
925
926/**
927 * Returns "TRUE" if two specified values are not equal and "FALSE" otherwise. Equivalent to the "<>" operator.
928 * @param one - The value to test as being not equal to `two`.
929 * @param two - The second valud.
930 * @returns {boolean}
931 * @constructor
932 */
933let NE = function (one, two) {
934 ArgsChecker.checkLength(arguments, 2, "NE");
935 let x = TypeConverter.firstValue(one);
936 let y = TypeConverter.firstValue(two);
937 return x !== y;
938};
939
940
941/**
942 * Returns one number divided by another. Equivalent to the `/` operator.
943 * @param dividend - The number to be divided.
944 * @param divisor - The number to divide by, cannot be 0.
945 * @returns {number} result of dividend / divisor.
946 * @constructor
947 */
948let DIVIDE = function (dividend, divisor) {
949 ArgsChecker.checkLength(arguments, 2, "DIVIDE");
950 let x = TypeConverter.firstValueAsNumber(dividend);
951 let y = TypeConverter.firstValueAsNumber(divisor);
952 if (y < 0) {
953 throw new DivZeroError("Function DIVIDE parameter 2 cannot be zero.");
954 }
955 let result = x / y;
956 if (result == Infinity) {
957 throw new DivZeroError("Evaluation caused divide by zero error.");
958 } else if (isNaN(result)) {
959 throw new DivZeroError("Evaluation caused divide by zero error.");
960 }
961 return result;
962};
963
964
965/**
966 * Returns a random number between 0 inclusive and 1 exclusive.
967 * @returns {number}
968 * @constructor
969 */
970let RAND = function () {
971 ArgsChecker.checkLength(arguments, 0, "RAND");
972 return Math.random();
973};
974
975
976/**
977 * Returns a uniformly random integer between two values, inclusive on high and low. Values with decimal parts may be
978 * used for low and/or high; this will cause the least and greatest possible values to be the next integer greater than
979 * low and/or the next integer less than high, respectively.
980 * @param low - lowest value
981 * @param high - highest value
982 * @returns {number} between low and high.
983 * @constructor
984 */
985let RANDBETWEEN = function (low, high) {
986 ArgsChecker.checkLength(arguments, 2, "RAND");
987 low = Math.floor(TypeConverter.firstValueAsNumber(low));
988 high = Math.ceil(TypeConverter.firstValueAsNumber(high));
989 if (low > high) {
990 throw new NumError("Function RANDBETWEEN parameter 2 value is " + low + ". It should be greater than or equal to "
991 + high + ".");
992 }
993 let diff = Math.abs(low - high);
994 return Math.round(low + (Math.random() * diff));
995};
996
997
998/**
999 * Given an input number, returns `-1` if it is negative, `1` if positive, and `0` if it is zero.
1000 * @param value - The value to check the sign for
1001 * @returns {number} `-1` if it is negative, `1` if positive, and `0` if it is zero.
1002 * @constructor
1003 */
1004let SIGN = function (value) {
1005 ArgsChecker.checkLength(arguments, 1, "SIGN");
1006 let x = TypeConverter.firstValueAsNumber(value);
1007 if (x === 0) {
1008 return 0;
1009 }
1010 return x > 0 ? 1 : -1;
1011};
1012
1013
1014/**
1015 * Truncates a number to a certain number of significant digits by omitting less significant digits.
1016 * @param value - The value to be truncated.
1017 * @param places - [ OPTIONAL - 0 by default ] - The number of significant digits to the right of the decimal point to
1018 * retain. If places is greater than the number of significant digits in value, value is returned without modification.
1019 * places may be negative, in which case the specified number of digits to the left of the decimal place are changed to
1020 * zero. All digits to the right of the decimal place are discarded. If all digits of value are changed to zero, TRUNC
1021 * simply returns 0.
1022 * @returns {number} after truncation
1023 * @constructor
1024 */
1025let TRUNC = function (value, places?) : number {
1026 ArgsChecker.checkLengthWithin(arguments, 1, 2, "TRUNC");
1027 let n = TypeConverter.firstValueAsNumber(value);
1028 let digits = 0;
1029 if (places !== undefined) {
1030 digits = TypeConverter.firstValueAsNumber(places);
1031 }
1032 let sign = (n > 0) ? 1 : -1;
1033 return sign * (Math.floor(Math.abs(n) * Math.pow(10, digits))) / Math.pow(10, digits);
1034};
1035
1036
1037/**
1038 * Converts an angle value in degrees to radians.
1039 * @param angle - The angle to convert from degrees to radians.
1040 * @returns {number} radians
1041 * @constructor
1042 */
1043let RADIANS = function (angle) {
1044 ArgsChecker.checkLength(arguments, 1, "RADIANS");
1045 let d = TypeConverter.firstValueAsNumber(angle);
1046 return d * Math.PI / 180;
1047};
1048
1049/**
1050 * Converts an angle value in radians to degrees.
1051 * @param angle - The angle to convert from radians to degrees.
1052 * @returns {number} degrees
1053 * @constructor
1054 */
1055let DEGREES = function (angle) {
1056 ArgsChecker.checkLength(arguments, 1, "DEGREES");
1057 let r = TypeConverter.firstValueAsNumber(angle);
1058 return r * 180 / Math.PI;
1059};
1060
1061
1062/**
1063 * Returns the complementary Gauss error function of a value.
1064 * @param value - The number for which to calculate the complementary Gauss error function.
1065 * @returns {number} complementary Gauss error function of a value
1066 * @constructor
1067 */
1068let ERFC = function (value) {
1069 ArgsChecker.checkLength(arguments, 1, "ERFC");
1070 let v = TypeConverter.firstValueAsNumber(value);
1071 return v === 0 ? 1 : 1 - erf(v);
1072};
1073
1074
1075/**
1076 * Returns the error function integrated between lower_limit and upper_limit.
1077 * @param lowerLimit - The lower bound for integrating ERF.
1078 * @param upperLimit - [Optional]. The upper bound for integrating ERF. If omitted, ERF integrates between
1079 * zero and lower_limit.
1080 * @returns {number} error function integrated between lower_limit and upper_limit
1081 * @constructor
1082 */
1083let ERF = function (lowerLimit, upperLimit?) : number {
1084 ArgsChecker.checkLengthWithin(arguments, 1, 2, "ERF");
1085 let lower = TypeConverter.firstValueAsNumber(lowerLimit);
1086 let upper = upperLimit !== undefined ? TypeConverter.firstValueAsNumber(upperLimit) : 0;
1087 return upperLimit === undefined ? erf(lower) : erf(upper) - erf(lower);
1088};
1089
1090
1091/**
1092 * Calculates the sum of the sums of the squares of values in two arrays.
1093 * @param arrayX - The array or range of values whose squares will be added to the squares of corresponding
1094 * entries in arrayY and added together.
1095 * @param arrayY - The array or range of values whose squares will be added to the squares of corresponding
1096 * entries in arrayX and added together.
1097 * @returns {number} sum of the sums of the squares
1098 * @constructor
1099 */
1100let SUMX2PY2 = function (arrayX, arrayY) : number {
1101 ArgsChecker.checkLength(arguments, 2, "SUMX2PY2");
1102 let arrOne = Filter.flattenAndThrow(arrayX);
1103 let arrTwo = Filter.flattenAndThrow(arrayY);
1104 if (arrOne.length !== arrTwo.length) {
1105 throw new NAError("Array arguments to SUMX2PY2 are of different size.");
1106 }
1107 let result = 0;
1108 for (let i = 0; i < arrOne.length; i++) {
1109 // If either values at this index are anything but numbers, skip them. This is the behavior in GS at least.
1110 if (typeof arrOne[i] === "number" && typeof arrTwo[i] === "number") {
1111 result += arrOne[i] * arrOne[i] + arrTwo[i] * arrTwo[i];
1112 }
1113 }
1114 return result;
1115};
1116
1117/**
1118 * Calculates the sum of the differences of the squares of values in two arrays.
1119 * @param arrayX - The array or range of values whose squares will be reduced by the squares of corresponding
1120 * entries in array_y and added together.
1121 * @param arrayY - The array or range of values whose squares will be subtracted from the squares of
1122 * corresponding entries in array_x and added together.
1123 * @returns {number} sum of the differences of the squares
1124 * @constructor
1125 */
1126let SUMX2MY2 = function (arrayX, arrayY) : number {
1127 ArgsChecker.checkLength(arguments, 2, "SUMX2MY2");
1128 let arrOne = Filter.flattenAndThrow(arrayX);
1129 let arrTwo = Filter.flattenAndThrow(arrayY);
1130 if (arrOne.length !== arrTwo.length) {
1131 throw new NAError("Array arguments to SUMX2MY2 are of different size.");
1132 }
1133 let result = 0;
1134 for (let i = 0; i < arrOne.length; i++) {
1135 // If either values at this index are anything but numbers, skip them. This is the behavior in GS at least.
1136 if (typeof arrOne[i] === "number" && typeof arrTwo[i] === "number") {
1137 result += arrOne[i] * arrOne[i] - arrTwo[i] * arrTwo[i];
1138 }
1139 }
1140 return result;
1141};
1142
1143
1144// Private function that will recursively generate an array of the unique primitives
1145let _countUnique = function (values: Array<any>) : Object {
1146 let uniques = {};
1147 for (let i = 0; i < values.length; i++) {
1148 if (Array.isArray(values[i])) {
1149 // For some reasons an empty range is converted to a range with a single empty string in it.
1150 if (values[i].length === 0) {
1151 values[i] = [""];
1152 }
1153 let uniquesOfArray = _countUnique(values[i]);
1154 for (let key in uniquesOfArray) {
1155 uniques[key] = true;
1156 }
1157 } else {
1158 uniques[Serializer.serialize(values[i])] = true;
1159 }
1160 }
1161 return uniques;
1162};
1163
1164/**
1165 * Counts the number of unique values in a list of specified values and ranges.
1166 * @param values The values or ranges to consider for uniqueness. Supports an arbitrary number of arguments for this
1167 * function.
1168 * @returns {number} of unique values passed in.
1169 * @constructor
1170 */
1171let COUNTUNIQUE = function (...values) : number {
1172 ArgsChecker.checkAtLeastLength(values, 1, "COUNTUNIQUE");
1173
1174 let uniques = _countUnique(values);
1175 return Object.keys(uniques).length;
1176};
1177
1178
1179/**
1180 * Calculates the sum of the products of corresponding entries in two equal-sized arrays or ranges.
1181 * @param values Arrays or ranges whose entries will be multiplied with corresponding entries in the second such array
1182 * or range.
1183 * @returns {number} sum of the products
1184 * @constructor
1185 */
1186let SUMPRODUCT = function (...values) : number {
1187 ArgsChecker.checkAtLeastLength(values, 1, "SUMPRODUCT");
1188 // Ensuring that all values are array values
1189 for (let x = 0; x < values.length; x++) {
1190 if (!Array.isArray(values[x])) {
1191 values[x] = [values[x]];
1192 }
1193 }
1194
1195 // Flatten any nested ranges (arrays) and check for mismatched range sizes
1196 let flattenedValues = [Filter.flattenAndThrow(values[0])];
1197 for (let x = 1; x < values.length; x++) {
1198 flattenedValues.push(Filter.flattenAndThrow(values[x]));
1199 if (flattenedValues[x].length !== flattenedValues[0].length) {
1200 throw new ValueError("SUMPRODUCT has mismatched range sizes. Expected count: "
1201 + flattenedValues[0].length + ". Actual count: " + flattenedValues[0].length + ".");
1202 }
1203 }
1204
1205 // Do the actual math
1206 let result = 0;
1207 for (let i = 0; i < flattenedValues[0].length; i++) {
1208 let product = 1;
1209 for (let x = 0; x < flattenedValues.length; x++) {
1210 product *= TypeConverter.valueToNumberGracefully(flattenedValues[x][i]);
1211 }
1212 result += product;
1213 }
1214 return result;
1215};
1216
1217
1218/**
1219 * Returns the number of ways to choose some number of objects from a pool of a given size of objects.
1220 * @param m - The size of the pool of objects to choose from.
1221 * @param k - The number of objects to choose.
1222 * @returns {number} number of ways
1223 * @constructor
1224 */
1225let COMBIN = function (m, k) : number {
1226 ArgsChecker.checkLength(arguments, 2, "COMBIN");
1227
1228 let MEMOIZED_FACT = [];
1229 function fact(number) {
1230 let n = Math.floor(number);
1231 if (n === 0 || n === 1) {
1232 return 1;
1233 } else if (MEMOIZED_FACT[n] > 0) {
1234 return MEMOIZED_FACT[n];
1235 } else {
1236 MEMOIZED_FACT[n] = fact(n - 1) * n;
1237 return MEMOIZED_FACT[n];
1238 }
1239 }
1240 let n = TypeConverter.firstValueAsNumber(m);
1241 let c = TypeConverter.firstValueAsNumber(k);
1242 if (n < c) {
1243 throw new NumError("Function COMBIN parameter 2 value is "
1244 + c + ". It should be less than or equal to value of Function COMBIN parameter 1 with " + n + ".");
1245 }
1246 n = Math.floor(n);
1247 c = Math.floor(c);
1248 let div = fact(c) * fact(n - c);
1249 if (div === 0) {
1250 throw new DivZeroError("Evaluation of function COMBIN caused a divide by zero error.");
1251 }
1252 return fact(n) / div;
1253};
1254
1255/**
1256 * Multiply a series of numbers together.
1257 * @param values - values or range of values to multiply by each other.
1258 * @constructor
1259 */
1260let PRODUCT = function (...values) {
1261 ArgsChecker.checkAtLeastLength(values, 2, "PRODUCT");
1262 let value = 1;
1263 let numbers = Filter.flattenAndThrow(values);
1264 for (let i = 0; i < numbers.length; i++) {
1265 value *= TypeConverter.valueToNumber(numbers[i]);
1266 }
1267 return value;
1268};
1269
1270
1271/**
1272 * Divide one number by another
1273 * @param dividend - number to be divided by the divisor.
1274 * @param divisor - number to divide the dividend.
1275 * @returns {number}
1276 * @constructor
1277 */
1278let QUOTIENT = function (dividend, divisor) {
1279 ArgsChecker.checkLength(arguments, 2, "QUOTIENT");
1280 let dv = TypeConverter.firstValueAsNumber(dividend);
1281 let ds = TypeConverter.firstValueAsNumber(divisor);
1282 if (ds === 0) {
1283 throw new DivZeroError("Function QUOTIENT parameter 2 cannot be zero.");
1284 }
1285 return dv / ds;
1286};
1287
1288
1289/**
1290 * Returns a value, but does nothing to it. If given a range, will return first value.
1291 * @param value to return
1292 * @returns any value
1293 * @constructor
1294 */
1295let UPLUS = function (value) : any {
1296 ArgsChecker.checkLength(arguments, 1, "UPLUS");
1297 return TypeConverter.firstValue(value);
1298};
1299
1300
1301/**
1302 * Returns the same number, but with the sign reversed.
1303 * @param value to reverse the sign on
1304 * @returns {number}
1305 * @constructor
1306 */
1307let UMINUS = function (value) {
1308 ArgsChecker.checkLength(arguments, 1, "UMINUS");
1309 let n = TypeConverter.firstValueAsNumber(value);
1310 return n * -1;
1311};
1312
1313
1314/**
1315 * Rounds a number to the nearest integer multiple of another.
1316 * @param value - value to round.
1317 * @param factor - multiple.
1318 * @returns {number}
1319 * @constructor
1320 */
1321let MROUND = function (value, factor) {
1322 ArgsChecker.checkLength(arguments, 2, "MROUND");
1323 let v = TypeConverter.firstValueAsNumber(value);
1324 let f = TypeConverter.firstValueAsNumber(factor);
1325 if (v * f < 0) {
1326 throw new NumError("Parameters of MROUND must have same signs (both positive or both negative).");
1327 }
1328 if (f === 0) {
1329 return 0;
1330 }
1331 return Math.round(v / f) * f;
1332};
1333
1334
1335/**
1336 * Calculates the double-factorial of a number.
1337 * @param value - value or reference to calculate.
1338 * @returns {number}
1339 * @constructor
1340 */
1341let FACTDOUBLE = function (value) {
1342 ArgsChecker.checkLength(arguments, 1, "FACTDOUBLE");
1343 let n = Math.floor(TypeConverter.firstValueAsNumber(value));
1344 function factDoublePrivate(n) {
1345 if (n <= 0) {
1346 return 1;
1347 } else {
1348 return n * factDoublePrivate(n - 2);
1349 }
1350 }
1351 if (n === 0) {
1352 return 0;
1353 } else if (n < 0) {
1354 throw new NumError("Function FACTDOUBLE parameter 1 value is '" + n
1355 + "'. It should be greater than or equal to 0.");
1356 } else {
1357 return factDoublePrivate(n);
1358 }
1359};
1360
1361/**
1362 * Returns a value as a percentage where 100 is 1.0, and 0 is 0.
1363 * @param value - To convert.
1364 * @returns {number}
1365 * @constructor
1366 */
1367let UNARY_PERCENT = function (value) {
1368 ArgsChecker.checkLength(arguments, 1, "UNARY_PERCENT");
1369 return TypeConverter.firstValueAsNumber(value) / 100;
1370};
1371
1372
1373/**
1374 * Returns the factorial of the sum of the arguments divided by the product of the factorials of the arguments.
1375 * @param values - Range of numbers.
1376 * @returns {number}
1377 * @constructor
1378 */
1379let MULTINOMIAL = function (...values) {
1380 ArgsChecker.checkAtLeastLength(values, 1, "MULTINOMIAL");
1381 values = Filter.flattenAndThrow(values).map(TypeConverter.valueToNumber);
1382 let memoizeFact = [];
1383 function _fact(value) {
1384 let n = Math.floor(value);
1385 if (n === 0 || n === 1) {
1386 return 1;
1387 } else if (memoizeFact[n] > 0) {
1388 return memoizeFact[n];
1389 } else {
1390 memoizeFact[n] = _fact(n - 1) * n;
1391 return memoizeFact[n];
1392 }
1393 }
1394 let sum = 0;
1395 let divisor = 1;
1396 for (let i = 0; i < values.length; i++) {
1397 sum += arguments[i];
1398 divisor *= _fact(values[i]);
1399 }
1400 return _fact(sum) / divisor;
1401};
1402
1403
1404/**
1405 * Returns a sum of powers of the number x in accordance with the following formula.
1406 * @param x - The number as an independent variable.
1407 * @param n - The starting power.
1408 * @param m - The number to increment by
1409 * @param coefficients - A series of coefficients. For each coefficient the series sum is extended by one section. You
1410 * can only enter coefficients using cell references.
1411 * @returns {number}
1412 * @constructor
1413 */
1414let SERIESSUM = function (x, n, m, coefficients) {
1415 ArgsChecker.checkLength(arguments, 4, "SERIESSUM");
1416 x = TypeConverter.firstValueAsNumber(x);
1417 n = TypeConverter.firstValueAsNumber(n);
1418 m = TypeConverter.firstValueAsNumber(m);
1419 coefficients = Filter.flattenAndThrow(coefficients).map(TypeConverter.valueToNumber);
1420 let result = coefficients[0] * Math.pow(x, n);
1421 for (let i = 1; i < coefficients.length; i++) {
1422 result += coefficients[i] * Math.pow(x, n + i * m);
1423 }
1424 return result;
1425};
1426
1427
1428/**
1429 * Calculates subtotals. If a range already contains subtotals, these are not used for further calculations.
1430 * @param functionCode - A value that stands for another function: 1=AVERAGE, 2=COUNT, 3=COUNTA, 4=MAX, 5=MIN,
1431 * 6=PRODUCT, 7=STDEV, 8=STDEVP, 9=SUM, 10=VAR, 11=VARP.
1432 * @param values - The ranges whose cells are included.
1433 * @returns {Array}
1434 * @constructor
1435 */
1436let SUBTOTAL = function (functionCode, ...values: Array<Array<any>>) {
1437 ArgsChecker.checkAtLeastLength(arguments, 2, "SUBTOTAL");
1438 functionCode = TypeConverter.firstValueAsNumber(functionCode);
1439 values = Filter.flattenAndThrow(values);
1440 switch (functionCode) {
1441 case 1:
1442 return AVERAGE(values);
1443 case 2:
1444 return COUNT(values);
1445 case 3:
1446 return COUNTA(values);
1447 case 4:
1448 return MAX(values);
1449 case 5:
1450 return MIN(values);
1451 case 6:
1452 return PRODUCT.apply(this, values);
1453 case 7:
1454 return STDEV(values);
1455 case 8:
1456 return STDEVP(values);
1457 case 9:
1458 return SUM(values);
1459 case 10:
1460 return VAR(values);
1461 case 11:
1462 return VARP(values);
1463 default:
1464 throw new ValueError("Value '" + functionCode +
1465 "' does not correspond to a function for use in SUBTOTAL. Value should be between 1 to 11.");
1466 }
1467};
1468
1469
1470export {
1471 ABS,
1472 ACOS,
1473 ACOSH,
1474 ACOTH,
1475 ASIN,
1476 ASINH,
1477 ATAN,
1478 ATAN2,
1479 ATANH,
1480 COT,
1481 COTH,
1482 COSH,
1483 COS,
1484 COUNTUNIQUE,
1485 EVEN,
1486 ERF,
1487 ERFC,
1488 INT,
1489 ISEVEN,
1490 ISODD,
1491 MOD,
1492 ODD,
1493 SIN,
1494 SINH,
1495 SUM,
1496 SQRT,
1497 SQRTPI,
1498 PI,
1499 POWER,
1500 LOG,
1501 LOG10,
1502 LN,
1503 MULTIPLY,
1504 MINUS,
1505 TAN,
1506 TANH,
1507 ROUND,
1508 ROUNDDOWN,
1509 ROUNDUP,
1510 SUMPRODUCT,
1511 SUMIF,
1512 SUMSQ,
1513 SUMX2MY2,
1514 SUMX2PY2,
1515 FLOOR,
1516 IF,
1517 COUNTIF,
1518 COUNTIFS,
1519 CEILING,
1520 TRUNC,
1521 RADIANS,
1522 DEGREES,
1523 COMBIN,
1524 RAND,
1525 RANDBETWEEN,
1526 SIGN,
1527 DIVIDE,
1528 EQ,
1529 GT,
1530 GTE,
1531 LT,
1532 LTE,
1533 NE,
1534 GCD,
1535 LCM,
1536 GAMMALN,
1537 PRODUCT,
1538 QUOTIENT,
1539 UPLUS,
1540 UMINUS,
1541 MROUND,
1542 FACTDOUBLE,
1543 UNARY_PERCENT,
1544 MULTINOMIAL,
1545 SERIESSUM,
1546 SUBTOTAL
1547}