Quaternions   are an extension of the idea of   complex numbers.

A complex number has a real and complex part,   sometimes written as     a + bi,  

where     a     and     b     stand for real numbers, and     i     stands for the square root of minus 1.

An example of a complex number might be     -3 + 2i,    

where the real part,     a     is     -3.0     and the complex part,     b     is     +2.0.  

A quaternion has one real part and three imaginary parts,     i,       j,     and     k.  

A quaternion might be written as     a + bi + cj + dk.  

In the quaternion numbering system:

     i∙i = j∙j = k∙k = i∙j∙k = -1,         or more simply,
     ii  = jj  = kk  = ijk   = -1.  
The order of multiplication is important, as, in general, for two quaternions:

     q1     and     q2:         q1q2 ≠ q2q1.  
An example of a quaternion might be     1 +2i +3j +4k  

There is a list form of notation where just the numbers are shown and the imaginary multipliers    i,       j,     and     k     are assumed by position.

So the example above would be written as     (1, 2, 3, 4)  



Task

Given the three quaternions and their components: 

   q  = (1, 2, 3, 4) = (a,  b,  c,  d)
   q1 = (2, 3, 4, 5) = (a1, b1, c1, d1)
   q2 = (3, 4, 5, 6) = (a2, b2, c2, d2) 

And a wholly real number     r = 7.  



Create functions   (or classes)   to perform simple maths with quaternions including computing:

 The norm of a quaternion: 




=



a

2


+

b

2


+

c

2


+

d

2






{\displaystyle ={\sqrt {a^{2}+b^{2}+c^{2}+d^{2}}}}

 
 The negative of a quaternion: 
   = (-a, -b, -c, -d)  
 The conjugate of a quaternion: 
   = ( a, -b, -c, -d)  
 Addition of a real number     r     and a quaternion     q:  
  r + q = q + r = (a+r, b, c, d)  
 Addition of two quaternions: 
   q1 + q2 = (a1+a2, b1+b2, c1+c2, d1+d2)  
 Multiplication of a real number and a quaternion: 
   qr = rq = (ar, br, cr, dr)  
 Multiplication of two quaternions     q1     and   q2     is given by: 
   ( a1a2 − b1b2 − c1c2 − d1d2, 
   a1b2 + b1a2 + c1d2 − d1c2, 
   a1c2 − b1d2 + c1a2 + d1b2, 
   a1d2 + b1c2 − c1b2 + d1a2 )  
 Show that, for the two quaternions     q1     and     q2: 
 q1q2 ≠ q2q1  


If a language has built-in support for quaternions, then use it.



C.f.

   Vector products
   On Quaternions;   or on a new System of Imaginaries in Algebra.   By Sir William Rowan Hamilton LL.D, P.R.I.A., F.R.A.S., Hon. M. R. Soc. Ed. and Dub., Hon. or Corr. M. of the Royal or Imperial Academies of St. Petersburgh, Berlin, Turin and Paris, Member of the American Academy of Arts and Sciences, and of other Scientific Societies at Home and Abroad, Andrews' Prof. of Astronomy in the University of Dublin, and Royal Astronomer of Ireland.


 
with(ArrayTools);
 
