diff --git a/.monadid b/.monadid
index dd982b4e68174f4b7f05621dd3d03234660fbcfd..cfc31769c168df453b48a444df27a8d2c3910e0a 100644
--- a/.monadid
+++ b/.monadid
@@ -1,4 +1,4 @@
 monad identification file
 This file is used for monad directory identification
-Built by toole1 on linux4.ews.illinois.edu
-Build Date: Sat Oct 29 04:30:53 CDT 2011
+Built by toole1 on linux3.ews.illinois.edu
+Build Date: Thu Nov 10 23:21:04 CST 2011
diff --git a/LICENSE.txt b/LICENSE.txt
index 79a48f793c5aa6db5b97f9c6bc4afd6787635dc4..6b2a61a7f25a21926f12fdb5fdfd57867b8859b8 100644
--- a/LICENSE.txt
+++ b/LICENSE.txt
@@ -5,7 +5,7 @@ Copyright (c) 2011 Jack Toole.  All rights reserved.
 
 Developed by: Jack Toole <jack@toole1.com>
               University of Illinois at Urbana-Champaign
-              http://code.toole1.com
+              http://code.autograder.org
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to
diff --git a/Makefile b/Makefile
index c5ac799d508a06ca2001ae31363493ed793b4513..08ebbb50505212363aa9c63c08fcd68bfa32eced 100644
--- a/Makefile
+++ b/Makefile
@@ -32,22 +32,23 @@ $(IDFILE): $(OBJS)
 $(EXENAME): $(OBJS) $(IDFILE)
 	$(CC) $(CFLAGS) $(LIBRT) $(OBJS) -o $@
 
-help.cpp: $(wildcard *.h) README.cg LICENSE.cg
+help.o: help.cpp $(wildcard *.h) README.cgo README_config.cgo README_tests.cgo LICENSE.cgo quotes.cga
 
-CONVERT_TXT_TO_CG=sed -e 's/"/\\"/g' -e 's/^/    << "/' -e 's/$$/" << endl/'
+CONVERT_TXT_TO_CGO=sed -e 's/"/\\"/g' -e 's/^/    << "/' -e 's/$$/" << endl/'
+CONVERT_TXT_TO_CGA=sed -e 's/"/\\"/g' -e 's/^/"/' -e 's/$$/",/'
 
-README.cg: README.txt
-	$(CONVERT_TXT_TO_CG) README.txt > README.cg
+%.cgo: %.txt
+	$(CONVERT_TXT_TO_CGO) $(@:.cgo=.txt) > $@
 
-LICENSE.cg: LICENSE.txt
-	$(CONVERT_TXT_TO_CG) LICENSE.txt > LICENSE.cg
+%.cga: %.txt
+	$(CONVERT_TXT_TO_CGA) $(@:.cga=.txt) > $@
 
 %.o : %.cpp $(wildcard *.h)
 	$(CC) $(CFLAGS) -c $(@:.o=.cpp) -o $@
 
 .PHONY: clean
 clean:
-	rm -f $(wildcard *.o) $(wildcard *.cg) $(EXENAME) $(IDFILE) $(wildcard $(EXENAME)$(EXENAME)*)
+	rm -f $(wildcard *.o) $(wildcard *.cgo) $(wildcard *.cga) $(EXENAME) $(IDFILE) $(wildcard $(EXENAME)$(EXENAME)*)
 
 .PHONY: clean.sh
 clean.sh: $(EXENAME)
diff --git a/README.txt b/README.txt
index dedb47279310c291ca27a63af9819370addd743c..f0b77e12102685a667e02c8a296d7f0100028a96 100644
--- a/README.txt
+++ b/README.txt
@@ -1,31 +1,41 @@
-Monad Autograder (c) Jack Toole
+OVERVIEW: Monad Autograder (c) Jack Toole
+  Monad is a system for grading students' C/C++ code with point-weighted unit
+  tests.
 
-Usage: monad ASSIGNMENT [options]
+USAGE: monad <assignment> [options]
+  Runs the tests for <assignment> (mp1, lab01, ...)
+  '../<assignment>/' must exist
+  If '../<assignment>_tests/' or any necessary library directories do not exist,
+  they will be downloaded from SVN
 
-Runs the tests for ASSIGNMENT (mp1, lab01, ...)
+GENERAL OPTIONS:
+  Note: Any option may be negated with --no<option> or --<option>=false
+  Options which default to true are shown negated below
 
-'../ASSIGNMENT/' must exist
-If '../ASSIGNMENT_tests/' or any necessary library directories do not exist, these will be downloaded to ./ from SVN
+  --noclean               Do not re-copy test cases into sandbox. This improves
+                          performance at the cost of safety
+  --provided              Use <assignment>_provided/ instead of
+                          <assignment>_tests/ for test cases
+  --noupdate              Do not call 'svn update' on the tests and library
+                          directories
+  --verbose               Display detailed test output information for passing
+                          tests
+  --parallel              Compiles code in parallel if possible
 
