Following the suggestions of Hamish Todd, group 0 forms the planar
quaternions (with the "complex number" in the first two components) and
transflections in group 1.
e0 is created only if there are null vectors in the algebra, and the 3d
and 2d basis groups have been rearranged to compensate in the changed
ordering in the basis blade array.
I was aware in the beginning that the signs were probably incorrect, but
I had left them as I wasn't sure how they worked. Thanks to enki
(bivector community), I was pointed in the right direction for getting
the calculations right: the product of a basis blade with its dual
(x !x) must product the positive pseudo-scalar.
I guess I had forgotten that new_mv() does *NOT* initialize the
components. Things just happened to work (usually) because memory was
not getting recycled.
According to enki (bivector community) when there are more than one null
vector in a geometry, usually all vectors are null, and it was what to
do with multiple null vectors that caused me to balk at using e0 for the
null vector. However, using e0 for the null vector makes life much
easier, especially as that's what most of the literature does. There
are plenty of places, particularly in layout handling, that still need
adjustment for the change, but things seem to work (minus duals, but
they were broken in the first place, thus the discussion with enki).
Lexing . as a single character makes it impossible to enter fractions.
Unfortunately, this means that . as dot product requires white space on
either side.
Currently only PGA(3) is supported, but that's because the parser is
rather simple (recursive descent with a lame lexer), but it works well
enough for playing with geometric algebra without having to recompile
every time.
This gives the resultant point the correct sign. Though the projective
divide would take care of the sign, this makes reading the point a
little less confusing (still need to sort out automatic blade reversals
for the likes of e31).
As the dot product is a metric product, using the metric is vital to
getting the correct results. This fixes the calculation of the closest
point on a line to a point other than the origin (and a whole pile of
other issues, I imagine).
Now that arrays work well enough for this case, no point in having the
workarounds (other than they're actually faster, but I'd like to
optimize *that* sometime).