diff --git a/.monadid b/.monadid
index 6b183d8c435da77218cceed399691999e14e5d00..e49988717e13f1a6e85621552a021e1365bcba12 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: Mon Oct 17 22:44:50 CDT 2011
+Build Date: Mon Oct 24 23:45:56 CDT 2011
diff --git a/monad b/monad
index 3977504d4c2559942bcada38cf547f0627a53d5d..9d1ca1f8b1c4c91babed89d8bd64d628d923b52b 100755
Binary files a/monad and b/monad differ
diff --git a/monad_shared.cpp b/monad_shared.cpp
index 1e53aaaea5a7fe34b64bd15c983e9cf59e79b9e8..7fa7f419c8d071070628a532a222d9ec53b83d41 100644
--- a/monad_shared.cpp
+++ b/monad_shared.cpp
@@ -19,6 +19,7 @@ const char * date          = "15 July 2011";
 }
 
 const char * unit_test::pass_string = "~~PASSED~~";
+const size_t header_length = 64;
 
 void printInfo()
 {
@@ -47,18 +48,16 @@ void set_error_message()
 void header(const string & title)
 {
 	cout << title << "..." << endl
-	     << "================================" << endl;
+	     << string(header_length, '=') << endl;
 }
 
 void warning(const string & message)
 {
 	cerr << endl
-		 << "********************************"
-			"********************************" << endl
+	     << string(header_length, '*') << endl
 		 << "WARNING!" << endl
 		 << message << endl
-		 << "********************************"
-			"********************************" << endl << endl;
+	     << string(header_length, '*') << endl << endl;
 }
 
 
@@ -113,7 +112,7 @@ void testname(const unit_test & curr_test, int32_t max_testname_len, int32_t max
 
 void detailed_info(const unit_test & curr_test)
 {
-	std::cout << "--------------------------------" << endl
+	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;
@@ -133,7 +132,7 @@ void detailed_info(const unit_test & curr_test)
 	cout << curr_test.timeout << "ms timeout)" << endl;
 
 	std::cout << "Output:" << endl
-			  << "--------------------------------" << endl;
+			  << string(header_length, '-') << endl;
 
 	// Tab the output over to distinguish it from the test case
 	if (output != "")
diff --git a/proxy.cpp b/proxy.cpp
index fd71b8dfb3666cc4175a3b82f804368efbcd17ed..bc8454f1dbc67336e6b18bae197032b5bf0af140 100644
--- a/proxy.cpp
+++ b/proxy.cpp
@@ -60,19 +60,28 @@ namespace proxy
 	vector<unit_test> * global_tests = NULL;
 	output_check_map * global_output_checks = NULL;
 
-	double runtime_ratio[TIME_COUNT] =
+	double time_constant(size_t smaller, size_t larger) { return 1.0; }
+	double time_logn    (size_t smaller, size_t larger) { return log(larger) / log(smaller); }
+	double time_linear  (size_t smaller, size_t larger) { return (double)larger / smaller; }
+	double time_nlogn   (size_t smaller, size_t larger) { return (larger*log(larger)) / (smaller*log(smaller)); }
+	double time_nsquared(size_t smaller, size_t larger) { return ((double)larger*larger) / ((double)smaller*smaller); }
+	double time_cubed   (size_t smaller, size_t larger) { return ((double)larger*larger*larger) / ((double)smaller*smaller*smaller); }
+	double time_infinity(size_t smaller, size_t larger) { return std::numeric_limits<double>::max(); }
+
+	runtime_ratio_func runtime_ratio[TIME_COUNT] =
 	{
-		1.0,
-		4.0,
-		5.20411998, // for 400/100
-//		8.0,
-		16.0,
-		64,
-		std::numeric_limits<double>::max()
+		time_constant,
+		time_logn,
+		time_linear,
+		time_nlogn,
+		time_nsquared,
+		time_cubed,
+		time_infinity
 	};
 	const char * runtime_str[TIME_COUNT] =
 	{
 		"O(1)",
+		"O(logn)",
 		"O(n)",
 		"O(nlogn)",
 //		"O(nrootn)",
@@ -401,7 +410,7 @@ void RunTests::output_detailed_tests_info(int32_t score)
 		if (!tests[test_i].passed() || opts::verbose)
 			output::detailed_info(tests[test_i]);
 	
-	cout << endl << "--------------------------------" << endl;
+	cout << endl << string(64, '-') << endl; // TODO (toole1): poor style, should be refactored to monad_shared::output
 }
 
 
diff --git a/proxy.h b/proxy.h
index 96c53bc157c8acea1e80a8ad1ed96c93f53cf9ca..68ee6aa87b2450e99329769fc75c35aaebc53702 100644
--- a/proxy.h
+++ b/proxy.h
@@ -6,9 +6,10 @@
 #ifndef MONAD_PROXY_H
 #define MONAD_PROXY_H
 
-#include <cmath>
+#include <math.h>
 
 #include <iostream>
+#include <functional>
 #include <limits>
 #include <map>
 #include <string>
@@ -24,6 +25,7 @@
 
 namespace proxy
 {
+	using namespace std;
 	using namespace monad_shared;
 
 	class RunTests;
@@ -242,6 +244,7 @@ inline std::string assert_equals_help(T expected, T actual, const char * expstr,
 enum proxy_runtime_t
 {
 	CONSTANT_TIME = 0,
+	LOGN_TIME,
 	N_TIME,
 	NLOGN_TIME,
 //	NROOTN_TIME,
@@ -253,35 +256,51 @@ enum proxy_runtime_t
 
 namespace proxy
 {
-	extern double runtime_ratio[TIME_COUNT];
+	typedef double (*runtime_ratio_func)(size_t, size_t);
+	extern runtime_ratio_func runtime_ratio[TIME_COUNT];
 	extern const char * runtime_str[TIME_COUNT];
 
+	struct TimeIterationsData
+	{
+		double timePerCall;
+		size_t iterations;
+		uint64_t totalTime;
+	};
+
+	template <typename Generator, typename Timer>                  TimeIterationsData timeIterationsImpl(Generator gen,            Timer timeFunctor, size_t input_size);
+	template <typename Generator, typename Timer>                  TimeIterationsData timeIterations    (Generator gen,            Timer timeFunctor, size_t input_size);
+	template <typename GenResult, typename GenArg, typename Timer> TimeIterationsData timeIterations    (GenResult (*gen)(GenArg), Timer timeFunctor, size_t input_size);
+	
 	template <typename Generator, typename Timer>
-	uint64_t timeIterationsImpl(Generator gen, Timer timeFunctor, size_t input_size);
-	template <typename Generator, typename Timer>
-	uint64_t timeIterations(Generator gen, Timer timeFunctor, size_t input_size);
-	template <typename GenResult, typename GenArg, typename Timer>
-	uint64_t timeIterations(GenResult (*gen)(GenArg), Timer timeFunctor, size_t input_size);
+	bool assert_time_impl(Generator gen, Timer functor, proxy_runtime_t expectedTime, size_t size1 = 100, size_t size2 = 400);
 }
 
-#define ASSERT_TIME(gen, functor, expectedTime)                                           \
+#define ASSERT_TIME3(gen, functor, expectedTime)                                          \
 	do {                                                                                  \
-		uint64_t diff400 = proxy::timeIterations(gen, functor, 400);                      \
-		uint64_t diff100 = proxy::timeIterations(gen, functor, 100);                      \
-		std::cout << diff400 << std::endl;                                                \
-		std::cout << diff100 << std::endl;                                                \
-		double ratio = (double)(diff400)/(diff100);                                       \
-		double diffFromExpected = abs(ratio - proxy::runtime_ratio[expectedTime]);        \
-		double diffFromWrong    = abs(ratio - proxy::runtime_ratio[expectedTime + 1]);    \
-		std::cout << ratio << std::endl;                                                  \
-		if (diffFromWrong < diffFromExpected)                                             \
-			FAIL(string("Runtime was larger than ") + proxy::runtime_str[expectedTime] + util::to_string(ratio));  \
+		if (proxy::assert_time_impl(gen, functor, expectedTime))                          \
+			FAIL(string("Runtime was larger than ") + proxy::runtime_str[expectedTime]);  \
 	} while(0)
 
+#define ASSERT_TIME5(gen, functor, expectedTime, size1, size2)                            \
+	do {                                                                                  \
+		if (proxy::assert_time_impl(gen, functor, expectedTime, size1, size2))            \
+			FAIL(string("Runtime was larger than ") + proxy::runtime_str[expectedTime]);  \
+	} while(0)
+
+// Crazy hack for overloading!
+// Arg counting from:
+// http://cplusplus.co.il/2010/07/17/variadic-macro-to-count-number-of-arguments/
+// Overloading tips:
+// http://stackoverflow.com/questions/3046889/optional-parameters-with-c-macros
+#define ASSERT_TIME_SIXTH_ARG(a, b, c, d, e, f, ...) f
+
+#define ASSERT_TIME(...)  \
+	ASSERT_TIME_SIXTH_ARG(__VA_ARGS__, ASSERT_TIME5, 0, ASSERT_TIME3, 0, 0) (__VA_ARGS__)
+
 namespace proxy {
 
 template <typename Generator, typename Timer>
-uint64_t timeIterations(Generator gen, Timer timeFunctor, size_t input_size)
+TimeIterationsData timeIterations(Generator gen, Timer timeFunctor, size_t input_size)
 {
 	return timeIterationsImpl(
 			bind1st(mem_fun(&Generator::operator()), &gen),
@@ -290,27 +309,86 @@ uint64_t timeIterations(Generator gen, Timer timeFunctor, size_t input_size)
 }
 
 template <typename GenResult, typename GenArg, typename Timer>
-uint64_t timeIterations(GenResult (*gen)(GenArg), Timer timeFunctor, size_t input_size)
+TimeIterationsData timeIterations(GenResult (*gen)(GenArg), Timer timeFunctor, size_t input_size)
 {
 	return timeIterationsImpl(ptr_fun(gen), timeFunctor, input_size);
 }
 
 template <typename Generator, typename Timer>
-uint64_t timeIterationsImpl(Generator gen, Timer timeFunctor, size_t input_size)
+TimeIterationsData timeIterationsImpl(Generator gen, Timer timeFunctor, size_t input_size)
+{
+	const uint64_t min_time = 1000000; // in microseconds
+	std::vector<typename Generator::result_type *> inputs;
+	inputs.reserve(2000); // arbitrary, guess at how big it will be
+
+	// Using pointers here allows us to avoid copying if the compiler supports copy elision
+	// Since we're intentionally using large inputs, this could potentially have a significant effect on speed
+	// We're also going to do something else weird here. Instead of generating a fixed number of inputs, we're
+	// going to generate inputs for a fixed time.
+	size_t max_iterations = 0;
+	for (uint64_t genstart = util::process_clock(); util::process_clock() - genstart < min_time; max_iterations++)
+		inputs.push_back(new typename Generator::result_type(gen(input_size)));
+
+	typename Generator::result_type warmup_temp = gen(1);
+	timeFunctor(warmup_temp); // Warm up time functor (i.e. initialize statics)
+
+	size_t succeeded_iterations;
+	uint64_t starttime = util::process_clock();
+	for (succeeded_iterations = 0; succeeded_iterations < max_iterations && util::process_clock() - starttime < min_time;)
+		for (uint32_t i = 0; i < 10 && succeeded_iterations < max_iterations; i++, succeeded_iterations++)
+			timeFunctor(*inputs[succeeded_iterations]);
+	uint64_t endtime = util::process_clock();
+
+	for (size_t i = 0; i < max_iterations; i++)
+		delete inputs[i];
+
+	TimeIterationsData result;
+	result.timePerCall = static_cast<double>(endtime - starttime) / succeeded_iterations;
+	result.iterations  = succeeded_iterations;
+	result.totalTime   = endtime - starttime;
+	return result;
+}
+
+inline void timeIterationsOutput(size_t size, const TimeIterationsData & data)
 {
-	const size_t num_iterations = 2000;
-	std::vector<typename Generator::result_type> inputs;
-	inputs.reserve(num_iterations);
-	for (size_t i = 0; i < num_iterations; i++)
-		inputs.push_back(gen(input_size));
-
-	uint32_t starttime = util::process_clock();
-	for (size_t i = 0; i < num_iterations; i++)
-		timeFunctor(inputs[i]);
-	uint32_t endtime = util::process_clock();
-	return endtime - starttime;
+	std::cout << "Input size " << size << ": "
+	          << data.iterations << " iterations in " << data.totalTime/1000 << " ms "
+			  << "for an average of " << data.timePerCall << " us per call" << endl;
 }
 
+template <typename Generator, typename Timer>
+bool assert_time_impl(Generator gen, Timer functor, proxy_runtime_t expectedTime, size_t size1, size_t size2)
+{
+	TimeIterationsData diff0 = timeIterations(gen, functor, 1);
+	TimeIterationsData diff1 = timeIterations(gen, functor, size1);
+	TimeIterationsData diff2 = timeIterations(gen, functor, size2);
+	timeIterationsOutput(    1, diff0);
+	timeIterationsOutput(size1, diff1);
+	timeIterationsOutput(size2, diff2);
+	double ratio = (diff2.timePerCall - diff0.timePerCall) / (diff1.timePerCall - diff0.timePerCall);
+	
+	double expected_ratio = proxy::runtime_ratio[expectedTime](size1, size2);
+	double toohigh_ratio  = proxy::runtime_ratio[expectedTime + 1](size1, size2);
+	double diffFromExpected = fabs(ratio - expected_ratio);
+	double diffFromWrong    = fabs(ratio - toohigh_ratio);
+	
+	std::cout << "Actual ratio:     " << ratio << std::endl;
+	std::cout << "Expected ratio:   " << expected_ratio << std::endl;
+	std::cout << "Wrong/high ratio: " << toohigh_ratio  << std::endl;
+	std::cout << "Diff from expected: " << diffFromExpected  << std::endl;
+	std::cout << "Diff from wrong:    " << diffFromWrong  << std::endl;
+
+#if 0 // This does not seem to be important. A sample of two iterations seems to work.
+	const size_t min_iters = 100;
+	if (diff0.iterations < min_iters || diff1.iterations < min_iters || diff2.iterations < min_iters)
+	{
+		std::cout << "Too few iterations: Code was too slow to be able to judge runtime accurately" << std::endl;
+		return true;
+	}
+#endif
+
+	return (diffFromWrong < diffFromExpected);
+}
 
 inline int32_t bitflags(unsigned long a, unsigned long b, unsigned long c,
                         unsigned long d, unsigned long e)