/* <head>
   math library for quaternions
   designed to be translated into other languages
     and be presented on the web with XML

   author: Douglas B Sweetser
           sweetser@TheWorld.com

   copyright 1999 Douglas B Sweetser

   see GPL license info at the end of this file

   </head> */

#include <math.h>
#include "Qlib.h"
#include <stdio.h>
#define PI 3.14159265359
#define DEGREES_TO_RADIANS 0.01745329252


/* for initialization, replacement, copying */
/* constructQ, replaceQ, qcopy, rqcopy */

/* <function name="constructQ" var="double T, double X, double Y, double Z" 
task="initialize a quaternion given 4 doubles"> */

Q*
constructQ(double T, double X, double Y, double Z)
{
  Q *Q1;

  Q1 = (Q *)malloc(sizeof(Q));

  Q1->T = T; Q1->X = X; Q1->Y = Y; Q1->Z = Z;

  return Q1;
}
/* </function> */


/* <function name="replaceQ" var="double T, double X, double Y, double Z, Q *Q1"
task="4 doubles replace a given quaternion"> */

void
replaceQ(double T, double X, double Y, double Z, Q *Q1)
{
  Q1->T = T;
  Q1->X = X;
  Q1->Y = Y;
  Q1->Z = Z;
}
/* </function> */


/* <function name="qcopy" var="Q *Q1"
task="returns a copy of a quaternion"> */

Q*
qcopy(Q *Q1)
{
  return constructQ(Q1->T, Q1->X, Q1->Y, Q1->Z);
}
/* </function> */


/* <function name="rqcopy" var="Q *Q1, Q *Q2"
task="copy of a quaternion replaces a Q"> */

void
rqcopy(Q *Q1, Q *Q2)
{
  Q2->T = Q1->T;
  Q2->X = Q1->X;
  Q2->Y = Q1->Y;
  Q2->Z = Q1->Z;
}
/* </function> */


/* Unary functions, result is a double */
/* dtau2, dabs, dabs_vector, dnorm, dnorm_vector, ddet */

/* <function name="dtau2" var="Q *Q1" 
task="returns the square of the inteveral of special relativity: t^2 - X.X"> */

double
dtau2(Q *Q1)
{
  return Q1->T * Q1->T - Q1->X * Q1->X - Q1->Y * Q1->Y - Q1->Z * Q1->Z;
}
/* </function> */


/* <function name="dabs" var="Q *Q1" 
task="returns the absolute value: (t^2 + X.X)^.5"> */

double
dabs(Q *Q1)
{
  return sqrt(Q1->T * Q1->T + Q1->X * Q1->X + Q1->Y * Q1->Y + Q1->Z * Q1->Z);
}
/* </function> */


/* <function name="dabs_vector" var="Q *Q1" 
task="returns the absolute value of the vector: (X.X)^.5"> */

double
dabs_vector(Q *Q1)
{
  return sqrt(Q1->X * Q1->X + Q1->Y * Q1->Y + Q1->Z * Q1->Z);
}
/* </function> */


/* <function name="dnorm" var="Q *Q1" 
task="returns the Euclidean norm"> */

double
dnorm(Q *Q1)
{
  return Q1->T * Q1->T + Q1->X * Q1->X + Q1->Y * Q1->Y + Q1->Z * Q1->Z;
}
/* </function> */


/* <function name="dnorm_vector" var="Q *Q1" 
task="returns the Euclidean norm of the vector"> */

double
dnorm_vector(Q *Q1)
{
  return Q1->X * Q1->X + Q1->Y * Q1->Y + Q1->Z * Q1->Z;
}
/* </function> */


/* <function name="ddet" var="Q *Q1" 
task="returns the deternimant"> */

double
ddet(Q *Q1)
{
  double D1 = 0;

  D1 = (Q1->T * Q1->T + Q1->X * Q1->X + Q1->Y * Q1->Y + Q1->Z * Q1->Z);
  D1 *= D1;

  return D1;
}
/* </function> */



/* Unary functions inverses, result is a double */
/* dtau2_inv,  dabs_inv,  dabs_vector_inv */
/* dnorm_inv,  dnorm_vector_inv,  ddet_inv */


/* <function name="dtau2_inv" var="Q *Q1" 
task="returns inverse of the square of the inteveral of special relativity: t^2 - X.X"> */

double
dtau2_inv(Q *Q1)
{
  if((Q1->T == 0) && (Q1->X == 0) && (Q1->Y == 0) && (Q1->Z == 0)) {
    printf("dividing by zero occured in dtau2_inv");
    exit(1);
  }
  
  return 1 / (Q1->T * Q1->T - Q1->X * Q1->X - Q1->Y * Q1->Y - Q1->Z * Q1->Z);
}
/* </function> */


/* <function name="dabs_inv" var="Q *Q1" 
task="returns inverse of the absolute value: (t^2 + X.X)^.5"> */

double
dabs_inv(Q *Q1)
{
  if((Q1->T == 0) && (Q1->X == 0) && (Q1->Y == 0) && (Q1->Z == 0)) {
    printf("dividing by zero occured in dabs_inv");
    exit(1);
  }
  return 1 / sqrt(Q1->T * Q1->T + Q1->X * Q1->X + Q1->Y * Q1->Y + Q1->Z * Q1->Z);
}
/* </function> */


/* <function name="dabs_vector_inv" var="Q *Q1" 
task="returns inverse of the absolute value of the vector: (X.X)^.5"> */

double
dabs_vector_inv(Q *Q1)
{
  if((Q1->X == 0) && (Q1->Y == 0) && (Q1->Z == 0)) {
    printf("dividing by zero occured in dvector_inv");
    exit(1);
  }
  return 1 / sqrt(Q1->X * Q1->X + Q1->Y * Q1->Y + Q1->Z * Q1->Z);
}
/* </function> */


/* <function name="dnorm_inv" var="Q *Q1" 
task="returns inverse of the Euclidean norm"> */

double
dnorm_inv(Q *Q1)
{
  if((Q1->T == 0) && (Q1->X == 0) && (Q1->Y == 0) && (Q1->Z == 0)) {
    printf("dividing by zero occured in dnorm_inv");
    exit(1);
  }
  return 1 / (Q1->T * Q1->T + Q1->X * Q1->X + Q1->Y * Q1->Y + Q1->Z * Q1->Z);
}
/* </function> */


/* <function name="dnorm_vector_inv" var="Q *Q1" 
task="returns inverse of the Euclidean norm of the vector"> */

double
dnorm_vector_inv(Q *Q1)
{
  if((Q1->X == 0) && (Q1->Y == 0) && (Q1->Z == 0)) {
    printf("dividing by zero occured in dnorm_vector_inv");
    exit(1);
  }
  return 1 / (Q1->X * Q1->X + Q1->Y * Q1->Y + Q1->Z * Q1->Z);
}
/* </function> */


/* <function name="ddet_inv" var="Q *Q1" 
task="returns inverse of the deternimant"> */

double
ddet_inv(Q *Q1)
{
  double D1 = 0;

  if((Q1->T == 0) && (Q1->X == 0) && (Q1->Y == 0) && (Q1->Z == 0)) {
    printf("dividing by zero occured in ddet_inv");
    exit(1);
  }

  D1 = 1 / (Q1->T * Q1->T + Q1->X * Q1->X + Q1->Y * Q1->Y + Q1->Z * Q1->Z);
  D1 *= D1;

  return D1;
}
/* </function> */



/* Unary functions */
/*  qscalar,  qvector,  qconj,  q_inv,  qadj,  qunit */
/* rqscalar, rqvector, rqconj, rq_inv, rqadj, rqunit */

/* <function name="qscalar" var="Q *Q1" 
task="returns the scalar of a quaternion: (t, 0)"> */

Q*
qscalar(Q *Q1)
{
  return constructQ(Q1->T,0,0,0);
}
/* </function> */


/* <function name="qvector" var="Q *Q1" 
task=" the vector of a quaternion: (0, X)"> */

Q*
qvector(Q *Q1)
{
  return constructQ(0, Q1->X, Q1->Y, Q1->Z);
}
/* </function> */


/* <function name="qconj" var="Q *Q1" 
task="returns the conjugate of a quaternion: (t, -X)"> */

Q*
qconj(Q *Q1)
{
  return constructQ(Q1->T, Q1->X * -1, Q1->Y * -1, Q1->Z * -1);
}  
/* </function> */


/* <function name="qinv" var="Q *Q1" 
task="returns the inverse of a quaternion: (t, -X)/(t^2 + X.X)"> */

Q*
qinv(Q *Q1)
{
  double norm_inv = 0;
  double norm_neg = 0;

  norm_inv = 1/(Q1->T * Q1->T + Q1->X * Q1->X + Q1->Y * Q1->Y + Q1->Z * Q1->Z);
  norm_neg = norm_inv * -1;

  return constructQ(Q1->T * norm_inv, Q1->X * norm_neg, Q1->Y * norm_neg, Q1->Z * norm_neg);
}  
/* </function> */


/* <function name="qadj" var="Q *Q1" 
task="returns the adjoint of a quaternion: (t, -X)(t^2 + X.X)"> */