-monad settings and individual test settings are stored in config.ini files
-The following options are available for monad's ./config.ini:
-[SVN Root]
-; svn base url here (http://.../cs225/)
-[Monad Files]
-; Any files to be copied from monad ./ to ./ASSIGNMENT/ testing directory
-; By default, proxy.cpp is used to run test cases:
-proxy.cpp
+INFORMATION OPTIONS:
+  --help                  Display this dialog
+  --help config           Help for writing config files
+  --help tests            Help for writing test cases
+  --info, --version       Display version information
+  --license               Display the license
 
-The following options are available for individual tests' ../ASSIGNMENT_tests/config.ini:
-[Required Files]
-; Any files that must be copied from the ../ASSIGNMENT directory
-main.cpp
-[Testing Files]
-; Any files that must be copied from ../ASSIGNMENT_tests
-; By default, unit_tests.cpp contains the test cases
-unit_tests.cpp
-[Libraries]
-; Any external library folders to be present in the same directory as the
-; testing directory. These should be mirrored in [SVN Root]/_public/
-; The 'testutil' library in this directory is also available
-EasyBMP
+STAFF OPTIONS:
+  --staff                 Use the staff SVN repository to download test cases.
+                          This allows monad to be run by staff without
+                          releasing the test cases to students
+  --solution              Grade <assignment>_solution/ instead of
+                          <assignment>/
+  --newtests              Use <assignment>_newtests/ instead of
+                          <assignment>_tests/ for test cases. This allows new
+                          test cases to be developed without replacing the old
+                          ones for release tests
diff --git a/README_config.txt b/README_config.txt
new file mode 100644
index 0000000000000000000000000000000000000000..149a8a9946e506a8428548a4c574c2c9150f8488
--- /dev/null
+++ b/README_config.txt
@@ -0,0 +1,22 @@
+monad settings and individual test settings are stored in config.ini files
+The following options are available for monad's ./config.ini:
+[SVN Root]
+; svn base url here (http://.../cs225/)
+[Monad Files]
+; Any files to be copied from monad ./ to ./ASSIGNMENT/ testing directory
+; By default, proxy.cpp is used to run test cases:
+proxy.cpp
+
+The following options are available for individual tests' ../ASSIGNMENT_tests/config.ini:
+[Required Files]
+; Any files that must be copied from the ../ASSIGNMENT directory
+main.cpp
+[Testing Files]
+; Any files that must be copied from ../ASSIGNMENT_tests
+; By default, unit_tests.cpp contains the test cases
+unit_tests.cpp
+[Libraries]
+; Any external library folders to be present in the same directory as the
+; testing directory. These should be mirrored in [SVN Root]/_public/
+; The 'testutil' library in this directory is also available
+EasyBMP
diff --git a/README_tests.txt b/README_tests.txt
new file mode 100644
index 0000000000000000000000000000000000000000..8d09c354791e09c2078f3ef4dc412b4763936c75
--- /dev/null
+++ b/README_tests.txt
@@ -0,0 +1 @@
+[not finished yet]
diff --git a/help.cpp b/help.cpp
index c8e9266ff968ff690d63fee5e41c518d9e4f1d21..9fbbb4115f38974093d2df0fd6b3d4644b238996 100644
--- a/help.cpp
+++ b/help.cpp
@@ -6,15 +6,38 @@ namespace monad
 void printHelp()
 {
 	cout
-#include "README.cg"
+#include "README.cgo"
+	;
+}
+
+void printHelpConfig()
+{
+	cout
+#include "README_config.cgo"
+	;
+}
+
+void printHelpTests()
+{
+	cout
+#include "README_tests.cgo"
 	;
 }
 
 void printLicense()
 {
 	cout
-#include "LICENSE.cg"
+#include "LICENSE.cgo"
 	;
 }
 
+const char * getRandomQuote()
+{
+	const char * quotes[] = {
+#include "quotes.cga"
+	};
+	size_t quotes_size = (sizeof quotes) / (sizeof quotes[0]);
+	return quotes[urandn(quotes_size)];
+}
+
 } // namespace monad
diff --git a/monad b/monad
index 018e061bc485b1180462fc3140e6197328632ef6..fe89f346910274549e2dd190ed392167c0cdf374 100755
Binary files a/monad and b/monad differ
diff --git a/monad.cpp b/monad.cpp
index 3089c7961a871cfa8e960917cc6cce144850a037..0281bdc6cd0d1573786d81a464af2e476e255160 100644
--- a/monad.cpp
+++ b/monad.cpp
@@ -7,33 +7,42 @@
 namespace monad
 {
 void find_base_dir(const char * argv0);
-void processArgs(int argc, char ** argv);
+void processArgs(int argc, const char * const * argv);
 void copyRequiredFiles();
 void getLibs(const vector<string> & libs);
 string updateFolder(const string & folder, bool link);
 string getFolder(const string & folder, bool link);
-void importFiles(const string & preservedFolder, const string & sourceFolder,
+void importFiles(const string & preservedFolder, const string & theSourceFolder,
                  const string & destFolder, const vector<string> & files);
 void exec_command(const string & command);
 
-string name;
+string assignment_base;
+int8_t mp_part;
+const int8_t no_mp_part = -1;
 
 string testsFolder;
 string sourceFolder;
 string gradeFolder;
 string tempFolder;
 
-FileMap    config;
+FileMap config;
 
-int8_t mp_part;
-const int8_t no_mp_part = -1;
-
-namespace opts {
+namespace opts
+{
+bool solution = false;
 bool clean    = true;
 bool update   = true;
 bool staff    = false;
 bool newtests = false;
 bool provided = false;
+
+bool verbose  = false;
+bool valgrind = false;
+bool parallel = false;
+
+bool help     = false;
+bool info     = false;
+bool license  = false;
 #if OPTIMIZE
 bool optimize = true;
 #else
@@ -57,7 +66,7 @@ string kyleGetDate() {
 
 
 
-int main(int argc, char ** argv)
+int main(int argc, const char * const * argv)
 {
 	using namespace monad;
 
@@ -71,8 +80,12 @@ int main(int argc, char ** argv)
 	readConfig("./", config);
 	processArgs(argc, argv);
 
-	cout << version::official_name << endl;
-	cout << "Testing " << name << "..." << endl << endl;
+	cout << versioninfo::official_name << endl;
+	cout << "Testing " << assignment_base;
+	if (mp_part != no_mp_part) cout << '.' << mp_part;
+	cout << "..." << endl;
+	cout << getRandomQuote() << endl;
+	cout << endl;
 
 	cout << "Setting up test environment..." << endl;
 
@@ -108,7 +121,9 @@ int main(int argc, char ** argv)
 	for (size_t i = 0; i < processing_commands.size(); i++)
 		exec_command(processing_commands[i]);
 
-	string makestr = "/usr/bin/make --quiet --warn-undefined-variables";
+	string makestr = "/usr/bin/make --quiet --warn-undefined-variables -Wfatal-errors";
+	if (opts::parallel)
+		makestr += " --jobs=4";
 	if (opts::optimize)
 		makestr += " OPTIMIZE=on";
 	if (!config["Make Options"].empty())
@@ -126,7 +141,9 @@ int main(int argc, char ** argv)
 	// TODO exec("make", "--quiet", "--warn-undefined-variables", 
 
 	cout << endl << endl;
-	int score = exec("./proxy");
+	const char * verboseflag  = (opts::verbose  ? "--verbose"  : NULL);
+	const char * valgrindflag = (opts::valgrind ? "--valgrind" : NULL);
+	int score = exec("./proxy", verboseflag, valgrindflag);
 
 	// TODO (toole1): this causes weird output when scores are like 200
 	if (score < 0)
@@ -143,54 +160,73 @@ int main(int argc, char ** argv)
 }
 
 
-void monad::processArgs(int argc, char ** argv)
+void monad::processArgs(int argc, const char * const * argv)
 {
     // Create OptionsMap for options and vector for positional arguments:
-	OptionsMap options;
-	vector<string> args;
+	OptionsParser options;
 
 	// Add our possible options to our map
-	options.insert(make_pair("solution", false));
-	options.insert(make_pair("newtests", opts::newtests));
-	options.insert(make_pair("provided", opts::provided));
-	options.insert(make_pair("clean",    opts::clean));
-	options.insert(make_pair("update",   opts::update));
-	options.insert(make_pair("staff",    false));
-	options.insert(make_pair("optimize", opts::optimize));
-	options.insert(make_pair("help", false));
-	options.insert(make_pair("h",    false));
-	options.insert(make_pair("info", false));
+	options.addOption("solution", opts::solution);
+	options.addOption("newtests", opts::newtests);
+	options.addOption("provided", opts::provided);
+	options.addOption("clean",    opts::clean);
+	options.addOption("update",   opts::update);
+	options.addOption("staff",    opts::staff);
+	
+	options.addOption("optimize", opts::optimize);
+	options.addOption("verbose",  opts::verbose);
+	options.addOption("valgrind", opts::valgrind);
+	options.addOption("parallel", opts::parallel);
+
+	options.addOption("help",     opts::help);
+	options.addOption("h",        opts::help);
+	options.addOption("info",     opts::info);
+	options.addOption("version",  opts::info);
+	options.addOption("v",        opts::info);
+	options.addOption("license",  opts::license);
+
+	// Add arguments
+	string assignment = "";
+	options.addArg(assignment);
 
 	// Read in options and arguments
-	char * badopt = processOptions(argc, argv, options, args);
-	if (badopt != NULL)
-		exit(-1);
+	vector<string> posargs = options.parse(argc, argv);
 	
-	// Save out options
-	bool solution  = options["solution"];
-	opts::newtests = options["newtests"];
-	opts::provided = options["provided"];
-	opts::clean    = options["clean"];
-	opts::update   = options["update"];
-	opts::staff    = options["staff"];
-
 	// Help
-	if (options["help"] || options["h"] ||
-	    (!args.empty() && strcasecmp(args[0].c_str(), "help") == 0))
+	if (opts::help || toLower(assignment) == "help")
 	{
-		printHelp();
+		if (toLower(assignment) == "config")
+			printHelpConfig();
+		else if (toLower(assignment) == "tests")
+			printHelpTests();
+		else
+			printHelp();
 		exit(0);
 	}
 
 	// Info
-	if (options["info"])
+	if (opts::info)
 	{
 		printInfo();
 		exit(0);
 	}
+	
+	// License
+	if (opts::license)
+	{
+		printLicense();
+		exit(0);
+	}
+	
+	// Clean
+	if (toLower(assignment) == "clean")
+	{
+		system("/bin/rm -rf *_grade/ *_tests/ *_newtests/ *_providedtests *_solution/");
+		exit(0);
+	}
 
 	// Check argument list length
-	if (args.empty() || args.size() > 1)
+	if (assignment == "")
 	{
 		cout << "Usage: " << argv[0] << " mp1" << endl;
 		cout << "Run \'" << argv[0] << " --help\' for more information" << endl;
@@ -198,41 +234,34 @@ void monad::processArgs(int argc, char ** argv)
 	}
 
 	// Find mp/lab name and tests folder
-	vector<string> splitname = tokenize(args[0], '.');
-	name = splitname[0];
+	vector<string> splitname = tokenize(assignment, '.');
+	assignment_base = splitname[0];
 	if (splitname.size() == 1)
 		mp_part = no_mp_part;
 	else
-		mp_part = atoi(splitname[1].c_str());
+		mp_part = lexical_cast<int>(splitname[1].c_str());
 	
-	// Clean
-	if (name == "clean")
-	{
-		system("/bin/rm -rf *_grade/ *_tests/ *_newtests/ *_providedtests *_solution/");
-		exit(0);
-	}
-
-	gradeFolder = "./" + name + "_grade/";
+	gradeFolder = "./" + assignment_base + "_grade/";
 	if (!exists(gradeFolder)) opts::clean = true;
 
 	if (opts::clean)
 		tempFolder = "";
 	else
-		tempFolder  = "./" + name + "_temp/";
+		tempFolder  = "./" + assignment_base + "_temp/";
 
 	// Find source folder (i.e. ../mp1)
-	if (solution)
-		sourceFolder = updateFolder(name + "_solution/", false);
+	if (opts::solution)
+		sourceFolder = updateFolder(assignment_base + "_solution/", false);
 	else
-		sourceFolder = getFolder(name + '/', false);
+		sourceFolder = getFolder(assignment_base + '/', false);
 
 	// tests folder
 	if (opts::provided)
-		testsFolder = updateFolder(name + "_provided/", false);
+		testsFolder = updateFolder(assignment_base + "_provided/", false);
 	else if (opts::newtests)
-		testsFolder = updateFolder(name + "_newtests/", false);
+		testsFolder = updateFolder(assignment_base + "_newtests/", false);
 	else
-		testsFolder = updateFolder(name + "_tests/", false);
+		testsFolder = updateFolder(assignment_base + "_tests/", false);
 }
 
 
@@ -295,7 +324,7 @@ void monad::copyRequiredFiles()
 }
 
 
-void monad::importFiles(const string & preservedFolder, const string & sourceFolder,
+void monad::importFiles(const string & preservedFolder, const string & theSourceFolder,
                  const string & destFolder, const vector<string> & files)
 {
 	// 0 for student errors for missing Required Files dir
@@ -303,7 +332,7 @@ void monad::importFiles(const string & preservedFolder, const string & sourceFol
 
 	assertExists(destFolder);
 	if (preservedFolder != "") assertExists(preservedFolder);
-	if (sourceFolder    != "") assertExists(sourceFolder, student_error_code);
+	if (theSourceFolder    != "") assertExists(theSourceFolder, student_error_code);
 
 	for (size_t i = 0; i < files.size(); i++)
 	{
@@ -312,15 +341,15 @@ void monad::importFiles(const string & preservedFolder, const string & sourceFol
 
 		// Move the file from it's preservation instance
 		if (preservedFolder != "" && exists(preservedFile) &&
-		    (sourceFolder == "" || (permissions(preservedFile) & S_IWUSR) == 0))
+		    (theSourceFolder == "" || (permissions(preservedFile) & S_IWUSR) == 0))
 		{
 //!!			cout << "mv " << preservedFile << ' ' << destFile << endl;
 			EXIT_IF_ERROR(rename(preservedFile.c_str(), destFile.c_str()));
 		}
-		else if (sourceFolder != "")
+		else if (theSourceFolder != "")
 		{
 			// copy the file from it's source
-			string sourceFile = sourceFolder + files[i];
+			string sourceFile = theSourceFolder + files[i];
 			assertExists(sourceFile, student_error_code);
 
 			// Remove hacky call to exec here...
diff --git a/monad.h b/monad.h
index 01237df7c53a5d5108fbbe4075372c7109d63dfc..1a7e0368758ebbd8c51bac6b34acfef052aed3b6 100644
--- a/monad.h
+++ b/monad.h
@@ -12,8 +12,9 @@
 #include <unistd.h>
 #include <vector>
 
-#include "util.h"
 #include "monad_shared.h"
+#include "random.h"
+#include "util.h"
 
 using namespace std;
 using namespace util;
@@ -23,7 +24,10 @@ namespace monad
 {
 
 void printHelp();
+void printHelpConfig();
+void printHelpTests();
 void printLicense();
+const char * getRandomQuote();
 
 }
 
diff --git a/monad_shared.cpp b/monad_shared.cpp
index 7fa7f419c8d071070628a532a222d9ec53b83d41..f46b844434cbdd6ed55b19a744bffb11aa1589c6 100644
--- a/monad_shared.cpp
+++ b/monad_shared.cpp
@@ -11,27 +11,26 @@ using std::endl;
 namespace monad_shared
 {
 
-namespace version
+namespace versioninfo
 {
-const char * official_name = "CS 225 Monad";
-const char * version_name  = "awakening";
-const char * date          = "15 July 2011";
+const char * official_name = "Monad Autograder";
+const char * version_name  = "confession";
+const Version version_num  = Version(2, 1, 1, 0);
+const char * date          = "10 Nov 2011";
 }
 
-const char * unit_test::pass_string = "~~PASSED~~";
+const char * unit_test_result::pass_string = "~~PASSED~~";
 const size_t header_length = 64;
 
 void printInfo()
 {
 	cout << endl
-	     << version::official_name << endl
-	     << "Version " << version::version_name << endl
-		 << "Released " << version::date << endl
+	     << versioninfo::official_name << endl
+	     << "Version " << versioninfo::version_num << ": " << versioninfo::version_name << endl
+		 << "Released " << versioninfo::date << endl
 	     << "Developed by Jack Toole Spring/Fall 2011" << endl
 	     << "Copyright 2011 Jack Toole" << endl
-	     << "Full rights granted to Jack Toole. Rights to use and modify granted to" << endl
-	     << "University of Illinois Urbana-Champaign Computer Science Data Structures" << endl
-	     << "instructors and course staff" << endl
+	     << "Run monad --license to see licensing information" << endl
 		 << endl;
 }
 
@@ -71,14 +70,14 @@ void total_score(int32_t score, int32_t outof)
 	cout << "TOTAL SCORE: " << score << endl << endl;
 }
 
-void testname(const unit_test & curr_test, int32_t max_testname_len, int32_t max_points_len)
+void testname(const unit_test_input & curr_test, int32_t max_testname_len, int32_t max_points_len)
 {
 	// Output test name
 	int32_t pos = 0; // keep track of cursor position
-	std::cout << curr_test.name << ' ';
-	pos += strlen(curr_test.name) + 1;
+	std::cout << curr_test.name() << ' ';
+	pos += strlen(curr_test.name()) + 1;
 
-	if (curr_test.is_valgrind)
+	if (curr_test.is_valgrind())
 	{
 		cout << "(valgrind) ";
 		pos += 11;
@@ -97,8 +96,8 @@ void testname(const unit_test & curr_test, int32_t max_testname_len, int32_t max
 	}
 	pos = 0; // reset column
 
-	std::cout << "[" << curr_test.points << " pts] ";
-	pos += intlen(curr_test.points) + 7;
+	std::cout << "[" << curr_test.points() << " pts] ";
+	pos += intlen(curr_test.points()) + 7;
 
 	while (pos < max_points_len + 7)
 	{
@@ -110,26 +109,26 @@ void testname(const unit_test & curr_test, int32_t max_testname_len, int32_t max
 }
 
 
-void detailed_info(const unit_test & curr_test)
+void detailed_info(const unit_test_result & curr_test)
 {
 	std::cout << string(header_length, '-') << endl
-			  << curr_test.name;
-	if (curr_test.is_valgrind) std::cout << " (run under valgrind)";
-	std::cout << " [" << curr_test.points << " points]" << endl;
+			  << curr_test.name();
+	if (curr_test.is_valgrind()) std::cout << " (run under valgrind)";
+	std::cout << " [" << curr_test.points() << " points]" << endl;
 
-	const string & error = curr_test.errormsg;
-	const string & output = curr_test.output;
+	const string & error = curr_test.errormsg();
+	const string & output = curr_test.output();
 
 	if (curr_test.passed())
 		std::cout << "Result: " << passed_string() << endl;
 	else
 		std::cout << "Result: " << failed_string() << ": " << error << endl;
 
-	if (curr_test.time < 0)
+	if (curr_test.time() < 0)
 		cout << "Took unknown time (";
 	else
-		cout << "Took " << curr_test.time << "ms (";
-	cout << curr_test.timeout << "ms timeout)" << endl;
+		cout << "Took " << curr_test.time() << "ms (";
+	cout << curr_test.timeout() << "ms timeout)" << endl;
 
 	std::cout << "Output:" << endl
 			  << string(header_length, '-') << endl;
diff --git a/monad_shared.h b/monad_shared.h
index 5fd16be955335f86e780833e17de9e11c5a6e699..be64ec40f0c1ad0bb108757a33040dc37af6d0b5 100644
--- a/monad_shared.h
+++ b/monad_shared.h
@@ -6,52 +6,109 @@
 
 namespace monad_shared
 {
+	struct Version
+	{
+		int32_t major_;
+		int32_t minor_;
+		int32_t patch_;
+		int32_t build_;
+
+		Version(int32_t maj, int32_t min, int32_t pat, int32_t bld)
+			: major_(maj), minor_(min), patch_(pat), build_(bld)
+		{ }
+
+#if 0
+		Version(const std::string & s)
+		{
+			size_t i = 0;
+			
+		}
+#endif
+
+		void print(std::ostream & out) const
+		{
+			out << major_ << '.' << minor_ << '.' << patch_ << '.' << build_;
+		}
+	};
+
+	inline std::ostream & operator<<(std::ostream & out, const Version & v)
+	{
+		v.print(out);
+		return out;
+	}
 
-	namespace version
+	namespace versioninfo
 	{
 		extern const char * official_name;
 		extern const char * version_name;
+		extern const Version version_num;
 		extern const char * date;
 	}
 
 	void printInfo();
 
-	struct unit_test
+	struct unit_test_input
 	{
-		typedef std::string return_type;
-		typedef return_type (*function)(unit_test & this_test);
-		static const char * pass_string;
+		typedef void (*function)(util::pipestream &);
+		
+		private:
+		function func_;
+		const char * name_;
+		long timeout_;
+		int32_t points_;
+		bool is_valgrind_;
 
-		std::string errormsg;
-		std::string output;
-		function func;
-		const char * name;
-		util::pipestream * checkstream;
-		long timeout;
-		long time;
-		int32_t points;
-		int32_t valgrind_flags;
-		bool is_valgrind;
-
-		unit_test(const char * name_,
-		          unit_test::function func_,
-		          int32_t points_,
-		          long timeout_,
-		          bool is_valgrind_)
-			: errormsg("message not set / proxy crashed"),
-			  func(func_),
-			  name(name_),
-			  checkstream(NULL),
-			  timeout(timeout_),
-			  time(-1),
-			  points(points_),
-			  valgrind_flags(-1),
-			  is_valgrind(is_valgrind_) { }
-
-		bool passed() const
-		{
-			return errormsg == pass_string;
-		}
+		public:
+		unit_test_input(const char * _name,
+		          unit_test_input::function _func,
+		          int32_t _points,
+		          long _timeout,
+		          bool _is_valgrind)
+			: func_(_func),
+			  name_(_name),
+			  timeout_(_timeout),
+			  points_(_points),
+			  is_valgrind_(_is_valgrind) { }
+		
+		void execute(util::pipestream & checkstream) const { func_(checkstream); }
+		const char * name()        const { return name_;        }
+		long         timeout()     const { return timeout_;     }
+		int32_t      points()      const { return points_;      }
+		bool         is_valgrind() const { return is_valgrind_; }
+	};
+
+	struct unit_test_result : public unit_test_input
+	{
+		private:
+		std::string errormsg_;
+		std::string output_;
+		long time_;
+		int32_t valgrind_flags_;
+
+		public:
+		unit_test_result(const unit_test_input & _input,
+		                 const std::string & _errormsg,
+		                 const std::string & _output,
+		                 const long _time,
+		                 const int32_t _valgrind_flags)
+			: unit_test_input(_input),
+			  errormsg_((_errormsg != "") ? _errormsg : "Unexpectedly Aborted"),
+			  output_(_output),
+			  time_(_time),
+			  valgrind_flags_(_valgrind_flags) { }
+
+		const std::string & errormsg()      const { return errormsg_;       }
+		const std::string & output()        const { return output_;         }
+		long               time()           const { return time_;           }
+		int32_t            valgrind_flags() const { return valgrind_flags_; }
+
+		bool passed() const { return errormsg_ == pass_string; }
+		bool failed() const { return !passed(); }
+		int32_t points_scored() const { return passed() ? points() : 0; }
+
+		// Static class data
+		static const char * pass_string;
+		static bool _failed(const unit_test_result & obj) { return obj.failed(); }
 	};
 
 	namespace output
@@ -60,8 +117,8 @@ namespace monad_shared
 		void header(const std::string & title);
 		void total_score(int32_t score, int32_t outof);
 		void warning(const std::string & message);
-		void testname(const unit_test & curr_test, int32_t max_testname_len, int32_t max_points_len);
-		void detailed_info(const unit_test & curr_test);
+		void testname(const unit_test_input & curr_test, int32_t max_testname_len, int32_t max_points_len);
+		void detailed_info(const unit_test_result & curr_test);
 		std::string passed_string();
 		std::string failed_string();
 	} // namespace output
diff --git a/proxy.cpp b/proxy.cpp
index 275ec25baa68d0de4cb106712dae252241b3d572..492b5402414faa6fb14927c895713f6e6da48721 100644
--- a/proxy.cpp
+++ b/proxy.cpp
@@ -8,13 +8,13 @@
 	#define _GNU_SOURCE
 #endif
 
-#include <signal.h>
-#include <string.h>
 #include <sys/types.h>
 #include <sys/wait.h>
 #include <sys/time.h>
 
+#include <algorithm>
 #include <iomanip>
+#include <numeric>
 
 #include "memcheck.h"
 #include "monad_shared.h"
@@ -23,19 +23,10 @@
 #include "util.h"
 #include "valgrind.h"
 
-using std::string;
-using std::vector;
-using std::pair;
-
+using namespace std;
 using namespace util;
 using namespace monad_shared;
 
-namespace opts
-{
-	bool verbose = false;
-	bool redirect_test_output = true;
-}
-
 
 OUTPUT_CHECK(equals)
 {
@@ -57,7 +48,7 @@ OUTPUT_CHECK(not_contains)
 
 namespace proxy
 {
-	vector<unit_test> * global_tests = NULL;
+	vector<unit_test_input> * global_tests = NULL;
 	output_check_map * global_output_checks = NULL;
 
 	double time_constant(size_t smaller, size_t larger) { return 1.0; }
@@ -96,15 +87,18 @@ namespace proxy
 int main(int argc, char ** argv)
 {
 	using namespace proxy;
-
+	
 	// set up EXIT_IF_ERROR messages
 	output::set_error_message();
 
+	RunTimeEnvironment::Options opts;
+	const char * testname = parse_options(argc, argv, opts);
+
 	// Set up run-time environment
-	RunTimeEnvironment env(global_tests, global_output_checks);
+	RunTimeEnvironment env(global_tests, global_output_checks, opts, testname);
 
 	// Set up the tests
-	RunTests runner(argc, argv, env);
+	RunTests runner(env);
 
 	// Execute
 	return runner.execute();
@@ -114,14 +108,14 @@ int main(int argc, char ** argv)
 namespace proxy {
 
 // class add_unit_test
-add_unit_test::add_unit_test(const char * name, unit_test::function func,
+add_unit_test::add_unit_test(const char * name, unit_test_input::function func,
                              int32_t points_in_part, int32_t points_in_total, long timeout,
                              bool is_valgrind)
 {
 	lazy_init_global_tests();
 	int32_t points = get_points(points_in_total, points_in_part);
 	// Add to global tests vector
-	global_tests->push_back(unit_test(name, func, points, timeout, is_valgrind));
+	global_tests->push_back(unit_test_input(name, func, points, timeout, is_valgrind));
 }
 
 
@@ -129,9 +123,8 @@ add_unit_test::add_unit_test(const char * name, unit_test::function func,
 void add_unit_test::lazy_init_global_tests()
 {
 	if (global_tests == NULL)
-		global_tests = new std::vector<unit_test>;
+		global_tests = new std::vector<unit_test_input>;
 }
-		
 
 // Discriminate which points value to add
 int32_t add_unit_test::get_points(int32_t points_in_total, int32_t points_in_part)
@@ -144,6 +137,24 @@ int32_t add_unit_test::get_points(int32_t points_in_total, int32_t points_in_par
 }
 
 
+const char * parse_options(int argc, const char * const * const argv, RunTimeEnvironment::Options & opts)
+{
+	string getTestName = "all";
+
+	OptionsParser parseopts;
+	parseopts.addOption("verbose",  opts.verbose);
+	parseopts.addOption("redirect", opts.redirect_test_output);
+	parseopts.addOption("valgrind", opts.valgrind);
+	parseopts.addArg(getTestName);
+	parseopts.parse(argc, argv);
+
+	const char * testname = "all";
+	for (int32_t i = 0; i < argc; i++)
+		if (getTestName == argv[i])
+			testname = argv[i];
+	return testname;
+}
+
 
 // class add_output_check
 add_output_check::add_output_check(const char * name, output_check func)
@@ -156,57 +167,45 @@ add_output_check::add_output_check(const char * name, output_check func)
 
 
 // class Run_Time_Environment
-RunTimeEnvironment::RunTimeEnvironment(vector<unit_test> *& init_tests,
-                                       output_check_map *& init_output_checks)
-	: timeout_signum0(SIGPROF),
-	  timeout_signum1(SIGALRM),
-	  max_output_length(8*1024), //arbitrary
+RunTimeEnvironment::RunTimeEnvironment(vector<unit_test_input> *& init_tests,
+                                       output_check_map *& init_output_checks,
+                                       const Options & init_opts, const char * testname)
+	: max_output_length(8*1024), //arbitrary
 	  single_test_passed_string("Result: passed"),
 	  heap_tests(init_tests),
-	  output_checks(init_output_checks)
+	  output_checks(init_output_checks != NULL ? init_output_checks : new output_check_map),
+	  opts(init_opts),
+	  mode(strcasecmp(testname, "all") == 0 ? ALL_TESTS : SINGLE_TEST),
+	  test_arg(testname)
 {
 	// Copy globals to the RunTimeEnvironment space
 	// And remove them from the global scope
 	static int8_t singleton = 0;
 	EXIT_IF_ERROR(singleton++ != 0, "There may only be one runtime environment");
 	EXIT_IF_ERROR(heap_tests == NULL, "No test cases found");
-	if (output_checks == NULL)
-		output_checks = new output_check_map;
 	
 	init_tests = NULL;
 	init_output_checks = NULL;
 }
 
 
-int RunTimeEnvironment::cleanup_globals()
+void RunTimeEnvironment::cleanup_globals()
 {
-	if (heap_tests    != NULL) delete heap_tests;
-	if (output_checks != NULL) delete output_checks;
-	heap_tests    = NULL;
-	output_checks = NULL;
-	return 0;
+	static bool called_already = false;
+	if (!called_already)
+	{
+		if (heap_tests    != NULL) delete heap_tests;
+		if (output_checks != NULL) delete output_checks;
+		called_already = true;
+	}
 }
 
 
 
 // class RunTests
-RunTests::RunTests(int argc, char ** argv, RunTimeEnvironment & env)
+RunTests::RunTests(RunTimeEnvironment & env)
 	: environment(env)
-{
-	process_args(argc, argv); // sets up mode and test_arg
-//	redirect_glibc_to_stderr();
-}
-
-
-void RunTests::redirect_glibc_to_stderr()
-{
-	// Turn off glibc errors default write-to-terminal behaviour, because
-	// it does not get caught by stderr. This instead results in an abort.
-	// Unfortunately, this has still-reachable memory leaks under valgrind
-	if (RUNNING_ON_VALGRIND == 0)
-		setenv("LIBC_FATAL_STDERR_","1",1);
-		//setenv("MALLOC_CHECK_","2",1);
-}
+{ }
 
 
 int32_t RunTests::execute()
@@ -219,43 +218,22 @@ int32_t RunTests::execute()
 
 int32_t RunTests::execute_by_mode()
 {
-	if (mode == SINGLE_TEST)
-		return run_single_test(test_arg);
-	else // if (mode == ALL_TESTS)
+	if (environment.mode == SINGLE_TEST)
+		return run_single_test(environment.test_arg);
+	else if (environment.mode == ALL_TESTS)
 		return run_all_tests();
-}
-
-
-void RunTests::process_args(int argc, char ** argv)
-{
-	if (argc > 2)
-	{
-		cout << "Usage: " << argv[0] << "[testname]" << endl;
-		exit(0);
-	}
-
-	if (argc == 2 && strcasecmp(argv[1], "--info") == 0)
-	{
-		printInfo();
-		exit(0);
-	}
-
-	if (argc == 1 || strcmp(argv[1], "all") == 0)
-		mode = ALL_TESTS;
-	else
-	{
-		mode = SINGLE_TEST;
-		test_arg = argv[1];
-	}
+	
+	EXIT_IF_ERROR(true, string("Failed to implement case for mode ") + environment.mode);
+	exit(-1);
 }
 
 
 int32_t RunTests::run_single_test(const char * testname)
 {
-	vector<unit_test> & tests = *environment.heap_tests;
+	const vector<unit_test_input> & tests = *environment.heap_tests;
 
 	for (size_t test_i = 0; test_i < tests.size(); test_i++)
-		if (strcmp(tests[test_i].name, testname) == 0)
+		if (strcmp(tests[test_i].name(), testname) == 0)
 			return run_single_test(tests[test_i]);
 
 	cout << "Test not found" << endl;
@@ -263,28 +241,23 @@ int32_t RunTests::run_single_test(const char * testname)
 }
 
 
-int32_t RunTests::run_single_test(unit_test & curr_test)
+int32_t RunTests::run_single_test(const unit_test_input & curr_test)
 {
-	cout << "Running " << curr_test.name << " [worth "
-		 << curr_test.points << " points, output below]" << endl;
-
-	bool is_parent_process = execute_test(curr_test, false);
-	if (!is_parent_process)
-		return environment.cleanup_globals();
+	cout << "Running " << curr_test.name() << " [worth "
+		 << curr_test.points() << " points, output below]" << endl;
 
-	string & error  = curr_test.errormsg;
+	unit_test_result result = execute_test(curr_test, false);
 
-	handle_single_test_output(curr_test.output);
+	const string & error  = result.errormsg();
 
-	if (error == "")
-		error = "Unexpectedly Aborted";
+	handle_single_test_output(result.output());
 
-	if (curr_test.passed())
+	if (result.passed())
 		cout << environment.single_test_passed_string << endl;
 	else
 		cout << "Result: FAILED:" << endl << error << endl;
 
-	return curr_test.valgrind_flags;
+	return result.valgrind_flags();
 }
 
 
@@ -298,10 +271,12 @@ void RunTests::handle_single_test_output(const string & output)
 	}
 }
 
+int32_t foldTestScore (int score,  const unit_test_result & test) { return score  + test.points_scored(); }
+int32_t foldTestPoints(int points, const unit_test_input  & test) { return points + test.points(); }
 
 int RunTests::run_all_tests()
 {
-	vector<unit_test> & tests = *environment.heap_tests;
+	const vector<unit_test_input> & tests = *environment.heap_tests;
 
 	output::header("Running tests");
 
@@ -316,27 +291,18 @@ int RunTests::run_all_tests()
 		output::warning("Unit test scores sum to " + to_string(points_sum) +
 		                ", this will overflow the return value. Should be <= 125");
 
-	int32_t score = 0;
+	vector<unit_test_result> results;
 	for (size_t test_i = 0; test_i < tests.size(); test_i++)
 	{
-		unit_test & curr_test = tests[test_i];
-		output::testname(curr_test, max_testname_len, max_points_len);
-
-		bool is_parent_process = execute_test(curr_test, true);
-		// Check for the child process
-		// This is unfortunately necessary (instead of an exit) to clean up
-		// all the memory in use in main and the global space for valgrind
-		if (!is_parent_process)
-			return environment.cleanup_globals();
-
-		// Check for success
-		if (curr_test.passed())
-			score += curr_test.points;
-		output_single_test_passfail(curr_test);
+		output::testname(tests[test_i], max_testname_len, max_points_len);
+		results.push_back(execute_test(tests[test_i], true));
+		output_single_test_passfail(results.back());
 	}
 
+	const int32_t score = accumulate(results.begin(), results.end(), 0, foldTestScore);
+
 	cout << endl << endl;
-	output_detailed_info_if_any_failed(score);
+	output_detailed_info_if_any_failed(results, score);
 	output::total_score(score, get_sum_points());
 	
 	return score;
@@ -346,23 +312,18 @@ int32_t RunTests::get_sum_points()
 {
 	static int32_t cached_sum = INT_MIN;
 	if (cached_sum == INT_MIN)
-	{
-		vector<unit_test> & tests = *environment.heap_tests;
-		int32_t points_sum = 0;
-		for (size_t test_i = 0; test_i < tests.size(); test_i++)
-			points_sum += tests[test_i].points;
-		cached_sum = points_sum;
-	}
+		cached_sum = accumulate(environment.heap_tests->begin(), environment.heap_tests->end(), 0, foldTestPoints);
 	return cached_sum;
 }
 
 int32_t RunTests::get_max_testname_length()
 {
-	vector<unit_test> & tests = *environment.heap_tests;
+	const vector<unit_test_input> & tests = *environment.heap_tests;
 	int32_t max_testname_len = 0;
 	for (size_t test_i = 0; test_i < tests.size(); test_i++)
 	{
-		int32_t currlen = strlen(tests[test_i].name) + (int)tests[test_i].is_valgrind * 11; // strlen(" (valgrind)");
+		// TODO (toole1): This is horrible style!
+		int32_t currlen = strlen(tests[test_i].name()) + (int)tests[test_i].is_valgrind() * 11; // strlen(" (valgrind)");
 
 		if (currlen > max_testname_len)
 			max_testname_len = currlen;
@@ -372,62 +333,50 @@ int32_t RunTests::get_max_testname_length()
 
 int32_t RunTests::get_max_points_length()
 {
-	vector<unit_test> & tests = *environment.heap_tests;
-	int32_t max_points_len = 0;
+	const vector<unit_test_input> & tests = *environment.heap_tests;
+	uint32_t max_points_len = 0;
 	for (size_t test_i = 0; test_i < tests.size(); test_i++)
-	{
-		if (tests[test_i].points >= 100)
-			max_points_len = 3;
-		else if (tests[test_i].points >= 10)
-			max_points_len = 2;
-	}
+		max_points_len = max(max_points_len, intlen(tests[test_i].points()));
 	return max_points_len;
 }
 
-void RunTests::output_detailed_info_if_any_failed(int32_t score)
+void RunTests::output_detailed_info_if_any_failed(const vector<unit_test_result> & results, int32_t score)
 {
-	vector<unit_test> & tests = *environment.heap_tests;
-	
-	bool any_failed = false;
-	for (size_t test_i = 0; test_i < tests.size(); test_i++)
-		if (!tests[test_i].passed())
-			any_failed = true;
-	
-	if (any_failed || opts::verbose)
-		output_detailed_tests_info(score);
+	if (count_if(results.begin(), results.end(), unit_test_result::_failed) ||
+			environment.opts.verbose)
+		output_detailed_tests_info(results, score);
 }
 	
 	
-void RunTests::output_detailed_tests_info(int32_t score)
+void RunTests::output_detailed_tests_info(const vector<unit_test_result> & results, int32_t score)
 {
 	output::total_score(score, get_sum_points());
 	cout << endl << endl;
 	
 	output::header("Detailed test output");
 	
-	vector<unit_test> & tests = *environment.heap_tests;
-	for (size_t test_i = 0; test_i < tests.size(); test_i++)
-		if (!tests[test_i].passed() || opts::verbose)
-			output::detailed_info(tests[test_i]);
+	for (size_t test_i = 0; test_i < results.size(); test_i++)
+		if (results[test_i].failed() || environment.opts.verbose)
+			output::detailed_info(results[test_i]);
 	
 	cout << endl << string(64, '-') << endl; // TODO (toole1): poor style, should be refactored to monad_shared::output
 }
 
 
-void RunTests::output_single_test_passfail(const unit_test & curr_test)
+void RunTests::output_single_test_passfail(const unit_test_result & curr_test)
 {
 	if (curr_test.passed())
 		std::cout << output::passed_string() << endl;
 	else
-		std::cout << output::failed_string() << ": " << curr_test.errormsg << endl;
+		std::cout << output::failed_string() << ": " << curr_test.errormsg() << endl;
 }
 
-test_execution::test_execution(unit_test & _test, RunTimeEnvironment & env, bool enable_valgrind_call)
+test_execution::test_execution(const unit_test_input & _test, RunTimeEnvironment & env, bool enable_valgrind_call)
 	: test(_test), environment(env)
 {
-	do_valgrind = enable_valgrind_call && test.is_valgrind;
-	if (!do_valgrind)
-		test.checkstream = new pipestream;
+	do_valgrind = enable_valgrind_call && (test.is_valgrind() || env.opts.valgrind);
+//!!	if (!do_valgrind)
+//!!		test.checkstream = new pipestream; // TODO (reimplement)
 }
 
 void test_execution::child()
@@ -437,7 +386,7 @@ void test_execution::child()
 	nums_pipe.close_read();
 
 	// Redirect stdout/stderr to pipe
-	if (opts::redirect_test_output)
+	if (environment.opts.redirect_test_output)
 	{
 		cout_pipe.steal_output(STDOUT_FILENO);
 		cout_pipe.steal_output(STDERR_FILENO);
@@ -458,49 +407,63 @@ void test_execution::parent()
 	fmsg_pipe.close_write();
 	cout_pipe.close_write();
 	nums_pipe.close_write();
-	if (test.checkstream != NULL)
-		test.checkstream->close_write();
+//!!	if (test.checkstream != NULL)
+	checkstream.close_write();
 
 	// Read stdout/stderr pipe while process is running
-	if (opts::redirect_test_output)
-		cout_pipe >> setmax(environment.max_output_length) >> test.output;
+	if (environment.opts.redirect_test_output)
+		cout_pipe >> setmax(environment.max_output_length) >> output;
 	else
-		test.output = "Test output was displayed above instead of being buffered\n";
+		output = "Test output was displayed above instead of being buffered\n";
 	
 	cout_pipe.close_read();
 }
 
-void test_execution::after_success(int8_t return_code)
+unit_test_result test_execution::result(int8_t return_code)
 {
 	if (do_valgrind)
-		after_valgrind_success(return_code);
+		return after_valgrind_success(return_code);
 	else
-		after_test_success();
+		return after_test_success();
 }
 
-void test_execution::after_failure(int8_t signal_number)
+unit_test_result test_execution::result(const Signal & s)
 {
-	fmsg_pipe.close_read();
-	nums_pipe.close_read();
-	if (environment.is_timeout_signal(signal_number))
+	string errormsg;
+	long time;
+	int32_t valgrind_flags = get_valgrind_flags(true);
+
+	fmsg_pipe.close();
+	nums_pipe.close();
+	if (environment.is_timeout_signal(s))
 	{
-		test.errormsg = string("Timed out") + " (" + to_string(test.timeout) + "ms)";
-		test.time = test.timeout;
+		errormsg = string("Timed out") + " (" + to_string(test.timeout()) + "ms)";
+		time = test.timeout();
 	}
 	else
-		test.errormsg = strsignal(signal_number);
+		errormsg = s.what();
+	
+	return unit_test_result(test, errormsg, output, time, valgrind_flags);
 }
 
 
-bool RunTests::execute_test(unit_test & test, bool enable_valgrind_call)
+unit_test_result RunTests::execute_test(const unit_test_input & test, bool enable_valgrind_call)
 {
 	cout << std::flush;
 	test_execution executor(test, environment, enable_valgrind_call);
-	return fork_execute(executor);
+	try
+	{
+		int32_t return_code = fork_execute(executor);
+		return executor.result(return_code);
+	}
+	catch (Signal & s)
+	{
+		return executor.result(s);
+	}
 }
 
 template <typename F>
-bool fork_execute(F & executor)
+int32_t fork_execute(F & executor)
 {
 	// Fork
 	pid_t process_id;
@@ -510,31 +473,25 @@ bool fork_execute(F & executor)
 	if (process_id == 0)
 	{
 		executor.child();
-		// Unfortunately necessary to use a return stack instead of
-		// exit() to get rid of valgrind errors
-		// (which is important if we use valgrind ./proxy recursively)
-		return false; // previously exit(0);
+		exit(0);
 	}
 	else // if (process_id > 0)
 	{
 		executor.parent();
 
 		int child_status;
-		pid_t ws = waitpid(process_id, &child_status, 0); //should return immediately
+		pid_t ws = waitpid(process_id, &child_status, 0); //should return immediately because it's after parent()
 		EXIT_IF_ERROR(ws == -1);
 
-		if (WIFEXITED(child_status)) /* exit code in child_status */
-			executor.after_success(WEXITSTATUS(child_status));
-		else if (WIFSIGNALED(child_status)) /* killed */
-			executor.after_failure(WTERMSIG(child_status));
+		if (WIFEXITED(child_status))
+			return WEXITSTATUS(child_status);
+		else if (WIFSIGNALED(child_status))
+			throw Signal(WTERMSIG(child_status));
 		else
-			executor.after_failure(SIGSTOP);
-
-		return true;
+			throw Signal(SIGSTOP);
 	}
 }
 
-
 void test_execution::child_valgrind()
 {
 	// We're giving up control to valgrind, so we can't
@@ -543,26 +500,35 @@ void test_execution::child_valgrind()
 	nums_pipe.close_write();
 	
 	start_timeout();
-	exec("valgrind", "--dsymutil=yes", "--trace-children=yes", /*"--log-fd=-1",*/ "-q", "./proxy", test.name, NULL);
+	exec("valgrind", "--dsymutil=yes", "--trace-children=yes", /*"--log-fd=-1",*/ "-q", "./proxy", test.name(), NULL);
 }
 
 
 void test_execution::child_test()
 {
-	test.checkstream->close_read();
+	checkstream.close_read();
 	// Execute test
+	bool test_failed = false;
+	long test_time = -1;
 	start_timeout();
-	string * error_msg = new unit_test::return_type(test.func(test)); // execute function
-	long test_time = end_timeout();
-
-	// Write failure message to pipe
-	fmsg_pipe << *error_msg;
+	try
+	{
+		test.execute(checkstream); // execute function
+		test_time = end_timeout();
+		fmsg_pipe << unit_test_result::pass_string;
+	}
+	catch (Failure & failure)
+	{
+		test_time = end_timeout();
+		test_failed = true;
+		// Write failure message to pipe
+		fmsg_pipe << failure.message();
+	}
 	fmsg_pipe.close();
 
 	// write time and valgrind flags to pipe
-	bool test_failed = (*error_msg != unit_test::pass_string);
-	delete error_msg;
-	delete test.checkstream;
+//!!	delete test.checkstream;
+	checkstream.close();
 	environment.cleanup_globals();
 	int32_t valgrind_flags = get_valgrind_flags(test_failed);
 	nums_pipe << test_time;
@@ -571,74 +537,83 @@ void test_execution::child_test()
 }
 
 
-void test_execution::after_valgrind_success(int8_t return_code)
+unit_test_result test_execution::after_valgrind_success(int8_t return_code)
 {
 	fmsg_pipe.close_read();
 	nums_pipe.close_read();
 
-	size_t last_endl = findNthLast(test.output, '\n', 2);
+	string errormsg;
+
+	size_t last_endl = findNthLast(output, '\n', 2);
+	int32_t valgrind_flags = return_code;
+
 	if (last_endl == string::npos)
 	{
-		if (opts::redirect_test_output)
-			test.errormsg = "Valgrind test did not complete";
+		if (environment.opts.redirect_test_output)
+			errormsg = "Valgrind test did not complete";
 		else
-			test.errormsg = "Valgrind test output was not redirected to pipe because opts::redirect_test_output was set.";
+			errormsg = "Valgrind test output was not redirected to pipe because --redirect was set.";
 	}
 	else
 	{
-		test.errormsg = test.output.substr(last_endl + 1,
-							test.output.length() - last_endl - 2);
+		errormsg = output.substr(last_endl + 1,
+				output.length() - last_endl - 2);
 
-		if (test.errormsg == "")
-			test.errormsg = "Exception Thrown / Aborted";
-
-		test.valgrind_flags = return_code;
-		if (test.errormsg == environment.single_test_passed_string)
-			test.errormsg = get_valgrind_string(test.valgrind_flags);
+		if (errormsg == environment.single_test_passed_string)
+			errormsg = get_valgrind_string(valgrind_flags);
 			// This will always be unit_test::pass_string unless someone tried to hack monad, in which case
 			// basing our passing on the return code (valgrind flags) rather than string parsing is the
 			// right thing to do
 	}
+
+	return unit_test_result(test, errormsg, output, -1, valgrind_flags);
 }
 
 
-void test_execution::after_test_success()
+unit_test_result test_execution::after_test_success()
 {
-	fmsg_pipe >> test.errormsg;
+	string errormsg;
+	long time;
+	int32_t valgrind_flags;
+
+	fmsg_pipe >> errormsg;
 	fmsg_pipe.close();
-	nums_pipe >> test.time;
-	nums_pipe >> test.valgrind_flags;
+	nums_pipe >> time;
+	nums_pipe >> valgrind_flags;
 	nums_pipe.close();
 	
 	// Check for output's correctness, if that was a condition of passing
-	if (test.passed())
+	if (errormsg == unit_test_result::pass_string)
 	{
-		while (!test.checkstream->eof())
+		while (!checkstream.eof())
 		{
 			string checkname;
 			string checkstr;
-			*test.checkstream >> checkname;
-			if (test.checkstream->eof()) break;
-			*test.checkstream >> checkstr;
-			if (test.checkstream->eof()) break;
+			checkstream >> checkname;
+			if (checkstream.eof()) break;
+			checkstream >> checkstr;
+			if (checkstream.eof()) break;
 
-			output_check check_function = (*environment.output_checks)[checkname];
+			output_check check_function = (*environment.output_checks).find(checkname)->second;
 			if (check_function == NULL)
 			{
-				cerr << "Internal Error: in test " << test.name << ": "
+				cerr << "Internal Error: in test " << test.name() << ": "
 				     << checkname << " is not a registered OUTPUT_CHECK function" << endl;
 				exit(-2);
 			}
 
-			if (!check_function(test.output, checkstr))
-				test.errormsg = "Incorrect Terminal Output";
+			if (!check_function(output, checkstr))
+				errormsg = "Incorrect Terminal Output";
 		}
 	}
+	
+	if (errormsg == unit_test_result::pass_string)
+		errormsg = get_valgrind_string(valgrind_flags);
 
-	if (test.passed())
-		test.errormsg = get_valgrind_string(test.valgrind_flags);
-
-	delete test.checkstream;
+//!!	delete test.checkstream;
+	checkstream.close();
+	
+	return unit_test_result(test, errormsg, output, time, valgrind_flags);
 }
 
 
@@ -661,7 +636,7 @@ int32_t get_valgrind_flags(bool test_failed)
 
 const char * get_valgrind_string(int32_t flags)
 {
-	if (flags == 0) return unit_test::pass_string;
+	if (flags == 0) return unit_test_result::pass_string;
 
 	bool test_failed = bitflag(flags, 0);
 	bool errors      = bitflag(flags, 1);
@@ -674,13 +649,16 @@ const char * get_valgrind_string(int32_t flags)
 	if (leaked)      return "Directly lost memory leaks";
 	if (dubious)     return "Possibly lost memory leaks";
 	// For now we will ignore reachable errors, as they are always present on Mac
-	if (reachable)   return unit_test::pass_string; //"Still-reachable memory leaks";
+	// TODO (toole1): Refactor. Still reachable should still hit "unknown" below, not be a free pass
+	// (this doesn't impact current code paths, but is inelegant)
+	if (reachable)   return unit_test_result::pass_string; //"Still-reachable memory leaks";
 	return "Unknown memory errors";
 }
 
 bool test_execution::prof_timeout_enabled()
 {
 	struct itimerval temp;
+	errno = 0;
 	if (getitimer(ITIMER_PROF, &temp) == 0)
 		return true;
 	if (errno == EINVAL)
@@ -696,8 +674,8 @@ void test_execution::start_timeout()
 	struct itimerval timeout;
 	timeout.it_interval.tv_sec  = 0;
 	timeout.it_interval.tv_usec = 0;
-	timeout.it_value.tv_sec  = test.timeout/1000;
-	timeout.it_value.tv_usec = (test.timeout%1000) * 1000;
+	timeout.it_value.tv_sec  = test.timeout()/1000;
+	timeout.it_value.tv_usec = (test.timeout()%1000) * 1000;
 
 	if (prof_enabled)
 	{
@@ -736,7 +714,7 @@ long test_execution::end_timeout()
 
 	// There seems to be a strange -1 error here. I may just be tired,
 	// but I can't figure out why right now
-	long time = test.timeout - remaining.it_value.tv_sec*1000 - remaining.it_value.tv_usec/1000;
+	long time = test.timeout() - remaining.it_value.tv_sec*1000 - remaining.it_value.tv_usec/1000;
 	return (time < 0) ? 0 : time;
 }
 
diff --git a/proxy.h b/proxy.h
index 12b44899b8104c57822bbbc5e6b93ec1e6506166..da3d654e7451bc9e36e211393440b68c37e8e3cd 100644
--- a/proxy.h
+++ b/proxy.h
@@ -7,6 +7,8 @@
 #define MONAD_PROXY_H
 
 #include <math.h>
+#include <signal.h>
+#include <string.h>
 
 #include <iostream>
 #include <functional>
@@ -31,14 +33,14 @@ namespace proxy
 	class RunTests;
 	typedef bool (*output_check)(const std::string &, const std::string &);
 
-	extern std::vector<unit_test> * global_tests;
-	typedef std::map<std::string, output_check, util::ci_less> output_check_map;
+	extern std::vector<unit_test_input> * global_tests;
+	typedef std::map<std::string, output_check> output_check_map;
 	extern output_check_map * global_output_checks;
 
 	class add_unit_test
 	{
 		public:
-		add_unit_test(const char * name, unit_test::function func,
+		add_unit_test(const char * name, unit_test_input::function func,
 		              int32_t points_in_part, int32_t points_in_total, long timeout,
 		              bool is_valgrind);
 
@@ -60,63 +62,98 @@ namespace proxy
 		ALL_TESTS
 	};
 
+
 	struct RunTimeEnvironment
 	{
 		public:
-		//!!const int itimer_number0;
-		//!!const int itimer_number1;
-		const int timeout_signum0;
-		const int timeout_signum1;
+		// Types
+		struct Options
+		{
+			bool verbose;
+			bool redirect_test_output;
+			bool valgrind;
+
+			Options()
+				: verbose(false),
+				  redirect_test_output(true),
+				  valgrind(false)
+			{ }
+		};
+
+		// Variables
+		public:
 		const size_t max_output_length;
-		const char * single_test_passed_string;
-		std::vector<unit_test> * heap_tests;
-		output_check_map * output_checks;
-		int32_t cleanup_globals();
-
-		RunTimeEnvironment(std::vector<unit_test> *& init_tests,
-		                   output_check_map *& init_output_checks);
+		const char * const single_test_passed_string;
+		const std::vector<unit_test_input> * const heap_tests;
+		const output_check_map * const output_checks;
+		const Options opts;
+		const mode_t mode;
+		const char * const test_arg;
+
+		// Methods
+		public:
+		RunTimeEnvironment(std::vector<unit_test_input> *& init_tests,
+		                   output_check_map *& init_output_checks,
+		                   const Options & init_opts, const char * testname);
 		
-		bool is_timeout_signal(int8_t signal_number)
+		bool is_timeout_signal(const util::Signal & s)
 		{
-			return signal_number == timeout_signum0 ||
-			       signal_number == timeout_signum1;
+			return s.number() == SIGPROF || s.number() == SIGALRM;
 		}
+		
+		void cleanup_globals();
 
 		private:
+		// Non-copyable
 		RunTimeEnvironment(const RunTimeEnvironment & other);
 		RunTimeEnvironment & operator=(RunTimeEnvironment & other);
 	};
 
+	class Failure
+	{
+		private:
+		const char * const myFile;
+		const size_t myLine;
+		const string myCause;
+		
+		public:
+		Failure(const char * file, size_t line, const string & cause)
+			: myFile(file), myLine(line), myCause(cause) { }
+
+		string message() const
+		{
+			stringstream ss;
+			ss << myFile << ":" << myLine << ": " << myCause;
+			return ss.str();
+		}
+	};
+
 	class RunTests
 	{
 		private:
 		RunTimeEnvironment & environment;
-		mode_t mode;
-		const char * test_arg;
-		int8_t mp_part;
 
 		public:
-		RunTests(int argc, char ** argv, RunTimeEnvironment & env);
+		RunTests(RunTimeEnvironment & env);
 		int execute();
 		private:
-		void redirect_glibc_to_stderr();
 		void process_args(int argc, char ** argv);
 
 		protected:
 		int32_t execute_by_mode();
 		int32_t run_single_test(const char * testname);
-		int32_t run_single_test(unit_test & curr_test);
+		int32_t run_single_test(const unit_test_input & curr_test);
 		void    handle_single_test_output(const std::string & output);
-		void    output_single_test_passfail(const unit_test & curr_test);
+		void    output_single_test_passfail(const unit_test_result & curr_test);
 		
 		int32_t run_all_tests();
 		int32_t get_sum_points();
 		int32_t get_max_testname_length();
 		int32_t get_max_points_length();
-		void output_detailed_info_if_any_failed(int32_t score);
-		void output_detailed_tests_info(int32_t score);
+		void output_detailed_info_if_any_failed(const vector<unit_test_result> & results, int32_t score);
+		void output_detailed_tests_info(const vector<unit_test_result> & results, int32_t score);
 
-		bool execute_test(unit_test & test, bool enable_valgrind_call);
+		unit_test_result execute_test(const unit_test_input & test, bool enable_valgrind_call);
 
 		private:
 		RunTests(const RunTests & other);
@@ -124,7 +161,7 @@ namespace proxy
 	};
 
 	template <typename F>
-	bool fork_execute(F & executor);
+	int32_t fork_execute(F & executor);
 	
 	class test_execution
 	{
@@ -132,23 +169,24 @@ namespace proxy
 		util::pipestream fmsg_pipe; // For error messages
 		util::pipestream cout_pipe; // For stdout/stderr
 		util::pipestream nums_pipe; // for numbers: time, valgrind
-		unit_test & test;
+		const unit_test_input & test;
 		RunTimeEnvironment & environment;
 		bool do_valgrind;
+		util::pipestream checkstream;
+		string output;
 
 		public:
-		test_execution(unit_test & _test, RunTimeEnvironment & env, bool enable_valgrind_call);
-		void before();
+		test_execution(const unit_test_input & _test, RunTimeEnvironment & env, bool enable_valgrind_call);
 		void parent();
 		void child();
-		void after_success(int8_t return_code);
-		void after_failure(int8_t signal_number);
-		
+		unit_test_result result(int8_t return_code);
+		unit_test_result result(const util::Signal & s);
+
 		private:
 		void child_test();
 		void child_valgrind();
-		void after_test_success();
-		void after_valgrind_success(int8_t return_code);
+		unit_test_result after_test_success();
+		unit_test_result after_valgrind_success(int8_t return_code);
 		void start_timeout();
 		long end_timeout();
 		static bool prof_timeout_enabled();
@@ -165,6 +203,8 @@ namespace proxy
 					 unsigned long d = 0, unsigned long e = 0);
 	bool bitflag(int32_t flags, int32_t num);
 
+	const char * parse_options(int argc, const char * const * const argv, RunTimeEnvironment::Options & opts);
+
 } // namespace proxy
 
 using std::cout;
@@ -172,47 +212,42 @@ using std::cerr;
 using std::endl;
 
 #define UNIT_TEST(func,pointsInPart,pointsInTotal,timeout)             \
-	monad_shared::unit_test::return_type                               \
-	func(monad_shared::unit_test & this_test);                         \
+	void func(util::pipestream & ___monad_checkstream___);             \
 	proxy::add_unit_test                                               \
 		func##_adder(#func, func, pointsInPart,                        \
 		             pointsInTotal, timeout, false);                   \
-	monad_shared::unit_test::return_type                               \
-	func(monad_shared::unit_test & this_test)
+	void func(util::pipestream & ___monad_checkstream___)
 
 #define VALGRIND_TEST(func,pointsInPart,pointsInTotal,timeout)         \
-	monad_shared::unit_test::return_type                               \
-	func(monad_shared::unit_test & this_test);                         \
+	void func(util::pipestream & ___monad_checkstream___);             \
 	proxy::add_unit_test                                               \
 		func##_adder(#func, func, pointsInPart,                        \
 		             pointsInTotal, timeout, true);                    \
-	monad_shared::unit_test::return_type                               \
-	func(monad_shared::unit_test & this_test)
+	void func(util::pipestream & ___monad_checkstream___)
 
 #define HELPER_TEST(func, ...)                                         \
-	monad_shared::unit_test::return_type                               \
-	func(monad_shared::unit_test & this_test, __VA_ARGS__)
+	void func(util::pipestream & ___monad_checkstream___, __VA_ARGS__)
 
 #define CALL_HELPER(func, ...)                                         \
 	do {                                                               \
-		monad_shared::unit_test::return_type helperval =               \
-			func(this_test, __VA_ARGS__);                              \
-		if (helperval != monad_shared::unit_test::pass_string)         \
-			FAIL(helperval);                                           \
+		func(___monad_checkstream___, __VA_ARGS__);                    \
 	} while (0)
 
-#define OUTPUT_CHECK(func)                                                              \
-	bool output_check_##func(const std::string & output, const std::string & expected); \
-	proxy::add_output_check                                                             \
-		output_check_##func##_adder(#func, output_check_##func);                        \
-	bool output_check_##func(const std::string & output, const std::string & expected)
+#define DECLARE_OUTPUT_CHECK(func)  \
+	bool ___monad_output_check_##func(const std::string & output, const std::string & expected);
+
+#define OUTPUT_CHECK(func)                                                                       \
+	bool ___monad_output_check_##func(const std::string & output, const std::string & expected); \
+	proxy::add_output_check                                                                      \
+		___monad_output_check_##func##_adder(#func, ___monad_output_check_##func);                        \
+	bool ___monad_output_check_##func(const std::string & output, const std::string & expected)
 
 #define STRINGIFY1(p)   #p
 #define STR(p)          STRINGIFY1(p)
 
-#define FAIL(error)     return std::string(__FILE__ ":" STR(__LINE__) ": ") + (error)
+#define FAIL(error)     throw proxy::Failure(__FILE__, __LINE__, (error))
 
-#define PASS            return monad_shared::unit_test::pass_string;
+#define PASS            return;
 
 #define ASSERT(expr)    if (!(expr))  \
                             FAIL("Assertion (" #expr ") failed")
@@ -227,19 +262,28 @@ inline std::string assert_equals_help(const T & expected, const T & actual, cons
 		ss << "[" << actstr << " => " << actual << "] != [" << expstr << " => " << expected << "]";
 		return ss.str();
 	}
-	return monad_shared::unit_test::pass_string;
+	return monad_shared::unit_test_result::pass_string;
 }
 }
 
 #define ASSERT_EQUALS(expected, actual)                                                     \
 	do {                                                                                    \
 		string errormsg = proxy::assert_equals_help(expected, actual, #expected, #actual);  \
-		if (errormsg != monad_shared::unit_test::pass_string)                               \
+		if (errormsg != monad_shared::unit_test_result::pass_string)                        \
 			FAIL(errormsg);                                                                 \
 	} while (0)
 
+namespace proxy {
+template <typename S>
+inline void assert_output_impl(util::pipestream & checkstream, output_check checkFunc, const char * checkFuncName, const S & str)
+{
+	if (checkFuncName == NULL) checkFunc("", ""); // Should never occur. Used to avoid unused variable warnings
+	checkstream << checkFuncName << str;
+}
+}
+
 #define ASSERT_OUTPUT(checkFunc, str)  \
-	*this_test.checkstream << #checkFunc << str;
+	proxy::assert_output_impl(___monad_checkstream___, ___monad_output_check_##checkFunc, #checkFunc, str)
 
 enum proxy_runtime_t
 {
@@ -428,4 +472,9 @@ inline bool bitflag(int32_t flags, int32_t num)
 }
 
 }
+
+DECLARE_OUTPUT_CHECK(equals);
+DECLARE_OUTPUT_CHECK(contains);
+DECLARE_OUTPUT_CHECK(not_contains);
+
 #endif
diff --git a/quotes.txt b/quotes.txt
new file mode 100644
index 0000000000000000000000000000000000000000..6cb2f44bd183ccbaf21c7690da3eaffe942e8b97
--- /dev/null
+++ b/quotes.txt
@@ -0,0 +1,49 @@
+"It's OK to quit a job with people you like because there are a lot of people to like out there." ~ Michael Lopp
+"That's legit as shit." ~ Rick Barber
+"The cheating sheet is key to success in CS 412 course." ~ CS 412 TA
+"Your job is not just what you're doing; it should be preparing you for what you want to do." ~ Michael Lopp
+"A reputation is a community-based opinion that you don't control. It takes years of work to develop and a single missed key responsibility to destroy." ~ Michael Lopp
+"One day I will shoot you with this bullet. Can you still say that you love me?" ~ Re-l Mayer
+"I didn't say I wanted to die in a blender! I said I wanted to be in a blender!" ~ Kyle Johnson
+"It is practically impossible to teach good programming to students that have had a prior exposure to BASIC: as potential programmers they are mentally mutilated beyond hope of regeneration." ~ Edsger Dijkstra
+"Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live. Code for readability." ~ John Woods
+"The primary purpose of the DATA statement is to give names to constants; instead of referring to pi as 3.141592653589793 at every appearance, the variable PI can be given that value with a DATA statement and used instead of the longer form of the constant. This also simplifies modifying the program, should the value of pi change." ~ FORTRAN manual for Xerox computers
+"Any sufficiently advanced bug is indistinguishable from a feature." ~ Rich Kulawiec
+"Worry loudly now or scream louder later." ~ Michael Lopp
+"I can always tell when I don't want to do something because... I never do it." ~ Michael Lopp
+"If you think you can do it all, you're thinking too small." ~ Michael Lopp
+"When you say 'You suck', I think 'I win'." ~ Michael Lopp
+"Honestly, if you don't fit in, then you're probably doing the right thing." ~ LIGHTS
+"It should be noted that no ethically-trained software engineer would ever consent to write a DestroyBaghdad procedure. Basic professional ethics would instead require him to write a DestroyCity procedure, to which Baghdad could be given as a parameter." ~ Nathaniel Borenstein
+"Simplicity and elegance are unpopular because thy require hard work and discipline to achieve and education to be appreciated." ~ Edsger Dijkstra
+"I love deadlines. I like the whooshing sound they make as they fly by." ~ Douglas Adams
+"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." ~ Brian Kernighan
+"The first 90% of the code accounts for the first 90% of the development time. The remaining 10% of the code accounts for the other 90% of the development time." ~ Tom Cargill
+"It always takes longer than you expect, even when you take into account Hofstadter's Law." ~ Hofstadter's Law
+"The most likely way for the world to be destroyed, most experts agree, is by accident. That's where we come in; we're computer professionals. We cause accidents." ~ Nathaniel Borenstein
+"Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away." ~ Antoine de Saint-Exupery
+"Even if by chance this world should end tomorrow, if it's together with you, would 'I'm okay with that too' be the cool attitude you'd take?" ~ Makino Yui
+"I just don't trust easily, and for good reasons... which are my own." ~ Carth Onasi
+"You think the sky is like a big big plate." ~ Prof. Jiawei Han
+"You look at their elbows, they actually have many good theoretical elbows." ~ Prof. Jiawei Han
+"If you don't hand in the next homework, you might miss some points" ~ Prof. Jiawei Han
+"I would build pyramids, if I were Pharoah" ~ Kyle Johnson
+"Most software today is very much like an Egyptian pyramid with millions of bricks piled on top of each other, with no structural integrity, but just done by brute force and thousands of slaves." ~ Alan Kay
+"Microsoft has a new version out, Windows XP, which according to everybody is the 'most reliable Windows ever.' To me, this is like saying that asparagus is 'the most articulate vegetable ever.'" ~ Dave Barry
+"The most amazing achievement of the computer software industry is its continuing cancellation of the steady and staggering gains made by the computer hardware industry." ~ Henry Petroski
+"There are two ways of constructing a software design. One way is to make it so simple that there are obviously no deficiencies. And the other way is to make it so complicated that there are no obvious deficiencies." ~ C.A.R. Hoare
+"There's an old story about the person who wished his computer were as easy to use as his telephone. That wish has come true, since I no longer know how to use my telephone." ~ Bjarne Stroustrup
+"The trouble with programmers is that you can never tell what a programmer is doing until it's too late." ~ Seymour Cray
+"Measuring programming progress by lines of code is like measuring aircraft building progress by weight." ~ Bill Gates
+"First, solve the problem. Then, write the code." ~ John Johnson
+"Optimism is an occupational hazard of programming; feedback is the treatment." ~ Kent Beck
+"To iterate is human, to recurse divine." ~ L. Peter Deutsch
+"Should array indices start at 0 or 1? My compromise of 0.5 was rejected without, I thought, proper consideration." ~ Stan Kelly-Bootle
+"I think Microsoft named .Net so it wouldn't show up in a Unix directory listing." ~ Oktal
+"C++ : Where friends have access to your private members." ~ Gavin Russell Baker
+"Java is, in many ways, C++–." ~ Michael Feldman
+"If debugging is the process of removing bugs, then programming must be the process of putting them in." ~ Edsger W. Dijkstra
+"A computer lets you make more mistakes faster than any invention in human history–with the possible exceptions of handguns and tequila." ~ Mitch Radcliffe
+"A computer will do what you tell it to do, but that may be much different from what you had in mind" ~ Joseph Weizenbaum
+"Computers are like Old Testament gods; lots of rules and no mercy." ~ Joseph Campbell
+"Programs must be written for people to read, and only incidentally for machines to execute." ~ Abelson and Sussman
diff --git a/random.h b/random.h
new file mode 100644
index 0000000000000000000000000000000000000000..be927fd7c575be7f62b049b701d46e55a6c75211
--- /dev/null
+++ b/random.h
@@ -0,0 +1,119 @@
+#ifndef UTIL_RANDOM_H
+#define UTIL_RANDOM_H
+
+#include <stdint.h>
+#include <sys/time.h>
+#include <time.h>
+
+// Taken from
+// http://software.intel.com/en-us/articles/fast-random-number-generator-on-the-intel-pentiumr-4-processor/
+
+namespace util
+{
+
+namespace internal
+{
+
+inline uint32_t time_seed()
+{
+	timeval tv;
+	gettimeofday(&tv, NULL);
+	return tv.tv_sec ^ tv.tv_usec;
+}
+
+inline int_fast16_t rand(uint32_t setseed)
+{
+	static uint32_t mySeed = (uint32_t)(-1);
+	if (setseed != (uint32_t)(-2))
+		mySeed = setseed;
+	if (mySeed == (uint32_t)(-1))
+		mySeed = time_seed();
+	mySeed = 214013 * mySeed + 2531011;
+	return (mySeed >> 16) & 0x7FFF;
+}
+
+}
+
+inline int_fast16_t urand()
+{
+	return internal::rand(-2);
+}
+
+inline int_fast16_t urandn(int32_t n)
+{
+	return urand() % n;
+}
+
+inline void usrand(uint64_t seed)
+{
+	internal::rand(seed);
+}
+
+class Random
+{
+	private:
+	static uint32_t auto_seed(uint32_t setSeed);
+	uint32_t mySeed;
+
+	public:
+	Random();
+	Random(uint64_t seed);
+
+	int_fast16_t rand();
+	int_fast16_t rand(int32_t n);
+	int_fast16_t operator()();
+	int_fast16_t operator()(int32_t n);
+	void         srand(uint64_t seed);
+};
+
+inline uint32_t Random::auto_seed(uint32_t setSeed)
+{
+	static uint32_t internalSeed = 0;
+	if (setSeed == 0)
+		return internalSeed;
+	
+	uint32_t result = internalSeed;
+	internalSeed = setSeed;
+	return result;
+}
+
+inline Random::Random()
+{
+	if (auto_seed(0) == 0)
+		auto_seed(internal::time_seed());
+	mySeed = auto_seed(0);
+	auto_seed((rand() << 16) ^ (rand()) ^ internal::time_seed());
+}
+
+inline Random::Random(uint64_t seed)
+	: mySeed(seed) { }
+
+inline void Random::srand(uint64_t seed)
+{
+	mySeed = seed;
+}
+
+inline int_fast16_t Random::rand()
+{
+	mySeed = 214013 * mySeed + 2531011;
+	return (mySeed >> 16) & 0x7FFF;
+}
+
+inline int_fast16_t Random::rand(int32_t n)
+{
+	return rand() % n;
+}
+
+inline int_fast16_t Random::operator()()
+{
+	return rand();
+}
+
+inline int_fast16_t Random::operator()(int32_t n)
+{
+	return rand(n);
+}
+
+} // namespace util
+
+#endif // UTIL_RANDOM_H
diff --git a/regression.sh b/regression.sh
new file mode 120000
index 0000000000000000000000000000000000000000..3023f4fb34aabd8ce5bb5f56690cf9d63b45c0ed
--- /dev/null
+++ b/regression.sh
@@ -0,0 +1 @@
+../monad_dev/regression.sh
\ No newline at end of file
diff --git a/update.sh b/update.sh
index d24eef13975e4acba0478bfac34549938451c5a7..66dcec874973fdf5cd5408fdb0836381007bfd29 100755
--- a/update.sh
+++ b/update.sh
@@ -22,11 +22,15 @@ cp ../monad_dev/proxy.h .
 cp ../monad_dev/util.cpp .
 cp ../monad_dev/util.h .
 cp ../monad_dev/valgrind.h .
-cp ../monad_dev/LICENSE.txt .
 
 # Monad Source
 cp ../monad_dev/Makefile .
-cp ../monad_dev/README.txt .
+cp ../monad_dev/LICENSE.txt .
 cp ../monad_dev/monad.h .
 cp ../monad_dev/monad.cpp .
+cp ../monad_dev/quotes.txt .
+cp ../monad_dev/random.h .
+cp ../monad_dev/README.txt .
+cp ../monad_dev/README_config.txt .
+cp ../monad_dev/README_tests.txt .
 cp ../monad_dev/help.cpp .
diff --git a/util.cpp b/util.cpp
index f624c92dc791a55dc6da7c64d0b5abc5e39ce823..d1cc6992b53e1107de822ddb95e619f7534fb166 100644
--- a/util.cpp
+++ b/util.cpp
@@ -57,15 +57,24 @@ int8_t exec(int redirect_fd, const char * command,
 	int childExitStatus;
 	pid_t pid;
 
+	const char * args[] = {arg1, arg2, arg3, arg4, arg5, arg6};
+	const size_t args_count = (sizeof args) / (sizeof args[0]);
+
+	// shift all nulls to end
+	size_t first_null = 0;
+	for (size_t i = 0; i < args_count; i++)
+		if (args[i] != NULL)
+			swap(args[i], args[first_null++]);
+
 	// For debugging:
 #if 0
 	cerr << "exec(" << command << ' '
-	     << ((arg1!=NULL)?arg1:"-") << ' '
-	     << ((arg2!=NULL)?arg2:"-") << ' '
-	     << ((arg3!=NULL)?arg3:"-") << ' '
-	     << ((arg4!=NULL)?arg4:"-") << ' '
-	     << ((arg5!=NULL)?arg5:"-") << ' '
-	     << ((arg6!=NULL)?arg6:"-") << ' '
+//!!	     << ((arg1!=NULL)?arg1:"-") << ' '
+//!!	     << ((arg2!=NULL)?arg2:"-") << ' '
+//!!	     << ((arg3!=NULL)?arg3:"-") << ' '
+//!!	     << ((arg4!=NULL)?arg4:"-") << ' '
+//!!	     << ((arg5!=NULL)?arg5:"-") << ' '
+//!!	     << ((arg6!=NULL)?arg6:"-") << ' '
 	     << ')' << endl;
 #endif
 
@@ -134,6 +143,8 @@ int8_t exec(int redirect_fd, const char * command,
 		// Sanitize the environment
 #if 1 //!! hack!
 		char path[] = "PATH=/bin/:/usr/bin:/usr/local/bin";
+		// Turn off glibc errors default write-to-terminal behaviour, because
+		// it does not get caught by stderr. This instead results in an abort.
 		char redirect_glibc[] = "LIBC_FATAL_STDERR_=1";
 		char * newenv[] = { path, redirect_glibc, NULL };
 		//char * newenv[] = { path, NULL };
@@ -142,16 +153,12 @@ int8_t exec(int redirect_fd, const char * command,
 
 		// Swap out child process image with the command, searching
 		// in the specified path
-		execlp(command, command, arg1, arg2, arg3, arg4, arg5, arg6, NULL);
+		execlp(command, command, args[0], args[1], args[2], args[3], args[4], args[5], NULL);
 		
         // An error occured
 		cerr << "exec(" << '\"' << command << '\"';
-		if (arg1 != NULL) cerr << ", \"" << arg1 << "\"";
-		if (arg2 != NULL) cerr << ", \"" << arg2 << "\"";
-		if (arg3 != NULL) cerr << ", \"" << arg3 << "\"";
-		if (arg4 != NULL) cerr << ", \"" << arg4 << "\"";
-		if (arg5 != NULL) cerr << ", \"" << arg5 << "\"";
-		if (arg6 != NULL) cerr << ", \"" << arg6 << "\"";
+		for (size_t i = 0; i < args_count; i++)
+			if (args[i] != NULL) cerr << ", \"" << args[i] << "\"";
 		cerr << ") failed: " << strerror(errno) << endl;
 		exit(-1);
 	}
@@ -194,6 +201,12 @@ int8_t exec(int redirect_fd, const char * command,
 	}
 }
 
+const char * Signal::what() const
+{
+	const char * desc = strsignal(signum);
+	return (desc == NULL) ? "Unknown Signal" : desc;
+}
+
 
 int chdir(const string & dir)
 {
@@ -609,14 +622,15 @@ OptionsParser::OptionsParser()
 	valueMap["0"]     = false;
 }
 
-vector<string> OptionsParser::parse(int argc, const char ** argv)
+vector<string> OptionsParser::parse(int argc, const char * const * argv)
 {
 	vector<string> unprocessedArgs;
 	size_t out_arg_i = 0;
 
 	for (int arg_i = 1; arg_i < argc; arg_i++)
 	{
-		string currarg = toLower(argv[arg_i]);
+		string originalCaseArg = argv[arg_i];
+		string currarg = toLower(originalCaseArg);
 
 		if (currarg.compare(0, 2, "--") == 0) //long option
 		{
@@ -661,9 +675,9 @@ vector<string> OptionsParser::parse(int argc, const char ** argv)
 		else //positional argument
 		{
 			if (out_arg_i < args.size())
-				*args[out_arg_i] = currarg;
+				*args[out_arg_i] = originalCaseArg;
 			else
-				unprocessedArgs.push_back(currarg);
+				unprocessedArgs.push_back(originalCaseArg);
 			out_arg_i++;
 		}
 	}
diff --git a/util.h b/util.h
index fd88ac05e25598a448123d05c77b3cf4f43f486a..93ecee324543df2e2475309ba08486c46475761e 100644
--- a/util.h
+++ b/util.h
@@ -15,6 +15,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <sys/stat.h>
+#include <sys/time.h>
 #include <sys/types.h>
 #include <time.h>
 #include <unistd.h>
@@ -58,6 +59,26 @@ struct ci_less : std::binary_function<std::string, std::string, bool>
 	}
 };
 
+// http://stackoverflow.com/questions/5300602/template-return-type-with-default-value
+template <typename T>
+struct Initializer
+{
+	T value;
+	Initializer()
+		:value() // ====> default construction, works for classes _and_ built-in
+	{ }
+};
+
+class Signal
+{
+	private:
+	const int signum;
+	public:
+	Signal(int signal_number) : signum(signal_number) { }
+	int number() const { return signum; }
+	const char * what() const;
+};
+
 
 
 /**
@@ -80,7 +101,7 @@ class OptionsParser
 	OptionsParser();
 	void addOption(const string & name, bool & setValue) { optsMap[name] = &setValue; }
 	void addArg(string & setValue) { args.push_back(&setValue); }
-	vector<string> parse(int argc, const char ** argv);
+	vector<string> parse(int argc, const char * const * argv);
 };
 
 // EXEC()
@@ -156,7 +177,7 @@ ssize_t read(int fd, long & val);
 
 
 // STRING TYPE OPERATIONS
-int intlen(unsigned int a);
+uint32_t intlen(uint32_t a);
 void makeLower(string & str);
 string toLower(const string & str);
 
@@ -180,6 +201,7 @@ string operator+(const string & str, const T & value);
 
 // CLOCK / TIMING
 uint64_t process_clock();
+uint64_t system_clock();
 
 // CONFIGURATION
 void readConfig(const string & testsFolder, FileMap & map, const string & discriminator = "");
@@ -270,24 +292,24 @@ inline T from_string(const string & s)
 template <typename T, typename F>
 inline T lexical_cast(const F & from)
 {
-	T to;
+	Initializer<T> to;
 	stringstream ss;
 	if (!(ss << from))
 	{
 		cerr << "ERROR: Could not convert types" << endl;
-		return to;
+		return to.value;
 	}
-	if (!(ss >> to))
+	if (!(ss >> to.value))
 	{
 		cerr << "ERROR: Could not convert types" << endl;
-		return to;
+		return to.value;
 	}
-	return to;
+	return to.value; // TODO (toole1): Not sure if this forces a copy
 }
 
-inline int intlen(unsigned int a)
+inline uint32_t intlen(uint32_t a)
 {
-	int len = 1;
+	uint32_t len = 1;
 	a /= 10;
 
 	while (a != 0)
@@ -365,6 +387,13 @@ inline uint64_t process_clock()
 #endif
 }
 
+inline uint64_t system_clock()
+{
+	timeval tv;
+	gettimeofday(&tv, NULL);
+	return tv.tv_sec * 1000000 + tv.tv_usec;
+}
+
 } // namespace util
 
 #endif // UTIL_H