#ifndef PROTOTYPE_SRC_L1_CODEGEN_GEOM_H
#define PROTOTYPE_SRC_L1_CODEGEN_GEOM_H

#include "../codegen/codegen.h"

SpanU8 vec_field_name(int ci) {
    assert(0 <= ci && ci < 4);
    return ci == 0 ? cstr("x") : (ci == 1 ? cstr("y") : (ci == 2 ? cstr("z") : cstr("w")));
}

NODISCARD VecU8 codegen_name_xvecn(SpanU8 xvec, int n) {
    assert(2 <= n && n <= 4);
    return VecU8_fmt("%s%c", xvec, '0' + n);
}

NODISCARD VecU8 generate_xvecn_struct_and_base_methods(SpanU8 xvec, SpanU8 memb, int n) {
    VecU8 g_xvecn = codegen_name_xvecn(xvec, n);
    SpanU8 xvecn = VecU8_to_span(&g_xvecn);
    VecU8 res = VecU8_new();
    /* Structure definition */
    VecU8_append_span(&res, cstr("typedef struct {\n"));
    for (int ci = 0; ci < n; ci++) {
        VecU8_append_vec(&res, VecU8_fmt(SPACE "%s %s;\n", memb, vec_field_name(ci)));
    }
    VecU8_append_vec(&res, VecU8_fmt("} %s;\n\n", xvecn));
    /* xvecn_add_xvecn method */
    VecU8_append_vec(&res, VecU8_fmt(
    "%s %s_add_%s(%s A, %s B) {\n"
    SPACE "return(%s){ ",
        xvecn, xvecn, xvecn, xvecn, xvecn, xvecn));
    for (int ci = 0; ci < n; ci++) {
        if (ci)
            VecU8_append_span(&res, cstr(", "));
        VecU8_append_vec(&res, VecU8_fmt("A.%s + B.%s", vec_field_name(ci), vec_field_name(ci)));
    }
    VecU8_append_span(&res, cstr(" };\n}\n\n"));
    /* xvecn_minus_xvecn method */
    VecU8_append_vec(&res, VecU8_fmt(
    "%s %s_minus_%s(%s A, %s B) {\n"
    SPACE "return (%s){ ",
        xvecn, xvecn, xvecn, xvecn, xvecn, xvecn));
    for (int ci = 0; ci < n; ci++) {
        if (ci)
            VecU8_append_span(&res, cstr(", "));
        VecU8_append_vec(&res, VecU8_fmt("A.%s - B.%s", vec_field_name(ci), vec_field_name(ci)));
    }
    VecU8_append_span(&res, cstr(" };\n}\n\n"));
    /* xvecn_minus method */
    VecU8_append_vec(&res, VecU8_fmt(
            "%s %s_minus(%s A) {\n"
            SPACE "return (%s){ ",
            xvecn, xvecn, xvecn, xvecn));
    for (int ci = 0; ci < n; ci++) {
        if (ci)
            VecU8_append_span(&res, cstr(", "));
        VecU8_append_vec(&res, VecU8_fmt("-A.%s", vec_field_name(ci)));
    }
    VecU8_append_span(&res, cstr(" };\n}\n\n"));

    VecU8_drop(g_xvecn);
    return res;
}

