*! version 1.0.0
*! 07apr2014
*! James Fiedler jrfiedler@gmail.com

mata
	class testcase {
		// users *must* set these values
		string scalar name
		string vector test_names


		// user *may* redefine these
		real scalar verbose
		real scalar noisy_capture
		real scalar noisy_assert
		real scalar exit_setup_error
		real scalar exit_teardown_error

		void new()
		virtual void setup(), setup_once()
		virtual void teardown(), teardown_once()


		// user *must not* redefine any of the following
		final void run()
		final void print_summary()
		final void assert()
		final void assert_equal()
		final void assert_unequal()
		final void assert_equal_contents()
		final void assert_close()
		final void assert_all()
		final void assert_any()
		final void assert_error()
		final void assert_method_error()

		final real scalar __capture()
		final real scalar __method_capture()
		final real scalar __run_method()
		final void __equal_struct_contents()
		final void __contents_failures()

		final real matrix __pass_fail_error
		final real scalar __testnum
		final real scalar __subtestnum
		final real scalar __subtestcomplete
		final pointer(pointer(string) vector) colvector __msgs
		final string colvector __setup_teardown_msgs
		final pointer matrix __arg_ptrs
		final pointer(function) scalar __func_ptr
	}

	void testcase::new()
	{
		this.verbose = 1
		this.noisy_capture = 1
		this.noisy_assert = 0
		this.exit_setup_error = 1
		this.exit_teardown_error = 1

		// subtestnum is set to zero at the beginning of each test,
		// but initialize it to zero in case it is used interactively
		this.__subtestnum = 0
	}

	void testcase::run()
	{
		real scalar n_tests, rc, i

		// check that testcase instance has been given a name
		if (this.name == "") {
			display(`"{err}need instance name in member variable -name-"')
			display(`"{err}example: mytest.name = "mytest""')
			return
		}

		n_tests = length(this.test_names)
		this.__pass_fail_error = J(n_tests, 3, 0)
		this.__msgs = J(n_tests, 1, &(&""))
		this.__setup_teardown_msgs = J(2, 1, "")

		// check that there are tests listed in test_name
		if (n_tests == 0) {
			display("{err}" + this.name + ".test_names is empty")
			return
		}

		this.__testnum = 0
		rc = this.__run_method("setup_once")
		if (rc) {
			this.__setup_teardown_msgs[1] = sprintf(
				"** unexpected error %f **", rc
			)
			if (this.exit_setup_error) {
				if (this.verbose) this.print_summary()
				return
			}
		}
		for (i = 1; i <= n_tests; i++) {
			this.__testnum = i
			this.__subtestnum = 0

			rc = this.__run_method("setup")
			if (rc) {
				this.__pass_fail_error[i, 3] = this.__pass_fail_error[i, 3] + 1
				this.__msgs[i] = &(
					*this.__msgs[i] \
					&sprintf("** unexpected error %f in setup() **", rc)
				)
				if (this.exit_setup_error) {
					if (this.verbose) this.print_summary()
					return
				}
			}
			rc = this.__run_method(this.test_names[i])
			if (rc) {
				this.__pass_fail_error[i, 3] = this.__pass_fail_error[i, 3] + 1
				if (this.__subtestnum == 0) {
					this.__msgs[i] = &(
						*this.__msgs[i] \
						&sprintf(
							"** unexpected error %f before first subtest **",
							rc
						)
					)
				}
				else if (this.__subtestcomplete) {
					this.__msgs[i] = &(
						*this.__msgs[i] \
						&sprintf(
							"** unexpected error %f after subtest %f **",
							rc,
							this.__subtestnum
						)
					)
				}
				else {
					this.__msgs[i] = &(
						*this.__msgs[i] \
						&sprintf(
							"** unexpected error %f in subtest %f **",
							rc,
							this.__subtestnum
						)
					)
				}
			}
			rc = this.__run_method("teardown")
			if (rc) {
				this.__pass_fail_error[i, 3] = this.__pass_fail_error[i, 3] + 1
				this.__msgs[i] = &(
					*this.__msgs[i] \
					&sprintf("** unexpected error %f in teardown() **", rc)
				)
				if (this.exit_teardown_error) {
					if (this.verbose) this.print_summary()
					return
				}
			}
		}
		rc = this.__run_method("teardown_once")
		if (rc) {
			this.__setup_teardown_msgs[2] = sprintf(
				"** unexpected error %f **", rc
			)
		}

		if (this.verbose) {
			this.print_summary()
		}
	}

	void testcase::print_summary()
	{
		real scalar i, j, ntests
		pointer(string) vector msgs
		real matrix pfe
		string colvector stm

		pfe = this.__pass_fail_error
		stm = this.__setup_teardown_msgs

		if (rows(pfe) == 0 & rows(stm) == 0) {
			display(`"{err}The summary is empty. Have you run any tests?"')
			return
		}

		ntests = min((this.__testnum, rows(pfe), length(this.test_names)))

		stata(`"noi di """')
		if (stm[1] != "") {
			stata(`"noi di "{txt}setup_once""')
			stata(`"noi di "  {err}"' + this.__setup_teardown_msgs[1] + `"""')
		}
		for (i = 1; i <= ntests; i++) {
			stata(`"noi di "{txt}"' + this.test_names[i] + `"" _continue"')
			if (pfe[i,2] == 0 && pfe[i,3] == 0) {
				stata(`"noi di " ... ok""')
			}
			else {
				msgs = *this.__msgs[i]
				for (j = 1; j <= length(msgs); j++) {
					stata(`"noi di "  {err}"' + *msgs[j] + `"""')
				}
			}
		}
		if (stm[2] != "") {
			stata(`"noi di "{txt}teardown_once""')
			stata(`"noi di "  {err}"' + this.__setup_teardown_msgs[2] + `"""')
		}
		stata(`"noi di """')
	}

	real scalar testcase::__run_method(string scalar test_name)
	{
		if (this.noisy_capture) {
			stata("capture noisily mata: " + this.name + "." + test_name + "()")
		}
		else {
			stata("capture mata: " + this.name + "." + test_name + "()")
		}
		stata("local __testcase_rc = _rc")
		return(strtoreal(st_local("__testcase_rc")))
	}

	void testcase::assert(transmorphic b)
	{
		real scalar n

		n = this.__testnum
		this.__subtestnum = this.__subtestnum + 1
		this.__subtestcomplete = 0

		if (eltype(b) != "real" | orgtype(b) != "scalar") {
			this.__pass_fail_error[n, 3] = this.__pass_fail_error[n, 3] + 1

			this.__msgs[n] = &(
				*this.__msgs[n] \
				(
					&sprintf("subtest %f: assert()", this.__subtestnum) \
					&sprintf(
						"    error: input was %s %s, should be real scalar",
						eltype(b),
						orgtype(b)
					)
				)
			)

			this.__subtestcomplete = 1
			return
		}

		if (b) {
			this.__pass_fail_error[n, 1] = this.__pass_fail_error[n, 1] + 1
			this.__subtestcomplete = 1
			return
		}

		this.__pass_fail_error[n, 2] = this.__pass_fail_error[n, 2] + 1

		this.__msgs[n] = &(
			*this.__msgs[n] \
			(
				&sprintf("subtest %f: assert()", this.__subtestnum) \
				&"    assertion failed"
			)
		)
		this.__subtestcomplete = 1
	}

	real scalar testcase::__capture(pointer(function) scalar func_ptr,
	                                pointer matrix arg_ptrs)
	{
		real scalar rc, i, nargs
		string scalar run_str, arg_template

		this.__func_ptr = func_ptr
		this.__arg_ptrs = arg_ptrs

		run_str = "(*" + this.name + ".__func_ptr)("
		arg_template = sprintf("*(%s.__arg_ptrs[%%f])%%s", this.name)

		nargs = length(arg_ptrs)
		for (i = 1; i <= nargs; i++) {
			run_str = run_str + sprintf(arg_template, i, i == nargs ? "" : ",")
		}
		run_str = run_str + ")"

		if (this.noisy_assert) {
			stata("capture noisily mata: " + run_str)
		}
		else {
			stata("capture mata: " + run_str)
		}
		stata("local __testcase_rc = _rc")
		rc = strtoreal(st_local("__testcase_rc"))

		// remove references
		this.__func_ptr = NULL
		this.__arg_ptrs = J(0,0,NULL)

		return(rc)
	}

	void testcase::assert_error(transmorphic err_num,
	                            transmorphic func_ptr,
	                            | transmorphic arg_ptrs)
	{
		real scalar n, rc, err_good, func_good, arg_good
		pointer(string) scalar msg_ptr

		msg_ptr = NULL

		n = this.__testnum
		this.__subtestnum = this.__subtestnum + 1
		this.__subtestcomplete = 0

		err_good = (eltype(err_num) == "real" & orgtype(err_num) == "scalar")
		func_good = (eltype(func_ptr) == "pointer" & orgtype(func_ptr) == "scalar")

		arg_good = 1
		if (args() == 3) {
			// Check dimensions of arg_ptrs. It is allowed to be a vector, or
			// a zero-dimensional matrix if wanting to pass zero arguments.
			// I.e., J(0,0,.), J(0,1,.) J(123,0,.) are all allowed.
			if (rows(arg_ptrs) > 1 & cols(arg_ptrs) > 1) {
				arg_good = 0
			}
			else if (rows(arg_ptrs) != 0 & cols(arg_ptrs) !=0 &
					eltype(arg_ptrs) != "pointer") {
				arg_good = 0
			}
		}
		else {
			arg_ptrs = J(0, 0, NULL)
		}

		if (!err_good | !func_good | !arg_good) {
			this.__pass_fail_error[n, 3] = this.__pass_fail_error[n, 3] + 1
			this.__msgs[n] = &(
				*this.__msgs[n] \
				&sprintf("subtest %f: assert_error()", this.__subtestnum)
			)
			if (!err_good) {
				this.__msgs[n] = &(
					*this.__msgs[n] \
					&sprintf(
						"    error: 1st input was %s %s, should be real scalar",
						eltype(err_num),
						orgtype(err_num)
					)
				)
			}
			if (!func_good) {
				this.__msgs[n] = &(
					*this.__msgs[n] \
					&sprintf(
						"    error: 2nd input was %s %s, " +
						"should be pointer scalar",
						eltype(func_ptr),
						orgtype(func_ptr)
					)
				)
			}
			if (!arg_good) {
				if (rows(arg_ptrs) > 1 & cols(arg_ptrs) > 1) {
					this.__msgs[n] = &(
						*this.__msgs[n] \
						&sprintf(
							"    error: 3rd input is %fx%f, " +
							"should be vector or zero-dim matrix",
							rows(arg_ptrs),
							cols(arg_ptrs)
						)
					)
				}
				else {
					this.__msgs[n] = &(
						*this.__msgs[n] \
						&sprintf(
							"    error: 3rd input was %s matrix, " +
							"should be pointer matrix",
							eltype(arg_ptrs)
						)
					)
				}
			}
			this.__subtestcomplete = 1
			return
		}

		rc = this.__capture(func_ptr, arg_ptrs)

		if (rc == err_num | (missing(err_num) & rc != 0)) {
			this.__pass_fail_error[n, 1] = this.__pass_fail_error[n, 1] + 1
			this.__subtestcomplete = 1
			return
		}

		this.__pass_fail_error[n, 2] = this.__pass_fail_error[n, 2] + 1
		this.__msgs[n] = &(
			*this.__msgs[n] \
			&sprintf("subtest %f: assert_error()", this.__subtestnum)
		)
		if (rc == 0) {
			msg_ptr = &sprintf(
				"    no error found when expecting error %f", err_num
			)
		}
		else {
			msg_ptr = &sprintf(
				"    got error %f when expecting error %f", rc, err_num
			)
		}
		this.__msgs[n] = &(*this.__msgs[n] \ msg_ptr)
		this.__subtestcomplete = 1
	}

	real scalar testcase::__method_capture(string scalar class_name,
	                                       string scalar func_name,
	                                       pointer matrix arg_ptrs)
	{
		real scalar rc, i, nargs
		string scalar run_str, arg_template

		this.__arg_ptrs = arg_ptrs

		run_str = class_name + "." + func_name + "("
		arg_template = sprintf("*(%s.__arg_ptrs[%%f])%%s", this.name)

		nargs = length(arg_ptrs)
		for (i = 1; i <= nargs; i++) {
			run_str = run_str + sprintf(arg_template, i, i == nargs ? "" : ",")
		}
		run_str = run_str + ")"

		if (this.noisy_assert) {
			stata("capture noisily mata: " + run_str)
		}
		else {
			stata("capture mata: " + run_str)
		}
		stata("local __testcase_rc = _rc")
		rc = strtoreal(st_local("__testcase_rc"))

		// remove references
		this.__arg_ptrs = J(0,0,NULL)

		return(rc)
	}

	void testcase::assert_method_error(transmorphic err_num,
	                                   transmorphic class_name,
	                                   transmorphic func_name,
	                                   | transmorphic arg_ptrs)
	{
		real scalar n, rc, err_good, class_good, func_good, arg_good
		pointer(string) scalar msg_ptr

		msg_ptr = NULL

		n = this.__testnum
		this.__subtestnum = this.__subtestnum + 1
		this.__subtestcomplete = 0

		err_good = (eltype(err_num) == "real" & orgtype(err_num) == "scalar")
		class_good = (eltype(class_name) == "string" & orgtype(class_name) == "scalar")
		func_good = (eltype(func_name) == "string" & orgtype(func_name) == "scalar")

		arg_good = 1
		if (args() == 4) {
			// Check dimensions of arg_ptrs. It is allowed to be a vector, or
			// a zero-dimensional matrix if wanting to pass zero arguments.
			// I.e., J(0,0,.), J(0,1,.) J(123,0,.) are all allowed.
			if (rows(arg_ptrs) > 1 & cols(arg_ptrs) > 1) {
				arg_good = 0
			}
			else if (rows(arg_ptrs) != 0 & cols(arg_ptrs) !=0 &
					eltype(arg_ptrs) != "pointer") {
				arg_good = 0
			}
		}
		else {
			arg_ptrs = J(0, 0, NULL)
		}

		if (!err_good | !class_good | !func_good | !arg_good) {
			this.__pass_fail_error[n, 3] = this.__pass_fail_error[n, 3] + 1
			this.__msgs[n] = &(
				*this.__msgs[n] \
				&sprintf("subtest %f: assert_method_error()", this.__subtestnum)
			)
			if (!err_good) {
				this.__msgs[n] = &(
					*this.__msgs[n] \
					&sprintf(
						"    error: 1st input was %s %s, should be real scalar",
						eltype(err_num),
						orgtype(err_num)
					)
				)
			}
			if (!class_good) {
				this.__msgs[n] = &(
					*this.__msgs[n] \
					&sprintf(
						"    error: 2nd input was %s %s, should be string scalar",
						eltype(class_name),
						orgtype(class_name)
					)
				)
			}
			if (!func_good) {
				this.__msgs[n] = &(
					*this.__msgs[n] \
					&sprintf(
						"    error: 3rd input was %s %s, should be string scalar",
						eltype(func_name),
						orgtype(func_name)
					)
				)
			}
			if (!arg_good) {
				if (rows(arg_ptrs) > 1 & cols(arg_ptrs) > 1) {
					this.__msgs[n] = &(
						*this.__msgs[n] \
						&sprintf(
							"    error: 4th input is %fx%f," +
							"should be vector or zero-dim matrix",
							rows(arg_ptrs),
							cols(arg_ptrs)
						)
					)
				}
				else {
					this.__msgs[n] = &(
						*this.__msgs[n] \
						&sprintf(
							"    error: 4th input was %s matrix," +
							"should be pointer matrix",
							orgtype(arg_ptrs)
						)
					)
				}
			}
			this.__subtestcomplete = 1
			return
		}

		rc = this.__method_capture(class_name, func_name, arg_ptrs)

		if (rc == err_num | (missing(err_num) & rc != 0)) {
			this.__pass_fail_error[n, 1] = this.__pass_fail_error[n, 1] + 1
			this.__subtestcomplete = 1
			return
		}

		this.__pass_fail_error[n, 2] = this.__pass_fail_error[n, 2] + 1
		this.__msgs[n] = &(
			*this.__msgs[n] \
			&sprintf(
				"subtest %f: assert_method_error()", this.__subtestnum
			)
		)
		if (rc == 0) {
			msg_ptr = &sprintf(
				"    no error found when expecting error %f", err_num
			)
		}
		else {
			msg_ptr = &sprintf(
				"    got error %f when expecting error %f", rc, err_num
			)
		}
		this.__msgs[n] = &(*this.__msgs[n] \ msg_ptr)
		this.__subtestcomplete = 1
	}

	void testcase::__contents_failures(
		string scalar which, | string scalar type1, string scalar type2
	)
	{
		real scalar n

		n = this.__testnum
		this.__pass_fail_error[n, 2] = this.__pass_fail_error[n, 2] + 1
		if (which == "difftypes") {
			this.__msgs[n] = &(
				*this.__msgs[n] \
				(
					&sprintf(
						"subtest %f: assert_equal_contents()",
						this.__subtestnum
					) \
					&"    contents are incomparable" \
					&(
						"    > element types differ: " +
						sprintf("%s vs %s", type1, type2)
					)
				)
			)
		}
		else if (which == "diffnum") {
			this.__msgs[n] = &(
				*this.__msgs[n] \
				(
					&sprintf(
						"subtest %f: assert_equal_contents()",
						this.__subtestnum
					) \
					&"    inputs have different numbers of distinct elements"
				)
			)
		}
		else if (which == "diffels") {
			this.__msgs[n] = &(
				*this.__msgs[n] \
				(
					&sprintf(
						"subtest %f: assert_equal_contents()",
						this.__subtestnum
					) \
					&"    inputs have elements not in common"
				)
			)
		}
		else if (which == "diffcounts") {
			this.__msgs[n] = &(
				*this.__msgs[n] \
				(
					&sprintf(
						"subtest %f: assert_equal_contents()",
						this.__subtestnum
					) \
					&"    counts differ"
				)
			)
		}
		else {
			this.__msgs[n] = &(
				*this.__msgs[n] \
				(
					&sprintf(
						"subtest %f: assert_equal_contents()",
						this.__subtestnum
					) \
					&("    unknown reason: " + which)
				)
			)
		}
	}

	void testcase::__equal_struct_contents(
		transmorphic a, transmorphic b, real scalar samecount
	)
	{
		transmorphic items, item
		real matrix counts
		real scalar nitems, i, j, k, found

		counts = J(1,2,0)

		items = a[1,1]
		nitems = 1

		for (i = 1; i <= rows(a); i++) {
			for (j = 1; j <= cols(a); j++) {
				item = a[i,j]
				found = 0
				for (k = 1; k <= nitems; k++) {
					if (item == items[k]) {
						found = 1
						counts[k,1] = counts[k,1] + 1
					}
				}
				if (!found) {
					items = items \ item
					counts = counts \ (1, 0)
					nitems = nitems + 1
				}
			}
		}

		for (i = 1; i <= rows(b); i++) {
			for (j = 1; j <= cols(b); j++) {
				item = b[i,j]
				found = 0
				for (k = 1; k <= nitems; k++) {
					if (item == items[k]) {
						found = 1
						counts[k,2] = counts[k,2] + 1
						if (samecount & counts[k,2] > counts[k,1]) {
							this.__contents_failures("diffcounts")
							return
						}
					}
				}
				if (!found) {
					this.__contents_failures("diffels")
					return
				}
			}
		}

		if (samecount & counts[.,1] != counts[.,2]) {
			this.__contents_failures("diffcounts")
			return
		}
	}

	void testcase::assert_equal_contents(
		transmorphic a, transmorphic b, | real scalar samecount
	)
	{
		transmorphic val, acounter, bcounter, loc, key
		real scalar n, i, j, acols, bcols
		string scalar atype, btype

		n = this.__testnum
		this.__subtestnum = this.__subtestnum + 1
		this.__subtestcomplete = 0

		if (args() == 2) samecount = 1

		acols = cols(a)
		bcols = cols(b)
		if (samecount & rows(a) * acols != rows(b) * bcols) {
			this.__pass_fail_error[n, 2] = this.__pass_fail_error[n, 2] + 1

			this.__msgs[n] = &(
				*this.__msgs[n] \
				(
					&sprintf(
						"subtest %f: assert_equal_contents()", this.__subtestnum
					) \
					&"    counts differ; (rows(a) * cols(a)) != (rows(b) * cols(b))"
				)
			)
			this.__subtestcomplete = 1
			return
		}

		atype = eltype(a)
		btype = eltype(b)
		if (atype == "class" | btype == "class") {
			this.__pass_fail_error[n, 3] = this.__pass_fail_error[n, 3] + 1

			this.__msgs[n] = &(
				*this.__msgs[n] \
				(
					&sprintf(
						"subtest %f: assert_equal_contents()", this.__subtestnum
					) \
					&"    error: classes not allowed"
				)
			)
			this.__subtestcomplete = 1
			return
		}

		if (atype == "struct" | btype == "struct") {
			if (atype != btype) {
				this.__contents_failures("difftypes", atype, btype)
			}
			else {
				this.__equal_struct_contents(a, b, samecount)
			}
			this.__subtestcomplete = 1
			return
		}

		if (atype != btype) {
			if (atype == "string" | btype == "string" |
					atype == "pointer" | btype == "pointer") {
				this.__contents_failures("difftypes", atype, btype)
				this.__subtestcomplete = 1
				return
			}

			// else one of a,b is real and the other is complex
			if (!isrealvalues(a) | !isrealvalues(b)) {
				this.__contents_failures("difftypes", atype, btype)
				this.__subtestcomplete = 1
				return
			}
			else {
				// Both are actually real. Need to make both eltype's
				// real so asarray keys types will be same.
				a = Re(a)
				b = Re(b)
				atype = "real"
				btype = "real"
			}
		}

		acounter = asarray_create(atype)
		bcounter = asarray_create(btype)
		asarray_notfound(acounter, 0)
		asarray_notfound(bcounter, 0)

		for (i = 1; i <= rows(a); i++) {
			for (j = 1; j <= acols; j++) {
				val = a[i,j]
				asarray(acounter, val, 1 + asarray(acounter, val))
			}
		}

		for (i = 1; i <= rows(b); i++) {
			for (j = 1; j <= bcols; j++) {
				val = b[i,j]
				asarray(bcounter, val, 1 + asarray(bcounter, val))
			}
		}

		if (length(asarray_keys(acounter)) != length(asarray_keys(bcounter))) {
			this.__contents_failures("diffnum")
			this.__subtestcomplete = 1
			return
		}

		if (samecount) {
			loc = asarray_first(acounter)
			while (loc != NULL) {
				key = asarray_key(acounter, loc)
				if (asarray(acounter, key) != asarray(bcounter, key)) {
					if (asarray(bcounter, key) == 0) {
						this.__contents_failures("diffels")
					}
					else {
						this.__contents_failures("diffcounts")
					}
					this.__subtestcomplete = 1
					return
				}
				loc = asarray_next(acounter, loc)
			}
		} else {
			loc = asarray_first(acounter)
			while (loc != NULL) {
				key = asarray_key(acounter, loc)
				if (asarray(bcounter, key) == 0) {
					this.__contents_failures("diffels")
					this.__subtestcomplete = 1
					return
				}
				loc = asarray_next(acounter, loc)
			}
		}
	}

	void testcase::assert_equal(transmorphic a, transmorphic b)
	{
		real scalar n
		string scalar eltype1, eltype2, orgtype1, orgtype2

		n = this.__testnum
		this.__subtestnum = this.__subtestnum + 1
		this.__subtestcomplete = 0

		if (a == b) {
			this.__pass_fail_error[n, 1] = this.__pass_fail_error[n, 1] + 1
			this.__subtestcomplete = 1
			return
		}

		this.__pass_fail_error[n, 2] = this.__pass_fail_error[n, 2] + 1

		this.__msgs[n] = &(
			*this.__msgs[n] \
			(
				&sprintf("subtest %f: assert_equal()", this.__subtestnum) \
				&"    values are not equal"
			)
		)

		eltype1 = eltype(a)
		eltype2 = eltype(b)
		orgtype1 = orgtype(a)
		orgtype2 = orgtype(b)

		if (eltype1 != eltype2) {
			this.__msgs[n] = &(
				*this.__msgs[n] \
				&(
					"    > element types differ: " +
					sprintf("%s vs %s", eltype1, eltype2)
				)
			)
		}
		if (orgtype1 != orgtype2) {
			this.__msgs[n] = &(
				*this.__msgs[n] \
				&(
					"    > org. types differ: " +
					sprintf("%s vs %s", orgtype1, orgtype2)
				)
			)
		}
		this.__subtestcomplete = 1
	}

	void testcase::assert_unequal(transmorphic a, transmorphic b)
	{
		real scalar n

		n = this.__testnum
		this.__subtestnum = this.__subtestnum + 1
		this.__subtestcomplete = 0

		if (a != b) {
			this.__pass_fail_error[n, 1] = this.__pass_fail_error[n, 1] + 1
			this.__subtestcomplete = 1
			return
		}

		this.__pass_fail_error[n, 2] = this.__pass_fail_error[n, 2] + 1

		this.__msgs[n] = &(
			*this.__msgs[n] \
			(
				&sprintf("subtest %f: assert_unequal()", this.__subtestnum) \
				&"    values are equal"
			)
		)
		this.__subtestcomplete = 1
	}

	void testcase::assert_all(transmorphic a)
	{
		real scalar n, nrows, ncols, i, j

		n = this.__testnum
		this.__subtestnum = this.__subtestnum + 1
		this.__subtestcomplete = 0

		if (eltype(a) != "real") {
			this.__pass_fail_error[n, 3] = this.__pass_fail_error[n, 3] + 1

			this.__msgs[n] = &(
				*this.__msgs[n] \
				(
					&sprintf("subtest %f: assert_all()", this.__subtestnum) \
					&sprintf(
						"    error: input was %s, should be real",
						eltype(a)
					)
				)
			)

			this.__subtestcomplete = 1
			return
		}

		nrows = rows(a)
		ncols = cols(a)

		for (i = 1; i <= nrows; i++) {
			for (j = 1; j <= ncols; j++) {
				if (!a[i,j]) {
					this.__pass_fail_error[n, 2] = this.__pass_fail_error[n, 2] + 1
					this.__msgs[n] = &(
						*this.__msgs[n] \
						(
							&sprintf(
								"subtest %f: assert_all()", this.__subtestnum
							) \
							&sprintf(
								"    first false entry in %f,%f", i, j
							)
						)
					)
					this.__subtestcomplete = 1
					return
				}
			}
		}

		this.__pass_fail_error[n,1] = this.__pass_fail_error[n, 1] + 1
		this.__subtestcomplete = 1
	}

	void testcase::assert_any(transmorphic a)
	{
		real scalar n, nrows, ncols, i, j
		string scalar inputtype

		n = this.__testnum
		this.__subtestnum = this.__subtestnum + 1
		this.__subtestcomplete = 0

		inputtype = eltype(a)
		if (inputtype != "real") {
			this.__pass_fail_error[n, 3] = this.__pass_fail_error[n, 3] + 1

			this.__msgs[n] = &(
				*this.__msgs[n] \
				(
					&sprintf("subtest %f: assert_any()", this.__subtestnum) \
					&sprintf(
						"    error: input was %s, should be real", inputtype
					)
				)
			)

			this.__subtestcomplete = 1
			return
		}

		nrows = rows(a)
		ncols = cols(a)

		for (i = 1; i <= nrows; i++) {
			for (j = 1; j <= ncols; j++) {
				if (a[i,j]) {
					this.__pass_fail_error[n,1] = this.__pass_fail_error[n,1] + 1
					this.__subtestcomplete = 1
					return
				}
			}
		}

		this.__pass_fail_error[n,2] = this.__pass_fail_error[n, 2] + 1
		this.__msgs[n] = &(
			*this.__msgs[n] \
			(
				&sprintf("subtest %f: assert_any()", this.__subtestnum) \
				&"    all entries evaluate to false"
			)
		)
		this.__subtestcomplete = 1
	}

	void testcase::assert_close(transmorphic a, transmorphic b,
	                            | transmorphic tolerance)
	{
		real scalar n, nrows, ncols, i, j, a_numeric, b_numeric, tol_good
		string scalar atype, btype

		n = this.__testnum
		this.__subtestnum = this.__subtestnum + 1
		this.__subtestcomplete = 0

		atype = eltype(a)
		btype = eltype(b)
		a_numeric = (atype == "real" | atype == "complex")
		b_numeric = (btype == "real" | btype == "complex")

		if (args() == 3) {
			tol_good = (
				eltype(tolerance) == "real" & orgtype(tolerance) == "scalar"
			)
		}
		else {
			tolerance = 1e-12
			tol_good = 1
		}

		if (!a_numeric | !b_numeric | !tol_good) {
			this.__pass_fail_error[n, 3] = this.__pass_fail_error[n, 3] + 1
			this.__msgs[n] = &(
				*this.__msgs[n] \
				&sprintf("subtest %f: assert_close()", this.__subtestnum)
			)
			if (!a_numeric) {
				this.__msgs[n] = &(
					*this.__msgs[n] \
					&sprintf(
						"    error: 1st input was %s, should be numeric", atype
					)
				)
			}
			if (!b_numeric) {
				this.__msgs[n] = &(
					*this.__msgs[n] \
					&sprintf(
						"    error: 2nd input was %s, should be numeric", btype
					)
				)
			}
			if (!tol_good) {
				this.__msgs[n] = &(
					*this.__msgs[n] \
					&sprintf(
						"    error: 3rd input was %s %s, should be real scalar",
						eltype(tolerance),
						orgtype(tolerance)
					)
				)
			}
			this.__subtestcomplete = 1
			return
		}

		nrows = rows(a)
		ncols = cols(b)

		// Check dimensions. If both are vectors, or zero-dim,
		// but one's dims is just transpose of other, flip b.
		// Otherwise, if dims don't match, test fails.
		if (rows(b) != nrows | cols(b) != ncols) {
			this.__pass_fail_error[n, 2] = this.__pass_fail_error[n, 2] + 1
			this.__msgs[n] = &(
				*this.__msgs[n] \
				(
					&sprintf(
						"subtest %f: assert_close()", this.__subtestnum
					) \
					&sprintf(
						"    dimensions don't match: %fx%f vs %fx%f",
						nrows,
						ncols,
						rows(b),
						cols(b)
					)
				)
			)
			this.__subtestcomplete = 1
			return
		}

		if (mreldif(a, b) <= tolerance) {
			this.__pass_fail_error[n, 1] = this.__pass_fail_error[n, 1] + 1
			this.__subtestcomplete = 1
			return
		}

		this.__pass_fail_error[n, 2] = this.__pass_fail_error[n, 2] + 1

		// find first failure
		for (i = 1; i <= nrows; i++) {
			for (j = 1; j <= ncols; j++) {
				if (reldif(a[i,j], b[i,j]) > tolerance) {
					this.__msgs[n] = &(
						*this.__msgs[n] \
						(
							&sprintf(
								"subtest %f: assert_close()",
								this.__subtestnum
							) \
							&sprintf(
								"    first not-close entry in %f,%f",
								i,
								j
							)
						)
					)
					this.__subtestcomplete = 1
					return
				}
			}
		}
		this.__subtestcomplete = 1
	}

	void testcase::setup()
	{
		return
	}

	void testcase::setup_once()
	{
		return
	}

	void testcase::teardown()
	{
		return
	}

	void testcase::teardown_once()
	{
		return
	}
end