#ifndef _EXAM_H
#define _EXAM_H

#include <cl_number.h>
#include <cl_io.h>

// Michael Stoll  23. 3. 1993
// C++ version: Bruno Haible 1.11.1995

struct plus_test {
	const char * arg1;
	const char * arg2;
	const char * result;
};

struct minus_test {
	const char * arg1;
	const char * arg2;
	const char * result;
};

struct mul_test {
	const char * arg1;
	const char * arg2;
	const char * result;
};

struct floor_test {
	const char * arg1;
	const char * arg2;
	const char * result1;
	const char * result2;
};

struct div_test {
	const char * arg1;
	const char * arg2;
	const char * result;
};

#define num_elements(array)  (sizeof(array)/sizeof(array[0]))

#define DO_BINOP_TEST(typename,type,rtype,opname,op)  \
static int test_##typename##_##opname (void)				\
{									\
	int error = 0;							\
	for (int i = 0; i < num_elements(typename##_##opname##_tests); i++) { \
		opname##_test& test = typename##_##opname##_tests[i];	\
		type arg1 = type(test.arg1);				\
		type arg2 = type(test.arg2);				\
		rtype computed_result = arg1 op arg2;			\
		rtype result = rtype(test.result);			\
		if (computed_result != result) {			\
			fprint(cl_stderr, "Error in " #typename "_" #opname "_tests["); \
			fprintdecimal(cl_stderr, i);			\
			fprint(cl_stderr, "] !\n");			\
			fprint(cl_stderr, "Result should be: ");	\
			fprint(cl_stderr, result);			\
			fprint(cl_stderr, "\n");			\
			fprint(cl_stderr, "Result computed : ");	\
			fprint(cl_stderr, computed_result);		\
			fprint(cl_stderr, "\n");			\
			fprint(cl_stderr, "\n");			\
			error = 1;					\
		}							\
	}								\
	return error;							\
}

#define DO_FLOOR_TEST(typename,type)  \
static int test_##typename##_floor (void)				\
{									\
	int error = 0;							\
	for (int i = 0; i < num_elements(typename##_floor_tests); i++) { \
		floor_test& test = typename##_floor_tests[i];		\
		type arg1 = type(test.arg1);				\
		type arg2 = type(test.arg2);				\
		type##_div_t computed_result = floor2(arg1,arg2);	\
		cl_I result1 = cl_I(test.result1);			\
		type result2 = type(test.result2);			\
		if ((computed_result.quotient != result1) || (computed_result.remainder != result2)) { \
			fprint(cl_stderr, "Error in " #typename "_floor_tests["); \
			fprintdecimal(cl_stderr, i);			\
			fprint(cl_stderr, "] !\n");			\
			fprint(cl_stderr, "Results should be: ");	\
			fprint(cl_stderr, result1);			\
			fprint(cl_stderr, ", ");			\
			fprint(cl_stderr, result2);			\
			fprint(cl_stderr, "\n");			\
			fprint(cl_stderr, "Results computed : ");	\
			fprint(cl_stderr, computed_result.quotient);	\
			fprint(cl_stderr, ", ");			\
			fprint(cl_stderr, computed_result.remainder);	\
			fprint(cl_stderr, "\n");			\
			fprint(cl_stderr, "\n");			\
			error = 1;					\
		}							\
	}								\
	return error;							\
}

#define DO_TESTS(typename,type,qtype)  \
  DO_BINOP_TEST(typename,type,type,plus,+)				\
  DO_BINOP_TEST(typename,type,type,minus,-)				\
  DO_BINOP_TEST(typename,type,type,mul,*)				\
  DO_FLOOR_TEST(typename,type)						\
  DO_BINOP_TEST(typename,type,qtype,div,/)				\
int test_##typename (void)						\
{									\
	int error = 0;							\
	error |= test_##typename##_plus();				\
	error |= test_##typename##_minus();				\
	error |= test_##typename##_mul();				\
	error |= test_##typename##_floor();				\
	error |= test_##typename##_div();				\
	return error;							\
}

#endif /* _EXAM_H */