NODISCARD VecU8 generate_xvecn_struct_and_cool_methods(SpanU8 xvec, SpanU8 memb, int n, SpanU8 sqrt_func) {
    VecU8 g_xvecn = codegen_name_xvecn(xvec, n);
    SpanU8 xvecn = VecU8_to_span(&g_xvecn);
    VecU8 res = generate_xvecn_struct_and_base_methods(xvec, memb, n);
    /* xvecn_length method */
    VecU8_append_vec(&res, VecU8_fmt(
        "%s %s_length(%s A) {\n"
        SPACE "return %s(",
        memb, xvecn, xvecn, sqrt_func));
    for (int i = 0; i < n; i++) {
        if (i)
            VecU8_append_span(&res, cstr(" + "));
        VecU8_append_vec(&res, VecU8_fmt("A.%s * A.%s", vec_field_name(i), vec_field_name(i)));
    }
    VecU8_append_span(&res, cstr(");\n}\n\n"));
    /* xvecn_mul_scal method */
    VecU8_append_vec(&res, VecU8_fmt(
    "%s %s_mul_scal(%s A, %s B) {\n"
    SPACE "return (%s) { ",
        xvecn, xvecn, xvecn, memb, xvecn));
    for (int ci = 0; ci < n; ci++) {
        if (ci)
            VecU8_append_span(&res, cstr(", "));
        VecU8_append_vec(&res, VecU8_fmt("A.%s * B", vec_field_name(ci)));
    }
    VecU8_append_span(&res, cstr(" };\n}\n\n"));
    /* xvecn_div_by_scal method */
    VecU8_append_vec(&res, VecU8_fmt(
    "%s %s_div_by_scal(%s A, %s B) {\n"
    SPACE "return %s_mul_scal(A, 1/B);\n"
    "}\n\n", xvecn, xvecn, xvecn, memb, xvecn));
    /* xvecn_mul_xvecn method */
    VecU8_append_vec(&res, VecU8_fmt(
    "%s %s_mul_%s(%s A, %s B) {\n"
    SPACE "return (%s){ ",
        xvecn, xvecn, xvecn, xvecn, xvecn, xvecn));
    for (int ci = 0; ci < n; ci++) {
        if (ci)
            VecU8_append_span(&res, cstr(", "));
        VecU8_append_vec(&res, VecU8_fmt("A.%s * B.%s", vec_field_name(ci), vec_field_name(ci)));
    }
    VecU8_append_span(&res, cstr(" };\n}\n\n"));
    /* xvecn_dot method */
    VecU8_append_vec(&res, VecU8_fmt(
    "%s %s_dot(%s A, %s B) {\n"
    SPACE "return ",
        memb, xvecn, xvecn, xvecn));
    for (int i = 0; i < n; i++) {
        if (i)
            VecU8_append_span(&res, cstr(" + "));
        VecU8_append_vec(&res, VecU8_fmt("A.%s * B.%s", vec_field_name(i), vec_field_name(i)));
    }
    VecU8_append_span(&res, cstr(";\n}\n\n"));
    /* xvecn_normalize method */
    VecU8_append_vec(&res, VecU8_fmt(
        "%s %s_normalize(%s A) {\n"
        SPACE "return %s_div_by_scal(A, %s_length(A));\n"
        "}\n\n", xvecn, xvecn, xvecn, xvecn, xvecn));

    VecU8_drop(g_xvecn);
    return res;
}

NODISCARD VecU8 generate_xvecn_method_and_one(SpanU8 xvec, int n) {
    assert(2 <= n && n < 4);
    VecU8 g_xvecn = codegen_name_xvecn(xvec, n);
    VecU8 g_xvecn_pp = codegen_name_xvecn(xvec, n + 1);
    SpanU8 xvecn = VecU8_to_span(&g_xvecn);
    SpanU8 xvecn_pp = VecU8_to_span(&g_xvecn_pp);

    VecU8 res = VecU8_fmt(
        "%s %s_and_one(%s A) {\n"
        SPACE "return (%s){ ",
        xvecn_pp, xvecn, xvecn, xvecn_pp);
    for (int i = 0; i < n; i++) {
        VecU8_append_vec(&res, VecU8_fmt("A.%s, ", vec_field_name(i)));
    }
    VecU8_append_span(&res, cstr("1 };\n}\n\n"));

    VecU8_drop(g_xvecn_pp);
    VecU8_drop(g_xvecn);
    return res;
}

NODISCARD VecU8 generate_xvec3_method_cross(SpanU8 xvec) {
    VecU8 g_xvec3 = codegen_name_xvecn(xvec, 3);
    SpanU8 xvec3 = VecU8_to_span(&g_xvec3);
    VecU8 res = VecU8_fmt(
        "%s %s_cross(%s A, %s B) {\n"
        SPACE "return (%s){A.y * B.z - A.z * B.y, -A.x * B.z + A.z * B.x, A.x * B.y - A.y * B.x};\n"
        "}\n\n", xvec3, xvec3, xvec3, xvec3, xvec3);
    VecU8_drop(g_xvec3);
    return res;
}