module Quaternion()
 option object;
 local real := 0;
 local i := 0;
 local j := 0;
 local k := 0;
 
 export getReal::static := proc(self::Quaternion, $)
  return self:-real;
 end proc;
 
 export getI::static := proc(self::Quaternion, $)
  return self:-i;
 end proc;
 
 export getJ::static := proc(self::Quaternion, $)
  return self:-j;
 end proc;
 
 export getK::static := proc(self::Quaternion, $)
  return self:-k;
 end proc;
 
 export Norm::static := proc(self::Quaternion, $)
  return sqrt(self:-real^2 + self:-i^2 + self:-j^2 + self:-k^2);
 end proc;
 
 # NegativeQuaternion returns the additive inverse of the quaternion
 export NegativeQuaternion::static := proc(self::Quaternion, $)
  return Quaternion(- self:-real, - self:-i, - self:-j, - self:-k);
 end proc;
 
 export Conjugate::static := proc(self::Quaternion, $)
  return Quaternion(self:-real, - self:-i, - self:-j, - self:-k);
 end proc;
 
 # quaternion addition
 export `+`::static := overload ([
  proc(self::Quaternion, x::Quaternion) option overload;
   return Quaternion(self:-real + getReal(x), self:-i + getI(x), self:-j + getJ(x), self:-k + getK(x));
  end proc,
  proc(self::Quaternion, x::algebraic) option overload;
   return Quaternion(self:-real + x, self:-i, self:-j, self:-k);
  end proc,
  proc(x::algebraic, self::Quaternion) option overload;
   return Quaternion(x + self:-real, self:-i, self:-j, self:-k);
  end
 ]);
 
 # convert quaternion to additive inverse
 export `-`::static := overload([
  proc(self::Quaternion) option overload;
   return Quaternion(-self:-real, -self:-i, -self:-j, -self:-k);
  end
 ]);
 
 # quaternion multiplication is non-abelian so the `.` operator needs to be used
 export `.`::static := overload([
  proc(self::Quaternion, x::Quaternion) option overload;
  return Quaternion(self:-real * getReal(x) - self:-i * getI(x) - self:-j * getJ(x) - self:-k * getK(x),
                    self:-real * getI(x) + self:-i * getReal(x) + self:-j * getK(x) - self:-k * getJ(x),
                    self:-real * getJ(x) + self:-j * getReal(x) - self:-i * getK(x) + self:-k * getI(x),
                    self:-real * getK(x) + self:-k * getReal(x) + self:-i * getJ(x) - self:-j * getI(x));
  end proc,
  proc(self::Quaternion, x::algebraic) option overload;
   return Quaternion(self:-real * x, self:-i * x, self:-j * x, self:-k * x);
  end proc,
  proc(x::algebraic, self::Quaternion) option overload;
   return Quaternion(self:-real * x, self:-i * x, self:-j * x, self:-k * x);
  end
 ]);
 
 # redirect division to `.` operator
 export `*`::static := overload([
  proc(self::Quaternion, x::Quaternion) option overload;
   use `*` = `.` in return self * x; end use
  end proc,
  proc(self::Quaternion, x::algebraic) option overload;
   use `*` = `.` in return x * self; end use
  end proc,
  proc(x::algebraic, self::Quaternion) option overload;
   use `*` = `.` in return x * self; end use
  end
 ]);
 
 # convert quaternion to multiplicative inverse
 export `/`::static := overload([
  proc(self::Quaternion) option overload;
   return Conjugate(self) . (1/(Norm(self)^2));
  end proc
 ]);
 
 # QuaternionCommutator computes the commutator of self and x
 export QuaternionCommutator::static := proc(x::Quaternion, y::Quaternion, $)
  return (x . y) - (y . x);
 end proc;
 
 # display quaternion
 export ModulePrint::static := proc(self::Quaternion, $);
  return cat(self:-real, " + ", self:-i, "i + ", self:-j, "j + ", self:-k, "k"):
 end proc;
 
 export ModuleApply::static := proc()
  Object(Quaternion, _passed);
 end proc;
 
 export ModuleCopy::static := proc(new::Quaternion, proto::Quaternion, R::algebraic, imag::algebraic, J::algebraic, K::algebraic, $)
  new:-real := R;
  new:-i := imag;
  new:-j := J;
  new:-k := K;
 end proc;
end module:
 
q := Quaternion(1, 2, 3, 4):
q1 := Quaternion(2, 3, 4, 5):
q2 := Quaternion(3, 4, 5, 6):
r := 7:
 
quats := Array([q, q1, q2]):
print("q, q1, q2"):
seq(quats[i], i = 1..3);
print("norms"):
seq(Norm(quats[i]), i = 1..3);
print("negative"):
seq(NegativeQuaternion(quats[i]), i = 1..3);
print("conjugate"):
seq(Conjugate(quats[i]), i = 1..3);
print("addition of real number 7"):
seq(quats[i] + r, i = 1..3);
print("multiplication by real number 7"):
seq(quats[i] . r, i = 1..3);
print("division by real number 7"):
seq(quats[i] / 7, i = 1..3);
print("add quaternions q1 and q2"):
q1 + q2;
print("multiply quaternions q1 and q2");
q1 . q2;
print("multiply quaternions q2 and q1"):
q2 . q1;
print("quaternion commutator of q1 and q2"):
QuaternionCommutator(q1,q2);
print("divide q1 by q2"):
q1 / q2;
 