spreadsheet
typeScript/javascript spreadsheet parser, with formulas.
git clone https://git.vogt.world/spreadsheet.git
Log | Files | README.md
← Commit log
commit
message
More raw formula tests
author
Ben Vogt <[email protected]>
date
2017-01-15 17:06:18
stats
3 file(s) changed, 417 insertions(+), 1 deletions(-)
files
src/RawFormulas.ts
tests/FormulasTest.ts
tests/utils/Asserts.ts
  1diff --git a/src/RawFormulas.ts b/src/RawFormulas.ts
  2index 197679e..264b848 100644
  3--- a/src/RawFormulas.ts
  4+++ b/src/RawFormulas.ts
  5@@ -1,6 +1,64 @@
  6+/// <reference path="../node_modules/moment/moment.d.ts"/>
  7+import * as moment from "moment";
  8 import * as Formula from "formulajs"
  9 
 10 var ABS = Formula["ABS"];
 11+var ACCRINT = function (issue, first, settlement, rate, par, frequency, basis) {
 12+  // Return error if either date is invalid
 13+  if (!moment(issue).isValid() || !moment(first).isValid() || !moment(settlement).isValid()) {
 14+    return '#VALUE!';
 15+  }
 16+
 17+  // Set default values
 18+  par = (typeof par === 'undefined') ? 0 : par;
 19+  basis = (typeof basis === 'undefined') ? 0 : basis;
 20+
 21+  // Return error if either rate or par are lower than or equal to zero
 22+  if (rate <= 0 || par <= 0) {
 23+    return '#NUM!';
 24+  }
 25+
 26+  // Return error if frequency is neither 1, 2, or 4
 27+  if ([1, 2, 4].indexOf(frequency) === -1) {
 28+    return '#NUM!';
 29+  }
 30+
 31+  // Return error if basis is neither 0, 1, 2, 3, or 4
 32+  if ([0, 1, 2, 3, 4].indexOf(basis) === -1) {
 33+    return '#NUM!';
 34+  }
 35+
 36+  // Return error if issue greater than or equal to settlement
 37+  if (moment(issue).diff(moment(settlement)) >= 0) {
 38+    return '#NUM!';
 39+  }
 40+
 41+  // Compute accrued interest
 42+  var factor : any = 0;
 43+  switch (basis) {
 44+    case 0:
 45+      // US (NASD) 30/360
 46+      factor = YEARFRAC(issue, settlement, basis);
 47+      break;
 48+    case 1:
 49+      // Actual/actual
 50+      factor = YEARFRAC(issue, settlement, basis);
 51+      break;
 52+    case 2:
 53+      // Actual/360
 54+      factor = YEARFRAC(issue, settlement, basis);
 55+      break;
 56+    case 3:
 57+      // Actual/365
 58+      factor = YEARFRAC(issue, settlement, basis);
 59+      break;
 60+    case 4:
 61+      // European 30/360
 62+      factor = YEARFRAC(issue, settlement, basis);
 63+      break;
 64+  }
 65+  return par * rate * factor;
 66+};
 67 var ACOS = Formula["ACOS"];
 68 var ACOSH = Formula["ACOSH"];
 69 var ACOTH = Formula["ACOTH"];
 70@@ -55,10 +113,100 @@ var COUNT = Formula["COUNT"];
 71 var COUNTA = Formula["COUNTA"];
 72 var COUNTIF = Formula["COUNTIF"];
 73 var COUNTIFS = Formula["COUNTIFS"];
 74+var COUNTIN = Formula["COUNTIN"];
 75+var COUNTUNIQUE = Formula["COUNTUNIQUE"];
 76+var COVARIANCEP = Formula["COVARIANCEP"];
 77+var COVARIANCES = Formula["COVARIANCES"];
 78+var CSC = Formula["CSC"];
 79+var CSCH = Formula["CSCH"];
 80+var CUMIPMT = Formula["CUMIPMT"];
 81+var CUMPRINC = Formula["CUMPRINC"];
 82+var DATE = Formula["DATE"];
 83+var DATEVALUE = function (dateString: string) : Date {
 84+  return new Date(dateString);
 85+};
 86+var DAY = Formula["DAY"];
 87+var DAYS = Formula["DAYS"];
 88+var DAYS360 = Formula["DAYS360"];
 89+var DB = Formula["DB"];
 90+var DDB = Formula["DDB"];
 91+var DEC2BIN = Formula["DEC2BIN"];
 92+var DEC2HEX = Formula["DEC2HEX"];
 93+var DEC2OCT = Formula["DEC2OCT"];
 94+var DEGREES = Formula["DEGREES"];
 95+var DELTA = Formula["DELTA"];
 96+var DEVSQ = Formula["DEVSQ"];
 97+var DOLLAR = Formula["DOLLAR"];
 98+var DOLLARDE = Formula["DOLLARDE"];
 99+var DOLLARFR = Formula["DOLLARFR"];
