#define TYPE_NIL 0
#define TYPE_INT 1
#define TYPE_FLOAT 2
#define TYPE_COMPLEX 3
#define TYPE_COLOR 4
#define TYPE_GSL_MATRIX 5
#define TYPE_V2 6
#define TYPE_V3 7
#define TYPE_M2X2 8

#define MAX_TYPE TYPE_M2X2

#define RUNTIME_VALUE_DECL int int_value; float float_value; complex float complex_value; color_t color_value; gsl_matrix * gsl_matrix_value; mm_v2_t v2_value; mm_v3_t v3_value; mm_m2x2_t m2x2_value;

#define MAKE_TYPE_C_TYPE_NAME static char* \
type_c_type_name (int type) \
{ \
switch (type) \
{ \
case TYPE_NIL : return 0; \
case TYPE_INT : return "int"; \
case TYPE_FLOAT : return "float"; \
case TYPE_COMPLEX : return "complex float"; \
case TYPE_COLOR : return "color_t"; \
case TYPE_GSL_MATRIX : return "gsl_matrix *"; \
case TYPE_V2 : return "mm_v2_t"; \
case TYPE_V3 : return "mm_v3_t"; \
case TYPE_M2X2 : return "mm_m2x2_t"; \
default : assert(0); return 0; \
} \
}

#define BUILTIN_INT_ARG(i) (g_array_index(invocation->mathmap->interpreter_values, runtime_value_t, arg_indexes[(i)]).int_value)
#define BUILTIN_FLOAT_ARG(i) (g_array_index(invocation->mathmap->interpreter_values, runtime_value_t, arg_indexes[(i)]).float_value)
#define BUILTIN_COMPLEX_ARG(i) (g_array_index(invocation->mathmap->interpreter_values, runtime_value_t, arg_indexes[(i)]).complex_value)
#define BUILTIN_COLOR_ARG(i) (g_array_index(invocation->mathmap->interpreter_values, runtime_value_t, arg_indexes[(i)]).color_value)
#define BUILTIN_GSL_MATRIX_ARG(i) (g_array_index(invocation->mathmap->interpreter_values, runtime_value_t, arg_indexes[(i)]).gsl_matrix_value)
#define BUILTIN_V2_ARG(i) (g_array_index(invocation->mathmap->interpreter_values, runtime_value_t, arg_indexes[(i)]).v2_value)
#define BUILTIN_V3_ARG(i) (g_array_index(invocation->mathmap->interpreter_values, runtime_value_t, arg_indexes[(i)]).v3_value)
#define BUILTIN_M2X2_ARG(i) (g_array_index(invocation->mathmap->interpreter_values, runtime_value_t, arg_indexes[(i)]).m2x2_value)
#define MAKE_CONST_PRIMARY_FUNCS \
MAKE_CONST_PRIMARY(int, int, TYPE_INT) \
MAKE_CONST_PRIMARY(float, float, TYPE_FLOAT) \
MAKE_CONST_PRIMARY(complex, complex float, TYPE_COMPLEX) \
MAKE_CONST_PRIMARY(color, color_t, TYPE_COLOR) \
MAKE_CONST_PRIMARY(gsl_matrix, gsl_matrix *, TYPE_GSL_MATRIX) \
MAKE_CONST_PRIMARY(v2, mm_v2_t, TYPE_V2) \
MAKE_CONST_PRIMARY(v3, mm_v3_t, TYPE_V3) \
MAKE_CONST_PRIMARY(m2x2, mm_m2x2_t, TYPE_M2X2)