NODISCARD VecU8 codegen_name_xmatnm(SpanU8 xmat, int cols, int rows) {
    assert(2 <= cols && cols <= 4 && 2 <= rows && rows <= 4);
    if (cols == rows)
        return VecU8_fmt("%s%c", xmat, '0' + cols);
    return VecU8_fmt("%s%cx%c", xmat, '0' + cols, '0' + rows);
}

NODISCARD VecU8 generate_xmatnm_struct_and_methods(
    SpanU8 xmat, SpanU8 xvec, SpanU8 memb, int cols, int rows, int sizeof_member
    ) {
    VecU8 g_xmatnm = codegen_name_xmatnm(xmat, cols, rows);
    SpanU8 xmatnm = VecU8_to_span(&g_xmatnm);
    VecU8 g_xvecm = codegen_name_xvecn(xvec, rows);
    SpanU8 xvecm = VecU8_to_span(&g_xvecm);
    VecU8 res = VecU8_new();

    /* Structure xmatnm
     * With columns padded to 16 bytes (for std140, std140 is our everything) */
    int sv = (rows * sizeof_member) % 16;
    VecU8_append_span(&res, cstr("typedef struct {\n"));
    for (int x = 0; x < cols; x++) {
        VecU8_append_vec(&res, VecU8_fmt(SPACE "%s %s;\n", xvecm, vec_field_name(x)));
        if (sv) {
            VecU8_append_vec(&res, VecU8_fmt(SPACE "char _padding_%u[%u];\n", (U64)x, (U64)(16 - sv)));
        }
    }
    VecU8_append_vec(&res, VecU8_fmt("} %s;\n\n", xmatnm));
    /* xmatnm_new method */
    VecU8_append_vec(&res, VecU8_fmt("%s %s_new(", xmatnm, xmatnm));
    for (int y = 0; y < rows; y++) {
        for (int x = 0; x < cols; x++) {
            if (x > 0 || y > 0)
                VecU8_append_span(&res, cstr(", "));
            VecU8_append_vec(&res, VecU8_fmt("%s %s%s", memb, vec_field_name(x), vec_field_name(y)));
        }
    }
    VecU8_append_vec(&res, VecU8_fmt(") {\n" SPACE "return (%s){ ", xmatnm));
    for (int x = 0; x < cols; x++) {
        if (x)
            VecU8_append_span(&res, cstr(", "));
        VecU8_append_vec(&res, VecU8_fmt(".%s = { ", vec_field_name(x)));
        for (int y = 0; y < rows; y++) {
            if (y)
                VecU8_append_span(&res, cstr(", "));
            VecU8_append_vec(&res, VecU8_fmt("%s%s", vec_field_name(x), vec_field_name(y)));
        }
        VecU8_append_span(&res, cstr(" }"));
    }
    VecU8_append_span(&res, cstr(" };\n}\n\n"));
    /* xmatnm_add_xmatnm method */
    VecU8_append_vec(&res, VecU8_fmt(
        "%s %s_add_%s(%s A, %s B) {\n"
        SPACE "return (%s){ ",
        xmatnm, xmatnm, xmatnm, xmatnm, xmatnm, xmatnm));
    for (int x = 0; x < cols; x++) {
        if (x)
            VecU8_append_span(&res, cstr(", "));
        VecU8_append_vec(&res, VecU8_fmt(".%s = %s_add_%s(A.%s, B.%s)",
            vec_field_name(x), xvecm, xvecm, vec_field_name(x), vec_field_name(x)));
    }
    VecU8_append_span(&res, cstr(" };\n}\n\n"));
    /* xmatnm_minus_xmatnm method */
    VecU8_append_vec(&res, VecU8_fmt(
        "%s %s_minus_%s(%s A, %s B) {\n"
        SPACE "return (%s){ ",
        xmatnm, xmatnm, xmatnm, xmatnm, xmatnm, xmatnm));
    for (int x = 0; x < cols; x++) {
        if (x)
            VecU8_append_span(&res, cstr(", "));
        VecU8_append_vec(&res, VecU8_fmt(".%s = %s_minus_%s(A.%s, B.%s)",
            vec_field_name(x), xvecm, xvecm, vec_field_name(x), vec_field_name(x)));
    }
    VecU8_append_span(&res, cstr(" };\n}\n\n"));
    /* xmatnm_minus method */
    VecU8_append_vec(&res, VecU8_fmt(
        "%s %s_minus(%s A) {\n"
        SPACE "return (%s){ ",
        xmatnm, xmatnm, xmatnm, xmatnm));
    for (int x = 0; x < cols; x++) {
        if (x)
            VecU8_append_span(&res, cstr(", "));
        VecU8_append_vec(&res, VecU8_fmt(".%s = %s_minus(A.%s)",
            vec_field_name(x), xvecm, vec_field_name(x)));
    }
    VecU8_append_span(&res, cstr(" };\n}\n\n"));
    /* xmatnm_mul_scal method */
    VecU8_append_vec(&res, VecU8_fmt(
        "%s %s_mul_scal(%s A, %s B) {\n"
        SPACE "return (%s){ ",
        xmatnm, xmatnm, xmatnm, memb, xmatnm));
    for (int x = 0; x < cols; x++) {
        if (x)
            VecU8_append_span(&res, cstr(", "));
        VecU8_append_vec(&res, VecU8_fmt(".%s = %s_mul_scal(A.%s, B)",
            vec_field_name(x), xvecm, vec_field_name(x)));
    }
    VecU8_append_span(&res, cstr(" };\n}\n\n"));
    /* xmatnm_div_by_scal method */
    VecU8_append_vec(&res, VecU8_fmt(
        "%s %s_div_by_scal(%s A, %s B) {\n"
        SPACE "return %s_mul_scal(A, 1/B);\n"
        "}\n\n", xmatnm, xmatnm, xmatnm, memb, xmatnm));


    VecU8 g_xvecn = codegen_name_xvecn(xvec, cols);
    SpanU8 xvecn = VecU8_to_span(&g_xvecn);
    /* xmatnm_mul_xvecn */
    VecU8_append_vec(&res, VecU8_fmt(
        "%s %s_mul_%s(%s A, %s B) {\n"
        SPACE "return (%s){ ",
        xvecm, xmatnm, xvecn, xmatnm, xvecn, xvecm));
    for (int y = 0; y < rows; y++) {
        if (y)
            VecU8_append(&res, ',');
        VecU8_append_span(&res, cstr("\n" SPACE8));
        for (int x = 0; x < cols; x++) {
            if (x)
                VecU8_append_span(&res, cstr(" + "));
            VecU8_append_vec(&res, VecU8_fmt("A.%s.%s * B.%s", vec_field_name(x), vec_field_name(y), vec_field_name(x)));
        }
    }
    VecU8_append_span(&res, cstr("\n" SPACE "};\n}\n\n"));

    VecU8_drop(g_xvecn);
    VecU8_drop(g_xvecm);
    VecU8_drop(g_xmatnm);
    return res;
}

