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.


 
q = .quaternion~new(1, 2, 3, 4)
q1 = .quaternion~new(2, 3, 4, 5)
q2 = .quaternion~new(3, 4, 5, 6)
r = 7
 
say "q            =" q
say "q1           =" q1
say "q2           =" q2
say "r            =" r
say "norm(q)      =" q~norm
say "-q           =" (-q)
say "q*           =" q~conjugate
say "q + r        =" q + r
say "q1 + q2      =" q1 + q2
say "q * r        =" q * r
q1q2 = q1 * q2
q2q1 = q2 * q1
say "q1 * q2      =" q1q2
say "q2 * q1      =" q2q1
say "q1 == q1     =" (q1 == q1)
say "q1q2 == q2q1 =" (q1q2 == q2q1)
 
 
::class quaternion
::method init
  expose r i j k
  use strict arg r, i = 0, j = 0, k = 0
 
-- quaternion instances are immutable, so these are
-- read only attributes
::attribute r GET
::attribute i GET
::attribute j GET
::attribute k GET
 
::method norm
  expose r i j k
  return rxcalcsqrt(r * r + i * i + j * j + k * k)
 
::method invert
  expose r i j k
  norm = self~norm
  return self~class~new(r / norm, i / norm, j / norm, k / norm)
 
::method negative
  expose r i j k
  return self~class~new(-r, -i, -j, -k)
 
::method conjugate
  expose r i j k
  return self~class~new(r, -i, -j, -k)
 
::method add
  expose r i j k
  use strict arg other
  if other~isa(.quaternion) then
     return self~class~new(r + other~r, i + other~i, j + other~j, k + other~k)
  else return self~class~new(r + other, i, j, k)
 
::method subtract
  expose r i j k
  use strict arg other
  if other~isa(.quaternion) then
     return self~class~new(r - other~r, i - other~i, j - other~j, k - other~k)
  else return self~class~new(r - other, i, j, k)
 
::method times
  expose r i j k
  use strict arg other
  if other~isa(.quaternion) then
     return self~class~new(r * other~r - i * other~i - j * other~j - k * other~k, -
                           r * other~i + i * other~r + j * other~k - k * other~j, -
                           r * other~j - i * other~k + j * other~r + k * other~i, -
                           r * other~k + i * other~j - j * other~i + k * other~r)
  else return self~class~new(r * other, i * other, j * other, k * other)
 
::method divide
  use strict arg other
  -- this is easier if everything is a quaternion
  if \other~isA(.quaternion) then other = .quaternion~new(other)
  -- division is multiplication with the inversion
  return self * other~invert
 
::method "=="
  expose r i j k
  use strict arg other
 
  if \other~isa(.quaternion) then return .false
  -- Note:  these are numeric comparisons, so we're using the "="
  -- method so those are handled correctly
  return r = other~r & i = other~i & j = other~j & k = other~k
 
::method "\=="
  use strict arg other
  return \self~"\=="(other)
 
::method "="
  -- this is equivalent of "=="
  forward message("==")
 
::method "\="
  -- this is equivalent of "\=="
  forward message("\==")
 
::method "<>"
  -- this is equivalent of "\=="
  forward message("\==")
 
::method "><"
  -- this is equivalent of "\=="
  forward message("\==")
 
-- some operator overrides -- these only work if the left-hand-side of the
-- subexpression is a quaternion
::method "*"
  forward message("TIMES")
 
::method "/"
  forward message("DIVIDE")
 
::method "-"
  -- need to check if this is a prefix minus or a subtract
  if arg() == 0 then
      forward message("NEGATIVE")
  else
      forward message("SUBTRACT")
 
::method "+"
  -- need to check if this is a prefix plus or an addition
  if arg() == 0 then
      return self  -- we can return this copy since it is immutable
  else
      forward message("ADD")
 
::method string
  expose r i j k
  return r self~formatnumber(i)"i" self~formatnumber(j)"j" self~formatnumber(k)"k"
 
::method formatnumber private
  use arg value
  if value > 0 then return "+" value
  else return "-" value~abs
 
-- override hashcode for collection class hash uses
::method hashCode
  expose r i j k
  return r~hashcode~bitxor(i~hashcode)~bitxor(j~hashcode)~bitxor(k~hashcode)
 
 
::requires rxmath LIBRARY
 
 