/****************************************** * C++ Quaternions * Version: 1.0.9 * Author: Douglas Wilhelm Harder * Date: 2008/03/03 * * Copyright (c) 2006-2008 by Douglas Wilhelm Harder. * All rights reserved. ******************************************/ #include "Complex.h" #include "Quaternion.h" #include "Support.h" #include #include #include /****************************************** * Constructors ******************************************/ template Quaternion::Quaternion( T re, T im, T jm, T km ):r(re), i(im), j(jm), k(km) { } template Quaternion::Quaternion( T real ):r(real), i(0.0), j(0.0), k(0.0) { } /****************************************** * Assignment Operator ******************************************/ template const Quaternion & Quaternion::operator = ( const T & x ) { r = x; i = 0.0; j = 0.0; k = 0.0; return *this; } /****************************************** * Mutating Arithmetic Operators ******************************************/ template Quaternion & Quaternion::operator += ( const Quaternion & q ) { r += q.r; i += q.i; j += q.j; k += q.k; return *this; } template Quaternion & Quaternion::operator -= ( const Quaternion & q ) { r -= q.r; i -= q.i; j -= q.j; k -= q.k; return *this; } template Quaternion & Quaternion::operator *= ( const Quaternion & q ) { T RE = r, I = i, J = j; r = RE*q.r - I*q.i - J*q.j - k*q.k; i = RE*q.i + I*q.r + J*q.k - k*q.j; j = RE*q.j - I*q.k + J*q.r + k*q.i; k = RE*q.k + I*q.j - J*q.i + k*q.r; return * this; } template Quaternion & Quaternion::operator /= ( const Quaternion & q ) { T denom = q.norm(); T RE = r, I = i, J = j; r = ( RE*q.r + I*q.i + J*q.j + k*q.k)/denom; i = (-RE*q.i + I*q.r - J*q.k + k*q.j)/denom; j = (-RE*q.j + I*q.k + J*q.r - k*q.i)/denom; k = (-RE*q.k - I*q.j + J*q.i + k*q.r)/denom; return * this; } template Quaternion & Quaternion::operator += ( T x ) { r += x; return *this; } template Quaternion & Quaternion::operator -= ( T x ) { r -= x; return *this; } template Quaternion & Quaternion::operator *= ( T x ) { if ( Support::is_inf( x ) && norm() > 0 ) { r *= ( r == 0 )?Support::sign( x ):x; i *= ( i == 0 )?Support::sign( x ):x; j *= ( j == 0 )?Support::sign( x ):x; k *= ( k == 0 )?Support::sign( x ):x; } else { r *= x; i *= x; j *= x; k *= x; } return * this; } template Quaternion & Quaternion::operator /= ( T x ) { if ( x == 0.0 && norm() > 0 ) { r /= ( r == 0 ) ? Support::sign( x ):x; i /= ( i == 0 ) ? Support::sign( x ):x; j /= ( j == 0 ) ? Support::sign( x ):x; k /= ( k == 0 ) ? Support::sign( x ):x; } else { r /= x; i /= x; j /= x; k /= x; } return * this; } template Quaternion & Quaternion::operator ++() { ++r; return *this; } template Quaternion Quaternion::operator ++( int ) { Quaternion copy = *this; ++r; return copy; } template Quaternion & Quaternion::operator --() { --r; return *this; } template Quaternion Quaternion::operator --( int ) { Quaternion copy = *this; --r; return copy; } /****************************************** * Real-valued Functions ******************************************/ template T Quaternion::real() const { return r; } template T Quaternion::operator []( int n ) const { return reinterpret_cast( this )[n]; } template T &Quaternion::operator []( int n ) { return reinterpret_cast( this )[n]; } template T Quaternion::imag_i() const { return i; } template T Quaternion::imag_j() const { return j; } template T Quaternion::imag_k() const { return k; } template T Quaternion::csgn() const { return is_zero() ? 0.0 : Support::sign( r ); } template T Quaternion::abs() const { return is_inf()? Support::POS_INF : std::sqrt( r*r + i*i + j*j + k*k); } template T Quaternion::norm() const { return is_inf() ? Support::POS_INF : r*r + i*i + j*j + k*k; } template T Quaternion::abs_imag() const { return is_inf() ? Support::POS_INF : std::sqrt( i*i + j*j + k*k ); } template T Quaternion::norm_imag() const { return is_inf() ? Support::POS_INF : i*i + j*j + k*k; } template T Quaternion::arg() const { return std::atan2( abs_imag(), r ); } /****************************************** * Quaternion-valued Functions ******************************************/ template Quaternion Quaternion::imag() const { return Quaternion( 0.0, i, j, k ); } template Quaternion Quaternion::conj() const { return Quaternion( r, -i, -j, -k ); } template Quaternion Quaternion::operator * () const { return Quaternion( r, -i, -j, -k ); } template Quaternion Quaternion::signum() const { T absq = abs(); if ( absq == 0.0 || Support::is_nan( absq ) || Support::is_inf( absq ) ) { return *this; } else { return Quaternion( r/absq, i/absq, j/absq, k/absq ); } } template Quaternion Quaternion::sqr() const { return Quaternion( r*r - i*i - j*j - k*k, 2*r*i, 2*r*j, 2*r*k ); } template Quaternion Quaternion::sqrt() const { T absIm = abs_imag(); Complex z = Complex( r, absIm ).sqrt(); T mltplr; if ( absIm == 0.0 ) { mltplr = z.imag_i(); } else { mltplr = z.imag_i() / absIm; } return multiplier( z.real(), mltplr, *this ); } template Quaternion Quaternion::rotate( const Quaternion & q ) const { // the assumption is that |q| = 1 // in this case, q.inverse() == q.conj() T rr = q.r*q.r; T ii = q.i*q.i; T jj = q.j*q.j; T kk = q.k*q.k; T ri = q.r*q.i; T ij = q.i*q.j; T ik = q.i*q.k; T rj = q.r*q.j; T jk = q.j*q.k; T rk = q.r*q.k; return Quaternion( ( ( r == 0 ) ? r : r*(rr + ii + jj + kk) ), i*(rr + ii - jj - kk) + 2*( j*(-rk + ij) + k*( rj + ik)), j*(rr - ii + jj - kk) + 2*(i*( rk + ij) + k*(-ri + jk)), k*(rr - ii - jj + kk) + 2*(i*(-rj + ik) + j*( ri + jk) ) ); } /****************************************** * Boolean-valued Functions ******************************************/ template bool Quaternion::is_imaginary() const { return ( r == 0.0 ); } template bool Quaternion::is_inf() const { return ( r == Support::POS_INF ) || ( r == Support::NEG_INF ) || ( i == Support::POS_INF ) || ( i == Support::NEG_INF ) || ( j == Support::POS_INF ) || ( j == Support::NEG_INF ) || ( k == Support::POS_INF ) || ( k == Support::NEG_INF ); } template bool Quaternion::is_nan() const { return ( r != r ) || ( i != i ) || ( j != j ) || ( k != k ); } template bool Quaternion::is_neg_inf() const { return ( r == Support::NEG_INF ) && ( i == 0.0 ) && ( j == 0.0 ) && ( k == 0.0 ); } template bool Quaternion::is_pos_inf() const { return ( r == Support::POS_INF ) && ( i == 0.0 ) && ( j == 0.0 ) && ( k == 0.0 ); } template bool Quaternion::is_real() const { return ( i == 0.0 ) && ( j == 0.0 ) && ( k == 0.0 ); } template bool Quaternion::is_real_inf() const { return ( r == Support::POS_INF || r == Support::NEG_INF ) && ( i == 0.0 ) && ( j == 0.0 ) && ( k == 0.0 ); } template bool Quaternion::is_zero() const { return ( r == 0.0 ) && ( i == 0.0 ) && ( j == 0.0 ) && ( k == 0.0 ); } /****************************************** * Multiplier Function ******************************************/ template inline Quaternion Quaternion::multiplier( T r, T mltplr, const Quaternion & q ) { if ( Support::is_nan( mltplr ) || Support::is_inf( mltplr ) ) { if ( q.i == 0 && q.j == 0 && q.k == 0 ) { return Quaternion( r, mltplr*q.i, mltplr*q.j, mltplr*q.k ); } else { return Quaternion( r, (q.i == 0) ? Support::sign( mltplr )*q.i : mltplr*q.i, (q.j == 0) ? Support::sign( mltplr )*q.j : mltplr*q.j, (q.k == 0) ? Support::sign( mltplr )*q.k : mltplr*q.k ); } } else { return Quaternion( r, mltplr*q.i, mltplr*q.j, mltplr*q.k ); } } template inline Quaternion Quaternion::make_inf( T r, T i ) { return Quaternion( r, i, i, i ); } template inline Quaternion Quaternion::make_i( T r, T i ) { return Quaternion( r, i, 0.0, 0.0 ); } /****************************************** * Exponential and Logarithmic Functions ******************************************/ template Quaternion Quaternion::exp() const { T absIm = abs_imag(); Complex z = Complex( r, absIm ).exp(); T mltplr; if ( absIm == 0.0 ) { mltplr = z.imag_i(); } else { mltplr = z.imag_i() / absIm; } return multiplier( z.real(), mltplr, *this ); } template Quaternion Quaternion::log() const { T absIm = abs_imag(); Complex z = Complex( r, absIm ).log(); T mltplr; if ( absIm == 0.0 ) { mltplr = z.imag_i(); } else { mltplr = z.imag_i() / absIm; } return multiplier( z.real(), mltplr, *this ); } template Quaternion Quaternion::log10() const { T absIm = abs_imag(); Complex z = Complex( r, absIm ).log10(); T mltplr; if ( absIm == 0.0 ) { mltplr = z.imag_i(); } else { mltplr = z.imag_i() / absIm; } return multiplier( z.real(), mltplr, *this ); } template Quaternion Quaternion::pow( const Quaternion & q ) const { return ( log() * q ).exp(); } template Quaternion Quaternion::pow(T x) const { T absIm = abs_imag(); Complex z = Complex( r, absIm ).pow( x ); T mltplr; if ( absIm == 0.0 ) { mltplr = z.imag_i(); } else { mltplr = z.imag_i() / absIm; } return multiplier( z.real(), mltplr, *this ); } template Quaternion Quaternion::inverse() const { if ( is_zero() ) { return Quaternion( Support::sign( r )*Support::POS_INF, -Support::sign( i )*Support::POS_INF, -Support::sign( j )*Support::POS_INF, -Support::sign( k )*Support::POS_INF ); } else if ( is_inf() ) { return Quaternion( Support::sign( r )*0.0, -Support::sign( i )*0.0, -Support::sign( j )*0.0, -Support::sign( k )*0.0 ); } else if ( is_nan() ) { return Quaternion( Support::NaN, Support::NaN, Support::NaN, Support::NaN ); } else { T denom = norm(); return Quaternion( r/denom, -i/denom, -j/denom, -k/denom ); } } /******************************************************************** * Trigonometric and Hyperbolic Functions * * For each function f:H -> H, we define: * * ~ Im(q) ~ * f(q) = Re(f(Re(q) + i|Im(q)|)) + ------- Im(f(Re(q) + i|Im(q)|)) * |Im(q)| * ~ * where f:C -> C is the complex equivalent of the * function f. ********************************************************************/ /********************************************************** * Sine Function **********************************************************/ template Quaternion Quaternion::sin() const { T absIm = abs_imag(); Complex z = Complex( r, absIm ).sin(); T mltplr; if ( absIm == 0.0 ) { mltplr = z.imag_i(); } else { mltplr = z.imag_i() / absIm; } return multiplier( z.real(), mltplr, *this ); } /********************************************************** * Complementary Sine Function **********************************************************/ template Quaternion Quaternion::cos() const { T absIm = abs_imag(); Complex z = Complex( r, absIm ).cos(); T mltplr; if ( absIm == 0.0 ) { mltplr = z.imag_i(); } else { mltplr = z.imag_i() / absIm; } return multiplier( z.real(), mltplr, *this ); } /********************************************************** * Tangent Function **********************************************************/ template Quaternion Quaternion::tan() const { T absIm = abs_imag(); Complex z = Complex( r, absIm ).tan(); T mltplr; if ( absIm == 0.0 ) { mltplr = z.imag_i(); } else { mltplr = z.imag_i() / absIm; } return multiplier( z.real(), mltplr, *this ); } /********************************************************** * Secant Function **********************************************************/ template Quaternion Quaternion::sec() const { T absIm = abs_imag(); Complex z = Complex( r, absIm ).sec(); T mltplr; if ( absIm == 0.0 ) { mltplr = z.imag_i(); } else { mltplr = z.imag_i() / absIm; } return multiplier( z.real(), mltplr, *this ); } /********************************************************** * Complementary Secant Function **********************************************************/ template Quaternion Quaternion::csc() const { T absIm = abs_imag(); Complex z = Complex( r, absIm ).csc(); T mltplr; if ( absIm == 0.0 ) { mltplr = z.imag_i(); } else { mltplr = z.imag_i() / absIm; } return multiplier( z.real(), mltplr, *this ); } /********************************************************** * Complementary Tangent Function **********************************************************/ template Quaternion Quaternion::cot() const { T absIm = abs_imag(); Complex z = Complex( r, absIm ).cot(); T mltplr; if ( absIm == 0.0 ) { mltplr = z.imag_i(); } else { mltplr = z.imag_i() / absIm; } return multiplier( z.real(), mltplr, *this ); } /********************************************************** * Hyperbolic Sine Function **********************************************************/ template Quaternion Quaternion::sinh() const { T absIm = abs_imag(); Complex z = Complex( r, absIm ).sinh(); T mltplr; if ( absIm == 0.0 ) { mltplr = z.imag_i(); } else { mltplr = z.imag_i() / absIm; } return multiplier( z.real(), mltplr, *this ); } /********************************************************** * Hyperbolic Complementary Sine Function **********************************************************/ template Quaternion Quaternion::cosh() const { T absIm = abs_imag(); Complex z = Complex( r, absIm ).cosh(); T mltplr; if ( absIm == 0.0 ) { mltplr = z.imag_i(); } else { mltplr = z.imag_i() / absIm; } return multiplier( z.real(), mltplr, *this ); } /********************************************************** * Hyperbolic Tangent Function **********************************************************/ template Quaternion Quaternion::tanh() const { T absIm = abs_imag(); Complex z = Complex( r, absIm ).tanh(); T mltplr; if ( absIm == 0.0 ) { mltplr = z.imag_i(); } else { mltplr = z.imag_i() / absIm; } return multiplier( z.real(), mltplr, *this ); } /********************************************************** * Hyperbolic Secant Function **********************************************************/ template Quaternion Quaternion::sech() const { T absIm = abs_imag(); Complex z = Complex( r, absIm ).sech(); T mltplr; if ( absIm == 0.0 ) { mltplr = z.imag_i(); } else { mltplr = z.imag_i() / absIm; } return multiplier( z.real(), mltplr, *this ); } /********************************************************** * Hyperbolic Complementary Secant Function **********************************************************/ template Quaternion Quaternion::csch() const { T absIm = abs_imag(); Complex z = Complex( r, absIm ).csch(); T mltplr; if ( absIm == 0.0 ) { mltplr = z.imag_i(); } else { mltplr = z.imag_i() / absIm; } return multiplier( z.real(), mltplr, *this ); } /********************************************************** * Hyperbolic Complementary Tangent Function **********************************************************/ template Quaternion Quaternion::coth() const { T absIm = abs_imag(); Complex z = Complex( r, absIm ).coth(); T mltplr; if ( absIm == 0.0 ) { mltplr = z.imag_i(); } else { mltplr = z.imag_i() / absIm; } return multiplier( z.real(), mltplr, *this ); } // Real Branch Cut: (-oo, -1) U (1, oo) /********************************************************** * Inverse Sine Function **********************************************************/ template Quaternion Quaternion::asin( const Quaternion & q ) const { T absIm = abs_imag(); // Branch Cuts: (-oo, -1) U (1, oo) if ( absIm == 0 ) { if ( r > 1 ) { // Branch cut (1, oo) T absq = q.abs_imag(); if ( q == I || absq == 0 ) { return make_i( Support::PI2, std::log( r + std::sqrt( r*r - 1 ) ) ); } else { return multiplier( Support::PI2, std::log( r + std::sqrt( r*r - 1 ) )/absq, q ); } } else if ( r < -1 ) { // Branch cut (-oo, -1) T absq = q.abs_imag(); if ( q == I || absq == 0 ) { return make_i( -Support::PI2, std::log( -r + std::sqrt( r*r - 1 ) ) ); } else { return multiplier( -Support::PI2, std::log( -r + std::sqrt( r*r - 1 ) )/absq, q ); } } } Complex z = Complex( r, absIm ).asin(); T mltplr; if ( absIm == 0.0 ) { mltplr = z.imag_i(); } else { mltplr = z.imag_i() / absIm; } return multiplier( z.real(), mltplr, *this ); } // Real Branch Cut: (-oo, -1) U (1, oo) /********************************************************** * Inverse Complementary Sine Function **********************************************************/ template Quaternion Quaternion::acos( const Quaternion & q ) const { T absIm = abs_imag(); // Branch Cuts: (-oo, -1) U (1, oo) if ( absIm == 0 ) { if ( r > 1 ) { // Branch cut (1, oo) T absq = q.abs_imag(); if ( q == I || absq == 0 ) { return make_i( 0.0, -std::log( r + std::sqrt( r*r - 1 ) ) ); } else { return multiplier( 0.0, -std::log( r + std::sqrt( r*r - 1 ) )/absq, q ); } } else if ( r < -1 ) { // Branch cut (-oo, -1) T absq = q.abs_imag(); if ( q == I || absq == 0 ) { return make_i( Support::PI, -std::log( -r + std::sqrt( r*r - 1 ) ) ); } else { return multiplier( Support::PI, -std::log( -r + std::sqrt( r*r - 1 ) )/absq, q ); } } } Complex z = Complex( r, absIm ).acos(); T mltplr; if ( absIm == 0.0 ) { mltplr = z.imag_i(); } else { mltplr = z.imag_i() / absIm; } return multiplier( z.real(), mltplr, *this ); } // Complex Branch Cut: (-ooi, -i] U [i, ooi) /********************************************************** * Inverse Tangent Function **********************************************************/ template Quaternion Quaternion::atan() const { T absIm = abs_imag(); if ( r == 0 ) { if ( absIm == 1 ) { return multiplier( Support::NaN, Support::POS_INF, *this ); } else if ( absIm > 1 ) { // Branch cut [ui, Inf) // - ui is a unit purely-imaginary quaternion T p = absIm + 1; T m = absIm - 1; T mltplr = 0.25*std::log( (p*p)/(m*m) )/absIm; return multiplier( Support::sign( r )*Support::PI2, mltplr, *this ); } } Complex z = Complex( r, absIm ).atan(); T mltplr; if ( absIm == 0.0 ) { mltplr = z.imag_i(); } else { mltplr = z.imag_i() / absIm; } return multiplier( z.real(), mltplr, *this ); } /********************************************************** * Inverse Secant Function **********************************************************/ template Quaternion Quaternion::asec( const Quaternion & q ) const { T absIm = abs_imag(); // Branch Cut: (-1, 1) if ( absIm == 0 ) { if ( r == 0.0 ) { return multiplier( Support::POS_INF, Support::POS_INF, q ); } else if ( r > 0.0 && r < 1.0 ) { T absq = q.abs_imag(); if ( q == I || absq == 0 ) { return make_i( 0.0, std::log( 1.0/r + std::sqrt( 1.0/(r*r) - 1.0 ) ) ); } else { return multiplier( 0.0, std::log( 1.0/r + std::sqrt( 1.0/(r*r) - 1.0 ) )/absq, q ); } } else if ( r > -1.0 && r < 0.0 ) { T absq = q.abs_imag(); if ( q == I || absq == 0 ) { return make_i( Support::PI, std::log( -1.0/r + std::sqrt( 1.0/(r*r) - 1.0 ) ) ); } else { return multiplier( Support::PI, std::log( -1.0/r + std::sqrt( 1.0/(r*r) - 1.0 ) )/absq, q ); } } } Complex z = Complex( r, absIm ).asec(); T mltplr; if ( absIm == 0.0 ) { mltplr = z.imag_i(); } else { mltplr = z.imag_i() / absIm; } return multiplier( z.real(), mltplr, *this ); } // Real Branch Cut: (-1, 1) /********************************************************** * Inverse Complementary Secant Function **********************************************************/ template Quaternion Quaternion::acsc( const Quaternion & q ) const { T absIm = abs_imag(); if ( absIm == 0.0 ) { if ( r == 0.0 ) { return multiplier( Support::POS_INF, Support::POS_INF, q ); } else if ( r > 0.0 && r < 1.0 ) { T absq = q.abs_imag(); if ( q == I || absq == 0 ) { return make_i( Support::PI2, -std::log( 1.0/r + std::sqrt( 1.0/(r*r) - 1.0 ) ) ); } else { return multiplier( Support::PI2, -std::log( 1.0/r + std::sqrt( 1.0/(r*r) - 1.0 ) )/absq, q ); } } else if ( r > -1.0 && r < 0.0 ) { T absq = q.abs_imag(); if ( q == I || absq == 0 ) { return make_i( -Support::PI2, -std::log( -1.0/r + std::sqrt( 1.0/(r*r) - 1.0 ) ) ); } else { return multiplier( -Support::PI2, -std::log( -1.0/r + std::sqrt( 1.0/(r*r) - 1.0 ) )/absq, q ); } } } Complex z = Complex( r, absIm ).acsc(); T mltplr; if ( absIm == 0.0 ) { mltplr = z.imag_i(); } else { mltplr = z.imag_i() / absIm; } return multiplier( z.real(), mltplr, *this ); } /********************************************************** * Inverse Complementary Tangent Function * Complex Branch Cut: (-i, i) **********************************************************/ template Quaternion Quaternion::acot() const { T absIm = abs_imag(); if ( r == 0 ) { if ( absIm == 0 ) { return Quaternion( Support::PI2, -i, -j, -k ); } else if ( absIm < 1 ) { // Branch cut [ui, Inf) // - ui is a unit purely-imaginary quaternion T p = absIm + 1; T m = absIm - 1; T mltplr = -0.25*std::log( (p*p)/(m*m) )/absIm; return multiplier( Support::PI2, mltplr, *this ); } } Complex z = Complex( r, absIm ).acot(); T mltplr; if ( absIm == 0.0 ) { mltplr = z.imag_i(); } else { mltplr = z.imag_i() / absIm; } return multiplier( z.real(), mltplr, *this ); } // Complex Branch Cut: (-ooi, -i) U (i, ooi) /********************************************************** * Inverse Hyperbolic Sine Function **********************************************************/ template Quaternion Quaternion::asinh() const { T absIm = abs_imag(); if ( r == 0 ) { if ( absIm > 1 ) { return multiplier( Support::sign( r )*std::log( absIm + std::sqrt(absIm*absIm - 1) ), Support::PI2/absIm, *this ); } } Complex z = Complex( r, absIm ).asinh(); T mltplr; if ( absIm == 0.0 ) { mltplr = z.imag_i(); } else { mltplr = z.imag_i() / absIm; } return multiplier( z.real(), mltplr, *this ); } /********************************************************** * Inverse Hyperbolic Complementary Sine Function * Real Branch Cut: (-oo, 1) **********************************************************/ template Quaternion Quaternion::acosh( const Quaternion & q ) const { T absIm = abs_imag(); if ( absIm == 0 ) { if ( r < -1 ) { T absq = q.abs_imag(); if ( q == I || absq == 0 ) { return make_i( std::log(-r + std::sqrt(r*r - 1)), Support::PI*Support::sign(i) ); } else { return multiplier( std::log(-r + std::sqrt(r*r - 1)), Support::PI/absq, q ); } } else if ( r == -1 ) { T absq = q.abs_imag(); if ( q == I || absq == 0 ) { return make_i( 0.0, Support::PI*Support::sign(i) ); } else { return multiplier( 0.0, Support::PI/absq, q ); } } else if ( r < 0 ) { T absq = q.abs_imag(); if ( q == I || absq == 0 ) { return make_i( 0.0, std::acos(r)*Support::sign(i) ); } else { return multiplier( 0.0, std::acos(r)/absq, q ); } } else if ( r == 0 ) { T absq = q.abs_imag(); if ( q == I || absq == 0 ) { return make_i( 0.0, Support::PI2*Support::sign(i) ); } else { return multiplier( 0.0, Support::PI2/absq, q ); } } else if ( r < 1 ) { T absq = q.abs_imag(); if ( q == I || absq == 0 ) { return make_i( 0.0, std::acos( r )*Support::sign(i) ); } else { return multiplier( 0.0, std::acos(r)/absq, q ); } } } Complex z = Complex( r, absIm ).acosh(); T mltplr; if ( absIm == 0.0 ) { mltplr = z.imag_i(); } else { mltplr = z.imag_i() / absIm; } return multiplier( z.real(), mltplr, *this ); } /********************************************************** * Inverse Hyperbolic Tangent Function * Real Branch Cut: (-oo, -1] U [1, oo) **********************************************************/ template Quaternion Quaternion::atanh( const Quaternion & q ) const { T absIm = abs_imag(); if ( absIm == 0 ) { if ( r == -1 ) { return make_inf( Support::NEG_INF, Support::NaN ); } else if ( r == 1 ) { return make_inf( Support::POS_INF, Support::NaN ); } else if ( r < -1 || r > 1 ) { T p = r + 1; T m = r - 1; T absq = q.abs_imag(); if ( q == I || absq == 0 ) { return make_i( 0.25*std::log( (p*p)/(m*m) ), -Support::sign(r)*Support::PI2 ); } else { return multiplier( 0.25*std::log( (p*p)/(m*m) ), -Support::sign(r)*Support::PI2/absq, q ); } } } Complex z = Complex( r, absIm ).atanh(); T mltplr; if ( absIm == 0.0 ) { mltplr = z.imag_i(); } else { mltplr = z.imag_i() / absIm; } return multiplier( z.real(), mltplr, *this ); } /********************************************************** * Inverse Hyperbolic Secant Function * Real Branch Cut: (-oo, 0] U (1, oo) **********************************************************/ template Quaternion Quaternion::asech( const Quaternion & q ) const { T absIm = abs_imag(); if ( absIm == 0 ) { if ( r < -1 || r > 1 ) { T absq = q.abs_imag(); if ( q == I || absq == 0 ) { return make_i( 0.0, -std::acos( 1/r )*Support::sign(i) ); } else { return multiplier( 0.0, -std::acos( 1/r )/absq, q ); } } else if ( r == -1 ) { T absq = q.abs_imag(); if ( q == I || absq == 0 ) { return make_i( 0.0, Support::PI*Support::sign(i) ); } else { return multiplier( 0.0, Support::PI/absq, q ); } } else if ( r < 0 ) { T absq = q.abs_imag(); if ( q == I || absq == 0 ) { return make_i( std::log( -1/r + std::sqrt( 1/(r*r) - 1 ) ), -Support::PI ); } else { return multiplier( std::log( -1/r + std::sqrt( 1/(r*r) - 1 ) ), -Support::PI/absq, q ); } } else if ( r == 0 ) { return make_inf( Support::POS_INF, Support::NaN ); } } Complex z = Complex( r, absIm ).asech(); T mltplr; if ( absIm == 0.0 ) { mltplr = z.imag_i(); } else { mltplr = z.imag_i() / absIm; } return multiplier( z.real(), mltplr, *this ); } /********************************************************** * Inverse Hyperbolic Complementary Secant Function * Complex Branch Cut: (-i, i) **********************************************************/ template Quaternion Quaternion::acsch() const { T absIm = abs_imag(); if ( r == 0 ) { if ( absIm == 0 ) { return make_inf( Support::NEG_INF, Support::NaN ); } else if ( absIm < 1 ) { return multiplier( Support::sign( r )*std::log( 1/absIm + std::sqrt(1/(absIm*absIm) - 1) ), -Support::PI2/absIm, *this ); } } Complex z = Complex( r, absIm ).acsch(); T mltplr; if ( absIm == 0.0 ) { mltplr = z.imag_i(); } else { mltplr = z.imag_i() / absIm; } return multiplier( z.real(), mltplr, *this ); } /********************************************************** * Inverse Hyperbolic Complementary Tangent Function * Real Branch Cut: [-1, 1] **********************************************************/ template Quaternion Quaternion::acoth( const Quaternion & q ) const { T absIm = abs_imag(); if ( absIm == 0 ) { if ( r == -1 ) { return make_inf( Support::NEG_INF, Support::NaN ); } else if ( r == 1 ) { return make_inf( Support::POS_INF, Support::NaN ); } else if ( r == 0 ) { T absq = q.abs_imag(); if ( q == I || absq == 0 ) { return make_i( 0.0, -Support::PI2*Support::sign(i) ); } else { return multiplier( 0.0, -Support::PI2/absq, q ); } } else if ( r > -1 && r < 0 ) { T p = r + 1; T m = r - 1; T absq = q.abs_imag(); if ( q == I || absq == 0 ) { return make_i( 0.25*std::log( (p*p)/(m*m) ), -Support::sign(r)*Support::PI2 ); } else { return multiplier( 0.25*std::log( (p*p)/(m*m) ), -Support::sign(r)*Support::PI2/absq, q ); } } } Complex z = Complex( r, absIm ).acoth(); T mltplr; if ( absIm == 0.0 ) { mltplr = z.imag_i(); } else { mltplr = z.imag_i() / absIm; } return multiplier( z.real(), mltplr, *this ); } /********************************************************** * Bessel J Function **********************************************************/ template Quaternion Quaternion::bessel_J( int n ) const { T absIm = abs_imag(); Complex z = Complex( r, absIm ).bessel_J( n ); T mltplr; if ( absIm == 0.0 ) { mltplr = z.imag_i(); } else { mltplr = z.imag_i() / absIm; } return multiplier( z.real(), mltplr, *this ); } /****************************************** * Integer Functions ******************************************/ template Quaternion Quaternion::ceil() const { return Quaternion( std::ceil(r), std::ceil(i), std::ceil(j), std::ceil(k) ); } template Quaternion Quaternion::floor() const { return Quaternion( std::floor(r), std::floor(i), std::floor(j), std::floor(k) ); } /****************************************** * Horner's Rule * * The polynomial is defined by giving the highest * coefficient first: * * n - 1 n - 2 * v[0]*q + v[1]*q + ... + v[n-2]*q + v[n-1] * * This is the same as with Matlab. Because quaternions are * not commutative, this only makes sense if the coefficients * and offsets are real. * * Re(q) + i |Imag(q)| * ******************************************/ template Quaternion Quaternion::horner( T * v, unsigned int n ) const { T absIm = abs_imag(); Complex z = Complex( r, absIm ).horner( v, n ); T mltplr; if ( absIm == 0.0 ) { mltplr = z.imag_i(); } else { mltplr = z.imag_i() / absIm; } return multiplier( z.real(), mltplr, *this ); } template Quaternion Quaternion::horner( T * v, T * c, unsigned int n ) const { T absIm = abs_imag(); Complex z = Complex( r, absIm ).horner( v, c, n ); T mltplr; if ( absIm == 0.0 ) { mltplr = z.imag_i(); } else { mltplr = z.imag_i() / absIm; } return multiplier( z.real(), mltplr, *this ); } /****************************************** * Random Factories ******************************************/ template Quaternion Quaternion::random() { return Quaternion( (static_cast( rand() ))/RAND_MAX, (static_cast( rand() ))/RAND_MAX, (static_cast( rand() ))/RAND_MAX, (static_cast( rand() ))/RAND_MAX ); } template Quaternion Quaternion::random_imag() { return Quaternion( 0.0, (static_cast( rand() ))/RAND_MAX, (static_cast( rand() ))/RAND_MAX, (static_cast( rand() ))/RAND_MAX ); } template Quaternion Quaternion::random_real() { return Quaternion( (static_cast( rand() ))/RAND_MAX, 0.0, 0.0, 0.0 ); } /****************************************** * Binary Arithmetic Operators ******************************************/ template Quaternion Quaternion::operator + ( const Quaternion & z ) const { return Quaternion( r + z.r, i + z.i, j + z.j, k + z.k ); } template Quaternion Quaternion::operator + ( T x ) const { return Quaternion( r + x, i, j, k ); } template Quaternion operator + ( T x, const Quaternion & z ) { return Quaternion( x + z.real(), z.imag_i(), z.imag_j(), z.imag_k() ); } template Quaternion operator + ( long x, const Quaternion & z ) { return Quaternion( static_cast( x ) + z.real(), z.imag_i(), z.imag_j(), z.imag_k() ); } template Quaternion Quaternion::operator - ( const Quaternion & z ) const { return Quaternion( r - z.r, i - z.i, j - z.j, k - z.k ); } template Quaternion Quaternion::operator - ( T x ) const { return Quaternion( r - x, i, j, k ); } template Quaternion operator - ( T x, const Quaternion & z ) { return Quaternion( x - z.real(), -z.imag_i(), -z.imag_j(), -z.imag_k() ); } template Quaternion operator - ( long x, const Quaternion & z ) { return Quaternion( static_cast( x ) - z.real(), -z.imag_i(), -z.imag_j(), -z.imag_k() ); } template Quaternion Quaternion::operator * ( const Quaternion & q ) const { return Quaternion( r*q.r - i*q.i - j*q.j - k*q.k, r*q.i + i*q.r + j*q.k - k*q.j, r*q.j - i*q.k + j*q.r + k*q.i, r*q.k + i*q.j - j*q.i + k*q.r ); } template Quaternion Quaternion::operator * ( T x ) const { if ( Support::is_inf( x ) && norm() > 0 ) { return Quaternion( ( ( r == 0 )?Support::sign(x):x )*r, ( ( i == 0 )?Support::sign(x):x )*i, ( ( j == 0 )?Support::sign(x):x )*j, ( ( k == 0 )?Support::sign(x):x )*k ); } else { return Quaternion( x*r, x*i, x*j, x*k ); } } template Quaternion operator * ( T x, const Quaternion & q ) { return q.operator * ( x ); } template Quaternion operator * ( long x, const Quaternion & q ) { return q.operator * ( static_cast( x ) ); } template Quaternion Quaternion::operator / ( const Quaternion & q ) const { T denom = q.norm(); return Quaternion( ( r*q.r + i*q.i + j*q.j + k*q.k)/denom, (-r*q.i + i*q.r - j*q.k + k*q.j)/denom, (-r*q.j + i*q.k + j*q.r - k*q.i)/denom, (-r*q.k - i*q.j + j*q.i + k*q.r)/denom ); } template Quaternion Quaternion::operator / ( T x ) const { if ( x == 0.0 && norm() > 0 ) { return Quaternion( r / ( ( r == 0 )?Support::sign( x ):x ), i / ( ( i == 0 )?Support::sign( x ):x ), j / ( ( j == 0 )?Support::sign( x ):x ), k / ( ( k == 0 )?Support::sign( x ):x ) ); } else { return Quaternion( r/x, i/x, j/x, k/x ); } } template Quaternion operator / ( T x, const Quaternion & q ) { T mltplr = x/q.norm(); return Quaternion( mltplr*q.real(), -mltplr*q.imag_i(), -mltplr*q.imag_j(), -mltplr*q.imag_k() ); } template Quaternion operator / ( long x, const Quaternion & q ) { T mltplr = static_cast( x )/q.norm(); return Quaternion( mltplr*q.real(), -mltplr*q.imag_i(), -mltplr*q.imag_j(), -mltplr*q.imag_k() ); } /****************************************** * Unary Arithmetic Operators ******************************************/ template Quaternion Quaternion::operator - () const { return Quaternion( -r, -i, -j, -k ); } /****************************************** * Binary Boolean Operators ******************************************/ template bool Quaternion::operator == ( const Quaternion & q ) const { return ( r == q.r ) && ( i == q.i ) && ( j == q.j ) && ( k == q.k ); } template bool Quaternion::operator == ( T x ) const { return ( r == x ) && ( i == 0.0 ) && ( j == 0.0 ) && ( k == 0.0 ); } template bool operator == ( T x, const Quaternion & q ) { return q.operator == ( x ); } template bool operator == ( long x, const Quaternion & q ) { return q.operator == ( static_cast( x ) ); } template bool Quaternion::operator != ( const Quaternion & q ) const { return ( r != q.r ) || ( i != q.i ) || ( j != q.j ) || ( k != q.k ); } template bool Quaternion::operator != ( T x ) const { return ( r != x ) || ( i != 0.0 ) || ( j != 0.0 ) || ( k != 0.0 ); } template bool operator != ( T x, const Quaternion & q ) { return q.operator != ( x ); } template bool operator != ( long x, const Quaternion & q ) { return q.operator != ( static_cast( x ) ); } /****************************************** * IO Stream Operators ******************************************/ template std::ostream & operator << ( std::ostream & out, const Quaternion & z ) { Support::print_real( z.real(), out ); Support::print_imaginary( z.imag_i(), 'i', out ); Support::print_imaginary( z.imag_j(), 'j', out ); Support::print_imaginary( z.imag_k(), 'k', out ); return out; } /****************************************** * ************************************** * * * * * * * Procedural Functions * * * * * * * ************************************** * ******************************************/ /****************************************** * Real-valued Functions ******************************************/ template T real( const Quaternion & q ) { return q.real(); } template T imag_i( const Quaternion & q ) { return q.imag_i(); } template T imag_j( const Quaternion & q ) { return q.imag_j(); } template T imag_k( const Quaternion & q ) { return q.imag_k(); } template T csgn( const Quaternion & q ) { return q.csgn(); } template T abs( const Quaternion & q ) { return q.abs(); } template T norm( const Quaternion & q ) { return q.norm(); } template T abs_imag( const Quaternion & z ) { return z.abs_imag(); } template T norm_imag( const Quaternion & z ) { return z.norm_imag(); } /****************************************** * Quaternion-valued Functions ******************************************/ template Quaternion imag( const Quaternion & q ) { return q.imag(); } template Quaternion conj( const Quaternion & q ) { return q.conj(); } template Quaternion signum( const Quaternion & q ) { return q.signum(); } template Quaternion sqr( const Quaternion & q ) { return q.sqr(); } template Quaternion sqrt( const Quaternion & q ) { return q.sqrt(); } template Quaternion rotate( const Quaternion & q, const Quaternion & p ) { return q.rotate( p ); } /****************************************** * Exponential and Logarithmic Functions ******************************************/ template Quaternion exp( const Quaternion & q ) { return q.exp(); } template Quaternion log( const Quaternion & q ) { return q.log(); } template Quaternion log10( const Quaternion & q ) { return q.log10(); } template Quaternion pow( const Quaternion & q, const Quaternion & w ) { return q.pow( w ); } template Quaternion pow( const Quaternion & q, T x ) { return q.pow( x ); } template Quaternion inverse( const Quaternion & q ) { return q.inverse(); } /****************************************** * Trigonometric and Hyperbolic Functions ******************************************/ template Quaternion sin( const Quaternion & q ) { return q.sin(); } template Quaternion cos( const Quaternion & q ) { return q.cos(); } template Quaternion tan( const Quaternion & q ) { return q.tan(); } template Quaternion sec( const Quaternion & q ) { return q.sec(); } template Quaternion csc( const Quaternion & q ) { return q.csc(); } template Quaternion cot( const Quaternion & q ) { return q.cot(); } template Quaternion sinh( const Quaternion & q ) { return q.sinh(); } template Quaternion cosh( const Quaternion & q ) { return q.cosh(); } template Quaternion tanh( const Quaternion & q ) { return q.tanh(); } template Quaternion sech( const Quaternion & q ) { return q.sech(); } template Quaternion csch( const Quaternion & q ) { return q.csch(); } template Quaternion coth( const Quaternion & q ) { return q.coth(); } template Quaternion asin( const Quaternion & q, const Quaternion & p ) { return q.asin( p ); } template Quaternion acos( const Quaternion & q, const Quaternion & p ) { return q.acos( p ); } template Quaternion atan( const Quaternion & q ) { return q.atan(); } template Quaternion asec( const Quaternion & q, const Quaternion & p ) { return q.asec( p ); } template Quaternion acsc( const Quaternion & q, const Quaternion & p ) { return p.acsc( p ); } template Quaternion acot( const Quaternion & q ) { return q.acot(); } template Quaternion asinh( const Quaternion & q ) { return q.asinh(); } template Quaternion acosh( const Quaternion & q, const Quaternion & p ) { return q.acosh( p ); } template Quaternion atanh( const Quaternion & q, const Quaternion & p ) { return q.atanh( p ); } template Quaternion asech( const Quaternion & q, const Quaternion & p ) { return q.asech( p ); } template Quaternion acsch( const Quaternion & q ) { return q.acsch(); } template Quaternion acoth( const Quaternion & q, const Quaternion & p ) { return q.acoth( p ); } template Quaternion bessel_J( int n, const Quaternion & z ) { return z.bessel_J( n ); } /****************************************** * Integer Functions ******************************************/ template Quaternion floor( const Quaternion & q ) { return q.floor(); } template Quaternion ceil( const Quaternion & q ) { return q.ceil(); } /****************************************** * Horner's Rule ******************************************/ template Quaternion horner( const Quaternion & q, T * v, unsigned int n ) { return q.horner( v, n ); } template Quaternion horner( const Quaternion & q, T * v, T * c, unsigned int n ) { return q.horner( v, c, n ); } /************************************************** * ********************************************** * * * * * * * Double-precision Floating-point * * * * Instance of Template * * * * * * * ********************************************** * **************************************************/ template class Quaternion; template std::ostream & operator << ( std::ostream &, const Quaternion & ); template Quaternion operator + ( double, const Quaternion & ); template Quaternion operator + ( long, const Quaternion & ); template Quaternion operator - ( double, const Quaternion & ); template Quaternion operator - ( long, const Quaternion & ); template Quaternion operator * ( double, const Quaternion & ); template Quaternion operator * ( long, const Quaternion & ); template Quaternion operator / ( double, const Quaternion & ); template Quaternion operator / ( long, const Quaternion & ); template bool operator == ( double, const Quaternion & ); template bool operator == ( long, const Quaternion & ); template bool operator != ( double, const Quaternion & ); template bool operator != ( long, const Quaternion & ); template <> const Quaternion Quaternion::ZERO = Quaternion( 0, 0, 0, 0 ); template <> const Quaternion Quaternion::ONE = Quaternion( 1, 0, 0, 0 ); template <> const Quaternion Quaternion::I = Quaternion( 0, 1, 0, 0 ); template <> const Quaternion Quaternion::J = Quaternion( 0, 0, 1, 0 ); template <> const Quaternion Quaternion::K = Quaternion( 0, 0, 0, 1 ); template <> const Quaternion Quaternion::UNITS[4] = { Quaternion::ONE, Quaternion::I, Quaternion::J, Quaternion::K }; template double real( const Quaternion & ); template double imag_i( const Quaternion & ); template double imag_j( const Quaternion & ); template double imag_k( const Quaternion & ); template double csgn( const Quaternion & ); template double abs( const Quaternion & ); template double norm( const Quaternion & ); template double abs_imag( const Quaternion & ); template double norm_imag( const Quaternion & ); template Quaternion imag( const Quaternion & ); template Quaternion conj( const Quaternion & ); template Quaternion signum( const Quaternion & ); template Quaternion sqr( const Quaternion & ); template Quaternion sqrt( const Quaternion & ); template Quaternion rotate( const Quaternion &, const Quaternion & ); template Quaternion exp( const Quaternion & ); template Quaternion log( const Quaternion & ); template Quaternion log10( const Quaternion & ); template Quaternion pow( const Quaternion &, const Quaternion & ); template Quaternion pow( const Quaternion &, double ); template Quaternion inverse( const Quaternion & ); template Quaternion sin( const Quaternion & ); template Quaternion cos( const Quaternion & ); template Quaternion tan( const Quaternion & ); template Quaternion sec( const Quaternion & ); template Quaternion csc( const Quaternion & ); template Quaternion cot( const Quaternion & ); template Quaternion sinh( const Quaternion & ); template Quaternion cosh( const Quaternion & ); template Quaternion tanh( const Quaternion & ); template Quaternion sech( const Quaternion & ); template Quaternion csch( const Quaternion & ); template Quaternion coth( const Quaternion & ); template Quaternion asin( const Quaternion &, const Quaternion & ); template Quaternion acos( const Quaternion &, const Quaternion & ); template Quaternion atan( const Quaternion & ); template Quaternion asec( const Quaternion &, const Quaternion & ); template Quaternion acsc( const Quaternion &, const Quaternion & ); template Quaternion acot( const Quaternion & ); template Quaternion asinh( const Quaternion & ); template Quaternion acosh( const Quaternion &, const Quaternion & ); template Quaternion atanh( const Quaternion &, const Quaternion & ); template Quaternion asech( const Quaternion &, const Quaternion & ); template Quaternion acsch( const Quaternion & ); template Quaternion acoth( const Quaternion &, const Quaternion & ); template Quaternion bessel_J( int, const Quaternion & ); template Quaternion floor( const Quaternion & ); template Quaternion ceil( const Quaternion & ); template Quaternion horner( const Quaternion &, double *, unsigned int ); template Quaternion horner( const Quaternion &, double *, double *, unsigned int ); /************************************************** * ********************************************** * * * * * * * Floating-point Instance of Template * * * * * * * ********************************************** * **************************************************/ template class Quaternion; template std::ostream & operator << ( std::ostream &, const Quaternion & ); template Quaternion operator + ( float, const Quaternion & ); template Quaternion operator + ( long, const Quaternion & ); template Quaternion operator - ( float, const Quaternion & ); template Quaternion operator - ( long, const Quaternion & ); template Quaternion operator * ( float, const Quaternion & ); template Quaternion operator * ( long, const Quaternion & ); template Quaternion operator / ( float, const Quaternion & ); template Quaternion operator / ( long, const Quaternion & ); template bool operator == ( float, const Quaternion & ); template bool operator == ( long, const Quaternion & ); template bool operator != ( float, const Quaternion & ); template bool operator != ( long, const Quaternion & ); template <> const Quaternion Quaternion::ZERO = Quaternion( 0, 0, 0, 0 ); template <> const Quaternion Quaternion::ONE = Quaternion( 1, 0, 0, 0 ); template <> const Quaternion Quaternion::I = Quaternion( 0, 1, 0, 0 ); template <> const Quaternion Quaternion::J = Quaternion( 0, 0, 1, 0 ); template <> const Quaternion Quaternion::K = Quaternion( 0, 0, 0, 1 ); template <> const Quaternion Quaternion::UNITS[4] = { Quaternion::ONE, Quaternion::I, Quaternion::J, Quaternion::K }; template float real( const Quaternion & ); template float imag_i( const Quaternion & ); template float imag_j( const Quaternion & ); template float imag_k( const Quaternion & ); template float csgn( const Quaternion & ); template float abs( const Quaternion & ); template float norm( const Quaternion & ); template float abs_imag( const Quaternion & ); template float norm_imag( const Quaternion & ); template Quaternion imag( const Quaternion & ); template Quaternion conj( const Quaternion & ); template Quaternion signum( const Quaternion & ); template Quaternion sqr( const Quaternion & ); template Quaternion sqrt( const Quaternion & ); template Quaternion rotate( const Quaternion &, const Quaternion & ); template Quaternion exp( const Quaternion & ); template Quaternion log( const Quaternion & ); template Quaternion log10( const Quaternion & ); template Quaternion pow( const Quaternion &, const Quaternion & ); template Quaternion pow( const Quaternion &, float ); template Quaternion inverse( const Quaternion & ); template Quaternion sin( const Quaternion & ); template Quaternion cos( const Quaternion & ); template Quaternion tan( const Quaternion & ); template Quaternion sec( const Quaternion & ); template Quaternion csc( const Quaternion & ); template Quaternion cot( const Quaternion & ); template Quaternion sinh( const Quaternion & ); template Quaternion cosh( const Quaternion & ); template Quaternion tanh( const Quaternion & ); template Quaternion sech( const Quaternion & ); template Quaternion csch( const Quaternion & ); template Quaternion coth( const Quaternion & ); template Quaternion asin( const Quaternion &, const Quaternion & ); template Quaternion acos( const Quaternion &, const Quaternion & ); template Quaternion atan( const Quaternion & ); template Quaternion asec( const Quaternion &, const Quaternion & ); template Quaternion acsc( const Quaternion &, const Quaternion & ); template Quaternion acot( const Quaternion & ); template Quaternion asinh( const Quaternion & ); template Quaternion acosh( const Quaternion &, const Quaternion & ); template Quaternion atanh( const Quaternion &, const Quaternion & ); template Quaternion asech( const Quaternion &, const Quaternion & ); template Quaternion acsch( const Quaternion & ); template Quaternion acoth( const Quaternion &, const Quaternion & ); template Quaternion bessel_J( int, const Quaternion & ); template Quaternion floor( const Quaternion & ); template Quaternion ceil( const Quaternion & ); template Quaternion horner( const Quaternion &, float *, unsigned int ); template Quaternion horner( const Quaternion &, float *, float *, unsigned int ); /************************************************************************ * ******************************************************************** * * * * * * * Long Double-precision Floating-point Instance of Template * * * * * * * ******************************************************************** * ************************************************************************/ template class Quaternion; template std::ostream & operator << ( std::ostream &, const Quaternion & ); template Quaternion operator + ( long double, const Quaternion & ); template Quaternion operator + ( long, const Quaternion & ); template Quaternion operator - ( long double, const Quaternion & ); template Quaternion operator - ( long, const Quaternion & ); template Quaternion operator * ( long double, const Quaternion & ); template Quaternion operator * ( long, const Quaternion & ); template Quaternion operator / ( long double, const Quaternion & ); template Quaternion operator / ( long, const Quaternion & ); template bool operator == ( long double, const Quaternion & ); template bool operator == ( long, const Quaternion & ); template bool operator != ( long double, const Quaternion & ); template bool operator != ( long, const Quaternion & ); template <> const Quaternion Quaternion::ZERO = Quaternion( 0, 0, 0, 0 ); template <> const Quaternion Quaternion::ONE = Quaternion( 1, 0, 0, 0 ); template <> const Quaternion Quaternion::I = Quaternion( 0, 1, 0, 0 ); template <> const Quaternion Quaternion::J = Quaternion( 0, 0, 1, 0 ); template <> const Quaternion Quaternion::K = Quaternion( 0, 0, 0, 1 ); template <> const Quaternion Quaternion::UNITS[4] = { Quaternion::ONE, Quaternion::I, Quaternion::J, Quaternion::K }; template long double real( const Quaternion & ); template long double imag_i( const Quaternion & ); template long double imag_j( const Quaternion & ); template long double imag_k( const Quaternion & ); template long double csgn( const Quaternion & ); template long double abs( const Quaternion & ); template long double norm( const Quaternion & ); template long double abs_imag( const Quaternion & ); template long double norm_imag( const Quaternion & ); template Quaternion imag( const Quaternion & ); template Quaternion conj( const Quaternion & ); template Quaternion signum( const Quaternion & ); template Quaternion sqr( const Quaternion & ); template Quaternion sqrt( const Quaternion & ); template Quaternion rotate( const Quaternion &, const Quaternion & ); template Quaternion exp( const Quaternion & ); template Quaternion log( const Quaternion & ); template Quaternion log10( const Quaternion & ); template Quaternion pow( const Quaternion &, const Quaternion & ); template Quaternion pow( const Quaternion &, long double ); template Quaternion inverse( const Quaternion & ); template Quaternion sin( const Quaternion & ); template Quaternion cos( const Quaternion & ); template Quaternion tan( const Quaternion & ); template Quaternion sec( const Quaternion & ); template Quaternion csc( const Quaternion & ); template Quaternion cot( const Quaternion & ); template Quaternion sinh( const Quaternion & ); template Quaternion cosh( const Quaternion & ); template Quaternion tanh( const Quaternion & ); template Quaternion sech( const Quaternion & ); template Quaternion csch( const Quaternion & ); template Quaternion coth( const Quaternion & ); template Quaternion asin( const Quaternion &, const Quaternion & ); template Quaternion acos( const Quaternion &, const Quaternion & ); template Quaternion atan( const Quaternion & ); template Quaternion asec( const Quaternion &, const Quaternion & ); template Quaternion acsc( const Quaternion &, const Quaternion & ); template Quaternion acot( const Quaternion & ); template Quaternion asinh( const Quaternion & ); template Quaternion acosh( const Quaternion &, const Quaternion & ); template Quaternion atanh( const Quaternion &, const Quaternion & ); template Quaternion asech( const Quaternion &, const Quaternion & ); template Quaternion acsch( const Quaternion & ); template Quaternion acoth( const Quaternion &, const Quaternion & ); template Quaternion bessel_J( int, const Quaternion & ); template Quaternion floor( const Quaternion & ); template Quaternion ceil( const Quaternion & ); template Quaternion horner( const Quaternion &, long double *, unsigned int ); template Quaternion horner( const Quaternion &, long double *, long double *, unsigned int );