/* xmatnm_transpose method */
NODISCARD VecU8 generate_xmatnm_transpose_method(SpanU8 xmat, SpanU8 xvec, SpanU8 memb, int cols, int rows) {
    VecU8 g_xmatnm = codegen_name_xmatnm(xmat, cols, rows);
    VecU8 g_xmatmn = codegen_name_xmatnm(xmat, rows, cols);
    SpanU8 xmatnm = VecU8_to_span(&g_xmatnm);
    SpanU8 xmatmn = VecU8_to_span(&g_xmatmn);
    VecU8 res =  VecU8_fmt(
    "%s %s_transpose(%s A) {\n"
    SPACE "return (%s){ ",
        xmatmn, xmatnm, xmatnm, xmatmn);
    for (int bx = 0; bx < rows; bx++) {
        if (bx)
            VecU8_append_span(&res, cstr(", "));
        VecU8_append_vec(&res, VecU8_fmt(".%s = { ", vec_field_name(bx)));
        for (int by = 0; by < cols; by++) {
            if (by)
                VecU8_append_span(&res, cstr(", "));
            VecU8_append_vec(&res, VecU8_fmt("A.%s.%s", vec_field_name(by), vec_field_name(bx)));
        }
        VecU8_append_span(&res, cstr(" }"));
    }
    VecU8_append_span(&res, cstr(" };\n}\n\n"));

    VecU8_drop(g_xmatmn);
    VecU8_drop(g_xmatnm);
    return res;
}

