[qfcc] Sort factors in summed terms

This allows them to be matched with cancelling factors. My fancy zero
test is now just that: a fancy zero:

    typedef @algebra(float(3,0,1)) PGA;
    typedef PGA.group_mask(0xa) bivector_t;
    typedef PGA.group_mask(0x1e) motor_t;
    typedef PGA.tvec point_t;
    typedef PGA.vec  plane_t;

    plane_t
    apply_motor (motor_t m, point_t p)
    {
        return (m * p * ~m).vec;
    }

    0000 nop there were plums...
    0001 adjstk 0, 0
    apply_motor:
    motor.r:32:{
    0002 with 2, 0, 1
    motor.r:33:     return (m * p * ~m).vec;
    0003 return (<void>)

The motor-point.r test fails because it uses (m * p * ~m).tvec to get
the value but the type system is slightly broken in that a mono-group
algebra type does not have a structure associated with it and thus the
"missing" field results in 0. Yes, I spent too long chasing that one,
too.
This commit is contained in:
Bill Currie 2023-09-30 15:13:14 +09:00
parent 546253cea7
commit 87cf48ffc4

View file

@ -27,7 +27,10 @@
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <string.h>
#include "QF/fbsearch.h"
#include "QF/heapsort.h"
#include "QF/math/bitop.h"
#include "tools/qfcc/include/algebra.h"
@ -422,6 +425,13 @@ is_sum (const expr_t *expr)
&& (expr->expr.op == '+' || expr->expr.op == '-'));
}
static bool __attribute__((pure))
is_mult (const expr_t *expr)
{
return (expr && expr->type == ex_expr
&& (expr->expr.op == '*' || expr->expr.op == HADAMARD));
}
static int __attribute__((pure))
count_terms (const expr_t *expr)
{
@ -440,12 +450,132 @@ count_terms (const expr_t *expr)
return terms;
}
static int __attribute__((pure))
count_factors (const expr_t *expr)
{
if (!is_mult (expr)) {
return 0;
}
auto e1 = expr->expr.e1;
auto e2 = expr->expr.e2;
int terms = !is_mult (e1) + !is_mult (e2);;
if (is_mult (e1)) {
terms += count_factors (expr->expr.e1);
}
if (is_mult (e2)) {
terms += count_factors (expr->expr.e2);
}
return terms;
}
static bool __attribute__((pure))
is_cross (const expr_t *expr)
{
return (expr && expr->type == ex_expr && (expr->expr.op == CROSS));
}
static void
distribute_factors_core (const expr_t *prod, const expr_t **factors, int *ind)
{
auto e1 = prod->expr.e1;
auto e2 = prod->expr.e2;
if (is_mult (e1)) {
distribute_factors_core (e1, factors, ind);
} else {
factors[(*ind)++] = e1;
}
if (is_mult (e2)) {
distribute_factors_core (e2, factors, ind);
} else {
factors[(*ind)++] = e2;
}
}
static void
distribute_factors (const expr_t *prod, const expr_t **factors)
{
if (!is_mult (prod)) {
internal_error (prod, "distribute_factors with no product");
}
int ind = 0;
distribute_factors_core (prod, factors, &ind);
}
static const expr_t *
collect_factors (type_t *type, int op, const expr_t **factors, int count)
{
if (!count) {
internal_error (0, "no factors to collect");
}
if (count == 1) {
return *factors;
}
const expr_t *a, *b;
if (count == 2) {
a = factors[0];
b = factors[1];
} else {
int mid = (count + 1) / 2;
a = collect_factors (type, op, factors, mid);
b = collect_factors (type, op, factors + mid, count - mid);
}
auto prod = typed_binary_expr (type, op, a, b);
return edag_add_expr (prod);
}
static int
expr_ptr_cmp (const void *_a, const void *_b)
{
auto a = *(const expr_t **) _a;
auto b = *(const expr_t **) _b;
return a - b;
}
#if 0
static void
insert_expr (const expr_t **array, const expr_t *e, int count)
{
auto dst = array;
if (e > *dst) {
dst = fbsearch (e, array, count, sizeof (dst[0]), expr_ptr_cmp);
}
memmove (dst + 1, dst, sizeof (expr_t *[count - (dst - array)]));
*dst = e;
}
#endif
static const expr_t *
sort_factors (type_t *type, const expr_t *e)
{
if (!is_mult (e)) {
internal_error (e, "not a product");
}
int count = count_factors (e);
const expr_t *factors[count + 1] = {};
distribute_factors (e, factors);
heapsort (factors, count, sizeof (factors[0]), expr_ptr_cmp);
auto mult = collect_factors (type, '*', factors, count);
return mult;
}
static const expr_t *
sum_expr_low (type_t *type, int op, const expr_t *a, const expr_t *b)
{
if (!a) {
return op == '-' ? neg_expr (b) : b;
}
if (!b) {
return a;
}
if (op == '-' && a == b) {
return 0;
}
auto sum = typed_binary_expr (type, op, a, b);
sum = fold_constants (sum);
sum = edag_add_expr (sum);
return sum;
}
static void
distribute_terms_core (const expr_t *sum,
const expr_t **adds, int *addind,
@ -480,34 +610,15 @@ distribute_terms_core (const expr_t *sum,
}
}
static const expr_t *
sum_expr_low (type_t *type, int op, const expr_t *a, const expr_t *b)
{
if (!a) {
return op == '-' ? neg_expr (b) : b;
}
if (!b) {
return a;
}
if (op == '-' && a == b) {
return 0;
}
auto sum = typed_binary_expr (type, op, a, b);
sum = fold_constants (sum);
sum = edag_add_expr (sum);
return sum;
}
static void
distribute_terms (const expr_t *sum, const expr_t **adds, const expr_t **subs)
{
int addind = 0;
int subind = 0;
if (!is_sum (sum)) {
internal_error (sum, "distribute_terms with no sum");
}
int addind = 0;
int subind = 0;
distribute_terms_core (sum, adds, &addind, subs, &subind, false);
}
@ -583,6 +694,18 @@ sum_expr (type_t *type, const expr_t *a, const expr_t *b)
merge_extends (adds, subs);
for (auto term = adds; *term; term++) {
if (is_mult (*term)) {
*term = sort_factors (type, *term);
}
}
for (auto term = subs; *term; term++) {
if (is_mult (*term)) {
*term = sort_factors (type, *term);
}
}
for (dstadd = adds, srcadd = adds; *srcadd; srcadd++) {
for (dstsub = subs, srcsub = subs; *srcsub; srcsub++) {
if (*srcadd == *srcsub) {