commit
message
Moving casting functions into TypeCaster, array functions into Filter
author
Ben Vogt <[email protected]>
date
2017-02-16 03:10:00
stats
6 file(s) changed,
457 insertions(+),
381 deletions(-)
files
src/RawFormulas/Logical.ts
src/RawFormulas/Math.ts
src/RawFormulas/Misc.ts
src/RawFormulas/RawFormulas.ts
src/RawFormulas/Utils.ts
tests/FormulasTest.ts
1diff --git a/src/RawFormulas/Logical.ts b/src/RawFormulas/Logical.ts
2index 54d0a90..2500c39 100644
3--- a/src/RawFormulas/Logical.ts
4+++ b/src/RawFormulas/Logical.ts
5@@ -1,4 +1,4 @@
6-import { checkArgumentsAtLeastLength, checkArgumentsLength, valueToString, valueToBoolean } from "./Utils"
7+import { ArgsChecker, Filter, TypeCaster } from "./Utils"
8 import { CellError } from "../Errors"
9 import * as ERRORS from "../Errors"
10
11@@ -9,7 +9,7 @@ import * as ERRORS from "../Errors"
12 * @constructor
13 */
14 var AND = function (...values) {
15- checkArgumentsAtLeastLength(values, 1);
16+ ArgsChecker.checkAtLeastLength(values, 1);
17 var result = true;
18 for (var i = 0; i < values.length; i++) {
19 if (typeof values[i] === "string") {
20@@ -35,7 +35,7 @@ var AND = function (...values) {
21 * @constructor
22 */
23 var EXACT = function (...values) {
24- checkArgumentsLength(values, 2);
25+ ArgsChecker.checkLength(values, 2);
26 var one = values[0];
27 var two = values[1];
28 if (one instanceof Array) {
29@@ -48,8 +48,8 @@ var EXACT = function (...values) {
30 throw new CellError(ERRORS.REF_ERROR, "Reference does not exist.");
31 }
32 }
33- one = valueToString(one);
34- two = valueToString(two);
35+ one = TypeCaster.valueToString(one);
36+ two = TypeCaster.valueToString(two);
37 return one === two;
38 };
39
40@@ -78,7 +78,7 @@ var FALSE = function () : boolean {
41 * @constructor
42 */
43 var NOT = function (...values) : boolean {
44- checkArgumentsLength(values, 1);
45+ ArgsChecker.checkLength(values, 1);
46 var X = values[0];
47 if (typeof(X) === "boolean") {
48 return !X;
49@@ -108,7 +108,7 @@ var NOT = function (...values) : boolean {
50 * @constructor
51 */
52 var OR = function (...values) {
53- checkArgumentsAtLeastLength(values, 1);
54+ ArgsChecker.checkAtLeastLength(values, 1);
55 for (var i = 0; i < values.length; i++) {
56 if (values[i] instanceof Array) {
57 if (values[i].length === 0) {
58@@ -117,7 +117,7 @@ var OR = function (...values) {
59 if (OR.apply(this, values[i])) {
60 return true;
61 }
62- } else if (valueToBoolean(values[i])) {
63+ } else if (TypeCaster.valueToBoolean(values[i])) {
64 return true;
65 }
66 }
67@@ -131,7 +131,7 @@ var OR = function (...values) {
68 * @constructor
69 */
70 var XOR = function (...values) {
71- checkArgumentsAtLeastLength(values, 1);
72+ ArgsChecker.checkAtLeastLength(values, 1);
73 var alreadyTruthy = false;
74 for (var i = 0; i < values.length; i++) {
75 if (values[i] instanceof Array) {
76@@ -144,7 +144,7 @@ var XOR = function (...values) {
77 }
78 alreadyTruthy = true;
79 }
80- } else if (valueToBoolean(values[i])) {
81+ } else if (TypeCaster.valueToBoolean(values[i])) {
82 if (alreadyTruthy) {
83 return false;
84 }
85diff --git a/src/RawFormulas/Math.ts b/src/RawFormulas/Math.ts
86index c378311..91e4ca3 100644
87--- a/src/RawFormulas/Math.ts
88+++ b/src/RawFormulas/Math.ts
89@@ -1,16 +1,8 @@
90 import {
91- checkArgumentsLength,
92- checkArgumentsAtLeastLength,
93- valueToNumber,
94- filterOutStringValues,
95- flatten,
96- filterOutNonNumberValues,
97- stringValuesToZeros,
98- firstValueAsNumber,
99- valueToBoolean,
100- checkArgumentsAtWithin,
101+ ArgsChecker,
102 CriteriaFunctionFactory,
103- valueCanCoerceToNumber
104+ Filter,
105+ TypeCaster,
106 } from "./Utils";
107 import { CellError } from "../Errors";
108 import * as ERRORS from "../Errors";
109@@ -22,8 +14,8 @@ import * as ERRORS from "../Errors";
110 * @constructor
111 */
112 var ABS = function (...values) {
113- checkArgumentsLength(values, 1);
114- var v = valueToNumber(values[0]);
115+ ArgsChecker.checkLength(values, 1);
116+ var v = TypeCaster.valueToNumber(values[0]);
117 return Math.abs(v);
118 };
119
120@@ -34,8 +26,8 @@ var ABS = function (...values) {
121 * @constructor
122 */
123 var ACOS = function (value?) {
124- checkArgumentsLength(arguments, 1);
125- value = valueToNumber(value);
126+ ArgsChecker.checkLength(arguments, 1);
127+ value = TypeCaster.valueToNumber(value);
128 if (value === -1) {
129 return Math.PI;
130 } else if (value > 1 || value < -1) {
131@@ -51,8 +43,8 @@ var ACOS = function (value?) {
132 * @constructor
133 */
134 var ACOSH = function (value?) {
135- checkArgumentsLength(arguments, 1);
136- value = valueToNumber(value);
137+ ArgsChecker.checkLength(arguments, 1);
138+ value = TypeCaster.valueToNumber(value);
139 if (value < 1) {
140 throw new CellError(ERRORS.NUM_ERROR, "Function ____ parameter 1 value is " + value + ". It should be greater than or equal to 1.");
141 }
142@@ -66,8 +58,8 @@ var ACOSH = function (value?) {
143 * @constructor
144 */
145 var ACOTH = function (value?) {
146- checkArgumentsLength(arguments, 1);
147- value = valueToNumber(value);
148+ ArgsChecker.checkLength(arguments, 1);
149+ value = TypeCaster.valueToNumber(value);
150 if (value <= 1 && value >= -1) {
151 throw new CellError(ERRORS.NUM_ERROR, "Function ____ parameter 1 value is " + value + ". Valid values cannot be between -1 and 1 inclusive.")
152 }
153@@ -81,7 +73,7 @@ var ACOTH = function (value?) {
154 * @constructor
155 */
156 var ARABIC = function (text?) {
157- checkArgumentsLength(arguments, 1);
158+ ArgsChecker.checkLength(arguments, 1);
159 if (typeof text !== "string") {
160 throw new CellError(ERRORS.VALUE_ERROR, 'Invalid roman numeral in ARABIC evaluation.');
161 }
162@@ -111,8 +103,8 @@ var ARABIC = function (text?) {
163 * @constructor
164 */
165 var ASIN = function (value?) {
166- checkArgumentsLength(arguments, 1);
167- value = valueToNumber(value);
168+ ArgsChecker.checkLength(arguments, 1);
169+ value = TypeCaster.valueToNumber(value);
170 if (value === -1) {
171 return Math.PI;
172 } else if (value > 1 || value < -1) {
173@@ -128,8 +120,8 @@ var ASIN = function (value?) {
174 * @constructor
175 */
176 var ASINH = function (value?) {
177- checkArgumentsLength(arguments, 1);
178- value = valueToNumber(value);
179+ ArgsChecker.checkLength(arguments, 1);
180+ value = TypeCaster.valueToNumber(value);
181 return Math.log(value + Math.sqrt(value * value + 1));
182 };
183
184@@ -141,8 +133,8 @@ var ASINH = function (value?) {
185 * @constructor
186 */
187 var ATAN = function (value?) {
188- checkArgumentsLength(arguments, 1);
189- value = valueToNumber(value);
190+ ArgsChecker.checkLength(arguments, 1);
191+ value = TypeCaster.valueToNumber(value);
192 if (value === -1) {
193 return Math.PI;
194 } else if (value > 1 || value < -1) {
195@@ -160,9 +152,9 @@ var ATAN = function (value?) {
196 * @constructor
197 */
198 var ATAN2 = function (x, y) {
199- checkArgumentsLength(arguments, 2);
200- x = valueToNumber(x);
201- y = valueToNumber(y);
202+ ArgsChecker.checkLength(arguments, 2);
203+ x = TypeCaster.valueToNumber(x);
204+ y = TypeCaster.valueToNumber(y);
205 if (x === 0 && y === 0) {
206 throw new CellError(ERRORS.DIV_ZERO_ERROR, "Evaluation of function ATAN2 caused a divide by zero error.");
207 }
208@@ -177,8 +169,8 @@ var ATAN2 = function (x, y) {
209 * @constructor
210 */
211 var ATANH = function (value?) : number {
212- checkArgumentsLength(arguments, 1);
213- value = valueToNumber(value);
214+ ArgsChecker.checkLength(arguments, 1);
215+ value = TypeCaster.valueToNumber(value);
216 if (value >= 1 || value <= -1) {
217 throw new CellError(ERRORS.NUM_ERROR, "Function ATANH parameter 1 value is " + value + ". Valid values are between -1 and 1 exclusive.");
218 }
219@@ -196,7 +188,7 @@ var ATANH = function (value?) : number {
220 * @constructor
221 */
222 var AVERAGE = function (...values) : number {
223- checkArgumentsAtLeastLength(values, 1);
224+ ArgsChecker.checkAtLeastLength(values, 1);
225 var result = 0;
226 var count = 0;
227 for (var i = 0; i < values.length; i++) {
228@@ -204,11 +196,11 @@ var AVERAGE = function (...values) : number {
229 if (values[i].length === 0) {
230 throw new CellError(ERRORS.REF_ERROR, "Reference does not exist.");
231 }
232- var filtered = filterOutStringValues(values[i]);
233+ var filtered = Filter.filterOutStringValues(values[i]);
234 result = result + SUM.apply(this, filtered);
235 count += filtered.length;
236 } else {
237- result = result + valueToNumber(values[i]);
238+ result = result + TypeCaster.valueToNumber(values[i]);
239 count++;
240 }
241 }
242@@ -222,7 +214,7 @@ var AVERAGE = function (...values) : number {
243 * @constructor
244 */
245 var AVEDEV = function (...values) {
246- checkArgumentsAtLeastLength(values, 1);
247+ ArgsChecker.checkAtLeastLength(values, 1);
248
249 // Sort to array-values, and non-array-values
250 var arrayValues = [];
251@@ -235,26 +227,26 @@ var AVEDEV = function (...values) {
252 }
253 arrayValues.push(X);
254 } else {
255- nonArrayValues.push(valueToNumber(X));
256+ nonArrayValues.push(TypeCaster.valueToNumber(X));
257 }
258 }
259
260 // Remove string values from array-values, but not from non-array-values, and concat.
261- var flatValues = filterOutStringValues(flatten(arrayValues)).map(function (value) {
262- return valueToNumber(value);
263+ var flatValues = Filter.filterOutStringValues(Filter.flatten(arrayValues)).map(function (value) {
264+ return TypeCaster.valueToNumber(value);
265 }).concat(nonArrayValues);
266
267 // Calculating mean
268 var result = 0;
269 var count = 0;
270 for (var i = 0; i < flatValues.length; i++) {
271- result = result + valueToNumber(flatValues[i]);
272+ result = result + TypeCaster.valueToNumber(flatValues[i]);
273 count++;
274 }
275 var mean = result / count;
276
277 for (var i = 0; i < flatValues.length; i++) {
278- flatValues[i] = ABS(valueToNumber(flatValues[i]) - mean);
279+ flatValues[i] = ABS(TypeCaster.valueToNumber(flatValues[i]) - mean);
280 }
281 return SUM(flatValues) / flatValues.length;
282 };
283@@ -266,7 +258,7 @@ var AVEDEV = function (...values) {
284 * @constructor
285 */
286 var AVERAGEA = function (...values) {
287- checkArgumentsAtLeastLength(values, 1);
288+ ArgsChecker.checkAtLeastLength(values, 1);
289 var result = 0;
290 var count = 0;
291 for (var i = 0; i < values.length; i++) {
292@@ -274,11 +266,11 @@ var AVERAGEA = function (...values) {
293 if (values[i].length === 0) {
294 throw new CellError(ERRORS.REF_ERROR, "Reference does not exist.");
295 }
296- var filtered = stringValuesToZeros(values[i]);
297+ var filtered = Filter.stringValuesToZeros(values[i]);
298 result = result + SUM.apply(this, filtered);
299 count += filtered.length;
300 } else {
301- result = result + valueToNumber(values[i]);
302+ result = result + TypeCaster.valueToNumber(values[i]);
303 count++;
304 }
305 }
306@@ -292,14 +284,14 @@ var AVERAGEA = function (...values) {
307 * @constructor
308 */
309 var EVEN = function (...values) : number {
310- checkArgumentsLength(values, 1);
311+ ArgsChecker.checkLength(values, 1);
312 if (values[0] instanceof Array) {
313 if (values[0].length === 0) {
314 throw new CellError(ERRORS.REF_ERROR, "Reference does not exist.");
315 }
316 return EVEN(values[0][0]);
317 }
318- var X = valueToNumber(values[0]);
319+ var X = TypeCaster.valueToNumber(values[0]);
320 return X % 2 === 1 ? X + 1 : X;
321 };
322
323@@ -310,19 +302,19 @@ var EVEN = function (...values) : number {
324 * @constructor
325 */
326 var MAX = function (...values) {
327- checkArgumentsAtLeastLength(values, 1);
328+ ArgsChecker.checkAtLeastLength(values, 1);
329 var maxSoFar = -Infinity;
330 for (var i = 0; i < values.length; i++) {
331 if (values[i] instanceof Array) {
332 if (values[i].length === 0) {
333 throw new CellError(ERRORS.REF_ERROR, "Reference does not exist.");
334 }
335- var filtered = filterOutStringValues(values[i]);
336+ var filtered = Filter.filterOutStringValues(values[i]);
337 if (filtered.length !== 0) {
338 maxSoFar = Math.max(MAX.apply(this, filtered), maxSoFar);
339 }
340 } else {
341- maxSoFar = Math.max(valueToNumber(values[i]), maxSoFar);
342+ maxSoFar = Math.max(TypeCaster.valueToNumber(values[i]), maxSoFar);
343 }
344 }
345 return maxSoFar;
346@@ -346,26 +338,26 @@ var MAXA = function (...values) : number {
347 * @constructor
348 */
349 var MEDIAN = function (...values) : number {
350- checkArgumentsAtLeastLength(values, 1);
351+ ArgsChecker.checkAtLeastLength(values, 1);
352 var sortedArray = [];
353 values.forEach(function (currentValue) {
354 if (currentValue instanceof Array) {
355 if (currentValue.length === 0) {
356 throw new CellError(ERRORS.REF_ERROR, "Reference does not exist.");
357 }
358- var filtered = filterOutStringValues(currentValue);
359+ var filtered = Filter.filterOutStringValues(currentValue);
360 sortedArray = sortedArray.concat(filtered);
361 } else {
362 sortedArray.push(currentValue);
363 }
364 });
365 sortedArray = sortedArray.sort(function (a, b) {
366- var aN = valueToNumber(a);
367- var bN = valueToNumber(b);
368+ var aN = TypeCaster.valueToNumber(a);
369+ var bN = TypeCaster.valueToNumber(b);
370 return aN - bN;
371 });
372 if (sortedArray.length === 1) {
373- return valueToNumber(sortedArray[0]);
374+ return TypeCaster.valueToNumber(sortedArray[0]);
375 }
376 if (sortedArray.length === 0) {
377 throw new CellError(ERRORS.NUM_ERROR, "MEDIAN has no valid input data.");
378@@ -391,19 +383,19 @@ var MEDIAN = function (...values) : number {
379 * @constructor
380 */
381 var MIN = function (...values) {
382- checkArgumentsAtLeastLength(values, 1);
383+ ArgsChecker.checkAtLeastLength(values, 1);
384 var minSoFar = Infinity;
385 for (var i = 0; i < values.length; i++) {
386 if (values[i] instanceof Array) {
387 if (values[i].length === 0) {
388 throw new CellError(ERRORS.REF_ERROR, "Reference does not exist.");
389 }
390- var filtered = filterOutStringValues(values[i]);
391+ var filtered = Filter.filterOutStringValues(values[i]);
392 if (filtered.length !== 0) {
393 minSoFar = Math.min(MIN.apply(this, filtered), minSoFar);
394 }
395 } else {
396- minSoFar = Math.min(valueToNumber(values[i]), minSoFar);
397+ minSoFar = Math.min(TypeCaster.valueToNumber(values[i]), minSoFar);
398 }
399 }
400 return minSoFar;
401@@ -429,9 +421,9 @@ var MINA = function (...values) : number {
402 * @constructor
403 */
404 var MOD = function (...values) : number {
405- checkArgumentsLength(values, 2);
406- var oneN = valueToNumber(values[0]);
407- var twoN = valueToNumber(values[1]);
408+ ArgsChecker.checkLength(values, 2);
409+ var oneN = TypeCaster.valueToNumber(values[0]);
410+ var twoN = TypeCaster.valueToNumber(values[1]);
411 if (twoN === 0) {
412 throw new CellError(ERRORS.DIV_ZERO_ERROR, "Function MOD parameter 2 cannot be zero.");
413 }
414@@ -446,14 +438,14 @@ var MOD = function (...values) : number {
415 * @constructor
416 */
417 var ODD = function (...values) : number {
418- checkArgumentsLength(values, 1);
419+ ArgsChecker.checkLength(values, 1);
420 if (values[0] instanceof Array) {
421 if (values[0].length === 0) {
422 throw new CellError(ERRORS.REF_ERROR, "Reference does not exist.");
423 }
424 return ODD(values[0][0]);
425 }
426- var X = valueToNumber(values[0]);
427+ var X = TypeCaster.valueToNumber(values[0]);
428 return X % 2 === 1 ? X : X + 1;
429 };
430
431@@ -465,9 +457,9 @@ var ODD = function (...values) : number {
432 * @constructor
433 */
434 var POWER = function (...values) : number {
435- checkArgumentsLength(values, 2);
436- var n = firstValueAsNumber(values[0]);
437- var p = firstValueAsNumber(values[1]);
438+ ArgsChecker.checkLength(values, 2);
439+ var n = TypeCaster.firstValueAsNumber(values[0]);
440+ var p = TypeCaster.firstValueAsNumber(values[1]);
441 return Math.pow(n, p);
442 };
443
444@@ -478,7 +470,7 @@ var POWER = function (...values) : number {
445 * @constructor
446 */
447 var SUM = function (...values) : number {
448- checkArgumentsAtLeastLength(values, 1);
449+ ArgsChecker.checkAtLeastLength(values, 1);
450 var result = 0;
451 for (var i = 0; i < values.length; i++) {
452 if (values[i] instanceof Array) {
453@@ -487,7 +479,7 @@ var SUM = function (...values) : number {
454 if (values[i] === "") {
455 throw new CellError(ERRORS.VALUE_ERROR, "Function SUM parameter "+i+" expects number values. But '"+values[i]+"' is a text and cannot be coerced to a number.");
456 }
457- result = result + valueToNumber(values[i]);
458+ result = result + TypeCaster.valueToNumber(values[i]);
459 }
460 }
461 return result;
462@@ -500,8 +492,8 @@ var SUM = function (...values) : number {
463 * @constructor
464 */
465 var SQRT = function (...values) : number {
466- checkArgumentsLength(values, 1);
467- var x = firstValueAsNumber(values[0]);
468+ ArgsChecker.checkLength(values, 1);
469+ var x = TypeCaster.firstValueAsNumber(values[0]);
470 if (x < 0) {
471 throw new CellError(ERRORS.VALUE_ERROR, "Function SQRT parameter 1 expects number values. But '" + values[0] + "' is a text and cannot be coerced to a number.");
472 }
473@@ -515,8 +507,8 @@ var SQRT = function (...values) : number {
474 * @constructor
475 */
476 var COS = function (...values) : number {
477- checkArgumentsLength(values, 1);
478- var r = firstValueAsNumber(values[0]);
479+ ArgsChecker.checkLength(values, 1);
480+ var r = TypeCaster.firstValueAsNumber(values[0]);
481 return Math.cos(r);
482 };
483
484@@ -527,8 +519,8 @@ var COS = function (...values) : number {
485 * @constructor
486 */
487 var COSH = function (...values) : number {
488- checkArgumentsLength(values, 1);
489- var r = firstValueAsNumber(values[0]);
490+ ArgsChecker.checkLength(values, 1);
491+ var r = TypeCaster.firstValueAsNumber(values[0]);
492 return Math["cosh"](r);
493 };
494
495@@ -539,8 +531,8 @@ var COSH = function (...values) : number {
496 * @constructor
497 */
498 var COT = function (...values) : number {
499- checkArgumentsLength(values, 1);
500- var x = firstValueAsNumber(values[0]);
501+ ArgsChecker.checkLength(values, 1);
502+ var x = TypeCaster.firstValueAsNumber(values[0]);
503 if (x === 0) {
504 throw new CellError(ERRORS.DIV_ZERO_ERROR, "Evaluation of function COT caused a divide by zero error.");
505 }
506@@ -554,8 +546,8 @@ var COT = function (...values) : number {
507 * @constructor
508 */
509 var COTH = function (...values) : number {
510- checkArgumentsLength(values, 1);
511- var x = firstValueAsNumber(values[0]);
512+ ArgsChecker.checkLength(values, 1);
513+ var x = TypeCaster.firstValueAsNumber(values[0]);
514 if (x === 0) {
515 throw new CellError(ERRORS.DIV_ZERO_ERROR, "Evaluation of function COTH caused a divide by zero error.");
516 }
517@@ -569,8 +561,8 @@ var COTH = function (...values) : number {
518 * @constructor
519 */
520 var INT = function (...values) : number {
521- checkArgumentsLength(values, 1);
522- var x = firstValueAsNumber(values[0]);
523+ ArgsChecker.checkLength(values, 1);
524+ var x = TypeCaster.firstValueAsNumber(values[0]);
525 return Math.floor(x);
526 };
527
528@@ -582,11 +574,11 @@ var INT = function (...values) : number {
529 * @constructor
530 */
531 var ISEVEN = function (...values) : boolean {
532- checkArgumentsLength(values, 1);
533+ ArgsChecker.checkLength(values, 1);
534 if (values[0] === "") {
535 throw new CellError(ERRORS.VALUE_ERROR, "Function ISEVEN parameter 1 expects boolean values. But '" + values[0] + "' is a text and cannot be coerced to a boolean.");
536 }
537- var x = firstValueAsNumber(values[0]);
538+ var x = TypeCaster.firstValueAsNumber(values[0]);
539 return Math.floor(x) % 2 === 0;
540 };
541
542@@ -598,11 +590,11 @@ var ISEVEN = function (...values) : boolean {
543 * @constructor
544 */
545 var ISODD = function (...values) : boolean {
546- checkArgumentsLength(values, 1);
547+ ArgsChecker.checkLength(values, 1);
548 if (values[0] === "") {
549 throw new CellError(ERRORS.VALUE_ERROR, "Function ISODD parameter 1 expects boolean values. But '" + values[0] + "' is a text and cannot be coerced to a boolean.");
550 }
551- var x = firstValueAsNumber(values[0]);
552+ var x = TypeCaster.firstValueAsNumber(values[0]);
553 return Math.floor(x) % 2 === 1;
554 };
555
556@@ -613,8 +605,8 @@ var ISODD = function (...values) : boolean {
557 * @constructor
558 */
559 var SIN = function (...values) {
560- checkArgumentsLength(values, 1);
561- var rad = firstValueAsNumber(values[0]);
562+ ArgsChecker.checkLength(values, 1);
563+ var rad = TypeCaster.firstValueAsNumber(values[0]);
564 return rad === Math.PI ? 0 : Math.sin(rad);
565 };
566
567@@ -625,8 +617,8 @@ var SIN = function (...values) {
568 * @constructor
569 */
570 var SINH = function (...values) : number {
571- checkArgumentsLength(values, 1);
572- var rad = firstValueAsNumber(values[0]);
573+ ArgsChecker.checkLength(values, 1);
574+ var rad = TypeCaster.firstValueAsNumber(values[0]);
575 return Math["sinh"](rad);
576 };
577
578@@ -646,8 +638,8 @@ var PI = function () {
579 * @constructor
580 */
581 var LOG10 = function (...values) : number {
582- checkArgumentsLength(values, 1);
583- var n = firstValueAsNumber(values[0]);
584+ ArgsChecker.checkLength(values, 1);
585+ var n = TypeCaster.firstValueAsNumber(values[0]);
586 if (n < 1) {
587 throw new CellError(ERRORS.NUM_ERROR, "Function LOG10 parameter 1 value is " + n + ". It should be greater than 0.");
588 }
589@@ -664,11 +656,11 @@ var LOG10 = function (...values) : number {
590 * @constructor
591 */
592 var LOG = function (...values) : number {
593- checkArgumentsAtLeastLength(values, 1);
594- var n = firstValueAsNumber(values[0]);
595+ ArgsChecker.checkAtLeastLength(values, 1);
596+ var n = TypeCaster.firstValueAsNumber(values[0]);
597 var b = 10;
598 if (values.length > 1) {
599- b = firstValueAsNumber(values[1]);
600+ b = TypeCaster.firstValueAsNumber(values[1]);
601 if (b < 1) {
602 throw new CellError(ERRORS.NUM_ERROR, "Function LOG parameter 2 value is " + b + ". It should be greater than 0.");
603 }
604@@ -691,8 +683,8 @@ var LOG = function (...values) : number {
605 * @constructor
606 */
607 var LN = function (...values) : number {
608- checkArgumentsLength(values, 1);
609- var n = firstValueAsNumber(values[0]);
610+ ArgsChecker.checkLength(values, 1);
611+ var n = TypeCaster.firstValueAsNumber(values[0]);
612 if (n < 1) {
613 throw new CellError(ERRORS.NUM_ERROR, "Function LN parameter 1 value is " + n + ". It should be greater than 0.");
614 }
615@@ -706,8 +698,8 @@ var LN = function (...values) : number {
616 * @constructor
617 */
618 var TAN = function (...values) : number {
619- checkArgumentsLength(values, 1);
620- var rad = firstValueAsNumber(values[0]);
621+ ArgsChecker.checkLength(values, 1);
622+ var rad = TypeCaster.firstValueAsNumber(values[0]);
623 return rad === Math.PI ? 0 : Math.tan(rad);
624 };
625
626@@ -718,8 +710,8 @@ var TAN = function (...values) : number {
627 * @constructor
628 */
629 var TANH = function (...values) : number {
630- checkArgumentsLength(values, 1);
631- var rad = firstValueAsNumber(values[0]);
632+ ArgsChecker.checkLength(values, 1);
633+ var rad = TypeCaster.firstValueAsNumber(values[0]);
634 return Math["tanh"](rad);
635 };
636
637@@ -735,14 +727,14 @@ var TANH = function (...values) : number {
638 * TODO: This needs to also accept a third parameter "average_range"
639 */
640 var AVERAGEIF = function (...values) {
641- checkArgumentsLength(values, 2);
642+ ArgsChecker.checkLength(values, 2);
643 var range = values[0];
644 var criteriaEvaluation = CriteriaFunctionFactory.createCriteriaFunction(values[1]);
645
646 var result = 0;
647 var count = 0;
648 for (var i = 0; i < range.length; i++) {
649- var val = valueToNumber(range[i]);
650+ var val = TypeCaster.valueToNumber(range[i]);
651 if (criteriaEvaluation(val)) {
652 result = result + val;
653 count++;
654@@ -762,12 +754,12 @@ var AVERAGEIF = function (...values) {
655 * @constructor
656 */
657 var CEILING = function (...values) : number {
658- checkArgumentsAtWithin(values, 1, 2);
659- var num = firstValueAsNumber(values[0]);
660+ ArgsChecker.checkLengthWithin(values, 1, 2);
661+ var num = TypeCaster.firstValueAsNumber(values[0]);
662 if (values.length === 1) {
663 return Math.ceil(num);
664 }
665- var significance = firstValueAsNumber(values[1]);
666+ var significance = TypeCaster.firstValueAsNumber(values[1]);
667 if (significance === 0) {
668 throw new CellError(ERRORS.DIV_ZERO_ERROR, "Function CEILING parameter 2 cannot be zero.");
669 }
670@@ -787,12 +779,12 @@ var CEILING = function (...values) : number {
671 * @constructor
672 */
673 var FLOOR = function (...values) : number {
674- checkArgumentsAtWithin(values, 1, 2);
675- var num = firstValueAsNumber(values[0]);
676+ ArgsChecker.checkLengthWithin(values, 1, 2);
677+ var num = TypeCaster.firstValueAsNumber(values[0]);
678 if (values.length === 1) {
679 return Math.floor(num);
680 }
681- var significance = firstValueAsNumber(values[1]);
682+ var significance = TypeCaster.firstValueAsNumber(values[1]);
683 if (significance === 0) {
684 throw new CellError(ERRORS.DIV_ZERO_ERROR, "Function FLOOR parameter 2 cannot be zero.");
685 }
686@@ -813,7 +805,7 @@ var FLOOR = function (...values) : number {
687 * @constructor
688 */
689 var IF = function (...values) : any {
690- checkArgumentsLength(values, 3);
691+ ArgsChecker.checkLength(values, 3);
692 if (values[0] instanceof Array) {
693 if (values[0].length === 0) {
694 throw new CellError(ERRORS.REF_ERROR, "Reference does not exist.");
695@@ -822,7 +814,7 @@ var IF = function (...values) : any {
696 } else if (values[0] === "") {
697 return values[2];
698 }
699- return (valueToBoolean(values[0])) ? values[1] : values[2];
700+ return (TypeCaster.valueToBoolean(values[0])) ? values[1] : values[2];
701 };
702
703 /**
704@@ -832,14 +824,14 @@ var IF = function (...values) : any {
705 * @constructor
706 */
707 var COUNT = function (...values) : number {
708- checkArgumentsAtLeastLength(values, 1);
709+ ArgsChecker.checkAtLeastLength(values, 1);
710 var count = 0;
711 for (var i = 0; i < values.length; i++) {
712 if (values[i] instanceof Array) {
713 if (values[i].length > 0) {
714 count += COUNT.apply(this, values[i]);
715 }
716- } else if (valueCanCoerceToNumber(values[i])) {
717+ } else if (TypeCaster.canCoerceToNumber(values[i])) {
718 count++;
719 }
720 }
721@@ -858,7 +850,7 @@ var COUNT = function (...values) : number {
722 * TODO: This needs to take nested range values.
723 */
724 var COUNTIF = function (...values) {
725- checkArgumentsLength(values, 2);
726+ ArgsChecker.checkLength(values, 2);
727 var range = values[0];
728 var criteria = values[1];
729
730@@ -884,7 +876,7 @@ var COUNTIF = function (...values) {
731 * TODO: This needs to take nested range values.
732 */
733 var COUNTIFS = function (...values) {
734- checkArgumentsAtLeastLength(values, 2);
735+ ArgsChecker.checkAtLeastLength(values, 2);
736 var criteriaEvaluationFunctions = values.map(function (criteria, index) {
737 if (index % 2 === 1) {
738 return CriteriaFunctionFactory.createCriteriaFunction(criteria);
739@@ -923,7 +915,7 @@ var COUNTIFS = function (...values) {
740 * @constructor
741 */
742 var COUNTA = function (...values) : number {
743- checkArgumentsAtLeastLength(values, 1);
744+ ArgsChecker.checkAtLeastLength(values, 1);
745 var count = 0;
746 for (var i = 0; i < values.length; i++) {
747 if (values[i] instanceof Array) {
748@@ -947,11 +939,11 @@ var COUNTA = function (...values) : number {
749 * @constructor
750 */
751 var DELTA = function (...values) : number {
752- checkArgumentsAtWithin(values, 1, 2);
753+ ArgsChecker.checkLengthWithin(values, 1, 2);
754 if (values.length === 1) {
755- return valueToNumber(values[0]) === 0 ? 1 : 0;
756+ return TypeCaster.valueToNumber(values[0]) === 0 ? 1 : 0;
757 }
758- return valueToNumber(values[0]) === valueToNumber(values[1]) ? 1 : 0;
759+ return TypeCaster.valueToNumber(values[0]) === TypeCaster.valueToNumber(values[1]) ? 1 : 0;
760 };
761
762 /**
763@@ -962,12 +954,12 @@ var DELTA = function (...values) : number {
764 * @constructor
765 */
766 var ROUND = function (...values) {
767- checkArgumentsAtWithin(values, 1, 2);
768- var n = firstValueAsNumber(values[0]);
769+ ArgsChecker.checkLengthWithin(values, 1, 2);
770+ var n = TypeCaster.firstValueAsNumber(values[0]);
771 if (values.length === 1) {
772 return Math.round(n);
773 }
774- var d = firstValueAsNumber(values[1]);
775+ var d = TypeCaster.firstValueAsNumber(values[1]);
776 return Math.round(n * Math.pow(10, d)) / Math.pow(10, d);
777 };
778
779@@ -979,12 +971,12 @@ var ROUND = function (...values) {
780 * @constructor
781 */
782 var ROUNDDOWN = function (...values) {
783- checkArgumentsAtWithin(values, 1, 2);
784- var n = firstValueAsNumber(values[0]);
785+ ArgsChecker.checkLengthWithin(values, 1, 2);
786+ var n = TypeCaster.firstValueAsNumber(values[0]);
787 if (values.length === 1) {
788 return Math.floor(n);
789 }
790- var d = firstValueAsNumber(values[1]);
791+ var d = TypeCaster.firstValueAsNumber(values[1]);
792 return Math.floor(n * Math.pow(10, d)) / Math.pow(10, d);
793 };
794
795@@ -996,12 +988,12 @@ var ROUNDDOWN = function (...values) {
796 * @constructor
797 */
798 var ROUNDUP = function (...values) {
799- checkArgumentsAtWithin(values, 1, 2);
800- var n = firstValueAsNumber(values[0]);
801+ ArgsChecker.checkLengthWithin(values, 1, 2);
802+ var n = TypeCaster.firstValueAsNumber(values[0]);
803 if (values.length === 1) {
804 return Math.ceil(n);
805 }
806- var d = firstValueAsNumber(values[1]);
807+ var d = TypeCaster.firstValueAsNumber(values[1]);
808 return Math.ceil(n * Math.pow(10, d)) / Math.pow(10, d);
809 };
810
811@@ -1018,7 +1010,7 @@ var ROUNDUP = function (...values) {
812 * TODO: This needs to take nested range values.
813 */
814 var SUMIF = function (...values) {
815- checkArgumentsAtWithin(values, 2, 3);
816+ ArgsChecker.checkLengthWithin(values, 2, 3);
817 var range = values[0];
818 var criteria = values[1];
819 var sumRange = null;
820@@ -1034,10 +1026,10 @@ var SUMIF = function (...values) {
821 if (sumRange && i > sumRange.length-1) {
822 continue;
823 }
824- if (values.length === 2 && valueCanCoerceToNumber(x) && criteriaEvaluation(x)) {
825- sum = sum + x;
826- } else if (values.length === 3 && valueCanCoerceToNumber(sumRange[i]) && criteriaEvaluation(x)) {
827- sum = sum + sumRange[i];
828+ if (values.length === 2 && TypeCaster.canCoerceToNumber(x) && criteriaEvaluation(x)) {
829+ sum = sum + TypeCaster.valueToNumber(x);
830+ } else if (values.length === 3 && TypeCaster.canCoerceToNumber(sumRange[i]) && criteriaEvaluation(x)) {
831+ sum = sum + TypeCaster.valueToNumber(sumRange[i]);
832 }
833 }
834 return sum;
835@@ -1050,16 +1042,16 @@ var SUMIF = function (...values) {
836 * @constructor
837 */
838 var SUMSQ = function (...values) {
839- checkArgumentsAtLeastLength(values, 1);
840+ ArgsChecker.checkAtLeastLength(values, 1);
841 var result = 0;
842 for (var i = 0; i < values.length; i++) {
843 if (values[i] instanceof Array) {
844 if (values[i].length === 0) {
845 throw new CellError(ERRORS.REF_ERROR, "Reference does not exist.");
846 }
847- result = result + SUMSQ.apply(this, filterOutNonNumberValues(values[i]));
848+ result = result + SUMSQ.apply(this, Filter.filterOutNonNumberValues(values[i]));
849 } else {
850- var n = valueToNumber(values[i]);
851+ var n = TypeCaster.valueToNumber(values[i]);
852 result = result + (n * n);
853 }
854 }
855@@ -1079,11 +1071,11 @@ var SUMSQ = function (...values) {
856 * @constructor
857 */
858 var TRUNC = function (...values) : number {
859- checkArgumentsAtWithin(values, 1, 2);
860- var n = firstValueAsNumber(values[0]);
861+ ArgsChecker.checkLengthWithin(values, 1, 2);
862+ var n = TypeCaster.firstValueAsNumber(values[0]);
863 var digits = 0;
864 if (values.length === 2) {
865- digits = firstValueAsNumber(values[1]);
866+ digits = TypeCaster.firstValueAsNumber(values[1]);
867 }
868 var sign = (n > 0) ? 1 : -1;
869 return sign * (Math.floor(Math.abs(n) * Math.pow(10, digits))) / Math.pow(10, digits);
870diff --git a/src/RawFormulas/Misc.ts b/src/RawFormulas/Misc.ts
871index adfefdd..3bbe308 100644
872--- a/src/RawFormulas/Misc.ts
873+++ b/src/RawFormulas/Misc.ts
874@@ -1,11 +1,6 @@
875 import {
876- valueToString,
877- firstValueAsNumber,
878- firstValueAsString,
879- firstValueAsBoolean,
880- checkArgumentsLength,
881- checkArgumentsAtWithin,
882- checkArgumentsAtLeastLength
883+ ArgsChecker,
884+ TypeCaster
885 } from "./Utils";
886 import { CellError } from "../Errors"
887 import * as ERRORS from "../Errors"
888@@ -17,8 +12,8 @@ import * as ERRORS from "../Errors"
889 * @constructor
890 */
891 var CHAR = function (...values) : string {
892- checkArgumentsLength(values, 1);
893- var n = firstValueAsNumber(values[0]);
894+ ArgsChecker.checkLength(values, 1);
895+ var n = TypeCaster.firstValueAsNumber(values[0]);
896 if (n < 1 || n > 1114112) { //limit
897 throw new CellError(ERRORS.NUM_ERROR, "Function CHAR parameter 1 value " + n + " is out of range.");
898 }
899@@ -32,8 +27,8 @@ var CHAR = function (...values) : string {
900 * @constructor
901 */
902 var CODE = function (...values) : number {
903- checkArgumentsLength(values, 1);
904- var text = firstValueAsString(values[0]);
905+ ArgsChecker.checkLength(values, 1);
906+ var text = TypeCaster.firstValueAsString(values[0]);
907 if (text === "") {
908 throw new CellError(ERRORS.VALUE_ERROR, "Function CODE parameter 1 value should be non-empty.");
909 }
910@@ -51,12 +46,12 @@ var CODE = function (...values) : number {
911 * TODO: At some point this needs to return a more complex type than Array. Needs to return a type that has a dimension.
912 */
913 var SPLIT = function (...values) : Array<string> {
914- checkArgumentsAtWithin(values, 2, 3);
915- var text = firstValueAsString(values[0]);
916- var delimiter = firstValueAsString(values[1]);
917+ ArgsChecker.checkLengthWithin(values, 2, 3);
918+ var text = TypeCaster.firstValueAsString(values[0]);
919+ var delimiter = TypeCaster.firstValueAsString(values[1]);
920 var splitByEach = false;
921 if (values.length === 3) {
922- splitByEach = firstValueAsBoolean(values[2]);
923+ splitByEach = TypeCaster.firstValueAsBoolean(values[2]);
924 }
925 if (splitByEach) {
926 var result = [text];
927@@ -83,7 +78,7 @@ var SPLIT = function (...values) : Array<string> {
928 * @constructor
929 */
930 var CONCATENATE = function (...values) : string {
931- checkArgumentsAtLeastLength(values, 1);
932+ ArgsChecker.checkAtLeastLength(values, 1);
933 var string = '';
934 for (var i = 0; i < values.length; i++) {
935 if (values[i] instanceof Array) {
936@@ -92,7 +87,7 @@ var CONCATENATE = function (...values) : string {
937 }
938 string += CONCATENATE.apply(this, arguments[i]);
939 } else {
940- string += valueToString(values[i]);
941+ string += TypeCaster.valueToString(values[i]);
942 }
943 }
944 return string;
945diff --git a/src/RawFormulas/RawFormulas.ts b/src/RawFormulas/RawFormulas.ts
946index 8b79422..676ab5b 100644
947--- a/src/RawFormulas/RawFormulas.ts
948+++ b/src/RawFormulas/RawFormulas.ts
949@@ -73,19 +73,10 @@ import {
950 CONCATENATE
951 } from "./Misc";
952 import {
953- checkArgumentsAtLeastLength,
954- checkArgumentsAtWithin,
955- valueCanCoerceToNumber,
956- firstValueAsBoolean,
957- filterOutStringValues,
958 CriteriaFunctionFactory,
959- valueToNumber,
960- checkArgumentsLength,
961- filterOutNonNumberValues,
962- firstValueAsNumber,
963- firstValueAsString,
964- valueToBoolean,
965- valueToString
966+ ArgsChecker,
967+ Filter,
968+ TypeCaster
969 } from "./Utils";
970 import { CellError } from "../Errors"
971 import * as ERRORS from "../Errors"
972diff --git a/src/RawFormulas/Utils.ts b/src/RawFormulas/Utils.ts
973index f15cbc6..7042ea5 100644
974--- a/src/RawFormulas/Utils.ts
975+++ b/src/RawFormulas/Utils.ts
976@@ -1,221 +1,6 @@
977 import { CellError } from "../Errors"
978 import * as ERRORS from "../Errors"
979
980-/**
981- * Checks to see if the arguments are of the correct length.
982- * @param args to check length of
983- * @param length expected length
984- */
985-function checkArgumentsLength(args: any, length: number) {
986- if (args.length !== length) {
987- throw new CellError(ERRORS.NA_ERROR, "Wrong number of arguments to ___. Expected 1 arguments, but got " + args.length + " arguments.");
988- }
989-}
990-
991-/**
992- * Checks to see if the arguments are at least a certain length.
993- * @param args to check length of
994- * @param length expected length
995- */
996-function checkArgumentsAtLeastLength(args: any, length: number) {
997- if (args.length < length) {
998- throw new CellError(ERRORS.NA_ERROR, "Wrong number of arguments to ___. Expected 1 arguments, but got " + args.length + " arguments.");
999- }
1000-}
1001-
1002-/**
1003- * Checks to see if the arguments are within a max and min, inclusively
1004- * @param args to check length of
1005- * @param low least number of arguments
1006- * @param high max number of arguments
1007- */
1008-function checkArgumentsAtWithin(args: any, low: number, high: number) {
1009- if (args.length > high || args.length < low) {
1010- throw new CellError(ERRORS.NA_ERROR, "Wrong number of arguments to ___. Expected 1 arguments, but got " + args.length + " arguments.");
1011- }
1012-}
1013-
1014-/**
1015- * Filter out all strings from an array.
1016- * @param arr to filter
1017- * @returns {Array} filtered array
1018- */
1019-function filterOutStringValues(arr: Array<any>) : Array<any> {
1020- var toReturn = [];
1021- for (var i = 0; i < arr.length; i++) {
1022- if (typeof arr[i] !== "string") {
1023- toReturn.push(arr[i]);
1024- }
1025- }
1026- return toReturn;
1027-}
1028-
1029-function filterOutNonNumberValues(arr: Array<any>) : Array<any> {
1030- var toReturn = [];
1031- for (var i = 0; i < arr.length; i++) {
1032- if (typeof arr[i] !== "string" && typeof arr[i] !== "boolean") {
1033- toReturn.push(arr[i]);
1034- }
1035- }
1036- return toReturn;
1037-}
1038-
1039-/**
1040- * Convert a value to string.
1041- * @param value of any type, including array. array cannot be empty.
1042- * @returns {string} string representation of value
1043- */
1044-function valueToString(value: any) : string {
1045- if (typeof value === "number") {
1046- return value.toString();
1047- } else if (typeof value === "string") {
1048- return value;
1049- } else if (typeof value === "boolean") {
1050- return value ? "TRUE" : "FALSE";
1051- } else if (value instanceof Array) {
1052- return valueToString(value[0]); // TODO: Take this out. It's stupid. We should handle arrays at a different level.
1053- }
1054-}
1055-
1056-/**
1057- * Takes any input type and will throw a REF_ERROR or coerce it into a number.
1058- * @param input to attempt to coerce into a number
1059- * @returns {number} number representation of the input
1060- */
1061-function firstValueAsNumber(input: any) : number {
1062- if (input instanceof Array) {
1063- if (input.length === 0) {
1064- throw new CellError(ERRORS.REF_ERROR, "Reference does not exist.");
1065- }
1066- return firstValueAsNumber(input[0]);
1067- }
1068- return valueToNumber(input);
1069-}
1070-
1071-/**
1072- * Takes any input type and will throw a REF_ERROR or coerce it into a string.
1073- * @param input to attempt to coerce into a string
1074- * @returns {number} number representation of the input
1075- */
1076-function firstValueAsString(input: any) : string {
1077- if (input instanceof Array) {
1078- if (input.length === 0) {
1079- throw new CellError(ERRORS.REF_ERROR, "Reference does not exist.");
1080- }
1081- return firstValueAsString(input[0]);
1082- }
1083- return valueToString(input);
1084-}
1085-
1086-/**
1087- * Takes any input type and will throw a REF_ERROR or coerce it into a string.
1088- * @param input to attempt to coerce into a string
1089- * @returns {number} number representation of the input
1090- */
1091-function firstValueAsBoolean(input: any) : boolean {
1092- if (input instanceof Array) {
1093- if (input.length === 0) {
1094- throw new CellError(ERRORS.REF_ERROR, "Reference does not exist.");
1095- }
1096- return firstValueAsBoolean(input[0]);
1097- }
1098- return valueToBoolean(input);
1099-}
1100-
1101-/**
1102- * Converts any value to a number or throws an error if it cannot coerce it to the number type
1103- * @param value to convert
1104- * @returns {number} to return. Will always return a number or throw an error. Never returns undefined.
1105- */
1106-function valueToNumber(value: any) : number {
1107- if (typeof value === "number") {
1108- return value;
1109- } else if (typeof value === "string") {
1110- if (value === "") {
1111- return 0;
1112- }
1113- if (value.indexOf(".") > -1) {
1114- var fl = parseFloat(value);
1115- if (isNaN(fl)) {
1116- throw new CellError(ERRORS.VALUE_ERROR, "Function ____ expects number values, but is text and cannot be coerced to a number.");
1117- }
1118- return fl;
1119- }
1120- var fl = parseInt(value);
1121- if (isNaN(fl)) {
1122- throw new CellError(ERRORS.VALUE_ERROR, "Function ____ expects number values, but is text and cannot be coerced to a number.");
1123- }
1124- return fl;
1125- } else if (typeof value === "boolean") {
1126- return value ? 1 : 0;
1127- }
1128- return 0;
1129-}
1130-
1131-/**
1132- * Returns true if we can coerce it to the number type
1133- * @param value to coerce
1134- * @returns {boolean} if could be coerced to a number
1135- */
1136-function valueCanCoerceToNumber(value: any) : boolean {
1137- if (typeof value === "number" || typeof value === "boolean") {
1138- return true;
1139- } else if (typeof value === "string") {
1140- if (value === "") {
1141- return false;
1142- }
1143- if (value.indexOf(".") > -1) {
1144- return !isNaN(parseFloat(value));
1145- }
1146- return !isNaN(parseInt(value));
1147- }
1148- return false;
1149-}
1150-
1151-
1152-/**
1153- * Converts string values in array to 0
1154- * @param arr to convert
1155- * @returns {Array} array in which all string values have been converted to 0.
1156- */
1157-function stringValuesToZeros(arr: Array<any>) : Array<any> {
1158- var toReturn = [];
1159- for (var i = 0; i < arr.length; i++) {
1160- if (typeof arr[i] !== "string") {
1161- toReturn.push(arr[i]);
1162- } else {
1163- toReturn.push(0);
1164- }
1165- }
1166- return toReturn;
1167-}
1168-
1169-/**
1170- * Converts any value to a boolean or throws an error if it cannot coerce it to the boolean type.
1171- * @param value to convert
1172- * @returns {boolean} to return.
1173- */
1174-function valueToBoolean(value: any) : boolean {
1175- if (typeof value === "number") {
1176- return value !== 0;
1177- } else if (typeof value === "string") {
1178- throw new CellError(ERRORS.VALUE_ERROR, "___ expects boolean values. But '" + value + "' is a text and cannot be coerced to a boolean.")
1179- } else if (typeof value === "boolean") {
1180- return value;
1181- }
1182-}
1183-
1184-/**
1185- * Flatten an array of arrays of ...
1186- * @param values array of values
1187- * @returns {Array} flattened array
1188- */
1189-function flatten(values: Array<any>) : Array<any> {
1190- return values.reduce(function (flat, toFlatten) {
1191- return flat.concat(Array.isArray(toFlatten) ? flatten(toFlatten) : toFlatten);
1192- }, []);
1193-}
1194-
1195 /**
1196 * Converts wild-card style expressions (in which * matches zero or more characters, and ? matches exactly one character)
1197 * to regular expressions. * and ? can be escaped by prefixing ~
1198@@ -298,21 +83,242 @@ class CriteriaFunctionFactory {
1199 }
1200 }
1201
1202+/**
1203+ * Static class of helpers used to cast various types to each other.
1204+ */
1205+class TypeCaster {
1206+ /**
1207+ * Converts any value to a number or throws an error if it cannot coerce it to the number type
1208+ * @param value to convert
1209+ * @returns {number} to return. Will always return a number or throw an error. Never returns undefined.
1210+ */
1211+ static valueToNumber(value : any) {
1212+ if (typeof value === "number") {
1213+ return value;
1214+ } else if (typeof value === "string") {
1215+ if (value === "") {
1216+ return 0;
1217+ }
1218+ if (value.indexOf(".") > -1) {
1219+ var fl = parseFloat(value);
1220+ if (isNaN(fl)) {
1221+ throw new CellError(ERRORS.VALUE_ERROR, "Function ____ expects number values, but is text and cannot be coerced to a number.");
1222+ }
1223+ return fl;
1224+ }
1225+ var fl = parseInt(value);
1226+ if (isNaN(fl)) {
1227+ throw new CellError(ERRORS.VALUE_ERROR, "Function ____ expects number values, but is text and cannot be coerced to a number.");
1228+ }
1229+ return fl;
1230+ } else if (typeof value === "boolean") {
1231+ return value ? 1 : 0;
1232+ }
1233+ return 0;
1234+ }
1235+ /**
1236+ * Converts any value to a boolean or throws an error if it cannot coerce it to the boolean type.
1237+ * @param value to convert
1238+ * @returns {boolean} to return.
1239+ */
1240+ static valueToBoolean(value: any) {
1241+ if (typeof value === "number") {
1242+ return value !== 0;
1243+ } else if (typeof value === "string") {
1244+ throw new CellError(ERRORS.VALUE_ERROR, "___ expects boolean values. But '" + value + "' is a text and cannot be coerced to a boolean.")
1245+ } else if (typeof value === "boolean") {
1246+ return value;
1247+ }
1248+ }
1249+ /**
1250+ * Convert a value to string.
1251+ * @param value of any type, including array. array cannot be empty.
1252+ * @returns {string} string representation of value
1253+ */
1254+ static valueToString(value: any) : string {
1255+ if (typeof value === "number") {
1256+ return value.toString();
1257+ } else if (typeof value === "string") {
1258+ return value;
1259+ } else if (typeof value === "boolean") {
1260+ return value ? "TRUE" : "FALSE";
1261+ } else if (value instanceof Array) {
1262+ return this.valueToString(value[0]); // TODO: Take this out. It's stupid. We should handle arrays at a different level.
1263+ }
1264+ }
1265+
1266+ /**
1267+ * Returns true if we can coerce it to the number type.
1268+ * @param value to coerce
1269+ * @returns {boolean} if could be coerced to a number
1270+ */
1271+ static canCoerceToNumber(value: any) : boolean {
1272+ if (typeof value === "number" || typeof value === "boolean") {
1273+ return true;
1274+ } else if (typeof value === "string") {
1275+ if (value === "") {
1276+ return false;
1277+ }
1278+ if (value.indexOf(".") > -1) {
1279+ return !isNaN(parseFloat(value));
1280+ }
1281+ return !isNaN(parseInt(value));
1282+ }
1283+ return false;
1284+ }
1285+
1286+ /**
1287+ * Takes any input type and will throw a REF_ERROR or coerce it into a number.
1288+ * @param input to attempt to coerce into a number
1289+ * @returns {number} number representation of the input
1290+ */
1291+ static firstValueAsNumber(input: any) : number {
1292+ if (input instanceof Array) {
1293+ if (input.length === 0) {
1294+ throw new CellError(ERRORS.REF_ERROR, "Reference does not exist.");
1295+ }
1296+ return TypeCaster.firstValueAsNumber(input[0]);
1297+ }
1298+ return TypeCaster.valueToNumber(input);
1299+ }
1300+
1301+ /**
1302+ * Takes any input type and will throw a REF_ERROR or coerce it into a string.
1303+ * @param input to attempt to coerce into a string
1304+ * @returns {number} number representation of the input
1305+ */
1306+ static firstValueAsString(input: any) : string {
1307+ if (input instanceof Array) {
1308+ if (input.length === 0) {
1309+ throw new CellError(ERRORS.REF_ERROR, "Reference does not exist.");
1310+ }
1311+ return TypeCaster.firstValueAsString(input[0]);
1312+ }
1313+ return TypeCaster.valueToString(input);
1314+ }
1315+
1316+ /**
1317+ * Takes any input type and will throw a REF_ERROR or coerce it into a string.
1318+ * @param input to attempt to coerce into a string
1319+ * @returns {number} number representation of the input
1320+ */
1321+ static firstValueAsBoolean(input: any): boolean {
1322+ if (input instanceof Array) {
1323+ if (input.length === 0) {
1324+ throw new CellError(ERRORS.REF_ERROR, "Reference does not exist.");
1325+ }
1326+ return TypeCaster.firstValueAsBoolean(input[0]);
1327+ }
1328+ return TypeCaster.valueToBoolean(input);
1329+ }
1330+}
1331+
1332+/**
1333+ * Static class to help filter down Arrays
1334+ */
1335+class Filter {
1336+ /**
1337+ * Converts string values in array to 0
1338+ * @param arr to convert
1339+ * @returns {Array} array in which all string values have been converted to 0.
1340+ */
1341+ static stringValuesToZeros(arr: Array<any>) : Array<any> {
1342+ var toReturn = [];
1343+ for (var i = 0; i < arr.length; i++) {
1344+ if (typeof arr[i] !== "string") {
1345+ toReturn.push(arr[i]);
1346+ } else {
1347+ toReturn.push(0);
1348+ }
1349+ }
1350+ return toReturn;
1351+ }
1352+
1353+ /**
1354+ * Flatten an array of arrays of ...
1355+ * @param values array of values
1356+ * @returns {Array} flattened array
1357+ */
1358+ static flatten(values: Array<any>) : Array<any> {
1359+ return values.reduce(function (flat, toFlatten) {
1360+ return flat.concat(Array.isArray(toFlatten) ? Filter.flatten(toFlatten) : toFlatten);
1361+ }, []);
1362+ }
1363+
1364+ /**
1365+ * Filter out all strings from an array.
1366+ * @param arr to filter
1367+ * @returns {Array} filtered array
1368+ */
1369+ static filterOutStringValues(arr: Array<any>) : Array<any> {
1370+ var toReturn = [];
1371+ for (var i = 0; i < arr.length; i++) {
1372+ if (typeof arr[i] !== "string") {
1373+ toReturn.push(arr[i]);
1374+ }
1375+ }
1376+ return toReturn;
1377+ }
1378+
1379+ /**
1380+ * Filters out non number values.
1381+ * @param arr to filter
1382+ * @returns {Array} filtered array
1383+ */
1384+ static filterOutNonNumberValues(arr: Array<any>) : Array<any> {
1385+ var toReturn = [];
1386+ for (var i = 0; i < arr.length; i++) {
1387+ if (typeof arr[i] !== "string" && typeof arr[i] !== "boolean") {
1388+ toReturn.push(arr[i]);
1389+ }
1390+ }
1391+ return toReturn;
1392+ }
1393+}
1394+
1395+/**
1396+ * Static class to check argument length within expected ranges when calling functions.
1397+ */
1398+class ArgsChecker {
1399+ /**
1400+ * Checks to see if the arguments are of the correct length.
1401+ * @param args to check length of
1402+ * @param length expected length
1403+ */
1404+ static checkLength(args: any, length: number) {
1405+ if (args.length !== length) {
1406+ throw new CellError(ERRORS.NA_ERROR, "Wrong number of arguments to ___. Expected 1 arguments, but got " + args.length + " arguments.");
1407+ }
1408+ }
1409+
1410+ /**
1411+ * Checks to see if the arguments are at least a certain length.
1412+ * @param args to check length of
1413+ * @param length expected length
1414+ */
1415+ static checkAtLeastLength(args: any, length: number) {
1416+ if (args.length < length) {
1417+ throw new CellError(ERRORS.NA_ERROR, "Wrong number of arguments to ___. Expected 1 arguments, but got " + args.length + " arguments.");
1418+ }
1419+ }
1420+
1421+ /**
1422+ * Checks to see if the arguments are within a max and min, inclusively
1423+ * @param args to check length of
1424+ * @param low least number of arguments
1425+ * @param high max number of arguments
1426+ */
1427+ static checkLengthWithin(args: any, low: number, high: number) {
1428+ if (args.length > high || args.length < low) {
1429+ throw new CellError(ERRORS.NA_ERROR, "Wrong number of arguments to ___. Expected 1 arguments, but got " + args.length + " arguments.");
1430+ }
1431+ }
1432+ }
1433+
1434
1435 export {
1436- stringValuesToZeros,
1437- firstValueAsBoolean,
1438- filterOutNonNumberValues,
1439- flatten,
1440- valueCanCoerceToNumber,
1441- valueToNumber,
1442- valueToString,
1443- valueToBoolean,
1444- firstValueAsNumber,
1445- firstValueAsString,
1446- filterOutStringValues,
1447- checkArgumentsAtLeastLength,
1448- checkArgumentsAtWithin,
1449- checkArgumentsLength,
1450- CriteriaFunctionFactory
1451+ ArgsChecker,
1452+ CriteriaFunctionFactory,
1453+ Filter,
1454+ TypeCaster
1455 }
1456\ No newline at end of file
1457diff --git a/tests/FormulasTest.ts b/tests/FormulasTest.ts
1458index dec3d88..7711933 100644
1459--- a/tests/FormulasTest.ts
1460+++ b/tests/FormulasTest.ts
1461@@ -10,7 +10,6 @@ import { ABS, ACCRINT, ACOS, ACOSH, ACOTH, AND, ARABIC, ASIN, ASINH, ATAN, ATAN2
1462 SUMSQ, SUMX2MY2, SUMX2PY2, TAN, TANH, TRUNC, XOR, YEARFRAC } from "../src/RawFormulas/RawFormulas"
1463 import * as ERRORS from "../src/Errors"
1464 import {assertEquals, assertEqualsDates, assertArrayEquals} from "./utils/Asserts"
1465-import {firstValueAsNumber} from "../src/RawFormulas/Utils";
1466
1467 function catchAndAssertEquals(toExecute, expected) {
1468 var toThrow = null;