diff --git a/src/l1_5/anne/codegen.c b/src/l1_5/anne/codegen.c index 52ec26d..a451285 100644 --- a/src/l1_5/anne/codegen.c +++ b/src/l1_5/anne/codegen.c @@ -1,6 +1,5 @@ #include "../../l1/system/fsmanip.h" -#include "marie/clipping.h" #include "liza.h" #include "l1_5_templ_very_base.h" #include "margaret.h" @@ -8,8 +7,6 @@ int main() { mkdir_nofail("l1_5"); - mkdir_nofail("l1_5/marie"); - generate_marie_clipping_header(); generate_l1_5_liza_headers(); generate_l1_5_template_instantiation_for_base_types(); generate_l1_5_template_instantiations_for_margaret(); diff --git a/src/l1_5/anne/marie/clipping.h b/src/l1_5/anne/marie/clipping.h deleted file mode 100644 index 33d8492..0000000 --- a/src/l1_5/anne/marie/clipping.h +++ /dev/null @@ -1,419 +0,0 @@ -#ifndef PROTOTYPE1_SRC_L1_CODEGEN_CLIPPING_H -#define PROTOTYPE1_SRC_L1_CODEGEN_CLIPPING_H - -#include "../../../l1/codegen/codegen.h" -#include "../../../../gen/l1/VecAndSpan_SpanU8.h" - -// todo: move all of this to marie namespace -// todo: instead of returning triangles, return points of convex polygon -// todo: I would say that I need to rewrite all with VecU8_fmt, but I am not sure I even need this code -// todo: rewrite if I decide not to delete - -typedef struct { - int order; - bool negate; -} PossiblyNegatedTriangle; - -int comparison_triang_groups[18][3] = { - {10, 11, 20}, {10, 11, 21}, {10, 11, 22}, - {11, 12, 20}, {11, 12, 21}, {11, 12, 22}, - {12, 10, 20}, {12, 10, 21}, {12, 10, 22}, - - {20, 21, 10}, {20, 21, 11}, {20, 21, 12}, - {21, 22, 10}, {21, 22, 11}, {21, 22, 12}, - {22, 20, 10}, {22, 20, 11}, {22, 20, 12}, -}; - -int permutations_of_sigma3(const int ns[static 3]) { - return (ns[0] > ns[1]) + (ns[1] > ns[2]) + (ns[0] > ns[2]); -} - -PossiblyNegatedTriangle get_order_var_of_triangle_merged_ns(int arg[static 3]) { - for (int ord = 0; ord < 18; ord++) { - for (int x = 0; x < 3; x++) { - for (int y = 0; y < 3; y++) { - if (comparison_triang_groups[ord][y] == arg[x]) - goto found_this_one; - } - goto nah_not_this_one; - found_this_one: - } - return (PossiblyNegatedTriangle){ .order = ord, - .negate = ((permutations_of_sigma3(comparison_triang_groups[ord]) + - permutations_of_sigma3(arg)) % 2 == 1) }; - nah_not_this_one: /* We continue out search*/ - } - abortf("Impossible"); -} - - -PossiblyNegatedTriangle get_order_var_of_triangle(char tri, int idi, char trj, int idj, char tru, int idu) { - assert(tri == 'C' || tri == 'T'); - assert(trj == 'C' || trj == 'T'); - assert(tru == 'C' || tru == 'T'); - assert(0 <= idi && idi < 3); - assert(0 <= idj && idj < 3); - assert(0 <= idu && idu < 3); - assert(!(tri == trj && trj == tru)); - assert(!(tri == trj && idi == idj)); - assert(!(trj == tru && idj == idu)); - assert(!(tri == tru && idi == idu)); - int arg[3] = { (tri == 'C' ? 10 : 20) + idi, (trj == 'C' ? 10 : 20) + idj, (tru == 'C' ? 10 : 20) + idu }; - return get_order_var_of_triangle_merged_ns(arg); -} - -/* Appends code to string with code. - * Triangle is either 'T' or 'C' - * Vertexes in triangle are numbered 0,1,2 - * vertex vi: (index idi of triangle tri) - * vertex vj: (index idj of triangle trj) - * vertex u: (index idu of triangle tru) - * We walk along the vi -> vj ray. If u is on the left, this statement will show true - */ -void append_on_the_left_stmt(VecU8* str, char tri, int idi, char trj, int idj, char tru, int idu) { - PossiblyNegatedTriangle measure = get_order_var_of_triangle(tri, idi, trj, idj, tru, idu); - VecU8_append_vec(str, VecU8_format("(M%d %s 0)", measure.order, measure.negate ? "<=" : ">=")); -} - -void append_on_the_right_stmt(VecU8* str, char tri, int idi, char trj, int idj, char tru, int idu) { - PossiblyNegatedTriangle measure = get_order_var_of_triangle(tri, idi, trj, idj, tru, idu); - VecU8_append_vec(str, VecU8_format("(M%d %s 0)", measure.order, measure.negate ? ">=" : "<=")); -} - - -/* Generates statement that intersects two segments from 2 different triangles: - * First segment: (tr1::A1) to (tr1::B1) - * Second segment: (tr2::A2) to (tr2::B2) - * */ -void append_intersection_eol_stmt(VecU8* str, char tr1, int A1, int B1, char tr2, int A2, int B2) { - assert((tr1 == 'C' && tr2 == 'T') || (tr1 == 'T' && tr2 == 'C')); - assert(0 <= A1 && A1 < 3); - assert(0 <= B1 && B1 < 3); - assert(0 <= A2 && A2 < 3); - assert(0 <= B2 && B2 < 3); - assert(A1 != B1 && A2 != B2); - VecU8_append_vec(str, VecU8_format("marie_intersect_lines(%c.v%d, %c.v%d, %c.v%d, %c.v%d);\n", - tr1, A1, tr1, B1, tr2, A2, tr2, B2)); -} - -SpanU8 marie_names_of_two_clipping_triangles[6] = { - cstr("C.v0"), cstr("C.v1"), cstr("C.v2"), - cstr("T.v0"), cstr("T.v1"), cstr("T.v2"), -}; - -NODISCARD SpanU8 get_firstborn_vertex_stmt(char tr, int id) { - assert(0 <= id && id < 3); - if (tr == 'C') - return marie_names_of_two_clipping_triangles[id]; - if (tr == 'T') - return marie_names_of_two_clipping_triangles[3 + id]; - abortf("Wrong triangle"); -} - -void append_triangle_registration_stmt(VecU8* str, SpanU8 P0, SpanU8 P1, SpanU8 P2) { - VecU8_append_span(str, cstr("VecMarieTriangle_append(pile, (MarieTriangle){")); - VecU8_append_span(str, P0); - VecU8_append_span(str, cstr(", ")); - VecU8_append_span(str, P1); - VecU8_append_span(str, cstr(", ")); - VecU8_append_span(str, P2); - VecU8_append_span(str, cstr("});\n")); -} - -void append_answering_stmt(VecU8* res, SpanSpanU8 vertices, int tabulation_lvl) { - size_t n = vertices.len; - assert(n >= 3); - for (size_t i = 0; i < n - 2; i++) { - for (int sp = 0; sp < tabulation_lvl; sp++) - VecU8_append(res, ' '); - append_triangle_registration_stmt(res, *SpanSpanU8_at(vertices, i), - *SpanSpanU8_at(vertices, i + 1), *SpanSpanU8_at(vertices, n - 1)); - } - for (int sp = 0; sp < tabulation_lvl; sp++) - VecU8_append(res, ' '); - VecU8_append_span(res, cstr("return;\n")); -} - -int mod3_inc(int x) { - return x == 2 ? 0 : (x + 1); -} - -int mod3_dec(int x) { - return x ? (x - 1) : 2; -} - -void generate_func_clip_triang_on_triang_case_where_some_vertex_stuck(VecU8* res, char tC, char tT, bool tables_turned) { - /* Case where all 3 vertices of tT are inside tC */ - VecU8_append_span(res, cstr(SPACE "if (")); - for (int cs = 0; cs < 3; cs++) { - for (int tv = 0; tv < 3; tv++) { - if (cs != 0 || tv != 0) - VecU8_append_span(res, cstr(" && ")); - append_on_the_left_stmt(res, tC, cs, tC, (cs + 1) % 3, tT, tv); - } - } - VecU8_append_span(res, cstr(") {\n" SPACE8)); - append_triangle_registration_stmt(res, - get_firstborn_vertex_stmt(tT, 0), get_firstborn_vertex_stmt(tT, 1), get_firstborn_vertex_stmt(tT, 2)); - VecU8_append_span(res, cstr(SPACE8 "return;\n" SPACE "}\n\n")); - - /* Cases where two vertices of tT are inside tC, but one is outside */ - for (int ti = 0; ti < 3; ti++) { - VecU8_append_span(res, cstr(SPACE "if (")); - int TA = mod3_inc(ti); - int TB = mod3_inc(TA); - for (int j = 1; j <= 2; j++) { - for (int cs = 0; cs < 3; cs++) { - if (cs != 0 || j != 1) - VecU8_append_span(res, cstr(" && ")); - append_on_the_left_stmt(res, tC, cs, tC, mod3_inc(cs), tT, (ti + j) % 3); - } - } - VecU8_append_span(res, cstr(") {\n")); - for (int sc = 0; sc < 3; sc++) { - VecU8_append_span(res, cstr(SPACE8 "if (")); - append_on_the_right_stmt(res, tC, sc, tC, mod3_inc(sc), tT, ti); - VecU8_append_span(res, cstr(") {\n")); - { - /* 'Result hits one edge' case */ - VecU8_append_span(res, cstr(SPACE12 "if (")); - append_on_the_left_stmt(res, tT, TA, tC, sc, tT, ti); - VecU8_append_span(res, cstr(" && ")); - append_on_the_left_stmt(res, tT, TB, tC, sc, tT, ti); - VecU8_append_span(res, cstr(" && ")); - append_on_the_right_stmt(res, tT, TA, tC, mod3_inc(sc), tT, ti); - VecU8_append_span(res, cstr(" && ")); - append_on_the_right_stmt(res, tT, TB, tC, mod3_inc(sc), tT, ti); - VecU8_append_span(res, cstr(") {\n")); - { - VecU8_append_span(res, cstr(SPACE16 "vec2 PB = ")); - append_intersection_eol_stmt(res, tC, sc, mod3_inc(sc), tT, ti, TB); - VecU8_append_span(res, cstr(SPACE16 "vec2 PA = ")); - append_intersection_eol_stmt(res, tC, sc, mod3_inc(sc), tT, ti, TA); - SpanU8 quad[4] = { - get_firstborn_vertex_stmt(tT, TB), cstr("PB"), cstr("PA"), get_firstborn_vertex_stmt(tT, TA) }; - append_answering_stmt(res, (SpanSpanU8){.data = quad, .len = ARRAY_SIZE(quad)}, 16); - } - VecU8_append_span(res, cstr(SPACE12 "}\n")); - if (!tables_turned) { - /* 'Result hits the angle and two edges' case */ - VecU8_append_span(res, cstr(SPACE12 "if (")); - append_on_the_left_stmt(res, tT, TA, tC, sc, tT, ti); - VecU8_append_span(res, cstr(" && ")); - append_on_the_right_stmt(res, tT, TB, tC, sc, tT, ti); - VecU8_append_span(res, cstr(") {\n")); - { - VecU8_append_span(res, cstr(SPACE16 "vec2 PB = ")); - append_intersection_eol_stmt(res, tC, sc, mod3_dec(sc), tT, ti, TB); - VecU8_append_span(res, cstr(SPACE16 "vec2 PA = ")); - append_intersection_eol_stmt(res, tC, sc, mod3_inc(sc), tT, ti, TA); - SpanU8 pentagon[5] = { get_firstborn_vertex_stmt(tT, TB), cstr("PB"), - get_firstborn_vertex_stmt(tC, sc), cstr("PA"), get_firstborn_vertex_stmt(tT, TA)}; - append_answering_stmt(res, (SpanSpanU8){.data = pentagon, .len = ARRAY_SIZE(pentagon)}, 16); - } - VecU8_append_span(res, cstr(SPACE12 "}\n")); - } - } - VecU8_append_span(res, cstr(SPACE8 "}\n")); - } - VecU8_append_span(res, cstr(SPACE "}\n\n")); - } - - /* Case where one vertice of tT is inside tC, but other two are outside tC */ - for (int pl = 0; pl < 3; pl++) { - int TA = mod3_inc(pl); - int TB = mod3_inc(TA); - VecU8_append_span(res, cstr(SPACE "if (")); - for (int cb = 0; cb < 3; cb++) { - if (cb) - VecU8_append_span(res, cstr(" && ")); - append_on_the_left_stmt(res, tC, cb, tC, mod3_inc(cb), tT, pl); - } - VecU8_append_span(res, cstr(") {\n")); - for (int cr = 0; cr < 3; cr++) { - /* Cases where one vertex (pl) of tT is inside tC, but two other (TA and TB) are in - * the same 'third of a surface' */ - VecU8_append_span(res, cstr(SPACE8 "if (")); - append_on_the_left_stmt(res, tT, pl, tC, cr, tT, TA); - VecU8_append_span(res, cstr(" && ")); - append_on_the_left_stmt(res, tT, pl, tC, cr, tT, TB); - VecU8_append_span(res, cstr(" && ")); - append_on_the_left_stmt(res, tT, pl, tC, mod3_inc(cr), tT, TA); - VecU8_append_span(res, cstr(" && ")); - append_on_the_left_stmt(res, tT, pl, tC, mod3_inc(cr), tT, TB); - VecU8_append_span(res, cstr(") {\n")); - { - VecU8_append_span(res, cstr(SPACE12 "vec2 PA = ")); - append_intersection_eol_stmt(res, tT, pl, TA, tC, cr, mod3_inc(cr)); - VecU8_append_span(res, cstr(SPACE12 "vec2 PB = ")); - append_intersection_eol_stmt(res, tT, pl, TB, tC, cr, mod3_inc(cr)); - SpanU8 trig[3] = {get_firstborn_vertex_stmt(tT, pl), cstr("PA"), cstr("PB")}; - append_answering_stmt(res, (SpanSpanU8){.data = trig, .len = ARRAY_SIZE(trig)}, 12); - } - VecU8_append_span(res, cstr(SPACE8 "}\n")); - } - for (int rc = 0; rc < 3; rc++) { - VecU8_append_span(res, cstr(SPACE8 "if (")); - append_on_the_left_stmt(res, tT, pl, tC, rc, tT, TA); - VecU8_append_span(res, cstr(" && ")); - append_on_the_right_stmt(res, tT, pl, tC, mod3_inc(rc), tT, TA); - VecU8_append_span(res, cstr(" && ")); - append_on_the_left_stmt(res, tT, pl, tC, mod3_inc(rc), tT, TB); - VecU8_append_span(res, cstr(" && ")); - append_on_the_right_stmt(res, tT, pl, tC, mod3_dec(rc), tT, TB); - VecU8_append_span(res, cstr(") {\n")); - { - /* Case where TA and TB are in different 'thirds of surface' and the vertex of tC that defines - * border is outside tT. Result is a pentagon */ - VecU8_append_span(res, cstr(SPACE12 "if (")); - append_on_the_right_stmt(res, tT, TA, tT, TB, tC, mod3_inc(rc)); - VecU8_append_span(res, cstr(") {\n")); - { - VecU8_append_span(res, cstr(SPACE16 "vec2 PA = ")); - append_intersection_eol_stmt(res, tT, pl, TA, tC, rc, mod3_inc(rc)); - VecU8_append_span(res, cstr(SPACE16 "vec2 QA = ")); - append_intersection_eol_stmt(res, tT, TA, TB, tC, rc, mod3_inc(rc)); - VecU8_append_span(res, cstr(SPACE16 "vec2 QB = ")); - append_intersection_eol_stmt(res, tT, TA, TB, tC, mod3_inc(rc), mod3_dec(rc)); - VecU8_append_span(res, cstr(SPACE16 "vec2 PB = ")); - append_intersection_eol_stmt(res, tT, pl, TB, tC, mod3_inc(rc), mod3_dec(rc)); - SpanU8 pent[5] = {get_firstborn_vertex_stmt(tT, pl), cstr("PA"), cstr("QA"), cstr("QB"), cstr("PB")}; - append_answering_stmt(res, (SpanSpanU8){.data = pent, .len = ARRAY_SIZE(pent)}, 16); - } - VecU8_append_span(res, cstr(SPACE12 "}")); - if (!tables_turned) { - /* Case where TA and TB are in different sectors and rc++ is inside tT - * Result is a quadrangle */ - VecU8_append_span(res, cstr(" else {\n")); - VecU8_append_span(res, cstr(SPACE16 "vec2 PA = ")); - append_intersection_eol_stmt(res, tT, pl, TA, tC, rc, mod3_inc(rc)); - VecU8_append_span(res, cstr(SPACE16 "vec2 PB = ")); - append_intersection_eol_stmt(res, tT, pl, TB, tC, mod3_inc(rc), mod3_dec(rc)); - SpanU8 quad[4] = {get_firstborn_vertex_stmt(tT, pl), cstr("PA"), - get_firstborn_vertex_stmt(tC, mod3_inc(rc)), cstr("PB")}; - append_answering_stmt(res, (SpanSpanU8){.data = quad, .len = ARRAY_SIZE(quad)}, 16); - VecU8_append_span(res, cstr(SPACE12 "}")); - } - VecU8_append_span(res, cstr("\n")); - } - VecU8_append_span(res, cstr(SPACE8 "}\n")); - } - VecU8_append_span(res, cstr(SPACE "}\n\n")); - } -} - -/* It is assumed that it goes after two passes of generate_func_clip_triang_on_triang_case_where_some_vertex_stuck */ -void generate_func_clip_triang_on_triang_case_boring(VecU8* res) { - /* Star of David case */ - for (int cb = 0; cb < 3; cb++) { - VecU8_append_span(res, cstr(SPACE "if (")); - for (int i = 0; i < 3; i++) { - if (i) - VecU8_append_span(res, cstr(" && ")); - append_on_the_right_stmt(res, 'C', (i + cb) % 3, 'C', (i + cb + 1) % 3, 'T', i); - } - VecU8_append_span(res, cstr(") {\n")); - { - VecU8_append_span(res, cstr(SPACE8 "vec2 hex[6] = {\n")); - for (int ti = 0; ti < 3; ti++) { - for (int cj = 0; cj < 2; cj++) { - VecU8_append_vec(res, VecU8_format(SPACE12 "marie_intersect_lines(T.v%d, T.v%d, C.v%d, C.v%d),\n", - ti, (ti + 1) % 3, (ti + cb + cj) % 3, (ti + cb + cj + 1) % 3)); - } - } - VecU8_append_span(res, cstr(SPACE8 "};\n")); - VecU8_append_span(res, cstr(SPACE8 "for (int i = 0; i < 4; i++)\n" - SPACE12 "VecMarieTriangle_append(pile, (MarieTriangle){hex[i], hex[i + 1], hex[5]});\n")); - } - VecU8_append_span(res, cstr(SPACE "}\n")); - } - /* Wedge cases */ - for (int cf = 0; cf < 3; cf++) { - for (int ti = 0; ti < 3; ti++){ - VecU8_append_span(res, cstr(SPACE "if (")); - append_on_the_left_stmt(res, 'T', ti, 'T', mod3_dec(ti), 'C', cf); - VecU8_append_span(res, cstr(" && ")); - append_on_the_right_stmt(res, 'T', mod3_inc(ti), 'T', mod3_dec(ti), 'C', (cf + 2) % 3); - VecU8_append_span(res, cstr(" && ")); - append_on_the_left_stmt(res, 'C', cf, 'C', (cf + 2) % 3, 'T', (ti + 2) % 3); - VecU8_append_span(res, cstr(") {\n")); - { - SpanU8 quad[4] = {cstr("PA"), cstr("PB"), cstr("PC"), cstr("PD")}; - /* case A */ - VecU8_append_span(res, cstr(SPACE8 "if (")); - append_on_the_left_stmt(res, 'T', ti, 'T', mod3_dec(ti), 'C', mod3_inc(cf)); - VecU8_append_span(res, cstr(" && ")); - append_on_the_right_stmt(res, 'C', mod3_inc(cf), 'C', mod3_dec(cf), 'T', ti); - VecU8_append_span(res, cstr(" && ")); - append_on_the_right_stmt(res, 'C', mod3_inc(cf), 'C', mod3_dec(cf), 'T', mod3_inc(ti)); - VecU8_append_span(res, cstr(") {\n")); - { - VecU8_append_span(res, cstr(SPACE12 "vec2 PA = ")); - append_intersection_eol_stmt(res, 'T', mod3_dec(ti), ti, 'C', mod3_inc(cf), mod3_dec(cf)); - VecU8_append_span(res, cstr(SPACE12 "vec2 PB = ")); - append_intersection_eol_stmt(res, 'T', mod3_inc(ti), mod3_dec(ti), 'C', mod3_inc(cf), mod3_dec(cf)); - VecU8_append_span(res, cstr(SPACE12 "vec2 PC = ")); - append_intersection_eol_stmt(res, 'T', mod3_inc(ti), mod3_dec(ti), 'C', mod3_dec(cf), cf); - VecU8_append_span(res, cstr(SPACE12 "vec2 PD = ")); - append_intersection_eol_stmt(res, 'T', mod3_dec(ti), ti, 'C', mod3_dec(cf), cf); - append_answering_stmt(res, (SpanSpanU8){.data = quad, ARRAY_SIZE(quad)}, 12); - } - VecU8_append_span(res, cstr(SPACE8 "}\n")); - /* case B */ - VecU8_append_span(res, cstr(SPACE8 "if (")); - append_on_the_right_stmt(res, 'T', mod3_inc(ti), 'T', mod3_dec(ti), 'C', mod3_inc(cf)); - VecU8_append_span(res, cstr(" && ")); - append_on_the_right_stmt(res, 'C', cf, 'C', mod3_inc(cf), 'T', ti); - VecU8_append_span(res, cstr(" && ")); - append_on_the_right_stmt(res, 'C', cf, 'C', mod3_inc(cf), 'T', mod3_inc(ti)); - VecU8_append_span(res, cstr(") {\n")); - { - VecU8_append_span(res, cstr(SPACE12 "vec2 PA = ")); - append_intersection_eol_stmt(res, 'T', mod3_dec(ti), ti, 'C', cf, mod3_inc(cf)); - VecU8_append_span(res, cstr(SPACE12 "vec2 PB = ")); - append_intersection_eol_stmt(res, 'T', mod3_inc(ti), mod3_dec(ti), 'C', cf, mod3_inc(cf)); - VecU8_append_span(res, cstr(SPACE12 "vec2 PC = ")); - append_intersection_eol_stmt(res, 'T', mod3_inc(ti), mod3_dec(ti), 'C', mod3_dec(cf), cf); - VecU8_append_span(res, cstr(SPACE12 "vec2 PD = ")); - append_intersection_eol_stmt(res, 'T', mod3_dec(ti), ti, 'C', mod3_dec(cf), cf); - append_answering_stmt(res, (SpanSpanU8){.data = quad, ARRAY_SIZE(quad)}, 12); - } - VecU8_append_span(res, cstr(SPACE8 "}\n")); - } - VecU8_append_span(res, cstr(SPACE "}\n")); - } - } - -} - -NODISCARD VecU8 generate_func_clip_ccw_triang_with_ccw_triang_append_to_Vec() { - VecU8 res = VecU8_from_cstr( - "void marie_clip_ccw_triang_with_ccw_triang_append_to_Vec(MarieTriangle C, MarieTriangle T, VecMarieTriangle* pile) {\n"); - for (int ord = 0; ord < 18; ord++) { - VecU8_append_vec(&res, VecU8_format(SPACE "float M%d = marie_surface(", ord)); - for (int a = 0; a < 3; a++) { - if (a) - VecU8_append_span(&res, cstr(", ")); - int vsh = comparison_triang_groups[ord][a]; - VecU8_append(&res, (vsh / 10) == 1 ? 'C' : 'T'); - VecU8_append_span(&res, cstr(".v")); - VecU8_append(&res, '0' + vsh % 10); - } - VecU8_append_span(&res, cstr(");\n")); - } - generate_func_clip_triang_on_triang_case_where_some_vertex_stuck(&res, 'C', 'T', false); - generate_func_clip_triang_on_triang_case_where_some_vertex_stuck(&res, 'T', 'C', true); - generate_func_clip_triang_on_triang_case_boring(&res); - VecU8_append_span(&res, cstr("}\n\n")); - return res; -} - -void generate_marie_clipping_header() { - GeneratedHeader res = begin_header(cstr("l1_5/marie/clipping.h")); - VecU8_append_span(&res.result, cstr("#include \"../../l1/geom.h\"\n" - "#include \"../../../src/l1/marie/geom_alg_utils.h\"\n\n")); - VecU8_append_vec(&res.result, generate_func_clip_ccw_triang_with_ccw_triang_append_to_Vec()); - finish_header(res); -} - -#endif