NODISCARD VecU8 generate_square_xmatn_methods(SpanU8 xmat, SpanU8 xvec, SpanU8 memb, int n) {
    VecU8 g_xmatn = codegen_name_xmatnm(xmat, n, n);
    SpanU8 xmatn = VecU8_to_span(&g_xmatn);
    VecU8 res = VecU8_new();

    VecU8_append_vec(&res, VecU8_fmt(
        "const %s %s_E = { ", xmatn, xmatn));
    for (int x = 0; x < n; x++) {
        if (x)
            VecU8_append_span(&res, cstr(", "));
        VecU8_append_vec(&res, VecU8_fmt(".%s = {", vec_field_name(x)));
        for (int y = 0; y < n; y++) {
            if (y)
                VecU8_append_span(&res, cstr(", "));
            VecU8_append(&res, '0' + (x == y));
        }
        VecU8_append_span(&res, cstr(" }"));
    }
    VecU8_append_span(&res, cstr(" };\n\n"));

    VecU8_drop(g_xmatn);
    return res;
}

NODISCARD VecU8 generate_xmat_inverse_methods(SpanU8 xmat, SpanU8 xvec, SpanU8 memb){
    VecU8 res = VecU8_fmt("%s4 %s4_inverse(%s4 A) {\n", xmat, xmat, xmat);
    VecU8_append_vec(&res, VecU8_fmt(SPACE "%s m2[6][6] = {\n", memb));
    SpanU8 first_of_pair[6] = {cstr("x"), cstr("x"), cstr("x"), cstr("y"), cstr("y"), cstr("z")};
    SpanU8 second_of_pair[6] = {cstr("y"), cstr("z"), cstr("w"), cstr("z"), cstr("w"), cstr("w")};
    for (int w_col = 0; w_col < 6; w_col++) {
        VecU8_append_span(&res, cstr(SPACE SPACE "{ "));
        for (int w_row = 0; w_row < 6; w_row++) {
            if (w_row)
                VecU8_append_span(&res, cstr(", "));
            /* first first = A   second first = B
             * first second = C   second second = D
             * A * D - B * C */
            VecU8_append_vec(&res, VecU8_fmt("A.%s.%s * A.%s.%s - A.%s.%s * A.%s.%s",
                first_of_pair[w_col], first_of_pair[w_row],   second_of_pair[w_col], second_of_pair[w_row],
                second_of_pair[w_col], first_of_pair[w_row],  first_of_pair[w_col], second_of_pair[w_row]
            ));
        }
        VecU8_append_span(&res, cstr(" },\n"));
    }
    VecU8_append_span(&res, cstr(SPACE "};\n"));

    U64 a0_contr[4] = {5, 5, 4, 3};
    U64 a1_contr[4] = {4, 2, 2, 1};
    U64 a2_contr[4] = {3, 1, 0, 0};
    SpanU8 a0[4] = {cstr("y"), cstr("x"), cstr("x"), cstr("x")};
    SpanU8 a1[4] = {cstr("z"), cstr("z"), cstr("y"), cstr("y")};
    SpanU8 a2[4] = {cstr("w"), cstr("w"), cstr("w"), cstr("z")};
    VecU8_append_vec(&res, VecU8_fmt(SPACE "%s m3[4][4] = {\n", memb));
    for (int no_col = 0; no_col < 4; no_col++) {
        SpanU8 walking_column = a0[no_col];
        U64 minor_col_pair = a0_contr[no_col];
        VecU8_append_span(&res, cstr(SPACE SPACE "{ "));
        for (int no_row = 0; no_row < 4; no_row++) {
            if (no_row)
                VecU8_append_span(&res, cstr(", \n" SPACE SPACE));
            VecU8_append_vec(&res, VecU8_fmt(
                "A.%s.%s * m2[%u][%u] - A.%s.%s * m2[%u][%u] + A.%s.%s * m2[%u][%u]",
                walking_column, a0[no_row], minor_col_pair, a0_contr[no_row],
                walking_column, a1[no_row], minor_col_pair, a1_contr[no_row],
                walking_column, a2[no_row], minor_col_pair, a2_contr[no_row]));
        }
        VecU8_append_span(&res, cstr(" },\n"));
    }
    VecU8_append_span(&res, cstr(SPACE "};\n"));
    VecU8_append_vec(&res, VecU8_fmt(
        SPACE "%s d = 1 / (A.x.x * m3[0][0] - A.x.y * m3[0][1] + A.x.z * m3[0][2] - A.x.w * m3[0][3]);\n"
        SPACE "return (mat4){ "
        , memb));
    for (U64 i = 0; i < 4; i++) {
        if (i)
            VecU8_append_span(&res, cstr(",\n" SPACE SPACE ));
        VecU8_append_vec(&res, VecU8_fmt(".%s={ ", vec_field_name((int)i)));
        for (U64 j = 0; j < 4; j++) {
            if (j)
                VecU8_append_span(&res, cstr(", "));
            VecU8_append_vec(&res, VecU8_fmt("%sm3[%u][%u] * d",
                (i + j) % 2 ? cstr("-") : cstr(""), j, i));
        }
        VecU8_append_span(&res, cstr(" }"));
    }
    VecU8_append_span(&res, cstr(" };\n}\n\n"));

    VecU8_append_vec(&res, VecU8_fmt(
        "%s2 %s2_inverse(%s2 A) {\n"                                                         /* xmat, xmat, xmat */
        SPACE "%s d = 1 / (A.x.x * A.y.y - A.y.x * A.x.y);\n"                                /* memb */
        SPACE "return (%s2){ .x = { A.y.y * d, -A.x.y * d}, .y = {-A.y.x * d, A.x.x * d}};\n" /* xmat */
        "}\n\n", xmat, xmat, xmat, memb, xmat));

    // VecU8_append_vec(&res, VecU8_fmt( "%s3 %s3_inverse(%s3 A) {\n", xmat, xmat, xmat));
    // VecU8_append_vec(&res, VecU8_fmt(SPACE "%s d = 1 / ("));
    // VecU8_append_span(&res, cstr("}\n"));
    return res;
}

