spreadsheet
typeScript/javascript spreadsheet parser, with formulas.
git clone https://git.vogt.world/spreadsheet.git
Log | Files | README.md
← Commit log
commit
message
Refactoring functions into category files, creating Financial.ts
author
Ben Vogt <[email protected]>
date
2017-02-20 22:09:23
stats
3 file(s) changed, 222 insertions(+), 234 deletions(-)
files
src/RawFormulas/Financial.ts
src/RawFormulas/Math.ts
src/RawFormulas/RawFormulas.ts
  1diff --git a/src/RawFormulas/Financial.ts b/src/RawFormulas/Financial.ts
  2new file mode 100644
  3index 0000000..9726438
  4--- /dev/null
  5+++ b/src/RawFormulas/Financial.ts
  6@@ -0,0 +1,83 @@
  7+import {
  8+  ArgsChecker,
  9+  TypeCaster
 10+} from "./Utils";
 11+import {
 12+  CellError
 13+} from "../Errors"
 14+import * as ERRORS from "../Errors"
 15+
 16+/**
 17+ * Formats a number into the locale-specific currency format. WARNING: Currently the equivalent of TRUNC, since this
 18+ * returns numbers
 19+ * @param values[0] number - The value to be formatted.
 20+ * @param values[1] places - [ OPTIONAL - 2 by default ] - The number of decimal places to display.
 21+ * @returns {number} dollars
 22+ * @constructor
 23+ * TODO: In GS and Excel, Dollar values are primitive types at a certain level, meaning you can do =DOLLAR(10) + 10
 24+ * TODO(cont.) and the result will be 20. Right now, JS allows you to inherit from primitives so you can use operators
 25+ * TODO(cont.) on them (eg: new Number(10) + 10 == 20) but TS does not. So for now, Dollar values will be represented
 26+ * TODO(cont.) with the primitive number type. At some point TS might allow me to suppress the warnings with
 27+ * TODO(cont.) https://github.com/Microsoft/TypeScript/issues/9448 or
 28+ * TODO(cont.) https://github.com/Microsoft/TypeScript/issues/11051
 29+ *
 30+ * TODO: Also, this does not do local-specific, as is.
 31+ */
 32+var DOLLAR = function (...values) : number {
 33+  ArgsChecker.checkLengthWithin(values, 1, 2);
 34+  var v = TypeCaster.firstValueAsNumber(values[0]);
 35+  var places = values.length === 2 ? TypeCaster.firstValueAsNumber(values[1]) : 2;
 36+  var sign = (v > 0) ? 1 : -1;
 37+  return sign * (Math.floor(Math.abs(v) * Math.pow(10, places))) / Math.pow(10, places);
 38+};
 39+
 40+
 41+/**
 42+ * Converts a price quotation given as a decimal fraction into a decimal value.
 43+ * @param values[0] fractional_price - The price quotation given using fractional decimal conventions.
 44+ * @param values[1] unit - The units of the fraction, e.g. 8 for 1/8ths or 32 for 1/32nds.
 45+ * @returns {number} decimal value.
 46+ * @constructor
 47+ */
 48+var DOLLARDE = function (...values) : number {
 49+  ArgsChecker.checkLength(values, 2);
 50+  var dollar = TypeCaster.firstValueAsNumber(values[0]);
 51+  var fraction = Math.floor(TypeCaster.firstValueAsNumber(values[1]));
 52+  if (fraction === 0) {
 53+    throw new CellError(ERRORS.DIV_ZERO_ERROR, "Function DOLLARDE parameter 2 cannot be zero.");
 54+  }
 55+  var result = parseInt(dollar.toString(), 10);
 56+  result += (dollar % 1) * Math.pow(10, Math.ceil(Math.log(fraction) / Math.LN10)) / fraction;
 57+  var power = Math.pow(10, Math.ceil(Math.log(fraction) / Math.LN2) + 1);
 58+  if (power === 0) {
 59+    throw new CellError(ERRORS.DIV_ZERO_ERROR, "Evaluation of function DOLLARDE caused a divide by zero error.");
 60+  }
 61+  result = Math.round(result * power) / power;
 62+  return result;
 63+};
 64+
 65+
 66+/**
 67+ * Converts a price quotation given as a decimal value into a decimal fraction.
 68+ * @param values[0] decimal_price - The price quotation given as a decimal value.
 69+ * @param values[1] unit - The units of the desired fraction, e.g. 8 for 1/8ths or 32 for 1/32nds
 70+ * @returns {number} price quotation as decimal fraction.
 71+ * @constructor
 72+ */
 73+var DOLLARFR = function (...values) : number {
 74+  ArgsChecker.checkLength(values, 2);
 75+  var dollar = TypeCaster.firstValueAsNumber(values[0]);
 76+  var unit = Math.floor(TypeCaster.firstValueAsNumber(values[1]));
 77+  if (unit === 0) {
 78+    throw new CellError(ERRORS.DIV_ZERO_ERROR, "Function DOLLARFR parameter 2 cannot be zero.");
 79+  }
 80+  var result = parseInt(dollar.toString(), 10);
 81+  result += (dollar % 1) * Math.pow(10, -Math.ceil(Math.log(unit) / Math.LN10)) * unit;
 82+  return result;
 83+};
 84+
 85+export {
 86+  DOLLAR,
 87+  DOLLARDE,
 88+  DOLLARFR
 89+}
 90\ No newline at end of file
 91diff --git a/src/RawFormulas/Math.ts b/src/RawFormulas/Math.ts
 92index 466d7ba..a4c735c 100644
 93--- a/src/RawFormulas/Math.ts
 94+++ b/src/RawFormulas/Math.ts
 95@@ -1843,6 +1843,128 @@ var EXPONDIST = function (...values) : number {
 96 };
 97 
 98 
 99+/**
100+ * Returns the number of ways to choose some number of objects from a pool of a given size of objects.
101+ * @param values[0] n - The size of the pool of objects to choose from.
102+ * @param values[1] k - The number of objects to choose.
103+ * @returns {number} number of ways
104+ * @constructor
105+ */
106+var COMBIN = function (...values) : number {
107+  var MEMOIZED_FACT = [];
108+  function fact(number) {
109+    var n = Math.floor(number);
110+    if (n === 0 || n === 1) {
111+      return 1;
112+    } else if (MEMOIZED_FACT[n] > 0) {
113+      return MEMOIZED_FACT[n];
114+    } else {
115+      MEMOIZED_FACT[n] = fact(n - 1) * n;
116+      return MEMOIZED_FACT[n];
117+    }
118+  }
119+  ArgsChecker.checkLength(values, 2);
120+  var n = TypeCaster.firstValueAsNumber(values[0]);
121+  var c = TypeCaster.firstValueAsNumber(values[1]);
122+  if (n < c) {
123+    throw new CellError(ERRORS.NUM_ERROR, "Function COMBIN parameter 2 value is "
124+      + c + ". It should be less than or equal to value of Function COMBIN parameter 1 with " + n + ".");
125+  }
126+  n = Math.floor(n);
127+  c = Math.floor(c);
128+  var div = fact(c) * fact(n - c);
129+  if (div === 0) {
130+    throw new CellError(ERRORS.DIV_ZERO_ERROR, "Evaluation of function COMBIN caused a divide by zero error.");
131+  }
132+  return fact(n) / div;
133+};
134+
135+
136+/**
137+ * Calculates r, the Pearson product-moment correlation coefficient of a dataset. Any text encountered in the arguments
138+ * will be ignored. CORREL is synonymous with PEARSON.
139+ * @param values[0] data_y - The range representing the array or matrix of dependent data.
140+ * @param values[1] data_x - The range representing the array or matrix of independent data.
141+ * @returns {number} the Pearson product-moment correlation coefficient.
142+ * @constructor
143+ */
144+var CORREL = function (...values) : number {
145+  function stdev(arr, flag) {
146+    return Math.sqrt(variance(arr, flag));
147+  }
148+  function variance(arr, flag) {
149+    if ((arr.length - (flag ? 1 : 0)) === 0) {
150+      throw new CellError(ERRORS.DIV_ZERO_ERROR, "Evaluation of function CORREL caused a divide by zero error.");
151+    }
152+    return sumsqerr(arr) / (arr.length - (flag ? 1 : 0));
153+  }
154+  function sum(arr) {
155+    var sum = 0;
156+    var i = arr.length;
157+    while (--i >= 0) {
158+      sum += arr[i];
159+    }
160+    return sum;
161+  }
162+  function mean(arr) {
163+    return sum(arr) / arr.length;
164+  }
165+  function sumsqerr(arr) {
166+    var m = mean(arr);
167+    var sum = 0;
168+    var i = arr.length;
169+    var tmp;
170+    while (--i >= 0) {
171+      tmp = arr[i] - m;
172+      sum += tmp * tmp;
173+    }
174+    return sum;
175+  }
176+  function covariance(arr1, arr2) {
177+    var u = mean(arr1);
178+    var v = mean(arr2);
179+    var arr1Len = arr1.length;
180+    var sq_dev = new Array(arr1Len);
181+    for (var i = 0; i < arr1Len; i++) {
182+      sq_dev[i] = (arr1[i] - u) * (arr2[i] - v);
183+    }
184+    if ((arr1Len - 1) === 0) {
185+      throw new CellError(ERRORS.DIV_ZERO_ERROR, "Evaluation of function CORREL caused a divide by zero error.");
186+    }
187+    return sum(sq_dev) / (arr1Len - 1);
188+  }
189+  ArgsChecker.checkLength(values, 2);
190+  if (!Array.isArray(values[0])) {
191+    values[0] = [values[0]];
192+  }
193+  if (!Array.isArray(values[1])) {
194+    values[1] = [values[1]];
195+  }
196+  if (values[0].length !== values[1].length) {
197+    throw new CellError(ERRORS.NA_ERROR, "CORREL has mismatched argument count " + values[0] + " vs " + values[1] + ".");
198+  }
199+  var arr1 = Filter.filterOutNonNumberValues(Filter.flattenAndThrow(values[0]));
200+  var arr2 = Filter.filterOutNonNumberValues(Filter.flattenAndThrow(values[1]));
201+  var stdevArr1 = stdev(arr1, 1);
202+  var stdevArr2 = stdev(arr2, 1);
203+  if (stdevArr1 === 0 || stdevArr2 === 0) {
204+    throw new CellError(ERRORS.DIV_ZERO_ERROR, "Evaluation of function CORREL caused a divide by zero error.");
205+  }
206+  return covariance(arr1, arr2) / stdev(arr1, 1) / stdev(arr2, 1);
207+};
208+
209+/**
210+ * Calculates r, the Pearson product-moment correlation coefficient of a dataset. Any text encountered in the arguments
211+ * will be ignored. PEARSON is synonymous with CORREL.
212+ * @param values[0] data_y - The range representing the array or matrix of dependent data.
213+ * @param values[1] data_x - The range representing the array or matrix of independent data.
214+ * @returns {number} the Pearson product-moment correlation coefficient.
215+ * @constructor
216+ */
217+var PEARSON = function (...values) {
218+  return CORREL.apply(this, values);
219+};
220+
221 
222 export {
223   ABS,
224@@ -1916,5 +2038,8 @@ export {
225   CEILING,
226   TRUNC,
227   RADIANS,
228-  DEGREES
229+  DEGREES,
230+  PEARSON,
231+  CORREL,
232+  COMBIN
233 }
234\ No newline at end of file
235diff --git a/src/RawFormulas/RawFormulas.ts b/src/RawFormulas/RawFormulas.ts
236index cbbc5c4..763a5c3 100644
237--- a/src/RawFormulas/RawFormulas.ts
238+++ b/src/RawFormulas/RawFormulas.ts
239@@ -73,7 +73,10 @@ import {
240   CEILING,
241   TRUNC,
242   RADIANS,
243-  DEGREES
244+  DEGREES,
245+  PEARSON,
246+  CORREL,
247+  COMBIN
248 } from "./Math";
249 import {
250   AND,
251@@ -96,6 +99,11 @@ import {
252   DEC2HEX,
253   DEC2OCT
254 } from "./Misc";
255+import {
256+  DOLLAR,
257+  DOLLARDE,
258+  DOLLARFR
259+} from "./Financial";
260 import {
261   CriteriaFunctionFactory,
262   ArgsChecker,
263@@ -103,7 +111,9 @@ import {
264   TypeCaster,
265   Serializer
266 } from "./Utils";
267-import {CellError} from "../Errors"
268+import {
269+  CellError
270+} from "../Errors"
271 import * as ERRORS from "../Errors"
272 
273 var ACCRINT = Formula["ACCRINT"];
274@@ -124,206 +134,14 @@ var EOMONTH = function (start_date, months) {
275   var edate = moment(start_date).add(months, 'months');
276   return new Date(edate.year(), edate.month(), edate.daysInMonth());
277 };
278-var __COMPLEX = {
279-  "F.DIST": FDIST$LEFTTAILED
280-};
281 var YEARFRAC = Formula["YEARFRAC"];
282 
283 
284-/**
285- * Formats a number into the locale-specific currency format. WARNING: Currently the equivalent of TRUNC, since this
286- * returns numbers
287- * @param values[0] number - The value to be formatted.
288- * @param values[1] places - [ OPTIONAL - 2 by default ] - The number of decimal places to display.
289- * @returns {number} dollars
290- * @constructor
291- * TODO: In GS and Excel, Dollar values are primitive types at a certain level, meaning you can do =DOLLAR(10) + 10
292- * TODO(cont.) and the result will be 20. Right now, JS allows you to inherit from primitives so you can use operators
293- * TODO(cont.) on them (eg: new Number(10) + 10 == 20) but TS does not. So for now, Dollar values will be represented
294- * TODO(cont.) with the primitive number type. At some point TS might allow me to suppress the warnings with
295- * TODO(cont.) https://github.com/Microsoft/TypeScript/issues/9448 or
296- * TODO(cont.) https://github.com/Microsoft/TypeScript/issues/11051
297- *
298- * TODO: Also, this does not do local-specific, as is.
299- */
300-var DOLLAR = function (...values) : number {
301-  ArgsChecker.checkLengthWithin(values, 1, 2);
302-  var v = TypeCaster.firstValueAsNumber(values[0]);
303-  var places = values.length === 2 ? TypeCaster.firstValueAsNumber(values[1]) : 2;
304-  var sign = (v > 0) ? 1 : -1;
305-  return sign * (Math.floor(Math.abs(v) * Math.pow(10, places))) / Math.pow(10, places);
306-};
307-
308-
309-/**
310- * Converts a price quotation given as a decimal fraction into a decimal value.
311- * @param values[0] fractional_price - The price quotation given using fractional decimal conventions.
312- * @param values[1] unit - The units of the fraction, e.g. 8 for 1/8ths or 32 for 1/32nds.
313- * @returns {number} decimal value.
314- * @constructor
315- */
316-var DOLLARDE = function (...values) : number {
317-  ArgsChecker.checkLength(values, 2);
318-  var dollar = TypeCaster.firstValueAsNumber(values[0]);
319-  var fraction = Math.floor(TypeCaster.firstValueAsNumber(values[1]));
320-  if (fraction === 0) {
321-    throw new CellError(ERRORS.DIV_ZERO_ERROR, "Function DOLLARDE parameter 2 cannot be zero.");
322-  }
323-  var result = parseInt(dollar.toString(), 10);
324-  result += (dollar % 1) * Math.pow(10, Math.ceil(Math.log(fraction) / Math.LN10)) / fraction;
325-  var power = Math.pow(10, Math.ceil(Math.log(fraction) / Math.LN2) + 1);
326-  if (power === 0) {
327-    throw new CellError(ERRORS.DIV_ZERO_ERROR, "Evaluation of function DOLLARDE caused a divide by zero error.");
328-  }
329-  result = Math.round(result * power) / power;
330-  return result;
331-};
332-
333-
334-/**
335- * Converts a price quotation given as a decimal value into a decimal fraction.
336- * @param values[0] decimal_price - The price quotation given as a decimal value.
337- * @param values[1] unit - The units of the desired fraction, e.g. 8 for 1/8ths or 32 for 1/32nds
338- * @returns {number} price quotation as decimal fraction.
339- * @constructor
340- */
341-var DOLLARFR = function (...values) : number {
342-  ArgsChecker.checkLength(values, 2);
343-  var dollar = TypeCaster.firstValueAsNumber(values[0]);
344-  var unit = Math.floor(TypeCaster.firstValueAsNumber(values[1]));
345-  if (unit === 0) {
346-    throw new CellError(ERRORS.DIV_ZERO_ERROR, "Function DOLLARFR parameter 2 cannot be zero.");
347-  }
348-  var result = parseInt(dollar.toString(), 10);
349-  result += (dollar % 1) * Math.pow(10, -Math.ceil(Math.log(unit) / Math.LN10)) * unit;
350-  return result;
351-};
352-
353-
354-
355-/**
356- * Returns the number of ways to choose some number of objects from a pool of a given size of objects.
357- * @param values[0] n - The size of the pool of objects to choose from.
358- * @param values[1] k - The number of objects to choose.
359- * @returns {number} number of ways
360- * @constructor
361- */
362-var COMBIN = function (...values) : number {
363-  var MEMOIZED_FACT = [];
364-  function fact(number) {
365-    var n = Math.floor(number);
366-    if (n === 0 || n === 1) {
367-      return 1;
368-    } else if (MEMOIZED_FACT[n] > 0) {
369-      return MEMOIZED_FACT[n];
370-    } else {
371-      MEMOIZED_FACT[n] = fact(n - 1) * n;
372-      return MEMOIZED_FACT[n];
373-    }
374-  }
375-  ArgsChecker.checkLength(values, 2);
376-  var n = TypeCaster.firstValueAsNumber(values[0]);
377-  var c = TypeCaster.firstValueAsNumber(values[1]);
378-  if (n < c) {
379-    throw new CellError(ERRORS.NUM_ERROR, "Function COMBIN parameter 2 value is "
380-        + c + ". It should be less than or equal to value of Function COMBIN parameter 1 with " + n + ".");
381-  }
382-  n = Math.floor(n);
383-  c = Math.floor(c);
384-  var div = fact(c) * fact(n - c);
385-  if (div === 0) {
386-    throw new CellError(ERRORS.DIV_ZERO_ERROR, "Evaluation of function COMBIN caused a divide by zero error.");
387-  }
388-  return fact(n) / div;
389-};
390-
391-
392-/**
393- * Calculates r, the Pearson product-moment correlation coefficient of a dataset. Any text encountered in the arguments
394- * will be ignored. CORREL is synonymous with PEARSON.
395- * @param values[0] data_y - The range representing the array or matrix of dependent data.
396- * @param values[1] data_x - The range representing the array or matrix of independent data.
397- * @returns {number} the Pearson product-moment correlation coefficient.
398- * @constructor
399- */
400-var CORREL = function (...values) : number {
401-  function stdev(arr, flag) {
402-    return Math.sqrt(variance(arr, flag));
403-  }
404-  function variance(arr, flag) {
405-    if ((arr.length - (flag ? 1 : 0)) === 0) {
406-      throw new CellError(ERRORS.DIV_ZERO_ERROR, "Evaluation of function CORREL caused a divide by zero error.");
407-    }
408-    return sumsqerr(arr) / (arr.length - (flag ? 1 : 0));
409-  }
410-  function sum(arr) {
411-    var sum = 0;
412-    var i = arr.length;
413-    while (--i >= 0) {
414-      sum += arr[i];
415-    }
416-    return sum;
417-  }
418-  function mean(arr) {
419-    return sum(arr) / arr.length;
420-  }
421-  function sumsqerr(arr) {
422-    var m = mean(arr);
423-    var sum = 0;
424-    var i = arr.length;
425-    var tmp;
426-    while (--i >= 0) {
427-      tmp = arr[i] - m;
428-      sum += tmp * tmp;
429-    }
430-    return sum;
431-  }
432-  function covariance(arr1, arr2) {
433-    var u = mean(arr1);
434-    var v = mean(arr2);
435-    var arr1Len = arr1.length;
436-    var sq_dev = new Array(arr1Len);
437-    for (var i = 0; i < arr1Len; i++) {
438-      sq_dev[i] = (arr1[i] - u) * (arr2[i] - v);
439-    }
440-    if ((arr1Len - 1) === 0) {
441-      throw new CellError(ERRORS.DIV_ZERO_ERROR, "Evaluation of function CORREL caused a divide by zero error.");
442-    }
443-    return sum(sq_dev) / (arr1Len - 1);
444-  }
445-  ArgsChecker.checkLength(values, 2);
446-  if (!Array.isArray(values[0])) {
447-    values[0] = [values[0]];
448-  }
449-  if (!Array.isArray(values[1])) {
450-    values[1] = [values[1]];
451-  }
452-  if (values[0].length !== values[1].length) {
453-    throw new CellError(ERRORS.NA_ERROR, "CORREL has mismatched argument count " + values[0] + " vs " + values[1] + ".");
454-  }
455-  var arr1 = Filter.filterOutNonNumberValues(Filter.flattenAndThrow(values[0]));
456-  var arr2 = Filter.filterOutNonNumberValues(Filter.flattenAndThrow(values[1]));
457-  var stdevArr1 = stdev(arr1, 1);
458-  var stdevArr2 = stdev(arr2, 1);
459-  if (stdevArr1 === 0 || stdevArr2 === 0) {
460-    throw new CellError(ERRORS.DIV_ZERO_ERROR, "Evaluation of function CORREL caused a divide by zero error.");
461-  }
462-  return covariance(arr1, arr2) / stdev(arr1, 1) / stdev(arr2, 1);
463-};
464-
465-/**
466- * Calculates r, the Pearson product-moment correlation coefficient of a dataset. Any text encountered in the arguments
467- * will be ignored. PEARSON is synonymous with CORREL.
468- * @param values[0] data_y - The range representing the array or matrix of dependent data.
469- * @param values[1] data_x - The range representing the array or matrix of independent data.
470- * @returns {number} the Pearson product-moment correlation coefficient.
471- * @constructor
472- */
473-var PEARSON = function (...values) {
474-  return CORREL.apply(this, values);
475+// Using alias to bind dot-notation function names.
476+var __COMPLEX = {
477+  "F.DIST": FDIST$LEFTTAILED
478 };
479 
480-
481 export {
482   __COMPLEX,
483