[qfcc] Implement multi-vector dual

It's implemented as the Hodge dual, which is probably reasonable until
people complain. Both ⋆ and ! are supported, though the former is a
little hard to see in Consola.
This commit is contained in:
Bill Currie 2023-08-28 20:09:53 +09:00
parent 1329a1c43a
commit e2d812ab6a
7 changed files with 76 additions and 6 deletions

View file

@ -87,6 +87,7 @@ bool is_algebra (const struct type_s *type) __attribute__((pure));
struct type_s *algebra_type (struct type_s *type, struct expr_s *params); struct type_s *algebra_type (struct type_s *type, struct expr_s *params);
struct type_s *algebra_subtype (struct type_s *type, struct attribute_s *attr); struct type_s *algebra_subtype (struct type_s *type, struct attribute_s *attr);
struct type_s *algebra_mvec_type (algebra_t *algebra, pr_uint_t group_mask); struct type_s *algebra_mvec_type (algebra_t *algebra, pr_uint_t group_mask);
int algebra_count_flips (const algebra_t *alg, pr_uint_t a, pr_uint_t b) __attribute__((pure));
struct ex_value_s *algebra_blade_value (algebra_t *alg, const char *name); struct ex_value_s *algebra_blade_value (algebra_t *alg, const char *name);
struct symtab_s *algebra_scope (struct type_s *type, struct symtab_s *curscope); struct symtab_s *algebra_scope (struct type_s *type, struct symtab_s *curscope);
void algebra_print_type_str (struct dstring_s *str, const struct type_s *type); void algebra_print_type_str (struct dstring_s *str, const struct type_s *type);

View file