Q*
qadj(Q *Q1)
{
  double norm = 0;
  double norm_neg = 0;

  norm = Q1->T * Q1->T + Q1->X * Q1->X + Q1->Y * Q1->Y + Q1->Z * Q1->Z;
  norm_neg = norm * -1;

  return constructQ(Q1->T * norm, Q1->X * norm_neg, Q1->Y * norm_neg, Q1->Z * norm_neg);
}  
/* </function> */


/* <function name="qunit" var="Q *Q1" 
task="returns the quaternion with unit norm: (t, X)/(t^2 + X.X)^.5"> */

Q*
qunit(Q *Q1)
{
  double C = 0;

  C = dabs_inv(Q1);

  return constructQ(Q1->T * C, Q1->X * C, Q1->Y * C, Q1->Z * C);
}  
/* </function> */


/* <function name="rqscalar" var="Q *Q1, Q *Q2" 
task="the scalar of a quaternion: (t, 0) replaces a Q"> */

void
rqscalar(Q *Q1, Q *Q2)
{
  replaceQ(Q1->T,0,0,0, Q2);
}
/* </function> */


/* <function name="rqvector" var="Q *Q1, Q *Q2" 
task="the vector of a quaternion: (0, X) replaces a Q"> */

void
rqvector(Q *Q1, Q *Q2)
{
  replaceQ(0, Q1->X, Q1->Y, Q1->Z, Q2);
}
/* </function> */


/* <function name="rqconj" var="Q *Q1, Q *Q2" 
task="the conjugate of a quaternion: (t, -X) replaces a Q"> */

void 
rqconj(Q *Q1, Q *Q2)
{
  replaceQ(Q1->T, Q1->X * -1, Q1->Y * -1, Q1->Z * -1, Q2);
}  
/* </function> */


/* <function name="rqinv" var="Q *Q1, Q *Q2" 
task="the inverse of a quaternion: (t, -X)/(t^2 + X.X) replaces a Q"> */

void 
rqinv(Q *Q1, Q *Q2)
{
  double norm_inv = 0;
  double norm_neg = 0;

  norm_inv = 1/(Q1->T * Q1->T + Q1->X * Q1->X + Q1->Y * Q1->Y + Q1->Z * Q1->Z);
  norm_neg = norm_inv * -1;

  replaceQ(Q1->T * norm_inv, Q1->X * norm_neg, Q1->Y * norm_neg, Q1->Z * norm_neg, Q2);
}  
/* </function> */


/* <function name="rqadj" var="Q *Q1, Q *Q2" 
task="the adjoint of a quaternion: (t, -X)(t^2 + X.X) replaces a Q"> */

void 
rqadj(Q *Q1, Q *Q2)
{
  double norm = 0;
  double norm_neg = 0;

  norm = Q1->T * Q1->T + Q1->X * Q1->X + Q1->Y * Q1->Y + Q1->Z * Q1->Z;
  norm_neg = norm * -1;

  replaceQ(Q1->T * norm, Q1->X * norm_neg, Q1->Y * norm_neg, Q1->Z * norm_neg, Q2);
}  
/* </function> */



/* <function name="rqunit" var="Q *Q1, Q *Q2" 
task="the quaternion with unit norm: (t, X)/(t^2 + X.X)^.5 replaces a Q"> */

void 
rqunit(Q *Q1, Q *Q2)
{
  double C = 0;

  C = dabs_inv(Q1);

  replaceQ(Q1->T * C, Q1->X * C, Q1->Y * C, Q1->Z * C, Q2);
}  
/* </function> */



/* Unary trig functions */
/*   qsin,   qcos,   qtan,   qasin,   qacos,   qatan */
/*  rqsin,  rqcos,  ratan,  rqasin,  rqacos,  rqatan */
/*  qsinh, qacosh, qatanh,  qasinh,  qacosh,  qatanh */
/* rqsinh, rqcosh, rqtanh, rqasinh, rqacosh, rqatanh */

/* <function name="qsin" var="Q *Q1" 
task="returns the sine of a quaternion: 
  (sin(t) cosh(|X|), cos(t) sinh(|X|) X/|X|)"> */

Q*
qsin(Q *Q1)
{
  double C = 0;
  double abs_v = 0;

  abs_v = dabs_vector(Q1);

  /* if the quaternion is a pure scalar */
  if (abs_v == 0) 
  {
    return constructQ(sin(Q1->T),0,0,0);
  }
 
  C = cos(Q1->T) * sinh(abs_v) / abs_v;

  return constructQ(sin(Q1->T) * cosh(abs_v), C * Q1->X, C * Q1->Y, C * Q1->Z);
}
/* </function> */


/* <function name="qcos" var="Q *Q1" 
task="returns the cosine of a quaternion: 
  (cos(t) cosh(|X|), - sin(t) sinh(|X|) X/|X|)"> */

Q*
qcos(Q *Q1)
{
  double C = 0;
  double abs_v = 0;

  abs_v = dabs_vector(Q1);

  /* if the quaternion is a pure scalar */
  if (abs_v == 0) 
  {
    return constructQ(cos(Q1->T),0,0,0);;
  }
 
  C = - sin(Q1->T) * sinh(abs_v) / abs_v;

  return constructQ(cos(Q1->T) * cosh(abs_v), C * Q1->X, C * Q1->Y, C * Q1->Z);
}
/* </function> */


/* <function name="qtan" var="Q *Q1" 
task="returns he tangent of a quaternion: sin(q)/cos(q)"> */

Q*
qtan(Q *Q1)
{
  double abs_v = 0;
  Q *Q2;
  
  Q2 = constructQ(0,0,0,0);

  abs_v = dabs_vector(Q1);

  /* if the quaternion is a pure scalar */
  if (abs_v == 0) 
  {
    Q2->T = tan(Q1->T);
    return Q2;
  }

  Q2 = qxinv(qsin(Q1), qcos(Q1));

  return Q2;
}
/* </function> */


/* <function name="qasin" var="Q *Q1" 
task="returns the arc sine of a quaternion: 
  -V/|V| asinh(q V/|V|)"> */

Q*
qasin(Q *Q1)
{
  double C = 0;
  double abs_v = 0;
  Q *Q2;
  Q *Qv;
  
  Q2 = constructQ(0,0,0,0);
  Qv = constructQ(0,1,0,0);

  abs_v = dabs_vector(Q1);

  if (abs_v != 0) 
  {
    Qv = qvector(qxs(Q1, 1/abs_v));
  }
 
  Q2 = qx(Qv, qasinh(qx(Q1, qconj(Qv))));

  return Q2;
}
/* </function> */


/* <function name="qacos" var="Q *Q1" 
task="returns the arc cosine of a quaternion: 
  -V/|V| asinh(q V/|V|)"> */

Q*
qacos(Q *Q1)
{
  double C = 0;
  double abs_v = 0;
  Q *Q2;
  Q *Qv;
  
  Q2 = constructQ(0,0,0,0);
  Qv = constructQ(0,1,0,0);

  abs_v = dabs_vector(Q1);

  if (abs_v != 0) 
  {
    Qv = qvector(qxs(Q1, -1/abs_v));
  }
 
  Q2 = qx(Qv, qacosh(Q1));

  return Q2;
}
/* </function> */


/* <function name="qatan" var="Q *Q1" 
task="return the atan of a quaternion: 
  -V/|V| atanh(q V/|V|)"> */

Q*
qatan(Q *Q1)
{
  double C = 0;
  double abs_v = 0;
  Q *Q2;
  Q *Qv;
  
  Q2 = constructQ(0,0,0,0);
  Qv = constructQ(0,1,0,0);

  abs_v = dabs_vector(Q1);

  if (abs_v != 0) 
  {
    Qv = qvector(qxs(Q1, 1/abs_v));
  }
 
  Q2 = qx(qconj(Qv), qatanh(qx(Q1, Qv)));

  return Q2;
}
/* </function> */



/* <function name="qsinh" var="Q *Q1" 
task="returns the sinh of a quaternion: 
(sinh(t) cos(|X|), cosh(t) sin(|X|) X/|X|)"> */

Q*
qsinh(Q *Q1)
{
  double C = 0;
  double abs_v = 0;

  abs_v = dabs_vector(Q1);
  
  /* if the quaternion is a pure scalar */ 
  if (abs_v == 0) 
  {
    return constructQ(sinh(Q1->T),0,0,0);
  }
 
  C = cosh(Q1->T) * sin(abs_v) / abs_v;

  return constructQ(sinh(Q1->T) * cos(abs_v), C * Q1->X, C * Q1->Y, C * Q1->Z);
}
/* </function> */


/* <function name="qcosh" var="Q *Q1" 
task="returns the cosh of a quaternion: 
(cosh(t) cos(|X|), sinh(t) sin(|X|) X/|X|)"> */

Q*
qcosh(Q *Q1)
{
  double C = 0;
  double abs_v = 0;

  abs_v = dabs_vector(Q1);
  
  /* if the quaternion is a pure scalar */
  if (abs_v == 0) 
  {
    return constructQ(cosh(Q1->T),0,0,0);
  }
 
  C = sinh(Q1->T) * sin(abs_v) / abs_v;

  return constructQ(cosh(Q1->T) * cos(abs_v), C * Q1->X, C * Q1->Y, C * Q1->Z);  
}
/* </function> */