#define MAKE_CONST_COMPARATOR \
case TYPE_INT : return prim1->v.constant.int_value == prim2->v.constant.int_value; \
case TYPE_FLOAT : return prim1->v.constant.float_value == prim2->v.constant.float_value; \
case TYPE_COMPLEX : return prim1->v.constant.complex_value == prim2->v.constant.complex_value; \
case TYPE_COLOR : return prim1->v.constant.color_value == prim2->v.constant.color_value; \
case TYPE_GSL_MATRIX : return prim1->v.constant.gsl_matrix_value == prim2->v.constant.gsl_matrix_value; \
case TYPE_V2 : return prim1->v.constant.v2_value.v[0] == prim2->v.constant.v2_value.v[0] && prim1->v.constant.v2_value.v[1] == prim2->v.constant.v2_value.v[1]; \
case TYPE_V3 : return prim1->v.constant.v3_value.v[0] == prim2->v.constant.v3_value.v[0] && prim1->v.constant.v3_value.v[1] == prim2->v.constant.v3_value.v[1] && prim1->v.constant.v3_value.v[2] == prim2->v.constant.v3_value.v[2]; \
case TYPE_M2X2 : return prim1->v.constant.m2x2_value.a00 == prim2->v.constant.m2x2_value.a00 && prim1->v.constant.m2x2_value.a01 == prim2->v.constant.m2x2_value.a01 && prim1->v.constant.m2x2_value.a10 == prim2->v.constant.m2x2_value.a10 && prim1->v.constant.m2x2_value.a11 == prim2->v.constant.m2x2_value.a11;

#define TYPE_DEBUG_PRINTER \
case TYPE_NIL : fputs("NIL", out); break; \
case TYPE_INT : fputs("", out); fprintf(out, "%d", primary->v.constant.int_value);  break; \
case TYPE_FLOAT : fputs("", out); { gchar buf[G_ASCII_DTOSTR_BUF_SIZE]; g_ascii_dtostr(buf, sizeof(buf), primary->v.constant.float_value); fputs(buf, out); }  break; \
case TYPE_COMPLEX : fputs("", out); { gchar buf[G_ASCII_DTOSTR_BUF_SIZE]; g_ascii_dtostr(buf, sizeof(buf), crealf(primary->v.constant.complex_value)); fputs(buf, out); } fputs(" + ", out); { gchar buf[G_ASCII_DTOSTR_BUF_SIZE]; g_ascii_dtostr(buf, sizeof(buf), cimagf(primary->v.constant.complex_value)); fputs(buf, out); } fputs(" i", out); break; \
case TYPE_COLOR : fputs("(", out); fprintf(out, "%d", RED(primary->v.constant.color_value)); fputs(",", out); fprintf(out, "%d", GREEN(primary->v.constant.color_value)); fputs(",", out); fprintf(out, "%d", BLUE(primary->v.constant.color_value)); fputs(",", out); fprintf(out, "%d", ALPHA(primary->v.constant.color_value)); fputs(")", out); break; \
case TYPE_GSL_MATRIX : fputs("***MATRIX***", out); break; \
case TYPE_V2 : fputs("[", out); { gchar buf[G_ASCII_DTOSTR_BUF_SIZE]; g_ascii_dtostr(buf, sizeof(buf), primary->v.constant.v2_value.v[0]); fputs(buf, out); } fputs(",", out); { gchar buf[G_ASCII_DTOSTR_BUF_SIZE]; g_ascii_dtostr(buf, sizeof(buf), primary->v.constant.v2_value.v[1]); fputs(buf, out); } fputs("]", out); break; \
case TYPE_V3 : fputs("[", out); { gchar buf[G_ASCII_DTOSTR_BUF_SIZE]; g_ascii_dtostr(buf, sizeof(buf), primary->v.constant.v3_value.v[0]); fputs(buf, out); } fputs(",", out); { gchar buf[G_ASCII_DTOSTR_BUF_SIZE]; g_ascii_dtostr(buf, sizeof(buf), primary->v.constant.v3_value.v[1]); fputs(buf, out); } fputs(",", out); { gchar buf[G_ASCII_DTOSTR_BUF_SIZE]; g_ascii_dtostr(buf, sizeof(buf), primary->v.constant.v3_value.v[2]); fputs(buf, out); } fputs("]", out); break; \
case TYPE_M2X2 : fputs("[[", out); { gchar buf[G_ASCII_DTOSTR_BUF_SIZE]; g_ascii_dtostr(buf, sizeof(buf), primary->v.constant.m2x2_value.a00); fputs(buf, out); } fputs(",", out); { gchar buf[G_ASCII_DTOSTR_BUF_SIZE]; g_ascii_dtostr(buf, sizeof(buf), primary->v.constant.m2x2_value.a01); fputs(buf, out); } fputs("],[", out); { gchar buf[G_ASCII_DTOSTR_BUF_SIZE]; g_ascii_dtostr(buf, sizeof(buf), primary->v.constant.m2x2_value.a10); fputs(buf, out); } fputs(",", out); { gchar buf[G_ASCII_DTOSTR_BUF_SIZE]; g_ascii_dtostr(buf, sizeof(buf), primary->v.constant.m2x2_value.a11); fputs(buf, out); } fputs("]]", out); break;

