#include <algorithm>
#include <array>
#include <iostream>
#include <string>
#include <vector>
using namespace std;

static struct{
	const string name;
	bool isFloat;
	bool hasComparison;
} basicTypes[] = {
	// { "char"                  , false, true , },
	// { "signed char"           , false, true , },
	// { "unsigned char"         , false, true , },
	{ "signed short"          , false, true , },
	{ "unsigned short"        , false, true , },
	{ "signed int"            , false, true , },
	{ "unsigned int"          , false, true , },
	{ "signed long int"       , false, true , },
	{ "unsigned long int"     , false, true , },
	{ "signed long long int"  , false, true , },
	{ "unsigned long long int", false, true , },
	{ "float"                 , true , true , },
	{ "double"                , true , true , },
	{ "long double"           , true , true , },
	{ "float _Complex"        , true , false, },
	{ "double _Complex"       , true , false, },
	{ "long double _Complex"  , true , false, },
#if defined(__SIZEOF_INT128__)
	{ "__int128"              , false, true , },
	{ "unsigned __int128"     , false, true , },
#endif
#if defined(__i386__) || defined(__ia64__) || defined(__x86_64__)
	{ "__float80"             , true , true , },
	{ "_Float128"             , true , true , },
#endif
};

struct {
	const string name;
	bool assignment = false;
	bool floatCompat = true;
	bool isComparison = false;
	bool isEqual = false;
} arithmeticOperators[] = {
	{ "?++"  , true , true, false, false },
	{ "?--"  , true , true, false, false },
	{ "++?"  , true , true, false, false },
	{ "--?"  , true , true, false, false },
	{ "+?"   , false, true , false, false },
	{ "-?"   , false, true , false, false },
	{ "~?"   , false, false, false, false },
	{ "!?"   , false, true , false, true  },
	{ "?*?"  , false, true , false, false },
	{ "?/?"  , false, true , false, false },
	{ "?%?"  , false, false, false, false },
	{ "?+?"  , false, true , false, false },
	{ "?-?"  , false, true , false, false },
	{ "?<<?" , false, false, false, false },
	{ "?>>?" , false, false, false, false },
	{ "?<?"  , false, true , true , false },
	{ "?<=?" , false, true , true , true  },
	{ "?>?"  , false, true , true , false },
	{ "?>=?" , false, true , true , true  },
	{ "?==?" , false, true , false, true  },
	{ "?!=?" , false, true , false, true  },
	{ "?&?"  , false, false, false, false },
	{ "?^?"  , false, false, false, false },
	{ "?|?"  , false, false, false, false },
	{ "?=?"  , true , true , false, false },
	{ "?+=?" , true , true , false, false },
	{ "?-=?" , true , true , false, false },
	{ "?*=?" , true , true , false, false },
	{ "?/=?" , true , true , false, false },
	{ "?%=?" , true , false, false, false },
	{ "?<<=?", true , false, false, false },
	{ "?>>=?", true , false, false, false },
	{ "?&=?" , true , false, false, false },
	{ "?|=?" , true , false, false, false },
	{ "?^=?" , true , false, false, false },
};

enum ArgType { Normal, PtrDiff, CommPtrDiff };

struct {
	const string name;
	bool assignment = false;
	string diffReturn;
	ArgType diffArg2 = Normal;
	string sized;
} pointerOperators[] = {
	{ "?++", true, "", Normal, " | sized(DT)" },
	{ "?--", true, "", Normal, " | sized(DT)" },
	{ "++?", true, "", Normal, " | sized(DT)" },
	{ "--?", true, "", Normal, " | sized(DT)" },
	{ "!?" , false, "int", Normal, "" },
	{ "?<?", false, "signed int", Normal, "" },
	{ "?<=?", false, "signed int", Normal, "" },
	{ "?>?", false, "signed int", Normal, "" },
	{ "?>=?", false, "signed int", Normal, "" },
	{ "?==?", false, "signed int", Normal, "" },
	{ "?!=?", false, "signed int", Normal, "" },
	{ "?=?", true, "", Normal, "" }, // void * LHS, zero_t RHS ???
	{ "*?", false, "&", Normal, " | sized(DT)" }, // & ???

	{ "?-?", false, "ptrdiff_t", Normal, " | sized(DT)" },
	{ "?-?", false, "", PtrDiff, " | sized(DT)" },
	{ "?-=?", true, "", PtrDiff, " | sized(DT)" },

	{ "?+?", false, "", CommPtrDiff, " | sized(DT)" },
	{ "?[?]", false, "&", CommPtrDiff, " | sized(DT)" }, // & ???
	{ "?+=?" , true, "", PtrDiff, " | sized(DT)" },
};