/* <function name="qtanh" var="Q *Q1" 
task="returns the tanh of a quaternion: sinh(q)/cosh(q)"> */

Q*
qtanh(Q *Q1)
{
  double abs_v = 0;
  Q *Q2;
  
  Q2 = constructQ(0,0,0,0);

  abs_v = dabs_vector(Q1);

  /* if the quaternion is a pure scalar */
  if (abs_v == 0) 
  {
    Q2->T = tanh(Q1->T);
    return Q2;
  }

  Q2 = qxinv(qsinh(Q1), qcosh(Q1));

  return Q2;
}
/* </function> */


/* <function name="qasinh" var="Q *Q1" 
task="returns the asinh of a quaternion: 
ln(q + (q^2 + 1)^.5)"> */

Q*
qasinh(Q *Q1)
{
  Q *Qone;
  Q *Q2;
  Qone = constructQ(1,0,0,0);
  Q2 = constructQ(0,0,0,0);

  Q2 = qln(qsum(qtotheN(qsum(qx(Q1,Q1), Qone), .5), Q1));

  return Q2;
}
/* </function> */


/* <function name="qacosh" var="Q *Q1" 
task="returns the acosh of a quaternion: 
ln(q +/- (q^2 - 1)^.5)"> */

Q*
qacosh(Q *Q1)
{
  Q *Qone;
  Q *Q2;
  Q *Qsumroot;
  Q *Qdifroot;
  Qone = constructQ(1,0,0,0);
  Q2 = constructQ(0,0,0,0);
  Qsumroot = constructQ(0,0,0,0);
  Qdifroot = constructQ(0,0,0,0);

  Q2 = qtotheN(qdif(qx(Q1, Q1), Qone), .5);
  Qsumroot = qsum(Q1, Q2);
  Qdifroot = qdif(Q1, Q2);

  if (dnorm(Qsumroot) > dnorm(Qdifroot)) {
    Q2 = qln(Qsumroot);
  }
  else {
    Q2 = qln(Qdifroot);
  }

  return Q2;
}
/* </function> */


/* <function name="qatanh" var="Q *Q1" 
task="returns the atanh of a quaternion: 
.5 ln((1 + q)/(1 - q))"> */

Q*
qatanh(Q *Q1)
{
  double vnorm = 0;
  Q *Qone;
  Q *Q2;
  Qone = constructQ(1,0,0,0);
  Q2 = constructQ(0,0,0,0);

  Q2 = qxs(qln(qxinv(qsum(Qone, Q1), qdif(Qone, Q1))), 0.5);

  /* this feels like a hack... */
  vnorm = dnorm_vector(Q1);

  if (vnorm == 0 && Q1->T > 1) {
    Q2->X *= -1;
  }

  return Q2;
}
/* </function> */



/* <function name="rqsin" var="Q *Q1, Q *Q2" 
task="the sine of a quaternion: 
  (sin(t) cosh(|X|), cos(t) sinh(|X|) X/|X|) replaces a Q"> */

void
rqsin(Q *Q1, Q *Q2)
{
  double C = 0;
  double abs_v = 0;

  abs_v = dabs_vector(Q1);

  /* if the quaternion is a pure scalar */
  if (abs_v == 0) 
  {
    replaceQ(sin(Q1->T), 0, 0, 0, Q2);
    return;
  }
 
  C = cos(Q1->T) * sinh(abs_v) / abs_v;

  replaceQ(sin(Q1->T) * cosh(abs_v), C * Q1->X, C * Q1->Y, C * Q1->Z, Q2);
}
/* </function> */


/* <function name="rqcos" var="Q *Q1, Q *Q2" 
task="the cosine of a quaternion: 
  (cos(t) cosh(|X|), sin(t) sinh(|X|) X/|X|) replaces a Q"> */

void
rqcos(Q *Q1, Q *Q2)
{
  double C = 0;
  double abs_v = 0;
  
  abs_v = dabs_vector(Q1);

  /* if the quaternion is a pure scalar */
  if (abs_v == 0) 
  {
    replaceQ(cos(Q1->T),0,0,0, Q2);
    return;
  }
 
  C = - sin(Q1->T) * sinh(abs_v) / abs_v;

  replaceQ(cos(Q1->T) * cosh(abs_v), C * Q1->X, C * Q1->Y, C * Q1->Z, Q2);
}
/* </function> */


/* <function name="rqtan" var="Q *Q1, Q *Q2" 
task="The tangent of a quaternion: 
  sin(q)/cos(q)  (perhaps not the most efficient algorythm, but simple) replaces a Q"> */

void
rqtan(Q *Q1, Q *Q2)
{
  double abs_v = 0;
  
  abs_v = dabs_vector(Q1);

  /* if the quaternion is a pure scalar */
  if (abs_v == 0) 
  {
    replaceQ(tan(Q1->T),0,0,0, Q2);
    return;
  }

  rqcopy(qxinv(qsin(Q1), qcos(Q1)), Q2);
}
/* </function> */


/* <function name="rqasin" var="Q *Q1, Q *Q2" 
task="the arc sine of a quaternion: 
  -V/|V| asinh(q V/|V|) replaces a Q"> */

void
rqasin(Q *Q1, Q *Q2)
{
  double C = 0;
  double abs_v = 0;
  Q *Qv;
  
  Qv = constructQ(0,1,0,0);

  abs_v = dabs_vector(Q1);

  if (abs_v != 0) 
  {
    Qv = qvector(qxs(Q1, 1/abs_v));
  }
 
  rqcopy(qx(Qv, qasinh(qx(Q1, qconj(Qv)))), Q2);
}
/* </function> */


/* <function name="rqacos" var="Q *Q1, Q *Q2" 
task="the arc cosine of a quaternion: 
  -V/|V| asinh(q V/|V|) replaces a Q"> */

void
rqacos(Q *Q1, Q *Q2)
{
  double C = 0;
  double abs_v = 0;
  Q *Qv;
  
  Qv = constructQ(0,1,0,0);

  abs_v = dabs_vector(Q1);

  if (abs_v != 0) 
  {
    Qv = qvector(qxs(Q1, -1/abs_v));
  }
 
  rqcopy(qx(Qv, qacosh(Q1)), Q2);
}
/* </function> */


/* <function name="rqatan" var="Q *Q1, Q *Q2" 
task="return the arc tangent of a quaternion: 
  -V/|V| atanh(q V/|V|) replaces a Q"> */

void
rqatan(Q *Q1, Q *Q2)
{
  double C = 0;
  double abs_v = 0;
  Q *Qv;
  
  Qv = constructQ(0,1,0,0);

  abs_v = dabs_vector(Q1);

  if (abs_v != 0) 
  {
    Qv = qvector(qxs(Q1, 1/abs_v));
  }
 
  rqcopy(qx(qconj(Qv), qatanh(qx(Q1, Qv))), Q2);
}
/* </function> */



/* <function name="rqsinh" var="Q *Q1, Q *Q2" 
task="the hyperbolic sine of a quaternion: 
(sinh(t) cos(|X|), cosh(t) sin(|X|) X/|X|) replaces a Q"> */

void
rqsinh(Q *Q1, Q *Q2)
{
  double C = 0;
  double abs_v = 0;
  
  abs_v = dabs_vector(Q1);
  
  /* if the quaternion is a pure scalar */ 
  if (abs_v == 0) 
  {
    replaceQ(sinh(Q1->T),0,0,0, Q2);
    return;
  }
 
  C = cosh(Q1->T) * sin(abs_v) / abs_v;

  replaceQ(sinh(Q1->T) * cos(abs_v), C * Q1->X, C * Q1->Y, C * Q1->Z, Q2);
}
/* </function> */


/* <function name="rqcosh" var="Q *Q1, Q *Q2" 
task="the hyperbolic cosine of a quaternion: 
(cosh(t) cos(|X|), sinh(t) sin(|X|) X/|X|) replaces a Q"> */

void
rqcosh(Q *Q1, Q *Q2)
{
  double C = 0;
  double abs_v = 0;
  
  abs_v = dabs_vector(Q1);
  
  /* if the quaternion is a pure scalar */
  if (abs_v == 0) 
  {
    replaceQ(cosh(Q1->T),0,0,0, Q2);
    return;
  }
 
  C = sinh(Q1->T) * sin(abs_v) / abs_v;

  replaceQ(cosh(Q1->T) * cos(abs_v), C * Q1->X, C * Q1->Y, C * Q1->Z, Q2);  
}
/* </function> */


/* <function name="rqtanh" var="Q *Q1, Q *Q2" 
task="The tangent of a quaternion: 
sinh(q)/cosh(q)  (perhaps not the most efficient algorythm, but simple) replaces a Q"> */

void
rqtanh(Q *Q1, Q *Q2)
{
  double abs_v = 0;
  
  abs_v = dabs_vector(Q1);

  /* if the quaternion is a pure scalar */
  if (abs_v == 0) 
  {
    Q2->T = tanh(Q1->T);
    return;
  }

  rqcopy(qxinv(qsinh(Q1), qcosh(Q1)), Q2);
}
/* </function> */