#define TYPE_C_PRINTER \
case TYPE_NIL : fputs("NIL", out); break; \
case TYPE_INT : fputs("", out); fprintf(out, "%d", primary->v.constant.int_value);  break; \
case TYPE_FLOAT : fputs("", out); { gchar buf[G_ASCII_DTOSTR_BUF_SIZE]; g_ascii_dtostr(buf, sizeof(buf), primary->v.constant.float_value); fputs(buf, out); }  break; \
case TYPE_COMPLEX : fputs("COMPLEX(", out); { gchar buf[G_ASCII_DTOSTR_BUF_SIZE]; g_ascii_dtostr(buf, sizeof(buf), crealf(primary->v.constant.complex_value)); fputs(buf, out); } fputs(",", out); { gchar buf[G_ASCII_DTOSTR_BUF_SIZE]; g_ascii_dtostr(buf, sizeof(buf), cimagf(primary->v.constant.complex_value)); fputs(buf, out); } fputs(")", out); break; \
case TYPE_COLOR : fputs("MAKE_RGBA_COLOR(", out); fprintf(out, "%d", RED(primary->v.constant.color_value)); fputs(",", out); fprintf(out, "%d", GREEN(primary->v.constant.color_value)); fputs(",", out); fprintf(out, "%d", BLUE(primary->v.constant.color_value)); fputs(",", out); fprintf(out, "%d", ALPHA(primary->v.constant.color_value)); fputs(")", out); break; \
case TYPE_GSL_MATRIX : fputs("***MATRIX***", out); break; \
case TYPE_V2 : fputs("MAKE_V2(", out); { gchar buf[G_ASCII_DTOSTR_BUF_SIZE]; g_ascii_dtostr(buf, sizeof(buf), primary->v.constant.v2_value.v[0]); fputs(buf, out); } fputs(",", out); { gchar buf[G_ASCII_DTOSTR_BUF_SIZE]; g_ascii_dtostr(buf, sizeof(buf), primary->v.constant.v2_value.v[1]); fputs(buf, out); } fputs(")", out); break; \
case TYPE_V3 : fputs("MAKE_V3(", out); { gchar buf[G_ASCII_DTOSTR_BUF_SIZE]; g_ascii_dtostr(buf, sizeof(buf), primary->v.constant.v3_value.v[0]); fputs(buf, out); } fputs(",", out); { gchar buf[G_ASCII_DTOSTR_BUF_SIZE]; g_ascii_dtostr(buf, sizeof(buf), primary->v.constant.v3_value.v[1]); fputs(buf, out); } fputs(",", out); { gchar buf[G_ASCII_DTOSTR_BUF_SIZE]; g_ascii_dtostr(buf, sizeof(buf), primary->v.constant.v3_value.v[2]); fputs(buf, out); } fputs(")", out); break; \
case TYPE_M2X2 : fputs("MAKE_M2X2(", out); { gchar buf[G_ASCII_DTOSTR_BUF_SIZE]; g_ascii_dtostr(buf, sizeof(buf), primary->v.constant.m2x2_value.a00); fputs(buf, out); } fputs(",", out); { gchar buf[G_ASCII_DTOSTR_BUF_SIZE]; g_ascii_dtostr(buf, sizeof(buf), primary->v.constant.m2x2_value.a01); fputs(buf, out); } fputs(",", out); { gchar buf[G_ASCII_DTOSTR_BUF_SIZE]; g_ascii_dtostr(buf, sizeof(buf), primary->v.constant.m2x2_value.a10); fputs(buf, out); } fputs(",", out); { gchar buf[G_ASCII_DTOSTR_BUF_SIZE]; g_ascii_dtostr(buf, sizeof(buf), primary->v.constant.m2x2_value.a11); fputs(buf, out); } fputs(")", out); break;