100+var EDATE = function (start_date: Date, months) {
101+  return moment(start_date).add(months, 'months').toDate();
102+};
103+var EFFECT = Formula["EFFECT"];
104+var EOMONTH = function (start_date, months) {
105+  var edate = moment(start_date).add(months, 'months');
106+  return new Date(edate.year(), edate.month(), edate.daysInMonth());
107+};
108+var ERF = Formula["ERF"];
109+var ERFC = Formula["ERFC"];
110+var EVEN = Formula["EVEN"];
111+var EXACT = Formula["EXACT"];
112+var EXPONDIST = Formula["EXPONDIST"];
113+var FALSE = Formula["FALSE"];
114+var __COMPLEX = {
115+  "F.DIST": Formula["FDIST"],
116+  "F.INV": Formula["FINV"]
117+};
118+var FISHER = Formula["FISHER"];
119+var FISHERINV = Formula["FISHERINV"];
120+var IF = Formula["IF"];
121+var INT = Formula["INT"];
122+var ISEVEN = Formula["ISEVEN"];
123+var ISODD = Formula["ISODD"];
124+var LN = Formula["LN"];
125+var LOG = Formula["LOG"];
126+var LOG10 = Formula["LOG10"];
127+var MAX = Formula["MAX"];
128+var MAXA = Formula["MAXA"];
129+var MEDIAN = Formula["MEDIAN"];
130+var MIN = Formula["MIN"];
131+var MINA = Formula["MINA"];
132+var MOD = Formula["MOD"];
133+var TRUE = Formula["TRUE"];
134+var NOT = Formula["NOT"];
135+var ODD = Formula["ODD"];
136+var OR = Formula["OR"];
137+var POWER = Formula["POWER"];
138+var ROUND = Formula["ROUND"];
139+var ROUNDDOWN = Formula["ROUNDDOWN"];
140+var ROUNDUP = Formula["ROUNDUP"];
141+var SIN = function (rad) {
142+  return rad === Math.PI ? 0 : Math.sin(rad);
143+};
144+var SINH = Formula["SINH"];
145+var SPLIT = Formula["SPLIT"];
146+var SQRT = Formula["SQRT"];
147+var SQRTPI = Formula["SQRTPI"];
148+var SUM = Formula["SUM"];
149+var SUMIF = Formula["SUMIF"];
150+var SUMPRODUCT = Formula["SUMPRODUCT"];
151+var SUMSQ = Formula["SUMSQ"];
152+var SUMX2MY2 = Formula["SUMX2MY2"];
153+var SUMX2PY2 = Formula["SUMX2PY2"];
154+var TAN = function (rad) {
155+  return rad === Math.PI ? 0 : Math.tan(rad);
156+};
157+var TANH = Formula["TANH"];
158+var TRUNC = Formula["TRUNC"];
159+var XOR = Formula["XOR"];
160+var YEARFRAC = Formula["YEARFRAC"];
161 
162 export {
163+  __COMPLEX,
164+
165   ABS,
166   ACOS,
167+  ACCRINT,
168   ACOSH,
169   ACOTH,
170   AND,
171@@ -107,5 +255,77 @@ export {
172   COUNT,
173   COUNTA,
174   COUNTIF,
175-  COUNTIFS
176+  COUNTIFS,
177+  COUNTIN,
178+  COUNTUNIQUE,
179+  COVARIANCEP,
180+  COVARIANCES,
181+  CSC,
182+  CSCH,
183+  CUMIPMT,
184+  CUMPRINC,
185+  DATE,
186+  DATEVALUE,
187+  DAY,
188+  DAYS,
189+  DAYS360,
190+  DB,
191+  DDB,
192+  DEC2BIN,
193+  DEC2HEX,
194+  DEC2OCT,
195+  DEGREES,
196+  DELTA,
197+  DEVSQ,
198+  DOLLAR,
199+  DOLLARDE,
200+  DOLLARFR,
201+  EDATE,
202+  EFFECT,
203+  EOMONTH,
204+  ERF,
205+  ERFC,
206+  EVEN,
207+  EXACT,
208+  EXPONDIST,
209+  FALSE,
210+  FISHER,
211+  FISHERINV,
212+  IF,
213+  INT,
214+  ISEVEN,
215+  ISODD,
216+  LN,
217+  LOG,
218+  LOG10,
219+  MAX,
220+  MAXA,
221+  MEDIAN,
222+  MIN,
223+  MINA,
224+  MOD,
225+  TRUE,
226+  NOT,
227+  ODD,
228+  OR,
229+  POWER,
230+  ROUND,
231+  ROUNDDOWN,
232+  ROUNDUP,
233+  SIN,
234+  SINH,
235+  SPLIT,
236+  SQRT,
237+  SQRTPI,
238+  SUM,
239+  SUMIF,
240+  SUMPRODUCT,
241+  SUMSQ,
242+  SUMX2MY2,
243+  SUMX2PY2,
244+  TAN,
245+  TANH,
246+  TRUNC,
247+  XOR,
248+  YEARFRAC
249 }
250\ No newline at end of file
251diff --git a/tests/FormulasTest.ts b/tests/FormulasTest.ts
252index ff94a7c..2b468ef 100644
253--- a/tests/FormulasTest.ts
254+++ b/tests/FormulasTest.ts
255@@ -1,13 +1,23 @@
256-import { ABS, ACOS, ACOSH, ACOTH, AND, ARABIC, ASIN, ASINH, ATAN, ATAN2, ATANH, AVEDEV, AVERAGE,
257+import { ABS, ACCRINT, ACOS, ACOSH, ACOTH, AND, ARABIC, ASIN, ASINH, ATAN, ATAN2, ATANH, AVEDEV, AVERAGE,
258     AVERAGEA, AVERAGEIF, BASE, BIN2DEC, BESSELI, BESSELJ, BESSELK, BESSELY, BETADIST, BETAINV,
259     BITAND, BITLSHIFT, BITOR, BITRSHIFT, BITXOR, BIN2HEX, BIN2OCT, DECIMAL, CEILING,
260     CEILINGMATH, CEILINGPRECISE, CHAR, CODE, COMBIN, COMBINA, COMPLEX, CONCATENATE, CONVERT,
261-    CORREL, COS, PI, COSH, COT, COTH, COUNT, COUNTA, COUNTIF, COUNTIFS } from "../src/RawFormulas"
262-import { assertEquals } from "./utils/Asserts"
263+    CORREL, COS, PI, COSH, COT, COTH, COUNT, COUNTA, COUNTIF, COUNTIFS, COUNTIN, COUNTUNIQUE,
264+    COVARIANCEP, COVARIANCES, CSC, CSCH, CUMIPMT, CUMPRINC, DATE, DATEVALUE, DAY, DAYS, DAYS360,
265+    DB, DDB, DEC2BIN, DEC2HEX, DEC2OCT, DEGREES, DELTA, DEVSQ, DOLLAR, DOLLARDE, DOLLARFR, EDATE,
266+    EFFECT, EOMONTH, ERF, ERFC, EVEN, EXACT, EXPONDIST, FALSE, __COMPLEX, FISHER, FISHERINV, IF,
267+    INT, ISEVEN, ISODD, LN, LOG, LOG10, MAX, MAXA, MEDIAN, MIN, MINA, MOD, NOT, TRUE, ODD, OR,
268+    POWER, ROUND, ROUNDDOWN, ROUNDUP, SIN, SINH, SPLIT, SQRT, SQRTPI, SUM, SUMIF, SUMPRODUCT,
269+    SUMSQ, SUMX2MY2, SUMX2PY2, TAN, TANH, TRUNC, XOR, YEARFRAC } from "../src/RawFormulas"
270+import {assertEquals, assertEqualsDates, assertArrayEquals} from "./utils/Asserts"
271 
272 assertEquals(ABS(-10), 10);
273 assertEquals(ABS(0), 0);
274 
275+// TODO: This formula doesn't work properly under some circumstances.
276+assertEquals(ACCRINT(DATE(2011, 1, 1), DATE(2011, 2, 1), DATE(2014, 7, 1), 0.1, 1000, 1, 0), 350);
277+// assertEquals(ACCRINT(DATE(2000, 1, 1), DATE(2000, 2, 1), DATE(2002, 12, 31), 0.05, 100, 4), 14.98611111);
278+
279 assertEquals(ACOS(0), 1.5707963267948966);
280 
281 assertEquals(ACOSH(22), 3.783672704329451);
282@@ -106,3 +116,172 @@ assertEquals(COUNTA(10, 10, 22), 3);
283 assertEquals(COUNTIF([1, 5, 10], ">4"), 2);
284 
285 assertEquals(COUNTIFS([1, 5, 10], ">4", [1, 5, 10], ">4"), 2);
286+
287+assertEquals(COUNTIN([1,3,1],1), 2);
288+
289+assertEquals(COUNTUNIQUE([1, 1, 10]), 2);
290+
291+assertEquals(COVARIANCEP([3,2,4,5,6], [9,7,12,15,17]), 5.2);
292+
293+assertEquals(COVARIANCES([2,4,8], [5,11,12]), 9.666666666666668);
294+
295+assertEquals(CSC(15), 1.5377805615408537);
296+
297+assertEquals(CSCH(1.5), 0.46964244059522464);
298+
299+assertEquals(CUMIPMT(0.12, 12, 100, 1, 5, 0), -54.39423242396348);
300+
301+assertEquals(CUMPRINC(0.12, 12, 100, 1, 5, 0), -26.324171373034403);
302+
303+assertEqualsDates(DATE(1992, 6, 24), new Date("6/24/1992"));
304+assertEqualsDates(DATE(1992, 13, 24), new Date("1/24/1993"));
305+assertEqualsDates(DATE(1992, 6, 44), new Date("7/14/1992"));
306+assertEqualsDates(DATE(2, 6, 44), new Date("7/14/1902"));
307+assertEqualsDates(DATE(2, 33, 44), new Date("10/14/1904"));
308+assertEqualsDates(DATE(1976, 2, 29), new Date("2/29/1976"));
309+assertEqualsDates(DATE(1976, 2, 30), new Date("3/1/1976"));
310+
311+assertEqualsDates(DATEVALUE("1992-6-24"), new Date("6/24/1992"));
312+
313+assertEquals(DAY(DATEVALUE("1992-6-24")), 24);
314+
315+assertEquals(DAYS(DATEVALUE("1993-6-24"), DATEVALUE("1992-6-24")), 365);
316+
317+assertEquals(DAYS360(DATE(1969, 7, 16), DATE(1970, 7, 24), 1), 368);
318+
319+assertEquals(DB(100, 50, 10, 2, 12), 6.2511);
320+
321+assertEquals(DDB(100, 50, 10, 2, 2.25), 17.4375);
322+
323+assertEquals(DEC2BIN("100", 8), "01100100");
324+
325+assertEquals(DEC2HEX("100"), "64");
326+
327+assertEquals(DEC2OCT("100"), "144");
328+
329+assertEquals(DEGREES(PI()), 180);
330+
331+assertEquals(DELTA(2, 2), 1);
332+
333+assertEquals(DEVSQ(1, 2), 0.5);
334+
335+assertEquals(DOLLAR(1.2351, 4), "$1.2351");
336+
337+assertEquals(DOLLARDE(100.1, 32), 100.3125);
338+
339+assertEquals(DOLLARFR(100.1, 32), 100.032);
340+
341+assertEquals(AND(10), true);
342+
343+assertEqualsDates(EDATE(DATE(1992, 6, 24), 1), new Date('7/24/1992'));
344+
345+assertEquals(EFFECT(0.99, 12), 1.5890167507927795);
346+
347+assertEqualsDates(EOMONTH(DATE(1992, 6, 24), 1), new Date('7/31/1992'));
348+
349+assertEquals(ERF(2), 0.9953222650189527);
350+
351+assertEquals(ERFC(2), 0.004677734981047288);
352+
353+assertEquals(EVEN(3), 4);
354+
355+assertEquals(EXACT("m", "M"), false);
356+
357+assertEquals(EXPONDIST(4, 0.5, false), 0.06766764161830635);
358+
359+assertEquals(FALSE(), false);
360+
361+assertEquals(__COMPLEX["F.DIST"](15.35, 7, 6, false), 0.0003451054686025578);
362+assertEquals(__COMPLEX["F.DIST"](15.35, 7, 6, true), 0.9980694465675269);
363+
364+assertEquals(__COMPLEX["F.INV"](0.42, 2, 3), 0.6567804059458624);
365+
366+assertEquals(FISHER(0.962), 1.972066740199461);
367+
368+assertEquals(FISHERINV(0.962), 0.7451676440945232);
369+
370+assertEquals(IF("m" == "m", "hit", "miss"), 'hit');
371+
372+assertEquals(INT(99.33), 99);
373+
374+assertEquals(ISEVEN(4), true);
375+
376+assertEquals(ISODD(3), true);
377+
378+assertEquals(LN(100), 4.605170185988092);
379+
380+assertEquals(LOG(256, 2), 8);
381+
382+assertEquals(LOG10(100), 2);
383+
384+assertEquals(MAX(100, 22), 100);
385+
386+assertEquals(MAXA(100, 22, 44), 100);
387+
388+assertEquals(MEDIAN(100, 22, 54), 54);
389+
390+assertEquals(MIN(100, 22, 44), 22);
391+
392+assertEquals(MINA(100, 22, 44), 22);
393+
394+assertEquals(MOD(10, 3), 1);
395+
396+assertEquals(NOT(TRUE()), false);
397+
398+assertEquals(ODD(2), 3);
399+
400+assertEquals(OR(true, false), true);
401+
402+assertEquals(PI(), 3.141592653589793);
403+
404+assertEquals(POWER(4, 10), 1048576);
405+
406+assertEquals(ROUND(99.44, 1), 99.4);
407+
408+assertEquals(ROUNDDOWN(99.46, 1), 99.4);
409+
410+assertEquals(ROUNDUP(99.46, 1), 99.5);
411+
412+assertEquals(SIN(0), 0);
413+assertEquals(SIN(1), 0.8414709848078965);
414+assertEquals(SIN(PI() / 2), 1);
415+assertEquals(SIN(PI()), 0);
416+
417+assertEquals(SINH(PI()), 11.548739357257752);
418+
419+assertArrayEquals(SPLIT("1,2,3", ",", true), [ '1', '2', '3' ]);
420+
421+assertEquals(SQRT(9), 3);
422+
423+assertEquals(SQRTPI(9), 5.317361552716548);
424+
425+assertEquals(SUM(10, 10), 20);
426+
427+assertEquals(SUMIF([1, 5, 10], ">2"), 15);
428+
429+assertEquals(SUMPRODUCT([1, 5, 10]), 16);
430+
431+assertEquals(SUMSQ([1, 5, 10], 10), 226);
432+
433+assertEquals(SUMX2MY2([1,2,3],[4,5,6]), -63);
434+
435+assertEquals(SUMX2PY2([1, 2, 3], [4, 5, 6]), 91);
436+
437+assertEquals(TAN(0), 0);
438+assertEquals(TAN(1), 1.5574077246549023);
439+assertEquals(TAN(PI() / 2), 16331239353195370);
440+assertEquals(TAN(PI()), 0);
441+
442+assertEquals(TANH(PI()), 0.99627207622075);
443+
444+assertEquals(TRUE(), true);
445+
446+assertEquals(TRUNC(3.1415, 2), 3.14);
447+
448+assertEquals(XOR(1, 1), false);
449+
450+assertEquals(YEARFRAC(DATE(1969, 7, 6), DATE(1988, 7, 4), 0), 18.994444444444444);
451+// assertEquals(YEARFRAC(DATE(1969, 7, 6), DATE(1988, 7, 4), 1)', 18.99587544); // This is slightly off
452+assertEquals(YEARFRAC(DATE(1969, 7, 6), DATE(1988, 7, 4), 2), 19.272222222222222);
453+assertEquals(YEARFRAC(DATE(1969, 7, 6), DATE(1988, 7, 4), 3), 19.008219178082193);
454+assertEquals(YEARFRAC(DATE(1969, 7, 6), DATE(1988, 7, 4), 4), 18.994444444444444);
455\ No newline at end of file
456diff --git a/tests/utils/Asserts.ts b/tests/utils/Asserts.ts
457index f96f76f..5ada522 100644
458--- a/tests/utils/Asserts.ts
459+++ b/tests/utils/Asserts.ts
460@@ -18,7 +18,12 @@ function assertArrayEquals(expected: Array<any>, actual: Array<any>) {
461   }
462 }
463 
464+function assertEqualsDates(one: Date, expectation: Date) {
465+  assertEquals(one.getTime(), expectation.getTime());
466+}
467+
468 export {
469   assertEquals,
470-  assertArrayEquals
471+  assertArrayEquals,
472+  assertEqualsDates
473 }
474\ No newline at end of file