/* <function name="rqasinh" var="Q *Q1, Q *Q2" 
task="the arc hyperbolic sine of a quaternion: 
ln(q + (q^2 + 1)^.5) replaces a Q"> */

void
rqasinh(Q *Q1, Q *Q2)
{
  Q *Qone;

  Qone = constructQ(1,0,0,0);

  rqcopy(qln(qsum(qtotheN(qsum(qx(Q1,Q1), Qone), .5), Q1)), Q2);
}
/* </function> */


/* <function name="rqacosh" var="Q *Q1, Q *Q2" 
task="the arc hyperbolic sine of a quaternion: 
ln(q +/- (q^2 - 1)^.5) replaces a Q"> */

void
rqacosh(Q *Q1, Q *Q2)
{
  Q *Qone;
  Q *Qsumroot;
  Q *Qdifroot;

  Qone = constructQ(1,0,0,0);
  Qsumroot = constructQ(0,0,0,0);
  Qdifroot = constructQ(0,0,0,0);

  rqcopy(qtotheN(qdif(qx(Q1, Q1), Qone), .5), Q2);
  rqsum(Q1, Q2, Qsumroot);
  rqdif(Q1, Q2, Qdifroot);

  if (dnorm(Qsumroot) > dnorm(Qdifroot)) {
    rqcopy(qln(Qsumroot), Q2);
  }
  else {
    rqcopy(qln(Qdifroot), Q2);
  }
}
/* </function> */


/* <function name="rqatanh" var="Q *Q1, Q *Q2" 
task="the arc hyperbolic tan of a quaternion: 
.5 ln((1 + q)/(1 - q)) replaces a Q"> */

void
rqatanh(Q *Q1, Q *Q2)
{
  double vnorm = 0;
  Q *Qone;

  Qone = constructQ(1,0,0,0);

  rqcopy(qxs(qln(qxinv(qsum(Qone, Q1), qdif(Qone, Q1))), 0.5), Q2);

  /* this feels like a hack... */
  vnorm = dnorm_vector(Q1);

  if (vnorm == 0 && Q1->T > 1) {
    Q2->X *= -1;
  }
}
/* </function> */



/* Exponentials, logs */
/*  qexp,  qtotheN,  qtotheQ,  qln,  qlog */
/* rqexp, rqtotheN, rqtotheQ, rqln, rqlog */

/* <function name="qexp" var="Q *Q1" 
task="return the exponetial of a quaternion: 
(exp(t) cos(|X|), exp(t) sin(|X|) X/|X|)"> */

Q*
qexp(Q *Q1)
{
  double C = 0;
  double abs_v = 0;
  
  abs_v = dabs_vector(Q1);

  /* if the quaternion is a pure scalar */
  if (abs_v == 0) 
  {
    return constructQ(exp(Q1->T),0,0,0);
  }
 
  C = exp(Q1->T) * sin(abs_v) / abs_v;

  return constructQ(exp(Q1->T) * cos(abs_v), C * Q1->X, C * Q1->Y, C * Q1->Z);
}
/* </function> */


/* <function name="qtotheN" var="Q *Q1, double D, Q *Q2)" 
task="return the Nth power of a quaternion: 
exp (ln(q) x N)"> */

Q*
qtotheN(Q *Q1, double D)
{
  Q *Q2;
  Q2 = constructQ(0,0,0,0);

  Q2 = qexp(qxs(qln(Q1), D));

  return Q2;
}
/* </function> */


/* <function name="qtotheQ" var="Q *Q1, Q *Q2)" 
task="return the Nth power of a quaternion: 
exp (ln(q) x q)    (Note: method may have roundoff problems)"> */

Q*
qtotheQ(Q *Q1, Q *Q2)
{
  Q *Q3;
  Q3 = constructQ(0,0,0,0);

  rqcopy(qexp(qx(qln(Q1), Q2)), Q3);

  return Q3;
}
/* </function> */


/* <function name="qln" var="Q *Q1" 
task="return the natural logrhythm of a quaternion: 
(0.5 ln t^2 + V.V, atan2(|X|, t) X/|X|)"> */

Q*
qln(Q *Q1)
{
  double C = 0;
  double abs_v = 0;
  
  abs_v = dabs_vector(Q1);

  /* if the quaternion is a pure scalar */
  if (abs_v == 0) 
  {
    if (Q1->T > 0) {
        return constructQ(log(Q1->T),0,0,0);
    }
    /* wish I knew why this was valid */
    else {
        return constructQ(log(-1 * Q1->T),PI,0,0);
    }
  }
 
  C = atan2(abs_v, Q1->T) / abs_v;

  return constructQ(.5 * log(Q1->T * Q1->T + abs_v * abs_v), C * Q1->X, C * Q1->Y, C * Q1->Z);
}


/* </function> */
/* <function name="qlog" var="Q *Q1" 
task="return the base 10 logrhythm of a quaternion: 
ln(q)/ln(10)"> */

Q*
qlog(Q *Q1)
{
  Q *Q2;
  Q2 = constructQ(0,0,0,0);

  Q2 = qxs(qln(Q1), 1 / log(10));

  return Q2;
}
/* </function> */



/* <function name="rqexp" var="Q *Q1, Q *Q2" 
task="the exponetial of a quaternion: 
(exp(t) cos(|X|), exp(t) sin(|X|) X/|X|) replaces a Q"> */

void
rqexp(Q *Q1, Q *Q2)
{
  double C = 0;
  double abs_v = 0;

  abs_v = dabs_vector(Q1);

  /* if the quaternion is a pure scalar */
  if (abs_v == 0) 
  {
    replaceQ(exp(Q1->T),0,0,0, Q2);
    return;
  }
 
  C = exp(Q1->T) * sin(abs_v) / abs_v;

  replaceQ(exp(Q1->T) * cos(abs_v), C * Q1->X, C * Q1->Y, C * Q1->Z, Q2);
}
/* </function> */


/* <function name="rqtotheN" var="Q *Q1, Q *Q2, double D, Q *Q2)" 
task="the Nth power of a quaternion: 
exp (ln(q) x N) replaces a Q"> */

void
rqtotheN(Q *Q1, double D, Q *Q2)
{
  rqexp(qxs(qln(Q1), D), Q2);
}
/* </function> */


/* <function name="rqtotheQ" var="Q *Q1, Q *Q2, Q *Q2)" 
task="the Nth power of a quaternion: 
exp (ln(q) x q)    (Note: method may have roundoff problems) replaces a Q"> */

void
rqtotheQ(Q *Q1, Q *Q2, Q *Q3)
{
  rqexp(qx(qln(Q1), Q2), Q3);
}
/* </function> */


/* <function name="rqln" var="Q *Q1, Q *Q2" 
task="the natural logrhythm of a quaternion: 
(0.5 ln t^2 + V.V, atan2(|X|, t) X/|X|) replaces a Q"> */

void
rqln(Q *Q1, Q *Q2)
{
  double C = 0;
  double abs_v = 0;

  abs_v = dabs_vector(Q1);

  /* if the quaternion is a pure scalar */
  if (abs_v == 0) 
  {
    if (Q1->T > 0) {
        replaceQ(log(Q1->T),0,0,0, Q2);
        return;
    }
    /* wish I knew why this was valid */
    else {
        replaceQ(log(-1 * Q1->T),PI,0,0, Q2);
        return;
    }
  }
 
  C = atan2(abs_v, Q1->T) / abs_v;

  replaceQ(.5 * log(Q1->T * Q1->T + abs_v * abs_v), C * Q1->X, C * Q1->Y, C * Q1->Z, Q2);
}


/* </function> */
/* <function name="rqlog" var="Q *Q1, Q *Q2" 
task="the base 10 logrhythm of a quaternion: 
ln(q)/ln(10) replaces a Q"> */

void
rqlog(Q *Q1, Q *Q2)
{
  rqcopy(qxs(qln(Q1), 1 / log(10)), Q2);
}
/* </function> */



/* addition and subtraction */
/*  qsum,  qdif */
/* rqsum, rqdif */

/* <function name="qsum" var="Q *Q1, Q *Q2"
task="returns the sum of 2 quaternions: (t + t', X + X'"> */

Q*
qsum(Q *Q1, Q *Q2)
{
  return constructQ(Q1->T + Q2->T, Q1->X + Q2->X, Q1->Y + Q2->Y, Q1->Z + Q2->Z);
}
/* </function> */


/* <function name="qdif" var="Q *Q1, Q Q2" 
task="returns the difference of 2 quaternions: (t - t', X - X')"> */

Q*
qdif(Q *Q1, Q *Q2)
{
  return constructQ(Q1->T - Q2->T, Q1->X - Q2->X, Q1->Y - Q2->Y, Q1->Z - Q2->Z);
}
/* </function> */


/* <function name="rqsum" var="Q *Q1, Q *Q2, Q *Q3"
task="the sum of 2 quaternions: (t + t', X + X' replaces a Q"> */