NODISCARD VecU8 generate_xmatnm_method_mul_xmatkn(SpanU8 xmat, int n, int m, int k) {
    VecU8 g_xmatkm = codegen_name_xmatnm(xmat, k, m);
    VecU8 g_xmatnm = codegen_name_xmatnm(xmat, n, m);
    VecU8 g_xmatkn = codegen_name_xmatnm(xmat, k, n);
    SpanU8 xmatkm = VecU8_to_span(&g_xmatkm);
    SpanU8 xmatnm = VecU8_to_span(&g_xmatnm);
    SpanU8 xmatkn = VecU8_to_span(&g_xmatkn);

    VecU8 res = VecU8_fmt(
        "%s %s_mul_%s(%s A, %s B) {\n"
        SPACE "return (%s){ ",
        xmatkm, xmatnm, xmatkn, xmatnm, xmatkn, xmatkm);
    for (int x = 0; x < k; x++) {
        if (x)
            VecU8_append_span(&res, cstr(","));
        VecU8_append_span(&res, cstr("\n" SPACE8));

        VecU8_append_vec(&res, VecU8_fmt(".%s = { ", vec_field_name(x)));
        for (int y = 0; y < m; y++) {
            if (y)
                VecU8_append_span(&res, cstr(", "));
            for (int z = 0; z < n; z++) {
                if (z)
                    VecU8_append_span(&res, cstr(" + "));
                VecU8_append_vec(&res, VecU8_fmt("A.%s.%s * B.%s.%s",
                    vec_field_name(z), vec_field_name(y), vec_field_name(x), vec_field_name(z)));
            }
        }
        VecU8_append_span(&res, cstr(" }"));
    }
    VecU8_append_span(&res, cstr(" };\n}\n\n"));

    VecU8_drop(g_xmatkm);
    VecU8_drop(g_xmatnm);
    VecU8_drop(g_xmatkn);
    return res;
}

