user
**Geometric algebra** is another name for Clifford algebras and it's basically an algebra containing a vector space 

V
{\displaystyle {\displaystyle {\mathcal {V}}}}
 and obeying the following axioms:

:   (
    a
    b
    )
    c
    =
    a
    (
    b
    c
    )


    a
    (
    b
    +
    c
    )
    =
    a
    b
    +
    a
    c


    (
    a
    +
    b
    )
    c
    =
    a
    c
    +
    b
    c


    ∀

    x
    ∈


    V
    ,

    x

    2
    ∈

    R
    {\displaystyle {\displaystyle {\begin{array}{c}(ab)c=a(bc)\\a(b+c)=ab+ac\\(a+b)c=ac+bc\\\forall \mathbf {x} \in {\mathcal {V}},\,\mathbf {x} ^{2}\in \mathbb {R} \end{array}}}}

The product operation in such algebra is called the *geometric product*. Elements are called *multivectors*, while multivectors in 

V
{\displaystyle {\displaystyle {\mathcal {V}}}}
 are just called *vectors*.

There are a few simple examples of geometric algebras. A trivial one for instance is simply 

R
{\displaystyle {\displaystyle \mathbb {R} }}
, where 

V
=

R
{\displaystyle {\displaystyle {\mathcal {V}}=\mathbb {R} }}
. The complex numbers also form a geometric algebra, where the vector space is the one-dimensional space of all purely imaginary numbers. Another example is the space of quaternions, where the vector space is the three-dimensional space of all linear combinations of 

(
i
,
j
,
k
)
{\displaystyle {\displaystyle (i,j,k)}}
.

The purpose of this task is to implement a geometric algebra with a vector space 

V
{\displaystyle {\displaystyle {\mathcal {V}}}}
 of dimension *n* of at least five, but for extra-credit you can implement a version with *n* arbitrary large. Using a dimension five is useful as it is the dimension required for the so-called *conformal model* which will be the subject of a derived task.

To ensure the unicity of the solution (that is, up to some isomorphism), we will also restrict ourselves to the so-called euclidean case, where the square of a non-zero vector is positive:

∀

x
∈


V
,


x
≠
0

⟹

x

2
>
0
{\displaystyle {\displaystyle \forall \mathbf {x} \in {\mathcal {V}},\,\mathbf {x} \neq 0\implies \mathbf {x} ^{2}>0}}
.

You can of course, for extra credit, implement the general case. This would require the definition of a parameter for the *signature* of the metric.

In order to show that your solution uses a vector space of dimension at least five, you will create a function n -> e(n) such that the vectors e(0), e(1), e(2), e(3), e(4) are linearly independent. To do so you will make them orthonormal with the following scalar product:

x
⋅

y
=
(

x

y
+

y

x
)

/
2
{\displaystyle {\displaystyle \mathbf {x} \cdot \mathbf {y} =(\mathbf {x} \mathbf {y} +\mathbf {y} \mathbf {x} )/2}}

The fact that this so-called *inner product* defines a scalar product is a consequence of the fourth axiom. To see it one just needs to notice the relation:

x

y
+

y

x
=
(

x
+

y

)

2
−


x

2
−


y

2
{\displaystyle {\displaystyle \mathbf {x} \mathbf {y} +\mathbf {y} \mathbf {x} =(\mathbf {x} +\mathbf {y} )^{2}-\mathbf {x} ^{2}-\mathbf {y} ^{2}}}

Once you'll have shown that your vector space is at least of dimension five, you will show that the axioms are satisfied. For this purpose you will pick three random multivectors *a*, *b* and *c*, along with a random vector 

x
{\displaystyle {\displaystyle \mathbf {x} }}
.

Producing a random vector is easy. Just use a pseudo-random generation function rand and create a vector:

r
a
n
d
o
m
V
e
c
t
o
r
(
)
=

∑

i
=
0

4

r
a
n
d
(
)


e

i
{\displaystyle {\displaystyle \mathrm {randomVector} ()=\sum \_{i=0}^{4}\mathrm {rand} ()\mathbf {e} \_{i}}}

Producing a random multivector is slightly more involved. It is known that when the dimension of 

V
{\displaystyle {\displaystyle {\mathcal {V}}}}
 is *n*, then the dimension of the algebra (seen as a vector space with its natural scalar multiplication) is 2n. This means that for n=5 there is a basis of 25 = 32 basis multivectors from which any multivector can be written as a linear combination. Create such a basis 

m

0
,

m

1
,
…
,

m

31
{\displaystyle {\displaystyle m\_{0},m\_{1},\ldots ,m\_{31}}}
 along with a function producting a random multivector:

r
a
n
d
o
m
M
u
l
t
i
v
e
c
t
o
r
(
)
=

∑

i
=
0

31

r
a
n
d
(
)

m

i
{\displaystyle {\displaystyle \mathrm {randomMultivector} ()=\sum \_{i=0}^{31}\mathrm {rand} ()m\_{i}}}

To summarize, to solve this task you will:

* define the inner product of two vectors : 

  x
  ⋅

  y
  =
  (

  x
  y
  +

  y
  x
  )

  /
  2
  {\displaystyle {\displaystyle \mathbf {x} \cdot \mathbf {y} =(\mathbf {xy} +\mathbf {yx} )/2}}
  .
* define the function e
* verify the orthonormality 

  e

  i
  ⋅


  e

  j
  =

  δ

  i
  ,
  j
  {\displaystyle {\displaystyle \mathbf {e} \_{i}\cdot \mathbf {e} \_{j}=\delta \_{i,j}}}
   for i, j in 

  {
  0
  ,
  1
  ,
  2
  ,
  3
  ,
  4
  }
  {\displaystyle {\displaystyle \{0,1,2,3,4\}}}
  .
