spreadsheet
typeScript/javascript spreadsheet parser, with formulas.
git clone https://git.vogt.world/spreadsheet.git
Log | Files | README.md
← Commit log
commit
message
[Utilities] Breaking out Utils.ts into class files
author
Ben Vogt <[email protected]>
date
2017-04-30 17:46:12
stats
16 file(s) changed, 1072 insertions(+), 1009 deletions(-)
files
src/Formulas/Date.ts
src/Formulas/Engineering.ts
src/Formulas/Financial.ts
src/Formulas/Logical.ts
src/Formulas/Math.ts
src/Formulas/Statistical.ts
src/Formulas/Text.ts
src/Formulas/Utils.ts
src/Utilities/ArgsChecker.ts
src/Utilities/CriteriaFunctionFactory.ts
src/Utilities/DateRegExBuilder.ts
src/Utilities/Filter.ts
src/Utilities/Serializer.ts
src/Utilities/TypeCaster.ts
tests/Formulas/DateFormulasTest.ts
tests/Formulas/EngineeringTest.ts
   1diff --git a/src/Formulas/Date.ts b/src/Formulas/Date.ts
   2index 37e2940..684fa43 100644
   3--- a/src/Formulas/Date.ts
   4+++ b/src/Formulas/Date.ts
   5@@ -1,9 +1,11 @@
   6 /// <reference path="../../node_modules/moment/moment.d.ts"/>
   7 import * as moment from "moment";
   8 import {
   9-  ArgsChecker,
  10+  ArgsChecker
  11+} from "../Utilities/ArgsChecker";
  12+import {
  13   TypeCaster
  14-} from "./Utils";
  15+} from "../Utilities/TypeCaster";
  16 import {
  17   NumError,
  18   ValueError,
  19diff --git a/src/Formulas/Engineering.ts b/src/Formulas/Engineering.ts
  20index 72bbea5..59edbf7 100644
  21--- a/src/Formulas/Engineering.ts
  22+++ b/src/Formulas/Engineering.ts
  23@@ -1,7 +1,9 @@
  24 import {
  25-  ArgsChecker,
  26+  ArgsChecker
  27+} from "../Utilities/ArgsChecker";
  28+import {
  29   TypeCaster
  30-} from "./Utils";
  31+} from "../Utilities/TypeCaster";
  32 import {
  33   ValueError,
  34   NumError
  35diff --git a/src/Formulas/Financial.ts b/src/Formulas/Financial.ts
  36index 4e1f5c2..1affc70 100644
  37--- a/src/Formulas/Financial.ts
  38+++ b/src/Formulas/Financial.ts
  39@@ -1,8 +1,10 @@
  40 import {
  41-  ArgsChecker,
  42+  ArgsChecker
  43+} from "../Utilities/ArgsChecker";
  44+import {
  45   TypeCaster,
  46   checkForDevideByZero
  47-} from "./Utils";
  48+} from "../Utilities/TypeCaster";
  49 import {
  50   NumError,
  51   DivZeroError
  52diff --git a/src/Formulas/Logical.ts b/src/Formulas/Logical.ts
  53index 747acf8..289121e 100644
  54--- a/src/Formulas/Logical.ts
  55+++ b/src/Formulas/Logical.ts
  56@@ -1,5 +1,13 @@
  57-import { ArgsChecker, TypeCaster } from "./Utils"
  58-import { ValueError, RefError } from "../Errors"
  59+import {
  60+  ArgsChecker
  61+} from "../Utilities/ArgsChecker";
  62+import {
  63+  TypeCaster
  64+} from "../Utilities/TypeCaster";
  65+import {
  66+  ValueError,
  67+  RefError
  68+} from "../Errors";
  69 
  70 /**
  71  * Returns true if all of the provided arguments are logically true, and false if any of the provided arguments are logically false.
  72diff --git a/src/Formulas/Math.ts b/src/Formulas/Math.ts
  73index 335bcc3..1a0baf4 100644
  74--- a/src/Formulas/Math.ts
  75+++ b/src/Formulas/Math.ts
  76@@ -1,10 +1,18 @@
  77 import {
  78-  ArgsChecker,
  79-  CriteriaFunctionFactory,
  80-  Filter,
  81-  Serializer,
  82+  ArgsChecker
  83+} from "../Utilities/ArgsChecker";
  84+import {
  85   TypeCaster
  86-} from "./Utils";
  87+} from "../Utilities/TypeCaster";
  88+import {
  89+  Filter
  90+} from "../Utilities/Filter";
  91+import {
  92+  Serializer
  93+} from "../Utilities/Serializer";
  94+import {
  95+  CriteriaFunctionFactory
  96+} from "../Utilities/CriteriaFunctionFactory";
  97 import {
  98   NumError,
  99   DivZeroError,
 100@@ -933,7 +941,7 @@ var SUMX2MY2 = function (...values) : number {
 101 var COUNTUNIQUE = function (...values) : number {
 102   ArgsChecker.checkAtLeastLength(values, 1);
 103 
 104-  // Private function that will recursively generate an array of the unique primatives
 105+  // Private function that will recursively generate an array of the unique primitives
 106   var countUniquePrivate = function (values: Array<any>) : Object {
 107     var uniques = {};
 108     for (var i = 0; i < values.length; i++) {
 109diff --git a/src/Formulas/Statistical.ts b/src/Formulas/Statistical.ts
 110index 70d7f15..f57a71a 100644
 111--- a/src/Formulas/Statistical.ts
 112+++ b/src/Formulas/Statistical.ts
 113@@ -1,9 +1,15 @@
 114 import {
 115-  ArgsChecker,
 116-  CriteriaFunctionFactory,
 117-  Filter,
 118+  ArgsChecker
 119+} from "../Utilities/ArgsChecker";
 120+import {
 121+  CriteriaFunctionFactory
 122+} from "../Utilities/CriteriaFunctionFactory";
 123+import {
 124+  Filter
 125+} from "../Utilities/Filter";
 126+import {
 127   TypeCaster
 128-} from "./Utils";
 129+} from "../Utilities/TypeCaster";
 130 import {
 131   RefError, NumError, DivZeroError, NAError
 132 } from "../Errors";
 133diff --git a/src/Formulas/Text.ts b/src/Formulas/Text.ts
 134index dec14b8..6a9704e 100644
 135--- a/src/Formulas/Text.ts
 136+++ b/src/Formulas/Text.ts
 137@@ -1,7 +1,9 @@
 138 import {
 139-  ArgsChecker,
 140+  ArgsChecker
 141+} from "../Utilities/ArgsChecker";
 142+import {
 143   TypeCaster
 144-} from "./Utils";
 145+} from "../Utilities/TypeCaster";
 146 import {
 147   ValueError,
 148   NumError,
 149diff --git a/src/Utilities/ArgsChecker.ts b/src/Utilities/ArgsChecker.ts
 150new file mode 100644
 151index 0000000..be90c76
 152--- /dev/null
 153+++ b/src/Utilities/ArgsChecker.ts
 154@@ -0,0 +1,46 @@
 155+import {
 156+  NAError
 157+} from "../Errors";
 158+
 159+/**
 160+ * Static class to check argument length within expected ranges when calling functions.
 161+ */
 162+class ArgsChecker {
 163+  /**
 164+   * Checks to see if the arguments are of the correct length.
 165+   * @param args to check length of
 166+   * @param length expected length
 167+   */
 168+  static checkLength(args: Array<any> | IArguments, length: number) {
 169+    if (args.length !== length) {
 170+      throw new NAError("Wrong number of arguments to ___. Expected 1 arguments, but got " + args.length + " arguments.");
 171+    }
 172+  }
 173+
 174+  /**
 175+   * Checks to see if the arguments are at least a certain length.
 176+   * @param args to check length of
 177+   * @param length expected length
 178+   */
 179+  static checkAtLeastLength(args: any, length: number) {
 180+    if (args.length < length) {
 181+      throw new NAError("Wrong number of arguments to ___. Expected 1 arguments, but got " + args.length + " arguments.");
 182+    }
 183+  }
 184+
 185+  /**
 186+   * Checks to see if the arguments are within a max and min, inclusively
 187+   * @param args to check length of
 188+   * @param low least number of arguments
 189+   * @param high max number of arguments
 190+   */
 191+  static checkLengthWithin(args: any, low: number, high: number) {
 192+    if (args.length > high || args.length < low) {
 193+      throw new NAError("Wrong number of arguments to ___. Expected 1 arguments, but got " + args.length + " arguments.");
 194+    }
 195+  }
 196+}
 197+
 198+export {
 199+  ArgsChecker
 200+}
 201\ No newline at end of file
 202diff --git a/src/Utilities/CriteriaFunctionFactory.ts b/src/Utilities/CriteriaFunctionFactory.ts
 203new file mode 100644
 204index 0000000..bee94e3
 205--- /dev/null
 206+++ b/src/Utilities/CriteriaFunctionFactory.ts
 207@@ -0,0 +1,91 @@
 208+/**
 209+ * Converts wild-card style expressions (in which * matches zero or more characters, and ? matches exactly one character)
 210+ * to regular expressions. * and ? can be escaped by prefixing ~
 211+ * @param c input
 212+ * @returns {RegExp} resulting regex
 213+ */
 214+function wildCardRegex(c: string) {
 215+  var a = c.split("~?");
 216+  for (var i = 0; i < a.length; i++) {
 217+    a[i] = a[i].split("?").join(".{1}");
 218+  }
 219+  var b = a.join("\\\?");
 220+  var d = b.split("~*");
 221+  for (var i = 0; i < d.length; i++) {
 222+    d[i] = d[i].split("*").join(".*");
 223+  }
 224+  return new RegExp("^"+d.join(".*")+"$", "g");
 225+}
 226+
 227+
 228+
 229+
 230+/**
 231+ * Creates a criteria function to evaluate elements in a range in an *IF function.
 232+ */
 233+class CriteriaFunctionFactory {
 234+  /**
 235+   * If the criteria is a number, use strict equality checking.
 236+   * If the criteria is a string, check to see if it is a comparator.
 237+   * If the criteria is a string, and it is not a comparator, check for regex.
 238+   * If the criteria is a string and has not matched the above, finally use strict equality checking as a fallback.
 239+   * If the criteria has not been set, default to false-returning criteria function.
 240+   * @param criteria
 241+   * @returns {(x:any)=>boolean}
 242+   */
 243+  static createCriteriaFunction(criteria: string) : Function {
 244+    // Default criteria does nothing
 245+    var criteriaEvaluation = function (x) : boolean {
 246+      return false;
 247+    };
 248+
 249+    if (typeof criteria === "number" || typeof criteria === "boolean") {
 250+      criteriaEvaluation = function (x) : boolean {
 251+        return x === criteria;
 252+      };
 253+    } else if (typeof criteria === "string") {
 254+      var comparisonMatches = criteria.match(/^\s*(<=|>=|=|<>|>|<)\s*(-)?\s*(\$)?\s*([0-9]+([,.][0-9]+)?)\s*$/);
 255+      if (comparisonMatches !== null && comparisonMatches.length >= 6 && comparisonMatches[4] !== undefined) {
 256+        criteriaEvaluation = function (x) : boolean {
 257+          return eval(x + comparisonMatches[1] + (comparisonMatches[2] === undefined ? "" : "-") +  comparisonMatches[4]);
 258+        };
 259+        if (comparisonMatches[1] === "=") {
 260+          criteriaEvaluation = function (x) : boolean {
 261+            return eval(x + "===" + (comparisonMatches[2] === undefined ? "" : "-") +  comparisonMatches[4]);
 262+          };
 263+        }
 264+        if (comparisonMatches[1] === "<>") {
 265+          criteriaEvaluation = function (x) : boolean {
 266+            return eval(x + "!==" + (comparisonMatches[2] === undefined ? "" : "-") +  comparisonMatches[4]);
 267+          };
 268+        }
 269+      } else if (criteria.match(/\*|\~\*|\?|\~\?/) !== null) {
 270+        // Regular string
 271+        var matches = criteria.match(/\*|\~\*|\?|\~\?/);
 272+        if (matches !== null) {
 273+          criteriaEvaluation = function (x) : boolean {
 274+            try {
 275+              // http://stackoverflow.com/questions/26246601/wildcard-string-comparison-in-javascript
 276+              return wildCardRegex(criteria).test(x);
 277+            } catch (e) {
 278+              return false;
 279+            }
 280+          };
 281+        } else {
 282+          criteriaEvaluation = function (x) : boolean {
 283+            return x === criteria;
 284+          };
 285+        }
 286+      } else {
 287+        criteriaEvaluation = function (x) : boolean {
 288+          return x === criteria;
 289+        };
 290+      }
 291+    }
 292+    return criteriaEvaluation;
 293+  }
 294+}
 295+
 296+export {
 297+  CriteriaFunctionFactory
 298+}
 299\ No newline at end of file
 300diff --git a/src/Utilities/DateRegExBuilder.ts b/src/Utilities/DateRegExBuilder.ts
 301new file mode 100644
 302index 0000000..a25cb27
 303--- /dev/null
 304+++ b/src/Utilities/DateRegExBuilder.ts
 305@@ -0,0 +1,198 @@
 306+/**
 307+ * Build a regular expression step by step, to make it easier to build and read the resulting regular expressions.
 308+ */
 309+class DateRegExBuilder {
 310+  private regexString = "";
 311+  private static ZERO_OR_MORE_SPACES = "\\s*";
 312+
 313+  static DateRegExBuilder() : DateRegExBuilder {
 314+    return new DateRegExBuilder();
 315+  }
 316+
 317+  /**
 318+   * Start the regular expression builder by matching the start of a line and zero or more spaces.
 319+   * @returns {DateRegExBuilder} builder
 320+   */
 321+  start() : DateRegExBuilder {
 322+    this.regexString += "^" + DateRegExBuilder.ZERO_OR_MORE_SPACES;
 323+    return this;
 324+  }
 325+
 326+  /**
 327+   * End the regular expression builder by matching the end of the line.
 328+   * @returns {DateRegExBuilder} builder
 329+   */
 330+  end(): DateRegExBuilder {
 331+    this.regexString += "$";
 332+    return this;
 333+  }
 334+
 335+  /**
 336+   * Capture all month full name and short names to the regular expression.
 337+   * @returns {DateRegExBuilder} builder
 338+   */
 339+  MONTHNAME() : DateRegExBuilder {
 340+    this.regexString += "(january|february|march|april|may|june|july|august|september|october|november|december|jan|feb|mar|apr|jun|jul|aug|sep|oct|nov|dec)";
 341+    return this;
 342+  }
 343+
 344+  /**
 345+   * Capture all month full name and short names to the regular expression, in addition to any followed by one or more
 346+   * spaces.
 347+   * @returns {DateRegExBuilder} builder
 348+   * @constructor
 349+   */
 350+  MONTHNAME_W_SPACE() : DateRegExBuilder {
 351+    this.regexString += "(january|february|march|april|may|june|july|august|september|october|november|december|jan|feb|mar|apr|jun|jul|aug|sep|oct|nov|dec|january\\s+|february\\s+|march\\s+|april\\s+|may\\s+|june\\s+|july\\s+|august\\s+|september\\s+|october\\s+|november\\s+|december\\s+|jan\\s+|feb\\s+|mar\\s+|apr\\s+|jun\\s+|jul\\s+|aug\\s+|sep\\s+|oct\\s+|nov\\s+|dec\\s+)";
 352+    return this;
 353+  }
 354+
 355+  /**
 356+   * Add capture group for optionally capturing day names.
 357+   * @returns {DateRegExBuilder} builder
 358+   * @constructor
 359+   */
 360+  OPTIONAL_DAYNAME() : DateRegExBuilder {
 361+    this.regexString += "(sunday|monday|tuesday|wednesday|thursday|friday|saturday|sun|mon|tue|wed|thu|fri|sat)?";
 362+    return this;
 363+  }
 364+
 365+  /**
 366+   * Add capture group for optionally capturing a comma followed by one or more spaces.
 367+   * @returns {DateRegExBuilder} builder
 368+   * @constructor
 369+   */
 370+  OPTIONAL_COMMA() : DateRegExBuilder {
 371+    this.regexString += "(,?\\s+)?";
 372+    return this;
 373+  }
 374+
 375+  /**
 376+   * Add capture group for capturing month digits between 01 and 12, inclusively.
 377+   * @returns {DateRegExBuilder} builder
 378+   * @constructor
 379+   */
 380+  MM() : DateRegExBuilder {
 381+    this.regexString += "([1-9]|0[1-9]|1[0-2])";
 382+    return this;
 383+  }
 384+
 385+  /**
 386+   * Add capture group for capturing month digits between 01 and 12, inclusively, in addition to any followed by one or
 387+   * more spaces.
 388+   * @returns {DateRegExBuilder} builder
 389+   * @constructor
 390+   */
 391+  MM_W_SPACE() : DateRegExBuilder {
 392+    this.regexString += "([1-9]|0[1-9]|1[0-2]|[1-9]\\s+|0[1-9]\\s+|1[0-2]\\s+)";
 393+    return this;
 394+  }
 395+
 396+  /**
 397+   * Add capture group for capturing day digits between 01 and 31, inclusively.
 398+   * @returns {DateRegExBuilder} builder
 399+   * @constructor
 400+   */
 401+  DD() : DateRegExBuilder {
 402+    this.regexString += "(0?[0-9]|1[0-9]|2[0-9]|3[0-1])";
 403+    return this;
 404+  }
 405+
 406+  /**
 407+   * Add capture group for capturing day digits between 01 and 31, inclusively, in addition to any followed by one or
 408+   * more spaces.
 409+   * @returns {DateRegExBuilder} builder
 410+   * @constructor
 411+   */
 412+  DD_W_SPACE() : DateRegExBuilder {
 413+    this.regexString += "(0?[0-9]|1[0-9]|2[0-9]|3[0-1]|0?[0-9]\\s+|1[0-9]\\s+|2[0-9]\\s+|3[0-1]\\s+)";
 414+    return this;
 415+  }
 416+
 417+  /**
 418+   * Add capture group for capturing 4 digits or 3 digits starting with 0-9.
 419+   * @returns {DateRegExBuilder} builder
 420+   * @constructor
 421+   */
 422+  YYYY() : DateRegExBuilder {
 423+    this.regexString += "([0-9]{4}|[1-9][0-9][0-9])";
 424+    return this;
 425+  }
 426+
 427+  /**
 428+   * Add capture group for capturing 1 through 4 digits.
 429+   * @returns {DateRegExBuilder} builder
 430+   * @constructor
 431+   */
 432+  YYYY14() : DateRegExBuilder {
 433+    this.regexString += "([0-9]{1,4})";
 434+    return this;
 435+  }
 436+
 437+  /**
 438+   * Add capture group for capturing 1 through 4 digits, in addition to any followed by one or more spaces.
 439+   * @returns {DateRegExBuilder} builder
 440+   * @constructor
 441+   */
 442+  YYYY14_W_SPACE() : DateRegExBuilder {
 443+    this.regexString += "([0-9]{1,4}|[0-9]{1,4}\\s+)";
 444+    return this;
 445+  }
 446+
 447+  YYYY2_OR_4_W_SPACE() : DateRegExBuilder {
 448+    this.regexString += "([0-9]{2}|[0-9]{4}|[0-9]{2}\\s+|[0-9]{4}\\s+)";
 449+    return this;
 450+  }
 451+
 452+  /**
 453+   * Add capture group for a flexible delimiter, including ", ", " ", ". ", "\", "-".
 454+   * @returns {DateRegExBuilder} builder
 455+   * @constructor
 456+   */
 457+  FLEX_DELIMITER() : DateRegExBuilder {
 458+    // this.regexString += "(,?\\s+|\\s*-?\\.?-?\\/?\\s+)";// close to being right
 459+    this.regexString += "(,?\\s+|\\s*\\.\\s+|\\s*-\\s*|\\s*\\/\\s*)";
 460+    return this;
 461+  }
 462+
 463+  /**
 464+   * Add capture group for a flexible delimiter, including ", ", " ", ".", "\", "-". Different from FLEX_DELIMITER
 465+   * in that it will match periods with zero or more spaces on either side.
 466+   * For reference: https://regex101.com/r/q1fp1z/1/
 467+   * @returns {DateRegExBuilder} builder
 468+   * @constructor
 469+   */
 470+  FLEX_DELIMITER_LOOSEDOT() : DateRegExBuilder {
 471+    // this.regexString += "(,?\\s+|\\s*-?\\.?-?\\/?\\s+)";// close to being right
 472+    this.regexString += "(,?\\s+|\\s*\\.\\s*|\\s*-\\s*|\\s*\\/\\s*)";
 473+    return this;
 474+  }
 475+
 476+  /**
 477+   * Add a capture group for capturing timestamps including: "10am", "10:10", "10:10pm", "10:10:10", "10:10:10am", along
 478+   * with zero or more spaces after semi colons, AM or PM, and unlimited number of digits per unit.
 479+   * @returns {DateRegExBuilder} builder
 480+   * @constructor
 481+   */
 482+  OPTIONAL_TIMESTAMP_CAPTURE_GROUP() : DateRegExBuilder {
 483+    this.regexString += "((\\s+[0-9]+\\s*am\\s*$|[0-9]+\\s*pm\\s*$)|(\\s+[0-9]+:\\s*[0-9]+\\s*$)|(\\s+[0-9]+:\\s*[0-9]+\\s*am\\s*$|\\s+[0-9]+:\\s*[0-9]+\\s*pm\\s*$)|(\\s+[0-9]+:\\s*[0-9]+:\\s*[0-9]+\\s*$)|(\\s+[0-9]+:\\s*[0-9]+:\\s*[0-9]+\\s*am\\s*$|[0-9]+:\\s*[0-9]+:\\s*[0-9]+\\s*pm\\s*$))?";
 484+    return this;
 485+  }
 486+
 487+  TIMESTAMP_UNITS_CAPTURE_GROUP() : DateRegExBuilder {
 488+    this.regexString += "(\\s*([0-9]+)()()\\s*(am|pm)\\s*$)|(\\s*([0-9]+):\\s*([0-9]+)()()\\s*$)|(\\s*(([0-9]+):\\s*([0-9]+)()\\s*(am|pm))\\s*$)|(\\s*(([0-9]+):\\s*([0-9]+):\\s*([0-9]+)())\\s*$)|(\\s*(([0-9]+):\\s*([0-9]+):\\s*([0-9]+)\\s*(am|pm))\\s*$)";
 489+    return this;
 490+  }
 491+
 492+  /**
 493+   * Build the regular expression and ignore case.
 494+   * @returns {RegExp}
 495+   */
 496+  build() : RegExp {
 497+    return new RegExp(this.regexString, 'i');
 498+  }
 499+}
 500+
 501+export {
 502+  DateRegExBuilder
 503+}
 504\ No newline at end of file
 505diff --git a/src/Utilities/Filter.ts b/src/Utilities/Filter.ts
 506new file mode 100644
 507index 0000000..f7d070b
 508--- /dev/null
 509+++ b/src/Utilities/Filter.ts
 510@@ -0,0 +1,84 @@
 511+import {
 512+  RefError
 513+} from "../Errors";
 514+
 515+/**
 516+ * Static class to help filter down Arrays
 517+ */
 518+class Filter {
 519+  /**
 520+   * Converts string values in array to 0
 521+   * @param arr to convert
 522+   * @returns {Array} array in which all string values have been converted to 0.
 523+   */
 524+  static stringValuesToZeros(arr: Array<any>) : Array<any> {
 525+    var toReturn = [];
 526+    for (var i = 0; i < arr.length; i++) {
 527+      if (typeof arr[i] !== "string") {
 528+        toReturn.push(arr[i]);
 529+      } else {
 530+        toReturn.push(0);
 531+      }
 532+    }
 533+    return toReturn;
 534+  }
 535+
 536+  /**
 537+   * Flatten an array of arrays of ...etc.
 538+   * @param values array of values
 539+   * @returns {Array} flattened array
 540+   */
 541+  static flatten(values: Array<any>) : Array<any> {
 542+    return values.reduce(function (flat, toFlatten) {
 543+      return flat.concat(Array.isArray(toFlatten) ? Filter.flatten(toFlatten) : toFlatten);
 544+    }, []);
 545+  }
 546+
 547+  /**
 548+   * Flatten an array of arrays of... etc, but throw an error if any are empty references.
 549+   * @param values array of values
 550+   * @returns {Array} flattened array
 551+   */
 552+  static flattenAndThrow(values: Array<any>) : Array<any> {
 553+    return values.reduce(function (flat, toFlatten) {
 554+      if (Array.isArray(toFlatten) && toFlatten.length === 0) {
 555+        throw new RefError("Reference does not exist.");
 556+      }
 557+      return flat.concat(Array.isArray(toFlatten) ? Filter.flattenAndThrow(toFlatten) : toFlatten);
 558+    }, []);
 559+  }
 560+
 561+  /**
 562+   * Filter out all strings from an array.
 563+   * @param arr to filter
 564+   * @returns {Array} filtered array
 565+   */
 566+  static filterOutStringValues(arr: Array<any>) : Array<any> {
 567+    var toReturn = [];
 568+    for (var i = 0; i < arr.length; i++) {
 569+      if (typeof arr[i] !== "string") {
 570+        toReturn.push(arr[i]);
 571+      }
 572+    }
 573+    return toReturn;
 574+  }
 575+
 576+  /**
 577+   * Filters out non number values.
 578+   * @param arr to filter
 579+   * @returns {Array} filtered array
 580+   */
 581+  static filterOutNonNumberValues(arr: Array<any>) : Array<any> {
 582+    var toReturn = [];
 583+    for (var i = 0; i < arr.length; i++) {
 584+      if (typeof arr[i] !== "string" && typeof arr[i] !== "boolean") {
 585+        toReturn.push(arr[i]);
 586+      }
 587+    }
 588+    return toReturn;
 589+  }
 590+}
 591+
 592+export {
 593+  Filter
 594+}
 595\ No newline at end of file
 596diff --git a/src/Utilities/Serializer.ts b/src/Utilities/Serializer.ts
 597new file mode 100644
 598index 0000000..95e6c38
 599--- /dev/null
 600+++ b/src/Utilities/Serializer.ts
 601@@ -0,0 +1,13 @@
 602+/**
 603+ * Class to hold static methods for serialization.
 604+ */
 605+class Serializer {
 606+  static serialize(value: any) : string {
 607+    var t = typeof value;
 608+    return "<" +  t + ": " + value + ">";
 609+  }
 610+}
 611+
 612+export {
 613+  Serializer
 614+}
 615\ No newline at end of file
 616diff --git a/src/Formulas/Utils.ts b/src/Utilities/TypeCaster.ts
 617similarity index 60%
 618rename from src/Formulas/Utils.ts
 619rename to src/Utilities/TypeCaster.ts
 620index cd32b8e..119c6b8 100644
 621--- a/src/Formulas/Utils.ts
 622+++ b/src/Utilities/TypeCaster.ts
 623@@ -1,221 +1,15 @@
 624 /// <reference path="../../node_modules/moment/moment.d.ts"/>
 625 import * as moment from "moment";
 626-import { ValueError, RefError, NAError, DivZeroError } from "../Errors"
 627-import { ExcelDate } from "../ExcelDate";
 628-
 629-/**
 630- * Converts wild-card style expressions (in which * matches zero or more characters, and ? matches exactly one character)
 631- * to regular expressions. * and ? can be escaped by prefixing ~
 632- * @param c input
 633- * @returns {RegExp} resulting regex
 634- */
 635-function wildCardRegex(c: string) {
 636-  var a = c.split("~?");
 637-  for (var i = 0; i < a.length; i++) {
 638-    a[i] = a[i].split("?").join(".{1}");
 639-  }
 640-  var b = a.join("\\\?");
 641-  var d = b.split("~*");
 642-  for (var i = 0; i < d.length; i++) {
 643-    d[i] = d[i].split("*").join(".*");
 644-  }
 645-  return new RegExp("^"+d.join(".*")+"$", "g");
 646-}
 647-
 648-/**
 649- * Build a regular expression step by step, to make it easier to build and read the resulting regular expressions.
 650- */
 651-class DateRegExBuilder {
 652-  private regexString = "";
 653-  private static ZERO_OR_MORE_SPACES = "\\s*";
 654-
 655-  static DateRegExBuilder() : DateRegExBuilder {
 656-    return new DateRegExBuilder();
 657-  }
 658-
 659-  /**
 660-   * Start the regular expression builder by matching the start of a line and zero or more spaces.
 661-   * @returns {DateRegExBuilder} builder
 662-   */
 663-  start() : DateRegExBuilder {
 664-    this.regexString += "^" + DateRegExBuilder.ZERO_OR_MORE_SPACES;
 665-    return this;
 666-  }
 667-
 668-  /**
 669-   * End the regular expression builder by matching the end of the line.
 670-   * @returns {DateRegExBuilder} builder
 671-   */
 672-  end(): DateRegExBuilder {
 673-    this.regexString += "$";
 674-    return this;
 675-  }
 676-
 677-  /**
 678-   * Capture all month full name and short names to the regular expression.
 679-   * @returns {DateRegExBuilder} builder
 680-   */
 681-  MONTHNAME() : DateRegExBuilder {
 682-    this.regexString += "(january|february|march|april|may|june|july|august|september|october|november|december|jan|feb|mar|apr|jun|jul|aug|sep|oct|nov|dec)";
 683-    return this;
 684-  }
 685-
 686-  /**
 687-   * Capture all month full name and short names to the regular expression, in addition to any followed by one or more
 688-   * spaces.
 689-   * @returns {DateRegExBuilder} builder
 690-   * @constructor
 691-   */
 692-  MONTHNAME_W_SPACE() : DateRegExBuilder {
 693-    this.regexString += "(january|february|march|april|may|june|july|august|september|october|november|december|jan|feb|mar|apr|jun|jul|aug|sep|oct|nov|dec|january\\s+|february\\s+|march\\s+|april\\s+|may\\s+|june\\s+|july\\s+|august\\s+|september\\s+|october\\s+|november\\s+|december\\s+|jan\\s+|feb\\s+|mar\\s+|apr\\s+|jun\\s+|jul\\s+|aug\\s+|sep\\s+|oct\\s+|nov\\s+|dec\\s+)";
 694-    return this;
 695-  }
 696-
 697-  /**
 698-   * Add capture group for optionally capturing day names.
 699-   * @returns {DateRegExBuilder} builder
 700-   * @constructor
 701-   */
 702-  OPTIONAL_DAYNAME() : DateRegExBuilder {
 703-    this.regexString += "(sunday|monday|tuesday|wednesday|thursday|friday|saturday|sun|mon|tue|wed|thu|fri|sat)?";
 704-    return this;
 705-  }
 706-
 707-  /**
 708-   * Add capture group for optionally capturing a comma followed by one or more spaces.
 709-   * @returns {DateRegExBuilder} builder
 710-   * @constructor
 711-   */
 712-  OPTIONAL_COMMA() : DateRegExBuilder {
 713-    this.regexString += "(,?\\s+)?";
 714-    return this;
 715-  }
 716-
 717-  /**
 718-   * Add capture group for capturing month digits between 01 and 12, inclusively.
 719-   * @returns {DateRegExBuilder} builder
 720-   * @constructor
 721-   */
 722-  MM() : DateRegExBuilder {
 723-    this.regexString += "([1-9]|0[1-9]|1[0-2])";
 724-    return this;
 725-  }
 726-
 727-  /**
 728-   * Add capture group for capturing month digits between 01 and 12, inclusively, in addition to any followed by one or
 729-   * more spaces.
 730-   * @returns {DateRegExBuilder} builder
 731-   * @constructor
 732-   */
 733-  MM_W_SPACE() : DateRegExBuilder {
 734-    this.regexString += "([1-9]|0[1-9]|1[0-2]|[1-9]\\s+|0[1-9]\\s+|1[0-2]\\s+)";
 735-    return this;
 736-  }
 737-
 738-  /**
 739-   * Add capture group for capturing day digits between 01 and 31, inclusively.
 740-   * @returns {DateRegExBuilder} builder
 741-   * @constructor
 742-   */
 743-  DD() : DateRegExBuilder {
 744-    this.regexString += "(0?[0-9]|1[0-9]|2[0-9]|3[0-1])";
 745-    return this;
 746-  }
 747-
 748-  /**
 749-   * Add capture group for capturing day digits between 01 and 31, inclusively, in addition to any followed by one or
 750-   * more spaces.
 751-   * @returns {DateRegExBuilder} builder
 752-   * @constructor
 753-   */
 754-  DD_W_SPACE() : DateRegExBuilder {
 755-    this.regexString += "(0?[0-9]|1[0-9]|2[0-9]|3[0-1]|0?[0-9]\\s+|1[0-9]\\s+|2[0-9]\\s+|3[0-1]\\s+)";
 756-    return this;
 757-  }
 758-
 759-  /**
 760-   * Add capture group for capturing 4 digits or 3 digits starting with 0-9.
 761-   * @returns {DateRegExBuilder} builder
 762-   * @constructor
 763-   */
 764-  YYYY() : DateRegExBuilder {
 765-    this.regexString += "([0-9]{4}|[1-9][0-9][0-9])";
 766-    return this;
 767-  }
 768-
 769-  /**
 770-   * Add capture group for capturing 1 through 4 digits.
 771-   * @returns {DateRegExBuilder} builder
 772-   * @constructor
 773-   */
 774-  YYYY14() : DateRegExBuilder {
 775-    this.regexString += "([0-9]{1,4})";
 776-    return this;
 777-  }
 778-
 779-  /**
 780-   * Add capture group for capturing 1 through 4 digits, in addition to any followed by one or more spaces.
 781-   * @returns {DateRegExBuilder} builder
 782-   * @constructor
 783-   */
 784-  YYYY14_W_SPACE() : DateRegExBuilder {
 785-    this.regexString += "([0-9]{1,4}|[0-9]{1,4}\\s+)";
 786-    return this;
 787-  }
 788-
 789-  YYYY2_OR_4_W_SPACE() : DateRegExBuilder {
 790-    this.regexString += "([0-9]{2}|[0-9]{4}|[0-9]{2}\\s+|[0-9]{4}\\s+)";
 791-    return this;
 792-  }
 793-
 794-  /**
 795-   * Add capture group for a flexible delimiter, including ", ", " ", ". ", "\", "-".
 796-   * @returns {DateRegExBuilder} builder
 797-   * @constructor
 798-   */
 799-  FLEX_DELIMITER() : DateRegExBuilder {
 800-    // this.regexString += "(,?\\s+|\\s*-?\\.?-?\\/?\\s+)";// close to being right
 801-    this.regexString += "(,?\\s+|\\s*\\.\\s+|\\s*-\\s*|\\s*\\/\\s*)";
 802-    return this;
 803-  }
 804-
 805-  /**
 806-   * Add capture group for a flexible delimiter, including ", ", " ", ".", "\", "-". Different from FLEX_DELIMITER
 807-   * in that it will match periods with zero or more spaces on either side.
 808-   * For reference: https://regex101.com/r/q1fp1z/1/
 809-   * @returns {DateRegExBuilder} builder
 810-   * @constructor
 811-   */
 812-  FLEX_DELIMITER_LOOSEDOT() : DateRegExBuilder {
 813-    // this.regexString += "(,?\\s+|\\s*-?\\.?-?\\/?\\s+)";// close to being right
 814-    this.regexString += "(,?\\s+|\\s*\\.\\s*|\\s*-\\s*|\\s*\\/\\s*)";
 815-    return this;
 816-  }
 817-
 818-  /**
 819-   * Add a capture group for capturing timestamps including: "10am", "10:10", "10:10pm", "10:10:10", "10:10:10am", along
 820-   * with zero or more spaces after semi colons, AM or PM, and unlimited number of digits per unit.
 821-   * @returns {DateRegExBuilder} builder
 822-   * @constructor
 823-   */
 824-  OPTIONAL_TIMESTAMP_CAPTURE_GROUP() : DateRegExBuilder {
 825-    this.regexString += "((\\s+[0-9]+\\s*am\\s*$|[0-9]+\\s*pm\\s*$)|(\\s+[0-9]+:\\s*[0-9]+\\s*$)|(\\s+[0-9]+:\\s*[0-9]+\\s*am\\s*$|\\s+[0-9]+:\\s*[0-9]+\\s*pm\\s*$)|(\\s+[0-9]+:\\s*[0-9]+:\\s*[0-9]+\\s*$)|(\\s+[0-9]+:\\s*[0-9]+:\\s*[0-9]+\\s*am\\s*$|[0-9]+:\\s*[0-9]+:\\s*[0-9]+\\s*pm\\s*$))?";
 826-    return this;
 827-  }
 828-
 829-  TIMESTAMP_UNITS_CAPTURE_GROUP() : DateRegExBuilder {
 830-    this.regexString += "(\\s*([0-9]+)()()\\s*(am|pm)\\s*$)|(\\s*([0-9]+):\\s*([0-9]+)()()\\s*$)|(\\s*(([0-9]+):\\s*([0-9]+)()\\s*(am|pm))\\s*$)|(\\s*(([0-9]+):\\s*([0-9]+):\\s*([0-9]+)())\\s*$)|(\\s*(([0-9]+):\\s*([0-9]+):\\s*([0-9]+)\\s*(am|pm))\\s*$)";
 831-    return this;
 832-  }
 833-
 834-  /**
 835-   * Build the regular expression and ignore case.
 836-   * @returns {RegExp}
 837-   */
 838-  build() : RegExp {
 839-    return new RegExp(this.regexString, 'i');
 840-  }
 841-}
 842+import {
 843+  RefError,
 844+  ValueError, DivZeroError
 845+} from "../Errors";
 846+import {
 847+  ExcelDate
 848+} from "../ExcelDate";
 849+import {
 850+  DateRegExBuilder
 851+} from "./DateRegExBuilder";
 852 
 853 const YEAR_MONTHDIG_DAY = DateRegExBuilder.DateRegExBuilder()
 854   .start()
 855@@ -268,74 +62,6 @@ const FIRST_YEAR = 1900;
 856 // The year 2000.
 857 const Y2K_YEAR = 2000;
 858 
 859-
 860-/**
 861- * Creates a criteria function to evaluate elements in a range in an *IF function.
 862- */
 863-class CriteriaFunctionFactory {
 864-  /**
 865-   * If the criteria is a number, use strict equality checking.
 866-   * If the criteria is a string, check to see if it is a comparator.
 867-   * If the criteria is a string, and it is not a comparator, check for regex.
 868-   * If the criteria is a string and has not matched the above, finally use strict equality checking as a fallback.
 869-   * If the criteria has not been set, default to false-returning criteria function.
 870-   * @param criteria
 871-   * @returns {(x:any)=>boolean}
 872-   */
 873-  static createCriteriaFunction(criteria: string) : Function {
 874-    // Default criteria does nothing
 875-    var criteriaEvaluation = function (x) : boolean {
 876-      return false;
 877-    };
 878-
 879-    if (typeof criteria === "number" || typeof criteria === "boolean") {
 880-      criteriaEvaluation = function (x) : boolean {
 881-        return x === criteria;
 882-      };
 883-    } else if (typeof criteria === "string") {
 884-      var comparisonMatches = criteria.match(/^\s*(<=|>=|=|<>|>|<)\s*(-)?\s*(\$)?\s*([0-9]+([,.][0-9]+)?)\s*$/);
 885-      if (comparisonMatches !== null && comparisonMatches.length >= 6 && comparisonMatches[4] !== undefined) {
 886-        criteriaEvaluation = function (x) : boolean {
 887-          return eval(x + comparisonMatches[1] + (comparisonMatches[2] === undefined ? "" : "-") +  comparisonMatches[4]);
 888-        };
 889-        if (comparisonMatches[1] === "=") {
 890-          criteriaEvaluation = function (x) : boolean {
 891-            return eval(x + "===" + (comparisonMatches[2] === undefined ? "" : "-") +  comparisonMatches[4]);
 892-          };
 893-        }
 894-        if (comparisonMatches[1] === "<>") {
 895-          criteriaEvaluation = function (x) : boolean {
 896-            return eval(x + "!==" + (comparisonMatches[2] === undefined ? "" : "-") +  comparisonMatches[4]);
 897-          };
 898-        }
 899-      } else if (criteria.match(/\*|\~\*|\?|\~\?/) !== null) {
 900-        // Regular string
 901-        var matches = criteria.match(/\*|\~\*|\?|\~\?/);
 902-        if (matches !== null) {
 903-          criteriaEvaluation = function (x) : boolean {
 904-            try {
 905-              // http://stackoverflow.com/questions/26246601/wildcard-string-comparison-in-javascript
 906-              return wildCardRegex(criteria).test(x);
 907-            } catch (e) {
 908-              return false;
 909-            }
 910-          };
 911-        } else {
 912-          criteriaEvaluation = function (x) : boolean {
 913-            return x === criteria;
 914-          };
 915-        }
 916-      } else {
 917-        criteriaEvaluation = function (x) : boolean {
 918-          return x === criteria;
 919-        };
 920-      }
 921-    }
 922-    return criteriaEvaluation;
 923-  }
 924-}
 925-
 926-
 927 /**
 928  * Matches a timestamp string, adding the units to the moment passed in.
 929  * @param timestampString to parse. ok formats: "10am", "10:10", "10:10am", "10:10:10", "10:10:10am", etc.
 930@@ -398,7 +124,6 @@ function matchTimestampAndMutateMoment(timestampString : string, momentToMutate:
 931   return momentToMutate;
 932 }
 933 
 934-
 935 /**
 936  * Static class of helpers used to cast various types to each other.
 937  */
 938@@ -841,132 +566,6 @@ class TypeCaster {
 939   }
 940 }
 941 
 942-/**
 943- * Static class to help filter down Arrays
 944- */
 945-class Filter {
 946-  /**
 947-   * Converts string values in array to 0
 948-   * @param arr to convert
 949-   * @returns {Array} array in which all string values have been converted to 0.
 950-   */
 951-  static stringValuesToZeros(arr: Array<any>) : Array<any> {
 952-    var toReturn = [];
 953-    for (var i = 0; i < arr.length; i++) {
 954-      if (typeof arr[i] !== "string") {
 955-        toReturn.push(arr[i]);
 956-      } else {
 957-        toReturn.push(0);
 958-      }
 959-    }
 960-    return toReturn;
 961-  }
 962-
 963-  /**
 964-   * Flatten an array of arrays of ...etc.
 965-   * @param values array of values
 966-   * @returns {Array} flattened array
 967-   */
 968-  static flatten(values: Array<any>) : Array<any> {
 969-    return values.reduce(function (flat, toFlatten) {
 970-      return flat.concat(Array.isArray(toFlatten) ? Filter.flatten(toFlatten) : toFlatten);
 971-    }, []);
 972-  }
 973-
 974-  /**
 975-   * Flatten an array of arrays of... etc, but throw an error if any are empty references.
 976-   * @param values array of values
 977-   * @returns {Array} flattened array
 978-   */
 979-  static flattenAndThrow(values: Array<any>) : Array<any> {
 980-    return values.reduce(function (flat, toFlatten) {
 981-      if (Array.isArray(toFlatten) && toFlatten.length === 0) {
 982-        throw new RefError("Reference does not exist.");
 983-      }
 984-      return flat.concat(Array.isArray(toFlatten) ? Filter.flattenAndThrow(toFlatten) : toFlatten);
 985-    }, []);
 986-  }
 987-
 988-  /**
 989-   * Filter out all strings from an array.
 990-   * @param arr to filter
 991-   * @returns {Array} filtered array
 992-   */
 993-  static filterOutStringValues(arr: Array<any>) : Array<any> {
 994-    var toReturn = [];
 995-    for (var i = 0; i < arr.length; i++) {
 996-      if (typeof arr[i] !== "string") {
 997-        toReturn.push(arr[i]);
 998-      }
 999-    }
1000-    return toReturn;
1001-  }
1002-
1003-  /**
1004-   * Filters out non number values.
1005-   * @param arr to filter
1006-   * @returns {Array} filtered array
1007-   */
1008-  static filterOutNonNumberValues(arr: Array<any>) : Array<any> {
1009-    var toReturn = [];
1010-    for (var i = 0; i < arr.length; i++) {
1011-      if (typeof arr[i] !== "string" && typeof arr[i] !== "boolean") {
1012-        toReturn.push(arr[i]);
1013-      }
1014-    }
1015-    return toReturn;
1016-  }
1017-}
1018-
1019-/**
1020- * Static class to check argument length within expected ranges when calling functions.
1021- */
1022-class ArgsChecker {
1023-  /**
1024-   * Checks to see if the arguments are of the correct length.
1025-   * @param args to check length of
1026-   * @param length expected length
1027-   */
1028-  static checkLength(args: Array<any> | IArguments, length: number) {
1029-    if (args.length !== length) {
1030-      throw new NAError("Wrong number of arguments to ___. Expected 1 arguments, but got " + args.length + " arguments.");
1031-    }
1032-  }
1033-
1034-  /**
1035-   * Checks to see if the arguments are at least a certain length.
1036-   * @param args to check length of
1037-   * @param length expected length
1038-   */
1039-  static checkAtLeastLength(args: any, length: number) {
1040-    if (args.length < length) {
1041-      throw new NAError("Wrong number of arguments to ___. Expected 1 arguments, but got " + args.length + " arguments.");
1042-    }
1043-  }
1044-
1045-  /**
1046-   * Checks to see if the arguments are within a max and min, inclusively
1047-   * @param args to check length of
1048-   * @param low least number of arguments
1049-   * @param high max number of arguments
1050-   */
1051-  static checkLengthWithin(args: any, low: number, high: number) {
1052-    if (args.length > high || args.length < low) {
1053-      throw new NAError("Wrong number of arguments to ___. Expected 1 arguments, but got " + args.length + " arguments.");
1054-    }
1055-  }
1056-}
1057-
1058-/**
1059- * Class to hold static methods for serialization.
1060- */
1061-class Serializer {
1062-  static serialize(value: any) : string {
1063-    var t = typeof value;
1064-    return "<" +  t + ": " + value + ">";
1065-  }
1066-}
1067-
1068 /**
1069  * Catches divide by zero situations and throws them as errors
1070  * @param n number to check
1071@@ -980,16 +579,7 @@ var checkForDevideByZero = function(n : number) : number {
1072   return n;
1073 };
1074 
1075-var divideAndCheck
1076-
1077-
1078-
1079 export {
1080-  ArgsChecker,
1081-  CriteriaFunctionFactory,
1082-  DateRegExBuilder,
1083-  Filter,
1084-  Serializer,
1085   TypeCaster,
1086   checkForDevideByZero
1087 }
1088\ No newline at end of file
1089diff --git a/tests/Formulas/DateFormulasTest.ts b/tests/Formulas/DateFormulasTest.ts
1090index 7877a48..b66bf0e 100644
1091--- a/tests/Formulas/DateFormulasTest.ts
1092+++ b/tests/Formulas/DateFormulasTest.ts
1093@@ -1,4 +1,3 @@
1094-
1095 import {
1096   DATE,
1097   DATEVALUE,
1098diff --git a/tests/Formulas/EngineeringTest.ts b/tests/Formulas/EngineeringTest.ts
1099index 132b5cd..138d6d5 100644
1100--- a/tests/Formulas/EngineeringTest.ts
1101+++ b/tests/Formulas/EngineeringTest.ts
1102@@ -11,7 +11,7 @@ import * as ERRORS from "../../src/Errors";
1103 import {
1104   assertEquals,
1105   catchAndAssertEquals
1106-} from "../utils/Asserts"
1107+} from "../utils/Asserts";
1108 
1109 
1110 // Test BIN2DEC