void
rqsum(Q *Q1, Q *Q2, Q *Q3)
{
  replaceQ(Q1->T + Q2->T, Q1->X + Q2->X, Q1->Y + Q2->Y, Q1->Z + Q2->Z, Q3);
}
/* </function> */


/* <function name="rqdif" var="Q *Q1, Q *Q2, Q *Q3" 
task="the difference of 2 quaternions: (t - t', X - X') replaces a Q"> */

void
rqdif(Q *Q1, Q *Q2, Q *Q3)
{
  replaceQ(Q1->T - Q2->T, Q1->X - Q2->X, Q1->Y - Q2->Y, Q1->Z - Q2->Z, Q3);
}
/* </function> */



/* Products */
/*  qxs,  qx,  qxinv,  qinvx,  qxeven,  qxodd */
/* rqxs, rqx, rqxinv, rqinvx, rqxeven, rqxodd */
/*  qcx,  qcxeven,  qcxodd,  qxc,  qxceven,  qxcodd */
/* rqcx, rqcxeven, rqcxodd, rqxc, rqxceven, rqxcodd */

/* <function name="qxs" var="Q *Q1, double S" 
task="returns the product of a double and a quaternion: (s t, s X)"> */

Q*
qxs(Q *Q1, double S)
{
  return constructQ(Q1->T * S, Q1->X * S, Q1->Y * S, Q1->Z * S);
}
/* </function> */


/* <function name="qx" var="Q *Q1, Q *Q2" 
task="returns the product of 2 quaternions: (tt' - X.X', tX' + Xt' + XxX')"> */

Q*
qx(Q *Q1, Q *Q2)
{
  return constructQ(
    Q1->T * Q2->T - Q1->X * Q2->X - Q1->Y * Q2->Y - Q1->Z * Q2->Z,
    Q1->T * Q2->X + Q1->X * Q2->T + Q1->Y * Q2->Z - Q1->Z * Q2->Y,
    Q1->T * Q2->Y + Q1->Y * Q2->T + Q1->Z * Q2->X - Q1->X * Q2->Z,
    Q1->T * Q2->Z + Q1->Z * Q2->T + Q1->X * Q2->Y - Q1->Y * Q2->X);
}
/* </function> */


/* <function name="qxinv" var="Q *Q1, Q *Q2" 
task="returns the product of 2 quaternions, 
where the second one inverted first: (-tt' + X.X', -tX' + Xt' - XxX')"> */

Q*
qxinv(Q *Q1, Q *Q2)
{
  double norm_inv = 0;
 
  norm_inv = dnorm_inv(Q2);

  return constructQ(
  ( Q1->T * Q2->T + Q1->X * Q2->X + Q1->Y * Q2->Y + Q1->Z * Q2->Z) * norm_inv,
  (-Q1->T * Q2->X + Q1->X * Q2->T - Q1->Y * Q2->Z + Q1->Z * Q2->Y) * norm_inv,
  (-Q1->T * Q2->Y + Q1->Y * Q2->T - Q1->Z * Q2->X + Q1->X * Q2->Z) * norm_inv,
  (-Q1->T * Q2->Z + Q1->Z * Q2->T - Q1->X * Q2->Y + Q1->Y * Q2->X) * norm_inv);
}
/* </function> */


/* <function name="qinvx" var="Q *Q1, Q *Q2" 
task="returns the product of 2 quaternions, 
where the first one inverted first: (-tt' + X.X', tX' - Xt' - XxX')"> */

Q*
qinvx(Q *Q1, Q *Q2)
{
  double norm_inv = 0;
  norm_inv = 1/(Q1->T * Q1->T + Q1->X * Q1->X + Q1->Y * Q1->Y + Q1->Z * Q1->Z);

  return constructQ(
  (Q1->T * Q2->T + Q1->X * Q2->X + Q1->Y * Q2->Y + Q1->Z * Q2->Z) * norm_inv,
  (Q1->T * Q2->X - Q1->X * Q2->T - Q1->Y * Q2->Z + Q1->Z * Q2->Y) * norm_inv,
  (Q1->T * Q2->Y - Q1->Y * Q2->T - Q1->Z * Q2->X + Q1->X * Q2->Z) * norm_inv,
  (Q1->T * Q2->Z - Q1->Z * Q2->T - Q1->X * Q2->Y + Q1->Y * Q2->X) * norm_inv);
}
/* </function> */


/* <function name="qxeven" var="Q *Q1, Q *Q2" 
task="returns the even product of 2 quaternions: (tt' - X.X', tX' + Xt')"> */

Q*
qxeven(Q *Q1, Q *Q2)
{
  return constructQ( 
  Q1->T * Q2->T - Q1->X * Q2->X - Q1->Y * Q2->Y - Q1->Z * Q2->Z,
  Q1->T * Q2->X + Q1->X * Q2->T,
  Q1->T * Q2->Y + Q1->Y * Q2->T,
  Q1->T * Q2->Z + Q1->Z * Q2->T);
}
/* </function> */


/* <function name="qxodd" var="Q *Q1, Q *Q2" 
task="returns the odd product of 2 quaternions: (0, XxX')"> */

Q*
qxodd(Q *Q1, Q *Q2)
{
  return constructQ(0, 
  Q1->Y * Q2->Z - Q1->Z * Q2->Y,
  Q1->Z * Q2->X - Q1->X * Q2->Z,
  Q1->X * Q2->Y - Q1->Y * Q2->X);
}
/* </function> */


/* <function name="qcx" var="Q *Q1, Q *Q2" 
task="returns the Euclidean product of 2 quaternions
where the conjugate of the first term
multiplies the second: (tt' + X.X', tX' - Xt' - XxX')"> */

Q*
qcx(Q *Q1, Q *Q2)
{
  return constructQ(
  Q1->T * Q2->T + Q1->X * Q2->X + Q1->Y * Q2->Y + Q1->Z * Q2->Z,
  Q1->T * Q2->X - Q1->X * Q2->T - Q1->Y * Q2->Z + Q1->Z * Q2->Y,
  Q1->T * Q2->Y - Q1->Y * Q2->T - Q1->Z * Q2->X + Q1->X * Q2->Z,
  Q1->T * Q2->Z - Q1->Z * Q2->T - Q1->X * Q2->Y + Q1->Y * Q2->X);
}
/* </function> */


/* <function name="qcxeven" var="Q *Q1, Q *Q2" 
task="returns the even Euclidean product of 2 quaternions
where the conjugate of the first term
multiplies the second: (tt' + X.X', 0)"> */

Q*
qcxeven(Q *Q1, Q *Q2)
{
  return constructQ(
  Q1->T * Q2->T + Q1->X * Q2->X + Q1->Y * Q2->Y + Q1->Z * Q2->Z,
  0,0,0);
}
/* </function> */


/* <function name="qcxodd" var="Q *Q1, Q *Q2" 
task="returns the odd Euclidean product of 2 quaternions
where the conjugate of the first term
multiplies the second: (0, tX' - Xt' - XxX')"> */

Q*
qcxodd(Q *Q1, Q *Q2)
{
  return constructQ(0, 
  Q1->T * Q2->X - Q1->X * Q2->T - Q1->Y * Q2->Z + Q1->Z * Q2->Y,
  Q1->T * Q2->Y - Q1->Y * Q2->T - Q1->Z * Q2->X + Q1->X * Q2->Z,
  Q1->T * Q2->Z - Q1->Z * Q2->T - Q1->X * Q2->Y + Q1->Y * Q2->X);
}
/* </function> */


/* <function name="qxc" var="Q *Q1, Q *Q2" 
task="returns the Euclidean product of 2 quaternions
where the first term
multiplies the conjugate of the second: (tt' + X.X', -tX' + Xt' - XxX')"> */

Q*
qxc(Q *Q1, Q *Q2)
{
  return constructQ(
  Q1->T * Q2->T + Q1->X * Q2->X + Q1->Y * Q2->Y + Q1->Z * Q2->Z,
  -1 * Q1->T * Q2->X + Q1->X * Q2->T - Q1->Y * Q2->Z + Q1->Z * Q2->Y,
  -1 * Q1->T * Q2->Y + Q1->Y * Q2->T - Q1->Z * Q2->X + Q1->X * Q2->Z,
  -1 * Q1->T * Q2->Z + Q1->Z * Q2->T - Q1->X * Q2->Y + Q1->Y * Q2->X);
}
/* </function> */


/* <function name="qxceven" var="Q *Q1, Q *Q2" 
task="returns the even Euclidean product of 2 quaternions
where the first term
multiplies the conjugate of the second: (tt' + X.X', 0)"> */

Q*
qxceven(Q *Q1, Q *Q2)
{
  return constructQ(
  Q1->T * Q2->T + Q1->X * Q2->X + Q1->Y * Q2->Y + Q1->Z * Q2->Z,
  0,0,0);
}
/* </function> */


/* <function name="qxcodd" var="Q *Q1, Q *Q2" 
task="returns the odd Euclidean product of 2 quaternions
where the first term
multiplies the conjugate of the second: (0, - tX' + Xt' - XxX')"> */