* create a function returning a random multivector
* create a function returning a random vector
* verify the axioms for three rarndom multivectors *a*, *b*, *c* and a random vector **x**.

Optionally, you will repeat the last step a large number of times, in order to increase confidence in the result.
assistant
(formerly Perl 6)

Here we write a simplified version of the Clifford module. It is very general as it is of infinite dimension and also contains an anti-euclidean basis @ē in addition to the euclidean basis @e.

```
class MultiVector is Mix {
  subset Vector of ::?CLASS is export where *.grades.all == 1;

  method narrow { self.keys.any > 0 ?? self !!  (self{0} // 0) }
  method grades { self.keys.map: *.base(2).comb.sum }

  multi method new(Real $x) returns ::?CLASS { self.new-from-pairs: 0 => $x }
  multi method new(Str $ where /^^e(\d+)$$/) { self.new-from-pairs: (1 +< (2*$0)) => 1 }

  our @e is export = map { ::?CLASS.new: "e$_" }, ^Inf;

  my sub order(UInt:D $i is copy, UInt:D $j) {
    (state %){$i}{$j} //= do {
      my $n = 0;
      repeat {
	$i +>= 1;
	$n += [+] ($i +& $j).polymod(2 xx *);
      } until $i == 0;
      $n +& 1 ?? -1 !! 1;
    }
  }

  sub infix:<·>(Vector $x, Vector $y) returns Real is export { (($x*$y + $y*$x)/2){0} }
 
  multi infix:<+>(::?CLASS $A, ::?CLASS $B) returns ::?CLASS is export {
    return ::?CLASS.new-from-pairs: |$A.pairs, |$B.pairs;
  }
  multi infix:<+>(Real $s, ::?CLASS $B) returns ::?CLASS is export {
    samewith $B.new($s), $B
  }
  multi infix:<+>(::?CLASS $A, Real $s) returns ::?CLASS is export {
    samewith $s, $A
  }

  multi infix:<*>(::?CLASS $,  0) is export { 0  }
  multi infix:<*>(::?CLASS $A, 1) returns ::?CLASS is export { $A }
  multi infix:<*>(::?CLASS $A, Real $s) returns ::?CLASS is export {
    ::?CLASS.new-from-pairs: $A.pairs.map({Pair.new: .key, $s*.value})
  }
  multi infix:<*>(::?CLASS $A, ::?CLASS $B) returns ::?CLASS is export {
    ::?CLASS.new-from-pairs: gather
      for $A.pairs -> $a {
	for $B.pairs -> $b {
	  take ($a.key +^ $b.key) => [*]
	    $a.value, $b.value,
	    order($a.key, $b.key),
	    |grep +*, (
		|(1, -1) xx * Z*
		($a.key +& $b.key).polymod(2 xx *)
	    )
	}
      }
  }
  multi infix:<**>(::?CLASS $ , 0) returns ::?CLASS is export { ::?CLASS.new: (0 => 1).Mix }
  multi infix:<**>(::?CLASS $A, 1) returns ::?CLASS is export { $A }
  multi infix:<**>(::?CLASS $A, 2) returns ::?CLASS is export { $A * $A }
  multi infix:<**>(::?CLASS $A, UInt $n where $n %% 2) returns ::?CLASS is export { ($A ** ($n div 2)) ** 2 }
  multi infix:<**>(::?CLASS $A, UInt $n) returns ::?CLASS is export { $A * ($A ** ($n div 2)) ** 2 }

  multi infix:<*>(Real $s, ::?CLASS $A) returns ::?CLASS is export { $A * $s }
  multi infix:</>(::?CLASS $A, Real $s) returns ::?CLASS is export { $A * (1/$s) }
  multi prefix:<->(::?CLASS $A) returns ::?CLASS is export { return -1 * $A }
  multi infix:<->(::?CLASS $A, ::?CLASS $B) returns ::?CLASS is export { $A + -$B }
  multi infix:<->(::?CLASS $A, Real $s) returns ::?CLASS is export { $A + -$s }
  multi infix:<->(Real $s, ::?CLASS $A) returns ::?CLASS is export { $s + -$A }

  multi infix:<==>(::?CLASS $A, 0) returns Bool is export { $A.elems == 0 }
  multi infix:<==>(::?CLASS $A, ::?CLASS $B) returns Bool is export { samewith $A - $B, 0 }
  multi infix:<==>(Real $x, ::?CLASS $A) returns Bool is export { samewith $A, $x }
  multi infix:<==>(::?CLASS $A, Real $x) returns Bool is export { samewith $A, $A.new($x); }

  sub random is export {
      [+] map {
	  ::?CLASS.new-from-pairs: $_ => rand.round(.01)
      }, ^32;
  }
}

#########################################
##  Test code to verify the solution:  ##
#########################################

import MultiVector;
use Test;
 
constant N = 10;
plan 5;
 
subtest "Orthonormality", {
  for ^N X ^N -> ($i, $j) {
    my $s = $i == $j ?? 1 !! 0;
    ok @e[$i]·@e[$j] == $s, "e$i·e$j = $s";
  }
}
 
my ($a, $b, $c) = random() xx 3;
 
ok ($a*$b)*$c == $a*($b*$c), 'associativity';
ok $a*($b + $c) == $a*$b + $a*$c, 'left distributivity';
ok ($a + $b)*$c == $a*$c + $b*$c, 'right distributivity';
my @coeff = (.5 - rand) xx 5;
my $v = [+] @coeff Z* @e[^5];
ok ($v**2).narrow ~~ Real, 'contraction';

```