/****************************************** * C++ Octonions * 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 "Octonion.h" #include "Support.h" #include #include #include /****************************************** * Constructors ******************************************/ template Octonion::Octonion( T real, T imagi, T imagj, T imagk, T imagu, T imagie, T imagje, T imagke ): r(real), i(imagi), j(imagj), k(imagk), u1(imagu), i1(imagie), j1(imagje), k1(imagke) { } template Octonion::Octonion( T real ): r(real), i(0.0), j(0.0), k(0.0), u1(0.0), i1(0.0), j1(0.0), k1(0.0) { } /****************************************** * Assignment Operator ******************************************/ template const Octonion & Octonion::operator = ( const T & real ) { r = real; i = 0.0; j = 0.0; k = 0.0; u1 = 0.0; i1 = 0.0; j1 = 0.0; k1 = 0.0; return *this; } /****************************************** * Mutating Arithmetic Operators ******************************************/ template Octonion & Octonion::operator += ( const Octonion & q ) { r += q.r; i += q.i; j += q.j; k += q.k; u1 += q.u1; i1 += q.i1; j1 += q.j1; k1 += q.k1; return *this; } template Octonion & Octonion::operator -= ( const Octonion & q ) { r -= q.r; i -= q.i; j -= q.j; k -= q.k; u1 -= q.u1; i1 -= q.i1; j1 -= q.j1; k1 -= q.k1; return *this; } template Octonion & Octonion::operator *= ( const Octonion & q ) { T RE = r, I = i, J = j, K = k, E = u1, IE = i1, JE = j1; r = RE*q.r - I*q.i - J*q.j - K*q.k - E*q.u1 - IE*q.i1 - JE*q.j1 - k1*q.k1; i = RE*q.i + I*q.r + J*q.k - K*q.j + E*q.i1 - IE*q.u1 - JE*q.k1 + k1*q.j1; j = RE*q.j - I*q.k + J*q.r + K*q.i + E*q.j1 + IE*q.k1 - JE*q.u1 - k1*q.i1; k = RE*q.k + I*q.j - J*q.i + K*q.r + E*q.k1 - IE*q.j1 + JE*q.i1 - k1*q.u1; u1 = RE*q.u1 - I*q.i1 - J*q.j1 - K*q.k1 + E*q.r + IE*q.i + JE*q.j + k1*q.k; i1 = RE*q.i1 + I*q.u1 - J*q.k1 + K*q.j1 - E*q.i + IE*q.r - JE*q.k + k1*q.j; j1 = RE*q.j1 + I*q.k1 + J*q.u1 - K*q.i1 - E*q.j + IE*q.k + JE*q.r - k1*q.i; k1 = RE*q.k1 - I*q.j1 + J*q.i1 + K*q.u1 - E*q.k - IE*q.j + JE*q.i + k1*q.r; return * this; } template Octonion & Octonion::operator /= ( const Octonion & q ) { T denom = q.norm(); T RE = r, I = i, J = j, K = k, E = u1, IE = i1, JE = j1; r = ( RE*q.r + I*q.i + J*q.j + K*q.k + E*q.u1 + IE*q.i1 + JE*q.j1 + k1*q.k1)/denom; i = (-RE*q.i + I*q.r - J*q.k + K*q.j - E*q.i1 + IE*q.u1 + JE*q.k1 - k1*q.j1)/denom; j = (-RE*q.j + I*q.k + J*q.r - K*q.i - E*q.j1 - IE*q.k1 + JE*q.u1 + k1*q.i1)/denom; k = (-RE*q.k - I*q.j + J*q.i + K*q.r - E*q.k1 + IE*q.j1 - JE*q.i1 + k1*q.u1)/denom; u1 = (-RE*q.u1 + I*q.i1 + J*q.j1 + K*q.k1 + E*q.r - IE*q.i - JE*q.j - k1*q.k )/denom; i1 = (-RE*q.i1 - I*q.u1 + J*q.k1 - K*q.j1 + E*q.i + IE*q.r + JE*q.k - k1*q.j )/denom; j1 = (-RE*q.j1 - I*q.k1 - J*q.u1 + K*q.i1 + E*q.j - IE*q.k + JE*q.r + k1*q.i )/denom; k1 = (-RE*q.k1 + I*q.j1 - J*q.i1 - K*q.u1 + E*q.k + IE*q.j - JE*q.i + k1*q.r )/denom; return * this; } template Octonion & Octonion::operator += ( T x ) { r += x; return *this; } template Octonion & Octonion::operator -= ( T x ) { r -= x; return *this; } template Octonion & Octonion::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; u1 *= ( u1 == 0 )?Support::sign( x ):x; i1 *= ( i1 == 0 )?Support::sign( x ):x; j1 *= ( j1 == 0 )?Support::sign( x ):x; k1 *= ( k1 == 0 )?Support::sign( x ):x; } else { r *= x; i *= x; j *= x; k *= x; u1 *= x; i1 *= x; j1 *= x; k1 *= x; } return * this; } template Octonion & Octonion::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; u1 /= ( u1 == 0 )?Support::sign( x ):x; i1 /= ( i1 == 0 )?Support::sign( x ):x; j1 /= ( j1 == 0 )?Support::sign( x ):x; k1 /= ( k1 == 0 )?Support::sign( x ):x; } else { r /= x; i /= x; j /= x; k /= x; u1 /= x; i1 /= x; j1 /= x; k1 /= x; } return * this; } template Octonion & Octonion::operator ++() { ++r; return *this; } template Octonion Octonion::operator ++( int ) { Octonion copy = *this; ++r; return copy; } template Octonion & Octonion::operator --() { --r; return *this; } template Octonion Octonion::operator --( int ) { Octonion copy = *this; --r; return copy; } /****************************************** * Real-valued Functions ******************************************/ template T Octonion::real() const { return r; } template T Octonion::operator []( int n ) const { return reinterpret_cast( this )[n]; } template T& Octonion::operator []( int n ) { return reinterpret_cast( this )[n]; } template T Octonion::imag_i() const { return i; } template T Octonion::imag_j() const { return j; } template T Octonion::imag_k() const { return k; } template T Octonion::imag_u1() const { return u1; } template T Octonion::imag_i1() const { return i1; } template T Octonion::imag_j1() const { return j1; } template T Octonion::imag_k1() const { return k1; } template T Octonion::csgn() const { return is_zero() ? 0.0 : Support::sign( r ); } template T Octonion::abs() const { return is_inf()? Support::POS_INF : std::sqrt( r*r + i*i + j*j + k*k + u1*u1 + i1*i1 + j1*j1 + k1*k1 ); } template T Octonion::norm() const { return is_inf() ? Support::POS_INF : r*r + i*i + j*j + k*k + u1*u1 + i1*i1 + j1*j1 + k1*k1; } template T Octonion::abs_imag() const { return is_inf() ? Support::POS_INF : std::sqrt( i*i + j*j + k*k + u1*u1 + i1*i1 + j1*j1 + k1*k1 ); } template T Octonion::norm_imag() const { return is_inf() ? Support::POS_INF : i*i + j*j + k*k + u1*u1 + i1*i1 + j1*j1 + k1*k1; } template T Octonion::arg() const { return std::atan2( abs_imag(), r ); } /****************************************** * Octonion-valued Functions ******************************************/ template Octonion Octonion::imag() const { return Octonion( 0.0, i, j, k, u1, i1, j1, k1 ); } template Octonion Octonion::conj() const { return Octonion( r, -i, -j, -k, -u1, -i1, -j1, -k1 ); } template Octonion Octonion::operator * () const { return Octonion( r, -i, -j, -k, -u1, -i1, -j1, -k1 ); } template Octonion Octonion::signum() const { T absq = abs(); if ( absq == 0.0 || Support::is_nan( absq ) || Support::is_inf( absq ) ) { return *this; } else { return Octonion( r/absq, i/absq, j/absq, k/absq, u1/absq, i1/absq, j1/absq, k1/absq ); } } template Octonion Octonion::sqr() const { return Octonion( r*r - i*i - j*j - k*k - u1*u1 - i1*i1 - j1*j1 - k1*k1, 2*r*i, 2*r*j, 2*r*k, 2*r*u1, 2*r*i1, 2*r*j1, 2*r*k1 ); } template Octonion Octonion::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 Octonion Octonion::rotate( const Octonion & 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 u1u1 = q.u1*q.u1; T i1i1 = q.i1*q.i1; T j1j1 = q.j1*q.j1; T k1k1 = q.k1*q.k1; T iqi = i*q.i; T jqj = j*q.j; T kqk = k*q.k; T u1qu1 = u1*q.u1; T i1qi1 = i1*q.i1; T j1qj1 = j1*q.j1; T k1qk1 = k1*q.k1; T sum = rr - ii - jj - kk - u1u1 - i1i1 - j1j1 - k1k1; return Octonion( ( ( r == 0 ) ? r : r*(rr + ii + jj + kk + u1u1 + i1i1 + j1j1 + k1k1) ), (sum + 2*ii)*i + 2*( q.r*(-j*q.k + k*q.j - u1*q.i1 + i1*q.u1 + j1*q.k1 - k1*q.j1) + q.i*(jqj + kqk + u1qu1 + i1qi1 + j1qj1 + k1qk1) ), (sum + 2*jj)*j + 2*( q.r*( i*q.k - k*q.i - u1*q.j1 - i1*q.k1 + j1*q.u1 + k1*q.i1) + q.j*(i1qi1 + j1qj1 + k1qk1 + u1qu1 + kqk + iqi) ), (sum + 2*kk)*k + 2*( q.r*(-i*q.j + j*q.i - u1*q.k1 + i1*q.j1 - j1*q.i1 + k1*q.u1) + q.k*(k1qk1 + jqj + u1qu1 + i1qi1 + iqi + j1qj1) ), (sum + 2*u1u1)*u1 + 2*( q.r*( i*q.i1 + j*q.j1 + k*q.k1 - i1*q.i - j1*q.j - k1*q.k) + q.u1*(iqi + jqj + kqk + j1qj1 + k1qk1 + i1qi1) ), (sum + 2*i1i1)*i1 + 2*( q.r*(-i*q.u1 + j*q.k1 - k*q.j1 + u1*q.i + j1*q.k - k1*q.j) + q.i1*(iqi + jqj + u1qu1 + j1qj1 + k1qk1 + kqk) ), (sum + 2*j1j1)*j1 + 2*( q.r*(-i*q.k1 - j*q.u1 - i1*q.k + k1*q.i + k*q.i1 + u1*q.j) + q.j1*(u1qu1 + i1qi1 + k1qk1 + iqi + jqj + kqk) ), (sum + 2*k1k1)*k1 + 2*( q.r*(i*q.j1 - j*q.i1 - k*q.u1 + u1*q.k + i1*q.j - j1*q.i) + q.k1*(j1qj1 + jqj + kqk + u1qu1 + iqi + i1qi1) ) ); } /****************************************** * Boolean-valued Functions ******************************************/ template bool Octonion::is_imaginary() const { return ( r == 0.0 ); } template bool Octonion::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 ) || ( u1 == Support::POS_INF ) || ( u1 == Support::NEG_INF ) || ( i1 == Support::POS_INF ) || ( i1 == Support::NEG_INF ) || ( j1 == Support::POS_INF ) || ( j1 == Support::NEG_INF ) || ( k1 == Support::POS_INF ) || ( k1 == Support::NEG_INF ); } template bool Octonion::is_nan() const { return ( r != r ) || ( i != i ) || ( j != j ) || ( k != k ) || ( u1 != u1 ) || ( i1 != i1 ) || ( j1 != j1 ) || ( k1 != k1 ); } template bool Octonion::is_neg_inf() const { return ( r == Support::NEG_INF ) && ( i == 0.0 ) && ( j == 0.0 ) && ( k == 0.0 ) && ( u1 == 0.0 ) && ( i1 == 0.0 ) && ( j1 == 0.0 ) && ( k1 == 0.0 ); } template bool Octonion::is_pos_inf() const { return ( r == Support::POS_INF ) && ( i == 0.0 ) && ( j == 0.0 ) && ( k == 0.0 ) && ( u1 == 0.0 ) && ( i1 == 0.0 ) && ( j1 == 0.0 ) && ( k1 == 0.0 ); } template bool Octonion::is_real() const { return ( i == 0.0 ) && ( j == 0.0 ) && ( k == 0.0 ) && ( u1 == 0.0 ) && ( i1 == 0.0 ) && ( j1 == 0.0 ) && ( k1 == 0.0 ); } template bool Octonion::is_real_inf() const { return ( r == Support::POS_INF || r == Support::NEG_INF ) && ( i == 0.0 ) && ( j == 0.0 ) && ( k == 0.0 ) && ( u1 == 0.0 ) && ( i1 == 0.0 ) && ( j1 == 0.0 ) && ( k1 == 0.0 ); } template bool Octonion::is_zero() const { return ( r == 0.0 ) && ( i == 0.0 ) && ( j == 0.0 ) && ( k == 0.0 ) && ( u1 == 0.0 ) && ( i1 == 0.0 ) && ( j1 == 0.0 ) && ( k1 == 0.0 ); } /****************************************** * Multiplier Function ******************************************/ template inline Octonion Octonion::multiplier( T r, T mltplr, const Octonion & q ) { if ( Support::is_nan( mltplr ) || Support::is_inf( mltplr ) ) { if ( q.i == 0 && q.j == 0 && q.k == 0 ) { return Octonion( r, mltplr*q.i, mltplr*q.j, mltplr*q.k, mltplr*q.u1, mltplr*q.i1, mltplr*q.j1, mltplr*q.k1 ); } else { return Octonion( 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, (q.u1 == 0) ? Support::sign( mltplr )*q.u1 : mltplr*q.u1, (q.i1 == 0) ? Support::sign( mltplr )*q.i1 : mltplr*q.i1, (q.j1 == 0) ? Support::sign( mltplr )*q.j1 : mltplr*q.j1, (q.k1 == 0) ? Support::sign( mltplr )*q.k1 : mltplr*q.k1 ); } } else { return Octonion( r, mltplr*q.i, mltplr*q.j, mltplr*q.k, mltplr*q.u1, mltplr*q.i1, mltplr*q.j1, mltplr*q.k1 ); } } template inline Octonion Octonion::make_inf( T r, T i ) { return Octonion( r, i, i, i, i, i, i, i ); } template inline Octonion Octonion::make_i( T r, T i ) { return Octonion( r, i, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 ); } /****************************************** * Exponential and Logarithmic Functions ******************************************/ template Octonion Octonion::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 Octonion Octonion::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 Octonion Octonion::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 Octonion Octonion::pow( const Octonion & q ) const { return ( log() * q ).exp(); } template Octonion Octonion::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 Octonion Octonion::inverse() const { if ( is_zero() ) { return Octonion( Support::sign( r )*Support::POS_INF, -Support::sign( i )*Support::POS_INF, -Support::sign( j )*Support::POS_INF, -Support::sign( k )*Support::POS_INF, -Support::sign( u1 )*Support::POS_INF, -Support::sign( i1 )*Support::POS_INF, -Support::sign( j1 )*Support::POS_INF, -Support::sign( k1 )*Support::POS_INF ); } else if ( is_inf() ) { return Octonion( Support::sign( r )*0.0, -Support::sign( i )*0.0, -Support::sign( j )*0.0, -Support::sign( k )*0.0, -Support::sign( u1 )*0.0, -Support::sign( i1 )*0.0, -Support::sign( j1 )*0.0, -Support::sign( k1 )*0.0 ); } else if ( is_nan() ) { return Octonion( Support::NaN, Support::NaN, Support::NaN, Support::NaN, Support::NaN, Support::NaN, Support::NaN, Support::NaN ); } else { T denom = norm(); return Octonion( r/denom, -i/denom, -j/denom, -k/denom, -u1/denom, -i1/denom, -j1/denom, -k1/denom ); } } /******************************************************************** * Trigonometric and Hyperbolic Functions * * For each function f:O -> O, 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 Octonion Octonion::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 Octonion Octonion::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 Octonion Octonion::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 Octonion Octonion::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 Octonion Octonion::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 Octonion Octonion::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 Octonion Octonion::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 Octonion Octonion::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 Octonion Octonion::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 Octonion Octonion::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 Octonion Octonion::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 Octonion Octonion::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 Octonion Octonion::asin( const Octonion & 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 Octonion Octonion::acos( const Octonion & 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 Octonion Octonion::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 Octonion Octonion::asec( const Octonion & 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 Octonion Octonion::acsc( const Octonion & 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 Octonion Octonion::acot() const { T absIm = abs_imag(); if ( r == 0 ) { if ( absIm == 0 ) { return multiplier( Support::PI2, -1, *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::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 Octonion Octonion::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 Octonion Octonion::acosh( const Octonion & 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 Octonion Octonion::atanh( const Octonion & 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 Octonion Octonion::asech( const Octonion & 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 Octonion Octonion::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 Octonion Octonion::acoth( const Octonion & 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 Octonion Octonion::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 Octonion Octonion::ceil() const { return Octonion( std::ceil(r), std::ceil(i), std::ceil(j), std::ceil(k), std::ceil(u1), std::ceil(i1), std::ceil(j1), std::ceil(k1) ); } template Octonion Octonion::floor() const { return Octonion( std::floor(r), std::floor(i), std::floor(j), std::floor(k), std::floor(u1), std::floor(i1), std::floor(j1), std::floor(k1) ); } /****************************************** * 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 octonions are * not commutative, this only makes sense if the coefficients * and offsets are real. * * Re(q) + i |Imag(q)| * ******************************************/ template Octonion Octonion::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 Octonion Octonion::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 Octonion Octonion::random() { return Octonion( (static_cast( rand() ))/RAND_MAX, (static_cast( rand() ))/RAND_MAX, (static_cast( rand() ))/RAND_MAX, (static_cast( rand() ))/RAND_MAX, (static_cast( rand() ))/RAND_MAX, (static_cast( rand() ))/RAND_MAX, (static_cast( rand() ))/RAND_MAX, (static_cast( rand() ))/RAND_MAX ); } template Octonion Octonion::random_imag() { return Octonion( 0.0, (static_cast( rand() ))/RAND_MAX, (static_cast( rand() ))/RAND_MAX, (static_cast( rand() ))/RAND_MAX, (static_cast( rand() ))/RAND_MAX, (static_cast( rand() ))/RAND_MAX, (static_cast( rand() ))/RAND_MAX, (static_cast( rand() ))/RAND_MAX ); } template Octonion Octonion::random_real() { return Octonion( (static_cast( rand() ))/RAND_MAX, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 ); } /****************************************** * Binary Arithmetic Operators ******************************************/ template Octonion Octonion::operator + ( const Octonion & z ) const { return Octonion( r + z.r, i + z.i, j + z.j, k + z.k, u1 + z.u1, i1 + z.i1, j1 + z.j1, k1 + z.k1 ); } template Octonion Octonion::operator + ( T x ) const { return Octonion( r + x, i, j, k, u1, i1, j1, k1 ); } template Octonion operator + ( T x, const Octonion & z ) { return Octonion( x + z.real(), z.imag_i(), z.imag_j(), z.imag_k(), z.imag_u1(), z.imag_i1(), z.imag_j1(), z.imag_k1() ); } template Octonion operator + ( long x, const Octonion & z ) { return Octonion( static_cast( x ) + z.real(), z.imag_i(), z.imag_j(), z.imag_k(), z.imag_u1(), z.imag_i1(), z.imag_j1(), z.imag_k1() ); } template Octonion Octonion::operator - ( const Octonion & z ) const { return Octonion( r - z.r, i - z.i, j - z.j, k - z.k, u1 - z.u1, i1 - z.i1, j1 - z.j1, k1 - z.k1 ); } template Octonion Octonion::operator - ( T x ) const { return Octonion( r - x, i, j, k, u1, i1, j1, k1 ); } template Octonion operator - ( T x, const Octonion & z ) { return Octonion( static_cast( x ) - z.real(), -z.imag_i(), -z.imag_j(), -z.imag_k(), -z.imag_u1(), -z.imag_i1(), -z.imag_j1(), -z.imag_k1() ); } template Octonion operator - ( long x, const Octonion & z ) { return Octonion( x - z.real(), -z.imag_i(), -z.imag_j(), -z.imag_k(), -z.imag_u1(), -z.imag_i1(), -z.imag_j1(), -z.imag_k1() ); } template Octonion Octonion::operator * ( const Octonion & q ) const { return Octonion( r*q.r - i*q.i - j*q.j - k*q.k - u1*q.u1 - i1*q.i1 - j1*q.j1 - k1*q.k1, r*q.i + i*q.r + j*q.k - k*q.j + u1*q.i1 - i1*q.u1 - j1*q.k1 + k1*q.j1, r*q.j - i*q.k + j*q.r + k*q.i + u1*q.j1 + i1*q.k1 - j1*q.u1 - k1*q.i1, r*q.k + i*q.j - j*q.i + k*q.r + u1*q.k1 - i1*q.j1 + j1*q.i1 - k1*q.u1, r*q.u1 - i*q.i1 - j*q.j1 - k*q.k1 + u1*q.r + i1*q.i + j1*q.j + k1*q.k, r*q.i1 + i*q.u1 - j*q.k1 + k*q.j1 - u1*q.i + i1*q.r - j1*q.k + k1*q.j, r*q.j1 + i*q.k1 + j*q.u1 - k*q.i1 - u1*q.j + i1*q.k + j1*q.r - k1*q.i, r*q.k1 - i*q.j1 + j*q.i1 + k*q.u1 - u1*q.k - i1*q.j + j1*q.i + k1*q.r ); } template Octonion Octonion::operator * ( T x ) const { if ( Support::is_inf( x ) && norm() > 0 ) { return Octonion( ( ( 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, ( ( u1 == 0 )?Support::sign(x):x )*u1, ( ( i1 == 0 )?Support::sign(x):x )*i1, ( ( j1 == 0 )?Support::sign(x):x )*j1, ( ( k1 == 0 )?Support::sign(x):x )*k1 ); } else { return Octonion( x*r, x*i, x*j, x*k, x*u1, x*i1, x*j1, x*k1 ); } } template Octonion operator * ( T x, const Octonion & q ) { return q.operator * ( x ); } template Octonion operator * ( long x, const Octonion & q ) { return q.operator * ( static_cast( x ) ); } template Octonion Octonion::operator / ( const Octonion & q ) const { T denom = q.norm(); return Octonion( ( r*q.r + i*q.i + j*q.j + k*q.k + u1*q.u1 + i1*q.i1 + j1*q.j1 + k1*q.k1)/denom, (-r*q.i + i*q.r - j*q.k + k*q.j - u1*q.i1 + i1*q.u1 + j1*q.k1 - k1*q.j1)/denom, (-r*q.j + i*q.k + j*q.r - k*q.i - u1*q.j1 - i1*q.k1 + j1*q.u1 + k1*q.i1)/denom, (-r*q.k - i*q.j + j*q.i + k*q.r - u1*q.k1 + i1*q.j1 - j1*q.i1 + k1*q.u1)/denom, (-r*q.u1 + i*q.i1 + j*q.j1 + k*q.k1 + u1*q.r - i1*q.i - j1*q.j - k1*q.k )/denom, (-r*q.i1 - i*q.u1 + j*q.k1 - k*q.j1 + u1*q.i + i1*q.r + j1*q.k - k1*q.j )/denom, (-r*q.j1 - i*q.k1 - j*q.u1 + k*q.i1 + u1*q.j - i1*q.k + j1*q.r + k1*q.i )/denom, (-r*q.k1 + i*q.j1 - j*q.i1 - k*q.u1 + u1*q.k + i1*q.j - j1*q.i + k1*q.r )/denom ); } template Octonion Octonion::operator / ( T x ) const { if ( x == 0.0 && norm() > 0 ) { return Octonion( 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 ), u1 / ( ( u1 == 0 )?Support::sign( x ):x ), i1 / ( ( i1 == 0 )?Support::sign( x ):x ), j1 / ( ( j1 == 0 )?Support::sign( x ):x ), k1 / ( ( k1 == 0 )?Support::sign( x ):x ) ); } else { return Octonion( r/x, i/x, j/x, k/x, u1/x, i1/x, j1/x, k1/x ); } } template Octonion operator / ( T x, const Octonion & q ) { T mltplr = x/q.norm(); return Octonion( mltplr*q.real(), -mltplr*q.imag_i(), -mltplr*q.imag_j(), -mltplr*q.imag_k(), -mltplr*q.imag_u1(), -mltplr*q.imag_i1(), -mltplr*q.imag_j1(), -mltplr*q.imag_k1() ); } template Octonion operator / ( long x, const Octonion & q ) { T mltplr = static_cast( x )/q.norm(); return Octonion( mltplr*q.real(), -mltplr*q.imag_i(), -mltplr*q.imag_j(), -mltplr*q.imag_k(), -mltplr*q.imag_u1(), -mltplr*q.imag_i1(), -mltplr*q.imag_j1(), -mltplr*q.imag_k1() ); } /****************************************** * Unary Arithmetic Operators ******************************************/ template Octonion Octonion::operator - () const { return Octonion( -r, -i, -j, -k, -u1, -i1, -j1, -k1 ); } /****************************************** * Binary Boolean Operators ******************************************/ template bool Octonion::operator == ( const Octonion & q ) const { return ( r == q.r ) && ( i == q.i ) && ( j == q.j ) && ( k == q.k ) && ( u1 == q.u1 ) && ( i1 == q.i1 ) && ( j1 == q.j1 ) && ( k1 == q.k1 ); } template bool Octonion::operator == ( T x ) const { return ( r == x ) && ( i == 0.0 ) && ( j == 0.0 ) && ( k == 0.0 ) && ( u1 == 0.0 ) && ( i1 == 0.0 ) && ( j1 == 0.0 ) && ( k1 == 0.0 ); } template bool operator == ( T x, const Octonion & q ) { return q.operator == ( x ); } template bool operator == ( long x, const Octonion & q ) { return q.operator == ( static_cast( x ) ); } template bool Octonion::operator != ( const Octonion & q ) const { return ( r != q.r ) || ( i != q.i ) || ( j != q.j ) || ( k != q.k ) || ( u1 != q.u1 ) && ( i1 != q.i1 ) && ( j1 != q.j1 ) && ( k1 != q.k1 ); } template bool Octonion::operator != ( T x ) const { return ( r != x ) || ( i != 0.0 ) || ( j != 0.0 ) || ( k != 0.0 ) || ( u1 != 0.0 ) && ( i1 != 0.0 ) && ( j1 != 0.0 ) && ( k1 != 0.0 ); } template bool operator != ( T x, const Octonion & q ) { return q.operator != ( x ); } template bool operator != ( long x, const Octonion & q ) { return q.operator != ( static_cast( x ) ); } /****************************************** * IO Stream Operators ******************************************/ template std::ostream & operator << ( std::ostream & out, const Octonion & 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 ); Support::print_imaginary( z.imag_u1(), "u1", out ); Support::print_imaginary( z.imag_i1(), "i1", out ); Support::print_imaginary( z.imag_j1(), "j1", out ); Support::print_imaginary( z.imag_k1(), "k1", out ); return out; } /****************************************** * ************************************** * * * * * * * Procedural Functions * * * * * * * ************************************** * ******************************************/ /****************************************** * Real-valued Functions ******************************************/ template T real( const Octonion & q ) { return q.real(); } template T imag_i( const Octonion & q ) { return q.imag_i(); } template T imag_j( const Octonion & q ) { return q.imag_j(); } template T imag_k( const Octonion & q ) { return q.imag_k(); } template T imag_u1( const Octonion & q ) { return q.imag_u1(); } template T imag_i1( const Octonion & q ) { return q.imag_i1(); } template T imag_j1( const Octonion & q ) { return q.imag_j1(); } template T imag_k1( const Octonion & q ) { return q.imag_k1(); } template T csgn( const Octonion & q ) { return q.csgn(); } template T abs( const Octonion & q ) { return q.abs(); } template T norm( const Octonion & q ) { return q.norm(); } template T abs_imag( const Octonion & z ) { return z.abs_imag(); } template T norm_imag( const Octonion & z ) { return z.norm_imag(); } /****************************************** * Octonion-valued Functions ******************************************/ template Octonion imag( const Octonion & q ) { return q.imag(); } template Octonion conj( const Octonion & q ) { return q.conj(); } template Octonion signum( const Octonion & q ) { return q.signum(); } template Octonion sqr( const Octonion & q ) { return q.sqr(); } template Octonion sqrt( const Octonion & q ) { return q.sqrt(); } template Octonion rotate( const Octonion & q, const Octonion & p ) { return q.rotate( p ); } /****************************************** * Exponential and Logarithmic Functions ******************************************/ template Octonion exp( const Octonion & q ) { return q.exp(); } template Octonion log( const Octonion & q ) { return q.log(); } template Octonion log10( const Octonion & q ) { return q.log10(); } template Octonion pow( const Octonion & q, const Octonion & w ) { return q.pow( w ); } template Octonion pow( const Octonion & q, T x ) { return q.pow( x ); } template Octonion inverse( const Octonion & q ) { return q.inverse(); } /****************************************** * Trigonometric and Hyperbolic Functions ******************************************/ template Octonion sin( const Octonion & q ) { return q.sin(); } template Octonion cos( const Octonion & q ) { return q.cos(); } template Octonion tan( const Octonion & q ) { return q.tan(); } template Octonion sec( const Octonion & q ) { return q.sec(); } template Octonion csc( const Octonion & q ) { return q.csc(); } template Octonion cot( const Octonion & q ) { return q.cot(); } template Octonion sinh( const Octonion & q ) { return q.sinh(); } template Octonion cosh( const Octonion & q ) { return q.cosh(); } template Octonion tanh( const Octonion & q ) { return q.tanh(); } template Octonion sech( const Octonion & q ) { return q.sech(); } template Octonion csch( const Octonion & q ) { return q.csch(); } template Octonion coth( const Octonion & q ) { return q.coth(); } template Octonion asin( const Octonion & q, const Octonion & p ) { return q.asin( p ); } template Octonion acos( const Octonion & q, const Octonion & p ) { return q.acos( p ); } template Octonion atan( const Octonion & q ) { return q.atan(); } template Octonion asec( const Octonion & q, const Octonion & p ) { return q.asec( p ); } template Octonion acsc( const Octonion & q, const Octonion & p ) { return p.acsc( p ); } template Octonion acot( const Octonion & q ) { return q.acot(); } template Octonion asinh( const Octonion & q ) { return q.asinh(); } template Octonion acosh( const Octonion & q, const Octonion & p ) { return q.acosh( p ); } template Octonion atanh( const Octonion & q, const Octonion & p ) { return q.atanh( p ); } template Octonion asech( const Octonion & q, const Octonion & p ) { return q.asech( p ); } template Octonion acsch( const Octonion & q ) { return q.acsch(); } template Octonion acoth( const Octonion & q, const Octonion & p ) { return q.acoth( p ); } template Octonion bessel_J( int n, const Octonion & z ) { return z.bessel_J( n ); } /****************************************** * Integer Functions ******************************************/ template Octonion floor( const Octonion & q ) { return q.floor(); } template Octonion ceil( const Octonion & q ) { return q.ceil(); } /****************************************** * Horner's Rule ******************************************/ template Octonion horner( const Octonion & q, T * v, unsigned int n ) { return q.horner( v, n ); } template Octonion horner( const Octonion & q, T * v, T * c, unsigned int n ) { return q.horner( v, c, n ); } /************************************************** * ********************************************** * * * * * * * Double-precision Floating-point * * * * Instance of Template * * * * * * * ********************************************** * **************************************************/ template class Octonion; template std::ostream & operator << ( std::ostream &, const Octonion & ); template Octonion operator + ( double, const Octonion & ); template Octonion operator + ( long, const Octonion & ); template Octonion operator - ( double, const Octonion & ); template Octonion operator - ( long, const Octonion & ); template Octonion operator * ( double, const Octonion & ); template Octonion operator * ( long, const Octonion & ); template Octonion operator / ( double, const Octonion & ); template Octonion operator / ( long, const Octonion & ); template bool operator == ( double, const Octonion & ); template bool operator == ( long, const Octonion & ); template bool operator != ( double, const Octonion & ); template bool operator != ( long, const Octonion & ); template <> const Octonion Octonion::ZERO = Octonion( 0, 0, 0, 0, 0, 0, 0, 0 ); template <> const Octonion Octonion::ONE = Octonion( 1, 0, 0, 0, 0, 0, 0, 0 ); template <> const Octonion Octonion::I = Octonion( 0, 1, 0, 0, 0, 0, 0, 0 ); template <> const Octonion Octonion::J = Octonion( 0, 0, 1, 0, 0, 0, 0, 0 ); template <> const Octonion Octonion::K = Octonion( 0, 0, 0, 1, 0, 0, 0, 0 ); template <> const Octonion Octonion::U1 = Octonion( 0, 0, 0, 0, 1, 0, 0, 0 ); template <> const Octonion Octonion::I1 = Octonion( 0, 0, 0, 0, 0, 1, 0, 0 ); template <> const Octonion Octonion::J1 = Octonion( 0, 0, 0, 0, 0, 0, 1, 0 ); template <> const Octonion Octonion::K1 = Octonion( 0, 0, 0, 0, 0, 0, 0, 1 ); template <> const Octonion Octonion::UNITS[8] = { Octonion::ONE, Octonion::I, Octonion::J, Octonion::K, Octonion::U1, Octonion::I1, Octonion::J1, Octonion::K1 }; template double real( const Octonion & ); template double imag_i( const Octonion & ); template double imag_j( const Octonion & ); template double imag_k( const Octonion & ); template double imag_u1( const Octonion & ); template double imag_i1( const Octonion & ); template double imag_j1( const Octonion & ); template double imag_k1( const Octonion & ); template double csgn( const Octonion & ); template double abs( const Octonion & ); template double norm( const Octonion & ); template double abs_imag( const Octonion & ); template double norm_imag( const Octonion & ); template Octonion imag( const Octonion & ); template Octonion conj( const Octonion & ); template Octonion signum( const Octonion & ); template Octonion sqr( const Octonion & ); template Octonion sqrt( const Octonion & ); template Octonion rotate( const Octonion &, const Octonion & ); template Octonion exp( const Octonion & ); template Octonion log( const Octonion & ); template Octonion log10( const Octonion & ); template Octonion pow( const Octonion &, const Octonion & ); template Octonion pow( const Octonion &, double ); template Octonion inverse( const Octonion & ); template Octonion sin( const Octonion & ); template Octonion cos( const Octonion & ); template Octonion tan( const Octonion & ); template Octonion sec( const Octonion & ); template Octonion csc( const Octonion & ); template Octonion cot( const Octonion & ); template Octonion sinh( const Octonion & ); template Octonion cosh( const Octonion & ); template Octonion tanh( const Octonion & ); template Octonion sech( const Octonion & ); template Octonion csch( const Octonion & ); template Octonion coth( const Octonion & ); template Octonion asin( const Octonion &, const Octonion & ); template Octonion acos( const Octonion &, const Octonion & ); template Octonion atan( const Octonion & ); template Octonion asec( const Octonion &, const Octonion & ); template Octonion acsc( const Octonion &, const Octonion & ); template Octonion acot( const Octonion & ); template Octonion asinh( const Octonion & ); template Octonion acosh( const Octonion &, const Octonion & ); template Octonion atanh( const Octonion &, const Octonion & ); template Octonion asech( const Octonion &, const Octonion & ); template Octonion acsch( const Octonion & ); template Octonion acoth( const Octonion &, const Octonion & ); template Octonion bessel_J( int, const Octonion & ); template Octonion floor( const Octonion & ); template Octonion ceil( const Octonion & ); template Octonion horner( const Octonion &, double *, unsigned int ); template Octonion horner( const Octonion &, double *, double *, unsigned int ); /************************************************** * ********************************************** * * * * * * * Floating-point Instance of Template * * * * * * * ********************************************** * **************************************************/ template class Octonion; template std::ostream & operator << ( std::ostream &, const Octonion & ); template Octonion operator + ( float, const Octonion & ); template Octonion operator + ( long, const Octonion & ); template Octonion operator - ( float, const Octonion & ); template Octonion operator - ( long, const Octonion & ); template Octonion operator * ( float, const Octonion & ); template Octonion operator * ( long, const Octonion & ); template Octonion operator / ( float, const Octonion & ); template Octonion operator / ( long, const Octonion & ); template bool operator == ( float, const Octonion & ); template bool operator == ( long, const Octonion & ); template bool operator != ( float, const Octonion & ); template bool operator != ( long, const Octonion & ); template <> const Octonion Octonion::ZERO = Octonion( 0, 0, 0, 0, 0, 0, 0, 0 ); template <> const Octonion Octonion::ONE = Octonion( 1, 0, 0, 0, 0, 0, 0, 0 ); template <> const Octonion Octonion::I = Octonion( 0, 1, 0, 0, 0, 0, 0, 0 ); template <> const Octonion Octonion::J = Octonion( 0, 0, 1, 0, 0, 0, 0, 0 ); template <> const Octonion Octonion::K = Octonion( 0, 0, 0, 1, 0, 0, 0, 0 ); template <> const Octonion Octonion::U1 = Octonion( 0, 0, 0, 0, 1, 0, 0, 0 ); template <> const Octonion Octonion::I1 = Octonion( 0, 0, 0, 0, 0, 1, 0, 0 ); template <> const Octonion Octonion::J1 = Octonion( 0, 0, 0, 0, 0, 0, 1, 0 ); template <> const Octonion Octonion::K1 = Octonion( 0, 0, 0, 0, 0, 0, 0, 1 ); template <> const Octonion Octonion::UNITS[8] = { Octonion::ONE, Octonion::I, Octonion::J, Octonion::K, Octonion::U1, Octonion::I1, Octonion::J1, Octonion::K1 }; template float real( const Octonion & ); template float imag_i( const Octonion & ); template float imag_j( const Octonion & ); template float imag_k( const Octonion & ); template float imag_u1( const Octonion & ); template float imag_i1( const Octonion & ); template float imag_j1( const Octonion & ); template float imag_k1( const Octonion & ); template float csgn( const Octonion & ); template float abs( const Octonion & ); template float norm( const Octonion & ); template float abs_imag( const Octonion & ); template float norm_imag( const Octonion & ); template Octonion imag( const Octonion & ); template Octonion conj( const Octonion & ); template Octonion signum( const Octonion & ); template Octonion sqr( const Octonion & ); template Octonion sqrt( const Octonion & ); template Octonion rotate( const Octonion &, const Octonion & ); template Octonion exp( const Octonion & ); template Octonion log( const Octonion & ); template Octonion log10( const Octonion & ); template Octonion pow( const Octonion &, const Octonion & ); template Octonion pow( const Octonion &, float ); template Octonion inverse( const Octonion & ); template Octonion sin( const Octonion & ); template Octonion cos( const Octonion & ); template Octonion tan( const Octonion & ); template Octonion sec( const Octonion & ); template Octonion csc( const Octonion & ); template Octonion cot( const Octonion & ); template Octonion sinh( const Octonion & ); template Octonion cosh( const Octonion & ); template Octonion tanh( const Octonion & ); template Octonion sech( const Octonion & ); template Octonion csch( const Octonion & ); template Octonion coth( const Octonion & ); template Octonion asin( const Octonion &, const Octonion & ); template Octonion acos( const Octonion &, const Octonion & ); template Octonion atan( const Octonion & ); template Octonion asec( const Octonion &, const Octonion & ); template Octonion acsc( const Octonion &, const Octonion & ); template Octonion acot( const Octonion & ); template Octonion asinh( const Octonion & ); template Octonion acosh( const Octonion &, const Octonion & ); template Octonion atanh( const Octonion &, const Octonion & ); template Octonion asech( const Octonion &, const Octonion & ); template Octonion acsch( const Octonion & ); template Octonion acoth( const Octonion &, const Octonion & ); template Octonion bessel_J( int, const Octonion & ); template Octonion floor( const Octonion & ); template Octonion ceil( const Octonion & ); template Octonion horner( const Octonion &, float *, unsigned int ); template Octonion horner( const Octonion &, float *, float *, unsigned int ); /************************************************************************ * ******************************************************************** * * * * * * * Long Double-precision Floating-point Instance of Template * * * * * * * ******************************************************************** * ************************************************************************/ template class Octonion; template std::ostream & operator << ( std::ostream &, const Octonion & ); template Octonion operator + ( long double, const Octonion & ); template Octonion operator + ( long, const Octonion & ); template Octonion operator - ( long double, const Octonion & ); template Octonion operator - ( long, const Octonion & ); template Octonion operator * ( long double, const Octonion & ); template Octonion operator * ( long, const Octonion & ); template Octonion operator / ( long double, const Octonion & ); template Octonion operator / ( long, const Octonion & ); template bool operator == ( long double, const Octonion & ); template bool operator == ( long, const Octonion & ); template bool operator != ( long double, const Octonion & ); template bool operator != ( long, const Octonion & ); template <> const Octonion Octonion::ZERO = Octonion( 0, 0, 0, 0, 0, 0, 0, 0 ); template <> const Octonion Octonion::ONE = Octonion( 1, 0, 0, 0, 0, 0, 0, 0 ); template <> const Octonion Octonion::I = Octonion( 0, 1, 0, 0, 0, 0, 0, 0 ); template <> const Octonion Octonion::J = Octonion( 0, 0, 1, 0, 0, 0, 0, 0 ); template <> const Octonion Octonion::K = Octonion( 0, 0, 0, 1, 0, 0, 0, 0 ); template <> const Octonion Octonion::U1 = Octonion( 0, 0, 0, 0, 1, 0, 0, 0 ); template <> const Octonion Octonion::I1 = Octonion( 0, 0, 0, 0, 0, 1, 0, 0 ); template <> const Octonion Octonion::J1 = Octonion( 0, 0, 0, 0, 0, 0, 1, 0 ); template <> const Octonion Octonion::K1 = Octonion( 0, 0, 0, 0, 0, 0, 0, 1 ); template <> const Octonion Octonion::UNITS[8] = { Octonion::ONE, Octonion::I, Octonion::J, Octonion::K, Octonion::U1, Octonion::I1, Octonion::J1, Octonion::K1 }; template long double real( const Octonion & ); template long double imag_i( const Octonion & ); template long double imag_j( const Octonion & ); template long double imag_k( const Octonion & ); template long double imag_u1( const Octonion & ); template long double imag_i1( const Octonion & ); template long double imag_j1( const Octonion & ); template long double imag_k1( const Octonion & ); template long double csgn( const Octonion & ); template long double abs( const Octonion & ); template long double norm( const Octonion & ); template long double abs_imag( const Octonion & ); template long double norm_imag( const Octonion & ); template Octonion imag( const Octonion & ); template Octonion conj( const Octonion & ); template Octonion signum( const Octonion & ); template Octonion sqr( const Octonion & ); template Octonion sqrt( const Octonion & ); template Octonion rotate( const Octonion &, const Octonion & ); template Octonion exp( const Octonion & ); template Octonion log( const Octonion & ); template Octonion log10( const Octonion & ); template Octonion pow( const Octonion &, const Octonion & ); template Octonion pow( const Octonion &, long double ); template Octonion inverse( const Octonion & ); template Octonion sin( const Octonion & ); template Octonion cos( const Octonion & ); template Octonion tan( const Octonion & ); template Octonion sec( const Octonion & ); template Octonion csc( const Octonion & ); template Octonion cot( const Octonion & ); template Octonion sinh( const Octonion & ); template Octonion cosh( const Octonion & ); template Octonion tanh( const Octonion & ); template Octonion sech( const Octonion & ); template Octonion csch( const Octonion & ); template Octonion coth( const Octonion & ); template Octonion asin( const Octonion &, const Octonion & ); template Octonion acos( const Octonion &, const Octonion & ); template Octonion atan( const Octonion & ); template Octonion asec( const Octonion &, const Octonion & ); template Octonion acsc( const Octonion &, const Octonion & ); template Octonion acot( const Octonion & ); template Octonion asinh( const Octonion & ); template Octonion acosh( const Octonion &, const Octonion & ); template Octonion atanh( const Octonion &, const Octonion & ); template Octonion asech( const Octonion &, const Octonion & ); template Octonion acsch( const Octonion & ); template Octonion acoth( const Octonion &, const Octonion & ); template Octonion bessel_J( int, const Octonion & ); template Octonion floor( const Octonion & ); template Octonion ceil( const Octonion & ); template Octonion horner( const Octonion &, long double *, unsigned int ); template Octonion horner( const Octonion &, long double *, long double *, unsigned int );