Q*
qxcodd(Q *Q1, Q *Q2)
{
  return constructQ(0, 
  - 1 * Q1->T * Q2->X + Q1->X * Q2->T - Q1->Y * Q2->Z + Q1->Z * Q2->Y,
  - 1 * Q1->T * Q2->Y + Q1->Y * Q2->T - Q1->Z * Q2->X + Q1->X * Q2->Z,
   -1 * Q1->T * Q2->Z + Q1->Z * Q2->T - Q1->X * Q2->Y + Q1->Y * Q2->X);
}
/* </function> */



/* <function name="rqxs" var="Q *Q1, double S, Q *Q2"" 
task="the product of a double and a quaternion: (s t, s X) replaces a Q"> */

void
rqxs(Q *Q1, double S, Q *Q2)
{
  replaceQ(Q1->T * S, Q1->X * S, Q1->Y * S, Q1->Z * S, Q2);
}
/* </function> */


/* <function name="rqx" var="Q *Q1, Q *Q2, Q *Q3" 
task="the product of 2 quaternions: (tt' - X.X', tX' + Xt' + XxX')
replaces a Q"> */

void
rqx(Q *Q1, Q *Q2, Q *Q3)
{
  replaceQ(
    Q1->T * Q2->T - Q1->X * Q2->X - Q1->Y * Q2->Y - Q1->Z * Q2->Z,
    Q1->T * Q2->X + Q1->X * Q2->T + Q1->Y * Q2->Z - Q1->Z * Q2->Y,
    Q1->T * Q2->Y + Q1->Y * Q2->T + Q1->Z * Q2->X - Q1->X * Q2->Z,
    Q1->T * Q2->Z + Q1->Z * Q2->T + Q1->X * Q2->Y - Q1->Y * Q2->X, Q3);
}
/* </function> */


/* <function name="rqxinv" var="Q *Q1, Q *Q2, Q *Q3" 
task="the product of 2 quaternions, 
where the second one inverted first: (-tt' + X.X', -tX' + Xt' - XxX')
replaces a Q"> */

void
rqxinv(Q *Q1, Q *Q2, Q *Q3)
{
  double norm_inv = 0;
 
  norm_inv = 1/(Q2->T * Q2->T + Q2->X * Q2->X + Q2->Y * Q2->Y + Q2->Z * Q2->Z);

  replaceQ(
  (Q1->T * Q2->T + Q1->X * Q2->X + Q1->Y * Q2->Y + Q1->Z * Q2->Z) * norm_inv,
  (-Q1->T * Q2->X + Q1->X * Q2->T - Q1->Y * Q2->Z + Q1->Z * Q2->Y) * norm_inv,
  (-Q1->T * Q2->Y + Q1->Y * Q2->T - Q1->Z * Q2->X + Q1->X * Q2->Z) * norm_inv,
  (-Q1->T * Q2->Z + Q1->Z * Q2->T - Q1->X * Q2->Y + Q1->Y * Q2->X) * norm_inv, Q3);
}
/* </function> */


/* <function name="rqinvx" var="Q *Q1, Q *Q2, Q *Q3" 
task="the product of 2 quaternions, 
where the first one inverted first: (-tt' + X.X', tX' - Xt' - XxX')
replaces a Q"> */

void
rqinvx(Q *Q1, Q *Q2, Q *Q3)
{
  double norm_inv = 0;
  norm_inv = 1/(Q1->T * Q1->T + Q1->X * Q1->X + Q1->Y * Q1->Y + Q1->Z * Q1->Z);

  replaceQ(
  (Q1->T * Q2->T + Q1->X * Q2->X + Q1->Y * Q2->Y + Q1->Z * Q2->Z) * norm_inv,
  (Q1->T * Q2->X - Q1->X * Q2->T - Q1->Y * Q2->Z + Q1->Z * Q2->Y) * norm_inv,
  (Q1->T * Q2->Y - Q1->Y * Q2->T - Q1->Z * Q2->X + Q1->X * Q2->Z) * norm_inv,
  (Q1->T * Q2->Z - Q1->Z * Q2->T - Q1->X * Q2->Y + Q1->Y * Q2->X) * norm_inv, Q3);
}
/* </function> */


/* <function name="rqxeven" var="Q *Q1, Q *Q2, Q *Q3" 
task="the even product of 2 quaternions: (tt' - X.X', tX' + Xt')
replaces a Q"> */

void
rqxeven(Q *Q1, Q *Q2, Q *Q3)
{
  replaceQ( 
  Q1->T * Q2->T - Q1->X * Q2->X - Q1->Y * Q2->Y - Q1->Z * Q2->Z,
  Q1->T * Q2->X + Q1->X * Q2->T,
  Q1->T * Q2->Y + Q1->Y * Q2->T,
  Q1->T * Q2->Z + Q1->Z * Q2->T, Q3);
}
/* </function> */


/* <function name="rqxodd" var="Q *Q1, Q *Q2, Q *Q3" 
task="the odd product of 2 quaternions: (0, XxX')
replaces a Q"> */

void
rqxodd(Q *Q1, Q *Q2, Q *Q3)
{
  replaceQ(0, 
  Q1->Y * Q2->Z - Q1->Z * Q2->Y,
  Q1->Z * Q2->X - Q1->X * Q2->Z,
  Q1->X * Q2->Y - Q1->Y * Q2->X, Q3);
}
/* </function> */


/* <function name="rqcx" var="Q *Q1, Q *Q2, Q *Q3" 
task="the Euclidean product of 2 quaternions
where the conjugate of the first term
multiplies the second: (tt' + X.X', tX' - Xt' - XxX')
replaces a Q"> */

void
rqcx(Q *Q1, Q *Q2, Q *Q3)
{
  replaceQ(
  Q1->T * Q2->T + Q1->X * Q2->X + Q1->Y * Q2->Y + Q1->Z * Q2->Z,
  Q1->T * Q2->X - Q1->X * Q2->T - Q1->Y * Q2->Z + Q1->Z * Q2->Y,
  Q1->T * Q2->Y - Q1->Y * Q2->T - Q1->Z * Q2->X + Q1->X * Q2->Z,
  Q1->T * Q2->Z - Q1->Z * Q2->T - Q1->X * Q2->Y + Q1->Y * Q2->X, Q3);
}
/* </function> */


/* <function name="rqcxeven" var="Q *Q1, Q *Q2, Q *Q3" 
task="the even Euclidean product of 2 quaternions
where the conjugate of the first term
multiplies the second: (tt' + X.X', 0)
replaces a Q"> */

void
rqcxeven(Q *Q1, Q *Q2, Q *Q3)
{
  replaceQ(
  Q1->T * Q2->T + Q1->X * Q2->X + Q1->Y * Q2->Y + Q1->Z * Q2->Z,
  0,0,0, Q3);
}
/* </function> */


/* <function name="rqcxodd" var="Q *Q1, Q *Q2, Q *Q3" 
task="the odd Euclidean product of 2 quaternions
where the conjugate of the first term
multiplies the second: (0, tX' - Xt' - XxX')
replaces a Q"> */

void
rqcxodd(Q *Q1, Q *Q2, Q *Q3)
{
  replaceQ(0, 
  Q1->T * Q2->X - Q1->X * Q2->T - Q1->Y * Q2->Z + Q1->Z * Q2->Y,
  Q1->T * Q2->Y - Q1->Y * Q2->T - Q1->Z * Q2->X + Q1->X * Q2->Z,
  Q1->T * Q2->Z - Q1->Z * Q2->T - Q1->X * Q2->Y + Q1->Y * Q2->X, Q3);
}
/* </function> */


/* <function name="rqxc" var="Q *Q1, Q *Q2, Q *Q3" 
task="the Euclidean product of 2 quaternions
where the first term
multiplies the conjugate of the second: (tt' + X.X', -tX' + Xt' - XxX')
replaces a Q"> */

void
rqxc(Q *Q1, Q *Q2, Q *Q3)
{
  replaceQ(
  Q1->T * Q2->T + Q1->X * Q2->X + Q1->Y * Q2->Y + Q1->Z * Q2->Z,
  -1 * Q1->T * Q2->X + Q1->X * Q2->T - Q1->Y * Q2->Z + Q1->Z * Q2->Y,
  -1 * Q1->T * Q2->Y + Q1->Y * Q2->T - Q1->Z * Q2->X + Q1->X * Q2->Z,
  -1 * Q1->T * Q2->Z + Q1->Z * Q2->T - Q1->X * Q2->Y + Q1->Y * Q2->X, Q3);
}
/* </function> */


/* <function name="rqxceven" var="Q *Q1, Q *Q2, Q *Q3" 
task="the even Euclidean product of 2 quaternions
where the first term
multiplies the conjugate of the second: (tt' + X.X', 0)
replaces a Q"> */

void
rqxceven(Q *Q1, Q *Q2, Q *Q3)
{
  replaceQ(
  Q1->T * Q2->T + Q1->X * Q2->X + Q1->Y * Q2->Y + Q1->Z * Q2->Z,
  0,0,0, Q3);
}
/* </function> */


/* <function name="rqxcodd" var="Q *Q1, Q *Q2, Q *Q3" 
task="the odd Euclidean product of 2 quaternions
where the first term
multiplies the conjugate of the second: (0, - tX' + Xt' - XxX')
replaces a Q"> */