NODISCARD VecU8 generate_xvec234_structs_and_base_methods(SpanU8 xvec, SpanU8 memb) {
    VecU8 res = VecU8_new();
    for (int n = 2; n <= 4; n++)
        VecU8_append_vec(&res, generate_xvecn_struct_and_base_methods(xvec, memb, n));
    return res;
}

NODISCARD VecU8 generate_xvec234_structs_and_cool_methods(SpanU8 xvec, SpanU8 memb, SpanU8 sqrt_func) {
    VecU8 res = VecU8_new();
    for (int n = 2; n <= 4; n++)
        VecU8_append_vec(&res, generate_xvecn_struct_and_cool_methods(xvec, memb, n, sqrt_func));
    for (int n = 2; n <= 3; n++)
        VecU8_append_vec(&res, generate_xvecn_method_and_one(xvec, n));
    VecU8_append_vec(&res, generate_xvec3_method_cross(xvec));
    return res;
}

NODISCARD VecU8 generate_xmat234x234_structs_methods(SpanU8 xmat, SpanU8 xvec, SpanU8 memb, int sizeof_member) {
    VecU8 res = VecU8_new();
    for (int cols = 2; cols <= 4; cols++) {
        for (int rows = 2; rows <= 4; rows++) {
            VecU8_append_vec(&res, generate_xmatnm_struct_and_methods(xmat, xvec, memb, cols, rows, sizeof_member));
        }
    }
    for (int cols = 2; cols <= 4; cols++) {
        for (int rows = 2; rows <= 4; rows++) {
            VecU8_append_vec(&res, generate_xmatnm_transpose_method(xmat, xvec, memb, cols, rows));
        }
    }
    for (int n = 2; n <= 4; n++) {
        VecU8_append_vec(&res, generate_square_xmatn_methods(xmat, xvec, memb, n));
    }
    for (int n = 2; n <= 4; n++) {
        for (int m = 2; m <= 4; m++) {
            for (int k = 2; k <= 4; k++) {
                VecU8_append_vec(&res, generate_xmatnm_method_mul_xmatkn(xmat, n, m, k));
            }
        }
    }
    VecU8_append_vec(&res, generate_xmat_inverse_methods(xmat, xvec, memb));
    return res;
}

void generate_geom_header() {
    GeneratedHeader res = begin_header(cstr("l1/geom.h"));
    VecU8_append_span(&res.result, cstr("#include \"../../src/l1/core/int_primitives.h\"\n"));
    VecU8_append_span(&res.result, cstr("#include <math.h>\n\n"));
    VecU8_append_vec(&res.result, generate_xvec234_structs_and_base_methods(cstr("cvec"), cstr("U8")));
    VecU8_append_vec(&res.result, generate_xvec234_structs_and_base_methods(cstr("uvec"), cstr("U32")));
    VecU8_append_vec(&res.result, generate_xvec234_structs_and_base_methods(cstr("ivec"), cstr("S32")));

    VecU8_append_vec(&res.result, generate_xvec234_structs_and_cool_methods(cstr("vec"), cstr("float"), cstr("sqrtf")));
    VecU8_append_vec(&res.result, generate_xvec234_structs_and_cool_methods(cstr("dvec"), cstr("double"), cstr("sqrt")));
    VecU8_append_vec(&res.result, generate_xmat234x234_structs_methods(cstr("mat"), cstr("vec"), cstr("float"), sizeof(float)));
    /* VecU8_append_vec(&res.result, generate_xmat234x234_structs_methods(cstr("dmat"), cstr("dvec"), cstr("double"), sizeof(double))); */
    finish_header(res);
}

#endif
