From 5b1ce309efa20dc6a9cf9243c07a88d8fc7cc92c Mon Sep 17 00:00:00 2001 From: Bill Currie Date: Mon, 28 Aug 2023 11:56:35 +0900 Subject: [PATCH] [qfcc] Implement multi-vector reversion For a change, something that's actually general rather than specific to PGA. --- tools/qfcc/include/algebra.h | 3 ++ tools/qfcc/source/algebra.c | 59 ++++++++++++++++++++++++++++++++ tools/qfcc/source/expr.c | 2 ++ tools/qfcc/source/expr_algebra.c | 50 ++++++++++++++++++++++++--- tools/qfcc/source/qc-lex.l | 2 +- tools/qfcc/source/qc-parse.y | 3 +- tools/qfcc/test/pga2d.r | 11 ++++++ 7 files changed, 124 insertions(+), 6 deletions(-) diff --git a/tools/qfcc/include/algebra.h b/tools/qfcc/include/algebra.h index ea67fcefc..75210b030 100644 --- a/tools/qfcc/include/algebra.h +++ b/tools/qfcc/include/algebra.h @@ -101,6 +101,9 @@ algebra_t *algebra_get (const struct type_s *type) __attribute__((pure)); int algebra_type_assignable (const struct type_s *dst, const struct type_s *src) __attribute__((pure)); struct type_s *algebra_base_type (const struct type_s *type) __attribute__((pure)); +bool is_mono_grade (const struct type_s *type) __attribute__((pure)); +int algebra_get_grade (const struct type_s *type) __attribute__((pure)); +int algebra_blade_grade (basis_blade_t blade) __attribute__((const)); struct expr_s *algebra_binary_expr (int op, struct expr_s *e1, struct expr_s *e2); diff --git a/tools/qfcc/source/algebra.c b/tools/qfcc/source/algebra.c index 1d54ac389..df5be6a6a 100644 --- a/tools/qfcc/source/algebra.c +++ b/tools/qfcc/source/algebra.c @@ -699,3 +699,62 @@ algebra_base_type (const type_t *type) } return ev_types[type->type]; } + +int +algebra_blade_grade (basis_blade_t blade) +{ + return count_bits (blade.mask); +} + +bool +is_mono_grade (const type_t *type) +{ + if (!is_algebra (type)) { + return true; + } + if (type->type == ev_invalid) { + return false; + } + auto alg = algebra_get (type); + auto layout = &alg->layout; + auto multivec = type->t.multivec; + int grade = -1; + for (int i = 0; i < layout->count; i++) { + pr_uint_t mask = 1u << i; + if (mask & multivec->group_mask) { + auto group = &layout->groups[i]; + for (int j = 0; j < group->count; j++) { + int blade_grade = algebra_blade_grade (group->blades[j]); + if (grade == -1) { + grade = blade_grade; + } + if (blade_grade != grade) { + return false; + } + } + } + } + return true; +} + +int +algebra_get_grade (const type_t *type) +{ + if (!is_algebra (type)) { + return 0; + } + if (type->type == ev_invalid) { + return -1; + } + auto alg = algebra_get (type); + auto layout = &alg->layout; + auto multivec = type->t.multivec; + for (int i = 0; i < layout->count; i++) { + pr_uint_t mask = 1u << i; + if (mask & multivec->group_mask) { + auto group = &layout->groups[i]; + return algebra_blade_grade (group->blades[0]); + } + } + return 0; +} diff --git a/tools/qfcc/source/expr.c b/tools/qfcc/source/expr.c index deac6b78e..a3d52731d 100644 --- a/tools/qfcc/source/expr.c +++ b/tools/qfcc/source/expr.c @@ -2071,6 +2071,8 @@ bitnot_expr: if (!is_math (get_type (e))) return error (e, "invalid type for unary +"); return e; + case REVERSE: + return algebra_reverse (e); } internal_error (e, 0); } diff --git a/tools/qfcc/source/expr_algebra.c b/tools/qfcc/source/expr_algebra.c index 551c61dd1..9f1b92a45 100644 --- a/tools/qfcc/source/expr_algebra.c +++ b/tools/qfcc/source/expr_algebra.c @@ -1421,14 +1421,56 @@ algebra_dual (expr_t *e) return 0; } +static void +set_sign (pr_type_t *val, int sign, const type_t *type) +{ + if (is_float (type)) { + (*(float *) val) = sign; + } else { + (*(double *) val) = sign; + } +} + expr_t * algebra_reverse (expr_t *e) { - if (e) { - internal_error (e, "not implemented"); + auto t = get_type (e); + auto algebra = algebra_get (t); + auto layout = &algebra->layout; + expr_t *r[layout->count] = {}; + e = mvec_expr (e, algebra); + mvec_scatter (r, e, algebra); + + for (int i = 0; i < layout->count; i++) { + if (!r[i]) { + continue; + } + auto ct = get_type (r[i]); + if (is_mono_grade (ct)) { + int grade = algebra_get_grade (ct); + if (grade & 2) { + r[i] = unary_expr ('-', r[i]); + } + } else { + auto group = &layout->groups[i]; + pr_type_t ones[group->count * type_size (algebra->type)]; + bool neg = false; + for (int j = 0; j < group->count; j++) { + int grade = algebra_blade_grade (group->blades[i]); + int sign = grade & 2 ? -1 : 1; + if (sign < 0) { + neg = true; + } + set_sign (&ones[j * type_size (algebra->type)], sign, ct); + } + if (neg) { + auto rev = new_value_expr (new_type_value (ct, ones)); + r[i] = new_binary_expr (HADAMARD, r[i], rev); + r[i]->e.expr.type = ct; + } + } } - notice (e, "not implemented"); - return 0; + return mvec_gather (r, algebra); } expr_t * diff --git a/tools/qfcc/source/qc-lex.l b/tools/qfcc/source/qc-lex.l index a9d493463..dcf14a524 100644 --- a/tools/qfcc/source/qc-lex.l +++ b/tools/qfcc/source/qc-lex.l @@ -273,7 +273,7 @@ STRING \"(\\.|[^"\\])*\" "•" { return DOT; } "∧" { return WEDGE; } "∨" { return REGRESSIVE; } -"†" { return DAGGER; } +"†" { return REVERSE; } "∗" { return STAR; } "×" { return CROSS; } diff --git a/tools/qfcc/source/qc-parse.y b/tools/qfcc/source/qc-parse.y index b5b62a583..df01be23d 100644 --- a/tools/qfcc/source/qc-parse.y +++ b/tools/qfcc/source/qc-parse.y @@ -144,7 +144,7 @@ int yylex (void); %left '+' '-' %left '*' '/' '%' MOD SCALE GEOMETRIC %left HADAMARD CROSS DOT WEDGE REGRESSIVE -%right SIZEOF UNARY INCOP DAGGER STAR +%right SIZEOF UNARY INCOP REVERSE STAR %left HYPERUNARY %left '.' '(' '[' @@ -1701,6 +1701,7 @@ unary_expr | unary_expr '.' unary_expr { $$ = field_expr ($1, $3); } | INCOP unary_expr { $$ = incop_expr ($1, $2, 0); } | unary_expr INCOP { $$ = incop_expr ($2, $1, 1); } + | unary_expr REVERSE { $$ = unary_expr (REVERSE, $1); } | '+' cast_expr %prec UNARY { $$ = $2; } | '-' cast_expr %prec UNARY { $$ = unary_expr ('-', $2); } | '!' cast_expr %prec UNARY { $$ = unary_expr ('!', $2); } diff --git a/tools/qfcc/test/pga2d.r b/tools/qfcc/test/pga2d.r index 7eb9b62e9..e70127aaa 100644 --- a/tools/qfcc/test/pga2d.r +++ b/tools/qfcc/test/pga2d.r @@ -219,5 +219,16 @@ main (void) printf ("didn't get 0: %g %g", tvec • tvecb, tvec * tvecb); return 0; } + e.mvec = e.mvec†; // odd + if ((dvec3)e.vec != '4 6 1'd || (scalar_t)e.tvec != -20) { + printf ("odd† != '4 6 1' + -20: %lv %g\n", e.vec, e.tvec); + return 1; + } + s.mvec = s.mvec†; // even + if (s.scalar != -9 || (dvec3)s.bvec != '-14 -44 -46'd) { + printf ("even† != -9, '-14 -44 -46': %g %lv\n", + s.scalar, s.bvec); + return 1; + } return 0; // to survive and prevail :) }