void
rqxcodd(Q *Q1, Q *Q2, Q *Q3)
{
  replaceQ(0, 
  - 1 * Q1->T * Q2->X + Q1->X * Q2->T - Q1->Y * Q2->Z + Q1->Z * Q2->Y,
  - 1 * Q1->T * Q2->Y + Q1->Y * Q2->T - Q1->Z * Q2->X + Q1->X * Q2->Z,
   -1 * Q1->T * Q2->Z + Q1->Z * Q2->T - Q1->X * Q2->Y + Q1->Y * Q2->X, Q3);
}
/* </function> */



/* rotations */

/*  qrot_x_by_angle  qrot_y_by_angle  qrot_z_by_angle  qrot_xyz_by_angle */
/* rqrot_x_by_angle rqrot_y_by_angle rqrot_z_by_angle rqrot_xyz_by_angle */
/*  q3D_rotation  qcxq */
/* rq3D_rotation rqcxq */


/* <function name="qrot_x_by_angle" var="Q *Q1, double D1"
task="returns a left-handed rotation along x axix: u* q u 
u = (cos a/2, sin a/2, 0, 0)"> */

Q*
qrot_x_by_angle(Q *Q1, double D1) {

  double T = 0;
  double X = 0;

  /* to decrease dublicate multiplications */
  double T2 = 0;
  double X2 = 0;

  double TX = 0;

  if (D1 == 0) {
    return Q1;
  }

  T = cos(D1 * DEGREES_TO_RADIANS / 2);
  X = sin(D1 * DEGREES_TO_RADIANS / 2);

  T2 = T * T;
  X2 = X * X;

  TX = T * X;

  return constructQ(
  Q1->T * (T2 + X2),
  Q1->X * (T2 + X2),
  Q1->Y * (T2 - X2) + 2 * Q1->Z * TX, 
  Q1->Z * (T2 - X2) - 2 * Q1->Y * TX);

}

/* </function> */


/* <function name="qrot_y_by_angle" var="Q *Q1, double D1"
task="returns a rotation along z axix: u* q u 
u = (cos a/2, 0, 0, sin a/2)"> */

Q*
qrot_y_by_angle(Q *Q1, double D1) {

  double T = 0;
  double Y = 0;

  /* to decrease dublicate multiplications */
  double T2 = 0;
  double Y2 = 0;

  double TY = 0;

  if (D1 == 0) {
    return Q1;
  }

  T = cos(D1 * DEGREES_TO_RADIANS / 2);
  Y = sin(D1 * DEGREES_TO_RADIANS / 2);

  T2 = T * T;
  Y2 = Y * Y;

  TY = T * Y;

  return constructQ(
  Q1->T * (T2 + Y2), 
  Q1->X * (T2 - Y2) - 2 * Q1->Z * TY, 
  Q1->Y * (T2 + Y2),
  Q1->Z * (T2 - Y2) + 2 * Q1->X * TY);

}

/* </function> */


/* <function name="qrot_z_by_angle" var="Q *Q1, double D1"
task="returns a rotation along z axix: u* q u 
u = (cos a/2, 0, 0, sin a/2)"> */

Q*
qrot_z_by_angle(Q *Q1, double D1) {

  double T = 0;
  double Z = 0;

  /* to decrease dublicate multiplications */
  double T2 = 0;
  double Z2 = 0;

  double TZ = 0;

  if (D1 == 0) {
    return Q1;
  }

  T = cos(D1 * DEGREES_TO_RADIANS / 2);
  Z = sin(D1 * DEGREES_TO_RADIANS / 2);

  T2 = T * T;
  Z2 = Z * Z;

  TZ = T * Z;

  return constructQ(
  Q1->T * (T2 + Z2), 
  Q1->X * (T2 - Z2) + 2 * Q1->Y * TZ, 
  Q1->Y * (T2 - Z2) - 2 * Q1->X * TZ, 
  Q1->Z * (T2 + Z2));

}

/* </function> */


/* <function name="qrot_xyz_by_angle" var="Q *Q1, double DX, double DY, double DZ, double D1"
task="return a general rotation: u* q' u"> */

Q*
qrot_xyz_by_angle(Q *Q1, double DX, double DY, double DZ, double D1) {

  double T = 0;
  double X = 0;
  double Y = 0;
  double Z = 0;

  /* to decrease dublicate multiplications */
  double T2 = 0;
  double X2 = 0;
  double Y2 = 0;
  double Z2 = 0;

  double TY = 0;
  double XY = 0;
  double TZ = 0;
  double XZ = 0;
  double TX = 0;
  double YZ = 0;

  double n = 0;
  double angle = 0;

  if (D1 == 0 || (DX == 0 && DY == 0 && DZ == 0) ) {
    return Q1;
  }

  /* normalize the 3-vector */
  n = 1 / sqrt(DX * DX + DY * DY + DZ * DZ);
  DX = n * DX;
  DY = n * DY;
  DZ = n * DZ;

  angle = D1 * DEGREES_TO_RADIANS / 2;

  T = cos(angle);
  X = DX * sin(angle);
  Y = DY * sin(angle);
  Z = DZ * sin(angle);

  T2 = T * T;
  X2 = X * X;
  Y2 = Y * Y;
  Z2 = Z * Z;

  TY = T * Y;
  XY = X * Y;
  TZ = T * Z;
  XZ = X * Z;
  TX = T * X;
  YZ = Y * Z;

  return constructQ(
  Q1->T * (T2 + X2 + Y2 + Z2),
  Q1->X * (T2 + X2 - Y2 - Z2) + 2 * (Q1->Y * (XY + TZ) + Q1->Z * (XZ -TY)),
  Q1->Y * (T2 - X2 + Y2 - Z2) + 2 * (Q1->Z * (YZ + TX) + Q1->X * (XY -TZ)),
  Q1->Z * (T2 - X2 - Y2 + Z2) + 2 * (Q1->X * (XZ + TY) + Q1->Y * (YZ -TX)));

}

/* </function> */


/* <function name="q3D_rotation" var="Q *Q1, Q *Q2"
task="return a 3D rotation: u* q' u"> */

Q*
q3D_rotation(Q *Q1, Q *Q2) {

  /* to decrease dublicate multiplications */
  double T2 = 0;
  double X2 = 0;
  double Y2 = 0;
  double Z2 = 0;

  double TY = 0;
  double XY = 0;
  double TZ = 0;
  double XZ = 0;
  double TX = 0;
  double YZ = 0;

  double n = 0;

  n = dnorm_inv(Q1);

  T2 = Q1->T * Q1->T;
  X2 = Q1->X * Q1->X;
  Y2 = Q1->Y * Q1->Y;
  Z2 = Q1->Z * Q1->Z;

  TY = Q1->T * Q1->Y;
  XY = Q1->X * Q1->Y;
  TZ = Q1->T * Q1->Z;
  XZ = Q1->X * Q1->Z;
  TX = Q1->T * Q1->X;
  YZ = Q1->Y * Q1->Z;

  return constructQ(
  Q2->T,
  (Q2->X * (T2 + X2 - Y2 - Z2) + 2 * (Q2->Y * (XY + TZ) + Q2->Z * (XZ -TY))) * n,
  (Q2->Y * (T2 - X2 + Y2 - Z2) + 2 * (Q2->Z * (YZ + TX) + Q2->X * (XY -TZ))) * n,
  (Q2->Z * (T2 - X2 - Y2 + Z2) + 2 * (Q2->X * (XZ + TY) + Q2->Y * (YZ -TX))) * n);

}

/* </function> */


/* <function name="qcxq" var="Q *Q1, Q *Q2"
task="return a general rotation: q* q' q"> */

Q*
qcxq(Q *Q1, Q *Q2) {

  /* to decrease dublicate multiplications */
  double T2 = 0;
  double X2 = 0;
  double Y2 = 0;
  double Z2 = 0;

  double TY = 0;
  double XY = 0;
  double TZ = 0;
  double XZ = 0;
  double TX = 0;
  double YZ = 0;

  T2 = Q1->T * Q1->T;
  X2 = Q1->X * Q1->X;
  Y2 = Q1->Y * Q1->Y;
  Z2 = Q1->Z * Q1->Z;

  TY = Q1->T * Q1->Y;
  XY = Q1->X * Q1->Y;
  TZ = Q1->T * Q1->Z;
  XZ = Q1->X * Q1->Z;
  TX = Q1->T * Q1->X;
  YZ = Q1->Y * Q1->Z;

  return constructQ(
  Q2->T * (T2 + X2 + Y2 + Z2),
  Q2->X * (T2 + X2 - Y2 - Z2) + 2 * (Q2->Y * (XY + TZ) + Q2->Z * (XZ -TY)),
  Q2->Y * (T2 - X2 + Y2 - Z2) + 2 * (Q2->Z * (YZ + TX) + Q2->X * (XY -TZ)),
  Q2->Z * (T2 - X2 - Y2 + Z2) + 2 * (Q2->X * (XZ + TY) + Q2->Y * (YZ -TX)));

}