@ -492,6 +492,23 @@ static int pga_swaps_3d[16] = {
[0xd] = 1, // e032 [0xd] = 1, // e032
}; };
int
algebra_count_flips (const algebra_t *alg, pr_uint_t a, pr_uint_t b)
{
bool pga_2d = (alg->plus == 2 && alg->minus == 0 && alg->zero == 1);
bool pga_3d = (alg->plus == 3 && alg->minus == 0 && alg->zero == 1);
int swaps = count_flips (a, b);
if (pga_2d) {
swaps += pga_swaps_2d[a];
swaps += pga_swaps_2d[b];
}
if (pga_3d) {
swaps += pga_swaps_3d[a];
swaps += pga_swaps_3d[b];
}
return swaps;
}
ex_value_t * ex_value_t *
algebra_blade_value (algebra_t *alg, const char *name) algebra_blade_value (algebra_t *alg, const char *name)
{ {

View file

@ -1881,6 +1881,9 @@ unary_expr (int op, expr_t *e)
} }
break; break;
case '!': case '!':
if (is_algebra (get_type (e))) {
return algebra_dual (e);
}
if (is_constant (e)) { if (is_constant (e)) {
switch (extract_type (e)) { switch (extract_type (e)) {
case ev_entity: case ev_entity:
@ -1969,6 +1972,9 @@ unary_expr (int op, expr_t *e)
} }
break; break;
case '~': case '~':
if (is_algebra (get_type (e))) {
return algebra_reverse (e);
}
if (is_constant (e)) { if (is_constant (e)) {
switch (extract_type (e)) { switch (extract_type (e)) {
case ev_string: case ev_string:
@ -2076,6 +2082,8 @@ bitnot_expr:
return e; return e;
case REVERSE: case REVERSE:
return algebra_reverse (e); return algebra_reverse (e);
case DUAL:
return algebra_dual (e);
} }
internal_error (e, 0); internal_error (e, 0);
} }

View file

@ -1342,7 +1342,8 @@ geometric_product (expr_t *e1, expr_t *e2)
static expr_t * static expr_t *
regressive_product (expr_t *e1, expr_t *e2) regressive_product (expr_t *e1, expr_t *e2)
{ {
internal_error (e1, "not implemented"); notice (e1, "not implemented");
return 0;
} }
static expr_t * static expr_t *
@ -1458,11 +1459,39 @@ algebra_negate (expr_t *e)
expr_t * expr_t *
algebra_dual (expr_t *e) algebra_dual (expr_t *e)
{ {
if (e) { if (!is_algebra (get_type (e))) {
internal_error (e, "not implemented"); //FIXME check for being in an @algebra { } block
return error (e, "cannot take the dual of a scalar without context");
} }
notice (e, "not implemented"); auto algebra = algebra_get (get_type (e));
return 0; auto layout = &algebra->layout;
expr_t *a[layout->count] = {};
expr_t *b[layout->count] = {};
e = mvec_expr (e, algebra);
mvec_scatter (a, e, algebra);
pr_uint_t I_mask = (1u << algebra->dimension) - 1;
for (int i = 0; i < layout->count; i++) {
if (!a[i]) {
continue;
}
auto group = &layout->groups[i];
//FIXME assumes groups are mono-grade (either come up with something
//or reject mixed-grade groups)
pr_uint_t mask = I_mask ^ group->blades[0].mask;
int dual_ind = layout->group_map[layout->mask_map[mask]][0];
auto dual_group = &layout->groups[dual_ind];
auto dual_type = algebra_mvec_type (algebra, dual_group->group_mask);
auto dual = cast_expr (dual_type, a[i]);
if (algebra_count_flips (algebra, group->blades[0].mask, mask) & 1) {
b[dual_ind] = unary_expr ('-', dual);
} else {
b[dual_ind] = dual;
}
}
return mvec_gather (b, algebra);
} }
static void static void

View file

@ -276,6 +276,7 @@ STRING \"(\\.|[^"\\])*\"
"†" { return REVERSE; } "†" { return REVERSE; }
"" { return STAR; } "" { return STAR; }
"×" { return CROSS; } "×" { return CROSS; }
"⋆" { return DUAL; }
"%%" { "%%" {
return MOD; return MOD;

View file

@ -144,7 +144,7 @@ int yylex (void);
%left '+' '-' %left '+' '-'
%left '*' '/' '%' MOD SCALE GEOMETRIC %left '*' '/' '%' MOD SCALE GEOMETRIC
%left HADAMARD CROSS DOT WEDGE REGRESSIVE %left HADAMARD CROSS DOT WEDGE REGRESSIVE
%right <op> SIZEOF UNARY INCOP REVERSE STAR %right <op> SIZEOF UNARY INCOP REVERSE STAR DUAL
%left HYPERUNARY %left HYPERUNARY
%left '.' '(' '[' %left '.' '(' '['
@ -1702,6 +1702,7 @@ unary_expr
| INCOP unary_expr { $$ = incop_expr ($1, $2, 0); } | INCOP unary_expr { $$ = incop_expr ($1, $2, 0); }
| unary_expr INCOP { $$ = incop_expr ($2, $1, 1); } | unary_expr INCOP { $$ = incop_expr ($2, $1, 1); }
| unary_expr REVERSE { $$ = unary_expr (REVERSE, $1); } | unary_expr REVERSE { $$ = unary_expr (REVERSE, $1); }
| DUAL cast_expr %prec UNARY { $$ = unary_expr (DUAL, $2); }
| '+' cast_expr %prec UNARY { $$ = $2; } | '+' cast_expr %prec UNARY { $$ = $2; }
| '-' cast_expr %prec UNARY { $$ = unary_expr ('-', $2); } | '-' cast_expr %prec UNARY { $$ = unary_expr ('-', $2); }
| '!' cast_expr %prec UNARY { $$ = unary_expr ('!', $2); } | '!' cast_expr %prec UNARY { $$ = unary_expr ('!', $2); }

View file

@ -252,5 +252,18 @@ main (void)
s.scalar, s.bvec); s.scalar, s.bvec);
return 1; return 1;
} }
evengrades_t dual_e;
dual_e.mvec =e.mvec; // test both dual operators
if ((dvec3)dual_e.bvec != '-2 -3 -0.5'd || dual_e.scalar != 10) {
printf ("⋆odd != '-2 -3 -0.5' + 10: %lv %g\n", e.vec, e.tvec);
return 1;
}
oddgrades_t dual_s;
dual_s.mvec = !s.mvec; // test both dual operators
if ((scalar_t)dual_s.tvec != 4.5 || (dvec3)dual_s.vec != '7 22 23'd) {
printf ("!even != 4.5, '7 22 23': %g %lv\n",
s.scalar, s.bvec);
return 1;
}
return 0; // to survive and prevail :) return 0; // to survive and prevail :)
} }