template<size_t N>
string mask2string(unsigned int mask, array<string, N> names) {
	string result = "";
	int i = 0;
	for(auto name : names) {
		if(mask & (1 << i)) {
			result += name;
		}
		i++;
	}
	return result;
}

template <typename... T>
constexpr auto make_array(T&&... values) ->
    std::array<
        typename std::decay<typename std::common_type<T...>::type>::type,
        sizeof...(T)>
{
    return std::array<
        typename std::decay<
            typename std::common_type<T...>::type>::type,
        sizeof...(T)>{{std::forward<T>(values)...}};
}

int main() {
	cout << "# 2 \"prelude.cf\"  // needed for error messages from this file" << endl;
	cout << "trait sized(dtype T) {};" << endl;

	cout << "//////////////////////////" << endl;
	cout << "// Arithmetic Operators //" << endl;
	cout << "//////////////////////////" << endl;
	cout << endl;

	cout << "void	?{}( zero_t & );" << endl;
	cout << "void	?{}( one_t & );" << endl;
	cout << "void	?{}( zero_t &, zero_t );" << endl;
	cout << "void	?{}( one_t &, one_t );" << endl;
	cout << "void	^?{}( zero_t & );" << endl;
	cout << "void	^?{}( one_t & );" << endl;
	cout << "zero_t			?=?( zero_t &, zero_t );" << endl;
	cout << "one_t			?=?( one_t &, one_t );" << endl;
	cout << "signed int ?==?( zero_t, zero_t ),							?!=?( zero_t, zero_t );" << endl;
	cout << "signed int ?==?( one_t, one_t ),							?!=?( one_t, one_t );" << endl;

	cout << "signed int ?==?( _Bool, _Bool ),							?!=?( _Bool, _Bool );" << endl;
	cout << "void	?{}( _Bool & );" << endl;
	cout << "void	?{}( _Bool &, _Bool );" << endl;
	cout << "void	^?{}( _Bool & );" << endl;
	cout << "_Bool			?=?( _Bool &, _Bool ),					?=?( volatile _Bool &, _Bool );" << endl;
	cout << "signed int	!?( _Bool );" << endl;

	cout << "void	^?{}( char & );" << endl;
	cout << "void	^?{}( char unsigned & );" << endl;
	cout << "void	^?{}( char signed & );" << endl;
	cout << "void	?{}( char &, char );" << endl;
	cout << "void	?{}( unsigned char &, unsigned char );" << endl;
	cout << "void	?{}( char signed &, char signed );" << endl;
	cout << "void	?{}( char & );" << endl;
	cout << "void	?{}( unsigned char & );" << endl;
	cout << "void	?{}( char signed & );" << endl;
	cout << "char			?=?( char &, char ),					?=?( volatile char &, char );" << endl;
	cout << "char signed		?=?( char signed &, char signed ),			?=?( volatile char signed &, char signed );" << endl;
	cout << "char unsigned		?=?( char unsigned &, char unsigned ),			?=?( volatile char unsigned &, char unsigned );" << endl;


	for (auto op : arithmeticOperators) {
		for (auto type : basicTypes ) {
			auto operands = count(op.name.begin(), op.name.end(), '?');
			if (! op.floatCompat && type.isFloat) continue;
			if (op.isComparison && ! type.hasComparison) continue;
			if (op.assignment) {
				const char * qualifiers[] = { "", "volatile " };
				for (auto q : qualifiers){
					cout << type.name << " " << op.name << "(";
					cout << q << type.name << " &";
					for (int i = 1; i < operands; ++i) {
						cout << ", " << type.name;
					}
					cout << ");" << endl;
				}
			} else {
				if (op.isComparison || op.isEqual) cout << "signed int";
				else cout << type.name;
				cout << " " << op.name << "(";
				for (int i = 0; i < operands; ++i) {
					cout << type.name;
					if ((i+1) != operands) cout << ", ";
				}
				cout << ");" << endl;
			}
		}
		cout << endl;
	}
	cout << endl;

	cout << "/////////////////////////////" << endl;
	cout << "// Arithmetic Constructors //" << endl;
	cout << "/////////////////////////////" << endl;
	for (auto type : basicTypes) {
		cout << "void  ?{}(" << type.name << " &);" << endl;
		cout << "void  ?{}(" << type.name << " &, " << type.name << ");" << endl;
		cout << "void ^?{}(" << type.name << " &);" << endl;
		cout << endl;
	}
	cout << endl;

	cout << "//////////////////////////" << endl;
	cout << "// Pointer Constructors //" << endl;
	cout << "//////////////////////////" << endl;
	cout << "forall(ftype FT) void  ?{}( FT *&, FT * );" << endl;
	cout << "forall(ftype FT) void  ?{}( FT * volatile &, FT * );" << endl;

	// generate qualifiers for first and second parameters of copy constructors
	vector<pair<const string, const string>> qualifiersPair;
	const unsigned int NQ = 2;
	for(unsigned int lhs = 0; lhs < (1<<NQ); lhs++) {
		for(unsigned int rhs = 0; rhs < (1<<NQ); rhs++) {
			if((lhs & rhs) == rhs) {
				qualifiersPair.push_back({
					mask2string(lhs, make_array("const "s, "volatile "s)),
					mask2string(rhs, make_array("const "s, "volatile "s))
				});
			}
		}
	}

	for (auto type : { "DT", "void" }) {
		for (auto q : qualifiersPair) {
			cout << "forall(dtype DT) void  ?{}(" << q.first << type << " *&, " << q.second << "DT *);" << endl;
		}
	}


	// generate qualifiers for parameter of default constructor and destructor
	vector<string> qualifiersSingle;
	for (unsigned int lhs = 0; lhs < (1<<NQ); lhs++) {
		qualifiersSingle.push_back(mask2string(lhs, make_array("const "s, "volatile "s)));
	}

	for (auto type : { "DT", "void" }) {
		for (auto q : qualifiersSingle) {
			cout << "forall(dtype DT) void  ?{}(" << q << type << " *&);" << endl;
			cout << "forall(dtype DT) void ^?{}(" << q << type << " *&);" << endl;
		}
	}
	cout << endl;

	cout << "forall(dtype DT) void ?{}(		    DT *	  &, zero_t );" << endl;
	cout << "forall(dtype DT) void ?{}(		    DT * volatile &, zero_t );" << endl;
	cout << "forall(dtype DT) void ?{}( const	    DT *	  &, zero_t );" << endl;
	cout << "forall(dtype DT) void ?{}( volatile	    DT *	  &, zero_t );" << endl;
	cout << "forall(dtype DT) void ?{}( volatile	    DT * volatile &, zero_t );" << endl;
	cout << "forall(dtype DT) void ?{}( const volatile DT *	  &, zero_t );" << endl;
	cout << "forall(ftype FT) void	?{}( FT *	   &, zero_t );	" << endl;
	cout << "forall( ftype FT ) void	?{}( FT *	   & );" << endl;
	cout << "forall( ftype FT ) void	^?{}( FT *	   & );" << endl;
	cout << endl;

	cout << "///////////////////////" << endl;
	cout << "// Pointer Operators //" << endl;
	cout << "///////////////////////" << endl;

	cout << "forall( ftype FT ) FT *			?=?( FT *&, FT * );" << endl;
	cout << "forall( ftype FT ) FT *			?=?( FT * volatile &, FT * );" << endl;
	cout << "forall( ftype FT ) int !?( FT * );" << endl;
	cout << "forall( ftype FT ) signed int ?==?( FT *, FT * );" << endl;
	cout << "forall( ftype FT ) signed int ?!=?( FT *, FT * );" << endl;
	cout << "forall( ftype FT ) FT &		 *?( FT * );" << endl;


	cout << "forall( dtype DT ) void *		 ?=?(		     void *	     &,			DT * );" << endl;
	cout << "forall( dtype DT ) void *		 ?=?(		     void * volatile &,			DT * );" << endl;
	cout << "forall( dtype DT ) const void *		 ?=?( const	     void *	     &,			DT * );" << endl;
	cout << "forall( dtype DT ) const void *		 ?=?( const	     void * volatile &,			DT * );" << endl;
	cout << "forall( dtype DT ) const void *		 ?=?( const	     void *	     &, const		DT * );" << endl;
	cout << "forall( dtype DT ) const void *		 ?=?( const	     void * volatile &, const		DT * );" << endl;
	cout << "forall( dtype DT ) volatile void *	 ?=?(	    volatile void *	     &,			DT * );" << endl;
	cout << "forall( dtype DT ) volatile void *	 ?=?(	    volatile void * volatile &,			DT * );" << endl;
	cout << "forall( dtype DT ) volatile void *	 ?=?(	    volatile void *	     &,	      volatile	DT * );" << endl;
	cout << "forall( dtype DT ) volatile void *	 ?=?(	    volatile void * volatile &,	      volatile	DT * );" << endl;
	cout << "forall( dtype DT ) const volatile void * ?=?( const volatile void *	     &,			DT * );" << endl;
	cout << "forall( dtype DT ) const volatile void * ?=?( const volatile void * volatile &,			DT * );" << endl;
	cout << "forall( dtype DT ) const volatile void * ?=?( const volatile void *	     &, const		DT * );" << endl;
	cout << "forall( dtype DT ) const volatile void * ?=?( const volatile void * volatile &, const		DT * );" << endl;
	cout << "forall( dtype DT ) const volatile void * ?=?( const volatile void *	     &,	      volatile	DT * );" << endl;
	cout << "forall( dtype DT ) const volatile void * ?=?( const volatile void * volatile &,	      volatile	DT * );" << endl;
	cout << "forall( dtype DT ) const volatile void * ?=?( const volatile void *	     &, const volatile	DT * );" << endl;
	cout << "forall( dtype DT ) const volatile void * ?=?( const volatile void * volatile &, const volatile	DT * );" << endl;

	for (auto op : pointerOperators) {
		for (auto type : { "DT"/*, "void"*/ } ) {
			auto operands = count(op.name.begin(), op.name.end(), '?');
			if (op.assignment) {
				// const char * qualifiers[] = { "", "volatile ", "const ", "const volatile " };
				switch(op.diffArg2) {
					case Normal:
						if (operands == 1) {
							for (auto q : qualifiersSingle){
								for (auto q2 : { " ", " volatile " }) {
									cout << "forall(dtype DT" << op.sized <<  ") ";
									cout << q << type << " * " << op.name << "(";
									cout << q << type << " *" << q2 << "&";
									cout << ");" << endl;
								}
							}
						} else {
							for (auto q : qualifiersPair){
								for (auto q2 : { " ", " volatile " }) {
									cout << "forall(dtype DT" << op.sized <<  ") ";
									cout << q.first << type << " * " << op.name << "(";
									cout << q.first << type << " *" << q2 << "&";

									for (int i = 1; i < operands; ++i) {
										cout << ", " << q.second << type << " *";
									}
									cout << ");" << endl;
								}
							}
						}
						break;
					case PtrDiff:
						for (auto q : qualifiersSingle){
							for (auto q2 : { " ", " volatile " }) {
								cout << "forall(dtype DT" << op.sized << ") ";
								cout << q << type << " * " << op.name << "(";
								cout << q << type << " *" << q2 << "&";

								for (int i = 1; i < operands; ++i) {
									cout << ", ptrdiff_t";
								}
								cout << ");" << endl;
							}
						}
						break;
					default:
						abort();
					}
			} else {
				switch(op.diffArg2) {
					case Normal:
						for (auto q : qualifiersSingle) {
							cout << "forall(dtype DT" << op.sized << ") ";
							if (op.diffReturn == "&") cout << q << type << " &"; // -- qualifiers
							else if (op.diffReturn != "") cout << op.diffReturn;
							else cout << q << type << " *";
							cout << " " << op.name << "(";
							for (int i = 0; i < operands; ++i) {
								cout << q << type << " *";
								if ((i+1) != operands) cout << ", ";
							}
							cout << ");" << endl;
						}
						break;
					case CommPtrDiff:
						for (auto q : qualifiersSingle) {
							cout << "forall(dtype DT" << op.sized << ") ";
							if (op.diffReturn == "&") cout << q << type << " &"; // -- qualifiers
							else if (op.diffReturn != "") cout << op.diffReturn;
							else cout << q << type << " *";
							cout << " " << op.name << "(ptrdiff_t, " << q << type << " *);" << endl;
						}
						// fallthrough
					case PtrDiff:
						for (auto q : qualifiersSingle) {
							cout << "forall(dtype DT" << op.sized << ") ";
							if (op.diffReturn == "&") cout << q << type << " &"; // -- qualifiers
							else if (op.diffReturn != "") cout << op.diffReturn;
							else cout << q << type << " *";
							cout << " " << op.name << "(" << q << type << " *, ptrdiff_t);" << endl;
						}
						break;
				}
			}
		}
		cout << endl;
	}
	cout << endl;

	cout << "forall(dtype DT) DT *			?=?(		    DT *	  &, zero_t );" << endl;
	cout << "forall(dtype DT) DT *			?=?(		    DT * volatile &, zero_t );" << endl;
	cout << "forall(dtype DT) const DT *		?=?( const	    DT *	  &, zero_t );" << endl;
	cout << "forall(dtype DT) const DT *		?=?( const	    DT * volatile &, zero_t );" << endl;
	cout << "forall(dtype DT) volatile DT *	?=?( volatile	    DT *	  &, zero_t );" << endl;
	cout << "forall(dtype DT) volatile DT *	?=?( volatile	    DT * volatile &, zero_t );" << endl;
	cout << "forall(dtype DT) const volatile DT *	?=?( const volatile DT *	  &, zero_t );" << endl;
	cout << "forall(dtype DT) const volatile DT *	?=?( const volatile DT * volatile &, zero_t );" << endl;
	cout << "forall(ftype FT) FT *			?=?( FT *	   &, zero_t );" << endl;
	cout << "forall(ftype FT) FT *			?=?( FT * volatile &, zero_t );" << endl;
}