/* </function> */











/* <function name="rqrot_x_by_angle" var="Q *Q1, double D1, Q *Q2"
task="a left-handed rotation along x axix: u* q u 
u = (cos a/2, sin a/2, 0, 0) replaces a q"> */

void
rqrot_x_by_angle(Q *Q1, double D1, Q *Q2) {

  double T = 0;
  double X = 0;

  /* to decrease dublicate multiplications */
  double T2 = 0;
  double X2 = 0;

  double TX = 0;

  if (D1 == 0) {
    rqcopy(Q1, Q2);
  }

  T = cos(D1 * DEGREES_TO_RADIANS / 2);
  X = sin(D1 * DEGREES_TO_RADIANS / 2);

  T2 = T * T;
  X2 = X * X;

  TX = T * X;

  replaceQ(
  Q1->T * (T2 + X2),
  Q1->X * (T2 + X2),
  Q1->Y * (T2 - X2) + 2 * Q1->Z * TX, 
  Q1->Z * (T2 - X2) - 2 * Q1->Y * TX, Q2);

}

/* </function> */


/* <function name="rqrot_y_by_angle" var="Q *Q1, double D1, Q *Q2"
task="a rotation along z axix: u* q u 
u = (cos a/2, 0, 0, sin a/2) replaces a q"> */

void
rqrot_y_by_angle(Q *Q1, double D1, Q *Q2) {

  double T = 0;
  double Y = 0;

  /* to decrease dublicate multiplications */
  double T2 = 0;
  double Y2 = 0;

  double TY = 0;

  if (D1 == 0) {
    rqcopy(Q1, Q2);
  }

  T = cos(D1 * DEGREES_TO_RADIANS / 2);
  Y = sin(D1 * DEGREES_TO_RADIANS / 2);

  T2 = T * T;
  Y2 = Y * Y;

  TY = T * Y;

  replaceQ(
  Q1->T * (T2 + Y2), 
  Q1->X * (T2 - Y2) - 2 * Q1->Z * TY, 
  Q1->Y * (T2 + Y2),
  Q1->Z * (T2 - Y2) + 2 * Q1->X * TY, Q2);

}

/* </function> */


/* <function name="rqrot_z_by_angle" var="Q *Q1, double D1, Q *Q2"
task="a rotation along z axix: u* q u 
u = (cos a/2, 0, 0, sin a/2) replaces a q"> */

void
rqrot_z_by_angle(Q *Q1, double D1, Q *Q2) {

  double T = 0;
  double Z = 0;

  /* to decrease dublicate multiplications */
  double T2 = 0;
  double Z2 = 0;

  double TZ = 0;

  if (D1 == 0) {
    rqcopy(Q1, Q2);
  }

  T = cos(D1 * DEGREES_TO_RADIANS / 2);
  Z = sin(D1 * DEGREES_TO_RADIANS / 2);

  T2 = T * T;
  Z2 = Z * Z;

  TZ = T * Z;

  replaceQ(
  Q1->T * (T2 + Z2), 
  Q1->X * (T2 - Z2) + 2 * Q1->Y * TZ, 
  Q1->Y * (T2 - Z2) - 2 * Q1->X * TZ, 
  Q1->Z * (T2 + Z2), Q2);

}

/* </function> */


/* <function name="rqrot_xyz_by_angle" var="Q *Q1, double DX, double DY, double DZ, double D1"
task="a general rotation: u* q' u replaces a q"> */

void
rqrot_xyz_by_angle(Q *Q1, double DX, double DY, double DZ, double D1, Q *Q2) {

  double T = 0;
  double X = 0;
  double Y = 0;
  double Z = 0;

  /* to decrease dublicate multiplications */
  double T2 = 0;
  double X2 = 0;
  double Y2 = 0;
  double Z2 = 0;

  double TY = 0;
  double XY = 0;
  double TZ = 0;
  double XZ = 0;
  double TX = 0;
  double YZ = 0;

  double n = 0;
  double angle = 0;

  if (D1 == 0 || (DX == 0 && DY == 0 && DZ == 0) ) {
    rqcopy(Q1, Q2);
  }

  /* normalize the 3-vector */
  n = 1 / sqrt(DX * DX + DY * DY + DZ * DZ);
  DX = n * DX;
  DY = n * DY;
  DZ = n * DZ;

  angle = D1 * DEGREES_TO_RADIANS / 2;

  T = cos(angle);
  X = DX * sin(angle);
  Y = DY * sin(angle);
  Z = DZ * sin(angle);

  T2 = T * T;
  X2 = X * X;
  Y2 = Y * Y;
  Z2 = Z * Z;

  TY = T * Y;
  XY = X * Y;
  TZ = T * Z;
  XZ = X * Z;
  TX = T * X;
  YZ = Y * Z;

  replaceQ(
  Q1->T * (T2 + X2 + Y2 + Z2),
  Q1->X * (T2 + X2 - Y2 - Z2) + 2 * (Q1->Y * (XY + TZ) + Q1->Z * (XZ -TY)),
  Q1->Y * (T2 - X2 + Y2 - Z2) + 2 * (Q1->Z * (YZ + TX) + Q1->X * (XY -TZ)),
  Q1->Z * (T2 - X2 - Y2 + Z2) + 2 * (Q1->X * (XZ + TY) + Q1->Y * (YZ -TX)), Q2);

}

/* </function> */


/* <function name="rq3D_rotation" var="Q *Q1, Q *Q2, Q *Q3"
task="a 3D rotation: u* q' u replaces a q"> */

void
rq3D_rotation(Q *Q1, Q *Q2, Q *Q3) {

  /* to decrease dublicate multiplications */
  double T2 = 0;
  double X2 = 0;
  double Y2 = 0;
  double Z2 = 0;

  double TY = 0;
  double XY = 0;
  double TZ = 0;
  double XZ = 0;
  double TX = 0;
  double YZ = 0;

  double n = 0;

  n = dnorm_inv(Q1);

  T2 = Q1->T * Q1->T;
  X2 = Q1->X * Q1->X;
  Y2 = Q1->Y * Q1->Y;
  Z2 = Q1->Z * Q1->Z;

  TY = Q1->T * Q1->Y;
  XY = Q1->X * Q1->Y;
  TZ = Q1->T * Q1->Z;
  XZ = Q1->X * Q1->Z;
  TX = Q1->T * Q1->X;
  YZ = Q1->Y * Q1->Z;

  replaceQ(
  Q2->T,
  (Q2->X * (T2 + X2 - Y2 - Z2) + 2 * (Q2->Y * (XY + TZ) + Q2->Z * (XZ -TY))) * n,
  (Q2->Y * (T2 - X2 + Y2 - Z2) + 2 * (Q2->Z * (YZ + TX) + Q2->X * (XY -TZ))) * n,
  (Q2->Z * (T2 - X2 - Y2 + Z2) + 2 * (Q2->X * (XZ + TY) + Q2->Y * (YZ -TX))) * n, Q3);

}

/* </function> */


/* <function name="rqcxq" var="Q *Q1, Q *Q2, Q *Q3"
task="a general rotation: q* q' q replaces a q"> */

void
rqcxq(Q *Q1, Q *Q2, Q *Q3) {

  /* to decrease dublicate multiplications */
  double T2 = 0;
  double X2 = 0;
  double Y2 = 0;
  double Z2 = 0;

  double TY = 0;
  double XY = 0;
  double TZ = 0;
  double XZ = 0;
  double TX = 0;
  double YZ = 0;

  T2 = Q1->T * Q1->T;
  X2 = Q1->X * Q1->X;
  Y2 = Q1->Y * Q1->Y;
  Z2 = Q1->Z * Q1->Z;

  TY = Q1->T * Q1->Y;
  XY = Q1->X * Q1->Y;
  TZ = Q1->T * Q1->Z;
  XZ = Q1->X * Q1->Z;
  TX = Q1->T * Q1->X;
  YZ = Q1->Y * Q1->Z;

  replaceQ(
  Q2->T * (T2 + X2 + Y2 + Z2),
  Q2->X * (T2 + X2 - Y2 - Z2) + 2 * (Q2->Y * (XY + TZ) + Q2->Z * (XZ -TY)),
  Q2->Y * (T2 - X2 + Y2 - Z2) + 2 * (Q2->Z * (YZ + TX) + Q2->X * (XY -TZ)),
  Q2->Z * (T2 - X2 - Y2 + Z2) + 2 * (Q2->X * (XZ + TY) + Q2->Y * (YZ -TX)), Q3);

}

/* </function> */







/* <function name="printq" var="Q *Q1" 
task="prints the 4 doubles in a quaternion"> */

void
printq(Q *Q1)
{
  printf("%g %g %g %g", Q1->T, Q1->X, Q1->Y, Q1->Z);
}
/* </function> */


/* <function name="printlnq" var="Q *Q1" 
task="prints the 4 doubles in a quaternion"> */

void
printlnq(Q *Q1)
{
  printf("%g %g %g %g\n", Q1->T, Q1->X, Q1->Y, Q1->Z);
}
/* </function> */


/*  <license>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, version 2 of the License.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

    </licence>
*/








