/***************************************************************************
 *   Copyright (C) 2008 by Peter Eschright                                 *
 *   petereschright@gmail.com                                              *
 *                                                                         *
 *   This source code is presented for informational purposes only.        *
 *   Please contact the author for permision for use.                      *
 **************************************************************************/

#include "quaternion.h"

// /////////////////////////////////////////////////////////////
//
// File name: quaternion.cpp
// File description: implementation of a quaternion class
//
// /////////////////////////////////////////////////////////////



// display, print
ostream& operator<<(ostream& output_stream, const quaternion& right_quaternion){
   output_stream << right_quaternion.x << " + ";
   output_stream << right_quaternion.y << "*i + ";
   output_stream << right_quaternion.z << "*j + ";
   output_stream << right_quaternion.w << "*k";
   return(output_stream);
}



// scaler multiplication
quaternion operator*(const double a, const quaternion& right_quaternion){
   quaternion result;
   result.x = a * right_quaternion.x;
   result.y = a * right_quaternion.y;
   result.z = a * right_quaternion.z;
   result.w = a * right_quaternion.w;
   return(result);
}



quaternion operator*(const quaternion& left_quaternion, const double a){
   quaternion result;
   result.x = a * left_quaternion.x;
   result.y = a * left_quaternion.y;
   result.z = a * left_quaternion.z;
   result.w = a * left_quaternion.w;
   return(result);
}



//innerproduct
double inner_product(const quaternion& left_quaternion, const quaternion& right_quaternion){
   double result;
   result =  left_quaternion.x * right_quaternion.x;
   result += left_quaternion.y * right_quaternion.y;
   result += left_quaternion.z * right_quaternion.z;
   result += left_quaternion.w * right_quaternion.w;
   return(result);
}



// addition
quaternion operator+(const quaternion& left_quaternion, const quaternion& right_quaternion){
   quaternion result;
   result.x = left_quaternion.x + right_quaternion.x;
   result.y = left_quaternion.y + right_quaternion.y;
   result.z = left_quaternion.z + right_quaternion.z;
   result.w = left_quaternion.w + right_quaternion.w;
   return(result);
}



// subtraction
quaternion operator-(const quaternion& left_quaternion, const quaternion& right_quaternion){
   quaternion result;
   result.x = left_quaternion.x - right_quaternion.x;
   result.y = left_quaternion.y - right_quaternion.y;
   result.z = left_quaternion.z - right_quaternion.z;
   result.w = left_quaternion.w - right_quaternion.w;
   return(result);
}



// multiplication
quaternion operator*(const quaternion& l, const quaternion& r){
   quaternion result;
   result.x = l.x*r.x - l.y*r.y - l.z*r.z - l.w*r.w;
   result.y = l.x*r.y + l.y*r.x + l.z*r.w - l.w*r.z;
   result.z = l.x*r.z - l.y*r.w + l.z*r.x + l.w*r.y;
   result.w = l.x*r.w + l.y*r.z - l.z*r.y + l.w*r.x;
   return(result);
}



// equality relation
bool operator==(const quaternion& left_quaternion, const quaternion& right_quaternion){
   if(
     (left_quaternion.x == right_quaternion.x)
     &&
     (left_quaternion.y == right_quaternion.y)
     &&
     (left_quaternion.z == right_quaternion.z)
     &&
     (left_quaternion.w == right_quaternion.w)
   ){
      return(true);
   }
   // else
   return(false);
}



// constructors
quaternion::quaternion(){
   x = 0.0;
   y = 0.0;
   z = 0.0;
   w = 0.0;
}



quaternion::quaternion(double a, double b, double c, double d){
   x = a;
   y = b;
   z = c;
   w = d;
}



quaternion::quaternion(const quaternion& right_quaternion){
   x = right_quaternion.x;
   y = right_quaternion.y;
   z = right_quaternion.z;
   w = right_quaternion.w;
}



// assignment
const quaternion& quaternion::operator=(const quaternion& right_quaternion){
   if(this != &right_quaternion){
      x = right_quaternion.x;
      y = right_quaternion.y;
      z = right_quaternion.z;
      w = right_quaternion.w;
   }
   return(*this);
}



// part-wise access
double quaternion::get_real_part(){
   return(x);
}



double quaternion::set_real_part(double a){
   x = a;
   return(x);
}



double quaternion::get_i_part(){
   return(y);
}



double quaternion::set_i_part(double b){
   y = b;
   return(y);
}





double quaternion::get_j_part(){
   return(z);
}




double quaternion::set_j_part(double c){
   z = c;
   return(z);
}




double quaternion::get_k_part(){
   return(w);
}




double quaternion::set_k_part(double d){
   w = d;
   return(w);
}



// norm
// return the norm defined as:
// x^2 + Y^2 + z^2 + w^2
double quaternion::norm(){
   double norm_val = 0;
   norm_val = x*x;
   norm_val += y*y;
   norm_val += z*z;
   norm_val += w*w;
   return(sqrt(norm_val));
}



// unit
// get a quaternion of unit norm parellel to this one
quaternion quaternion::unit(){
   quaternion unit_quaternion;
   double norm_val = norm();
   if(norm_val > 0){
      unit_quaternion = (*this) * (1.0/norm_val);
   }
   return(unit_quaternion);
}



// conjugate
// return the conjugate defined as:
// x - Y*i - z*j - w*k
quaternion quaternion::conjugate(){
   quaternion conjugate_quaternion(get_real_part(),-get_i_part(),-get_j_part(),-get_k_part());
   return(conjugate_quaternion);
}