QuaternionNumber:比 GLM 代码可读性更强、更有学习价值的四元数类

· · 科技·工程

前言

你也许比较讨厌 GLM 的四元数,因为它不仅不是标准库实现,使用时还要包含三个头文件,而且精度不高。为了弥补这些缺点,我编写出了这个类。这个类不仅有题目所说的优点,还更为轻量。

代码

下载链接:link

云剪贴板地址:https://www.luogu.com.cn/paste/6xjdxuzb

:::info[也可以在这里查看]

#include <cmath>
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include <algorithm>
#include <type_traits>
#include <tuple>
#include <limits>
#include <array>
using namespace std;
#ifndef QUATERNION_HPP
#define QUATERNION_HPP
string TrimTrailingZeros(string a){
        while(a[a.size()-1]=='0'){
            a.erase(a.size()-1,1);
        }
    return a;
}
template <typename T>
struct Quaternion{
    static_assert(is_same<T,float>::value||is_same<T,double>::value||is_same<T,long double>::value,"T must be 'float','double' or 'long double',not 'int'");
    T r,i,j,k;
    Quaternion conj()const{
        return {r,-i,-j,-k};
    }
    constexpr T real()const{
        return r;
    }
    void real(T val){
        r=val;
    }
    constexpr T imagi()const{
        return i;
    }
    void imagi(T val){
        i=val;
    }
    constexpr T imagj()const{
        return j;
    }
    void imagj(T val){
        j=val;
    }
    constexpr T imagk()const{
        return k;
    }
    void imagk(T val){
        k=val;
    }
    constexpr Quaternion():r(0.0),i(0.0),j(0.0),k(0.0){}
    constexpr Quaternion(T a,T b,T c,T d):r(a),i(b),j(c),k(d){}
    constexpr Quaternion(T a):r(a){}
    template <typename U>
    Quaternion(const Quaternion<U> &a):r(a.r),i(a.i),j(a.j),k(a.k){}
    Quaternion operator =(Quaternion a){
        r=a.r;
        i=a.i;
        j=a.j;
        k=a.k;
        return *this;
    }
    template <typename U>
    Quaternion operator =(const Quaternion<U> &a){
        r=a.r;
        i=a.i;
        j=a.j;
        k=a.k;
        return *this;
    }
    Quaternion& operator +=(T b){
        r+=b;
        return *this;
    }
    Quaternion& operator -=(T b){
        r-=b;
        return *this;
    }
    Quaternion& operator *=(T b){
        r*=b;
        i*=b;
        j*=b;
        k*=b;
        return *this;
    }
    Quaternion& operator /=(T b){
        r/=b;
        i/=b;
        j/=b;
        k/=b;
        return *this;
    }
    bool is_real()const{
        return (abs(i)<1e-10)&&(abs(j)<1e-10)&&(abs(k)<1e-10);
    }
    bool is_imaginary()const{
        return (abs(r)<1e-10);
    }
    template<size_t N>
    decltype(auto) get()const{
        static_assert(N<4,"Index out of range");
        if(N==0){
            return r;
        }
        if(N==1){
            return i;
        }
        if(N==2){
            return j;
        }
        if(N==3){
            return k;
        }
    }
    static Quaternion identity(){
        return Quaternion(1.0);
    }
    static Quaternion unit(T r,T i,T j,T k){
        Quaternion q(r,i,j,k);
        return q.normalized();
    }
    Quaternion& normalize(){
        T al=sqrt(r*r+i*i+j*j+k*k);
        if(al>1e-10){
            r/=al;
            i/=al;
            j/=al;
            k/=al;
        }
        return *this;
    }
    Quaternion normalized()const{
        Quaternion re(*this);
        return re.normalize();
    }
    bool isunit(T eps=1e-10)const{
        T alsq=r*r+i*i+j*j+k*k;
        return abs(alsq-1)<eps;
    }
    static Quaternion FromAxisAngle(T angle,T x,T y,T z){
        return Quaternion(cos(angle/2),x*sin(angle/2),y*sin(angle/2),z*sin(angle/2));
    } 
    array<T,9> ToRotationMatrix()const{
        T xx=r*r,yy=i*i,zz=j*j,ww=k*k,xy=r*i,xz=r*j,xw=r*k,yz=i*j,yw=i*k,zw=j*k;
        return {
            1-2*(yy+zz),2*(xy-zw),2*(xz+yw),
            2*(xy+zw),1-2*(xx+zz),2*(yz-xw),
            2*(xz-yw),2*(yz+xw),1-2*(xx+yy)
        };
    }
    static Quaternion FromRotationMatrix(const array<T,9>& mat){
        T t=mat[0]+mat[4]+mat[8];
        Quaternion q;
        if(t>0){
            T s=0.5*(sqrt(t+1));
            q.r=0.25/s;
            q.i=(mat[5]-mat[7])*s;
            q.j=(mat[6]-mat[2])*s;
            q.k=(mat[1]-mat[3])*s;
        }else{
            if(mat[0]>mat[4]&&mat[0]>mat[8]){
                T s=2*sqrt(1+mat[0]-mat[4]-mat[8]);
                q.r=(mat[5]-mat[7])/s;
                q.i=0.25*s;
                q.j=(mat[3]+mat[1])/s;
                q.k=(mat[6]+mat[2])/s;
            }else{
                if(mat[4]>mat[8]){
                    T s=2*sqrt(1+mat[4]-mat[0]-mat[8]);
                    q.r=(mat[6]-mat[2])/s;
                    q.i=(mat[3]+mat[1])/s;
                    q.j=0.25*s;
                    q.k=(mat[7]+mat[5])/s;
                }else{
                    T s=2*sqrt(1+mat[8]-mat[4]-mat[0]);
                    q.r=(mat[1]-mat[3])/s;
                    q.i=(mat[6]+mat[2])/s;
                    q.j=(mat[7]+mat[5])/s;
                    q.k=0.25*s;
                }
            }
        }
        return q;
    }
    enum class EulerOrder{
        XYZ,XZY,YXZ,YZX,ZXY,ZYX
    };
    array<T,3> ToEulerAngles(EulerOrder order=EulerOrder::XYZ)const{
        Quaternion q=this->normalized();
        T rr=q.r,ii=q.i,jj=q.j,kk=q.k;
        T roll,pitch,yaw;
        switch(order){
            case EulerOrder::XYZ:
                pitch=asin(2*(rr*jj-kk*ii));
                if(abs(pitch-T(3.1415926535/2))<1e-10){
                    roll=0;
                    yaw=atan2(2*(rr*kk-ii*jj),1-2*(jj*jj+kk*kk));
                }else{
                    if(abs(pitch+T(3.1415926535/2))<1e-10){
                        roll=0;
                        yaw=-atan2(2*(rr*kk-ii*jj),1-2*(jj*jj+kk*kk));
                    }else{
                        roll=atan2(2*(rr*ii+jj*kk),1-2*(ii*ii+jj*jj));
                        yaw=atan2(2*(rr*kk+ii*jj),1-2*(jj*jj+kk*kk));
                    }
                }
                return {roll,pitch,yaw};
            case EulerOrder::XZY:
                yaw=asin(2*(rr*jj-kk*ii));
                if(abs(yaw-T(3.1415926535/2))<1e-10){
                    roll=0;
                    pitch=atan2(2*(rr*kk-ii*jj),1-2*(jj*jj+kk*kk));
                }else{
                    if(abs(yaw+T(3.1415926535/2))<1e-10){
                        roll=0;
                        pitch=-atan2(2*(rr*kk-ii*jj),1-2*(jj*jj+kk*kk));
                    }else{
                        roll=atan2(2*(rr*ii+jj*kk),1-2*(ii*ii+jj*jj));
                        pitch=atan2(2*(rr*kk+ii*jj),1-2*(jj*jj+kk*kk));
                    }
                }
                return {roll,pitch,yaw};
            case EulerOrder::YXZ:
                roll=asin(2*(rr*jj-kk*ii));
                if(abs(roll-T(3.1415926535/2))<1e-10){
                    pitch=0;
                    yaw=atan2(2*(rr*kk-ii*jj),1-2*(jj*jj+kk*kk));
                }else{
                    if(abs(roll+T(3.1415926535/2))<1e-10){
                        pitch=0;
                        yaw=-atan2(2*(rr*kk-ii*jj),1-2*(jj*jj+kk*kk));
                    }else{
                        pitch=atan2(2*(rr*ii+jj*kk),1-2*(ii*ii+jj*jj));
                        yaw=atan2(2*(rr*kk+ii*jj),1-2*(jj*jj+kk*kk));
                    }
                }
                return {roll,pitch,yaw};
            case EulerOrder::YZX:
                yaw=asin(2*(rr*jj-kk*ii));
                if(abs(yaw-T(3.1415926535/2))<1e-10){
                    pitch=0;
                    roll=atan2(2*(rr*kk-ii*jj),1-2*(jj*jj+kk*kk));
                }else{
                    if(abs(yaw+T(3.1415926535/2))<1e-10){
                        pitch=0;
                        roll=-atan2(2*(rr*kk-ii*jj),1-2*(jj*jj+kk*kk));
                    }else{
                        pitch=atan2(2*(rr*ii+jj*kk),1-2*(ii*ii+jj*jj));
                        roll=atan2(2*(rr*kk+ii*jj),1-2*(jj*jj+kk*kk));
                    }
                }
                return {roll,pitch,yaw};
            case EulerOrder::ZXY:
                roll=asin(2*(rr*jj-kk*ii));
                if(abs(roll-T(3.1415926535/2))<1e-10){
                    yaw=0;
                    pitch=atan2(2*(rr*kk-ii*jj),1-2*(jj*jj+kk*kk));
                }else{
                    if(abs(roll+T(3.1415926535/2))<1e-10){
                        yaw=0;
                        pitch=-atan2(2*(rr*kk-ii*jj),1-2*(jj*jj+kk*kk));
                    }else{
                        yaw=atan2(2*(rr*ii+jj*kk),1-2*(ii*ii+jj*jj));
                        pitch=atan2(2*(rr*kk+ii*jj),1-2*(jj*jj+kk*kk));
                    }
                }
                return {roll,pitch,yaw};
            case EulerOrder::ZYX:
                pitch=asin(2*(rr*jj-kk*ii));
                if(abs(pitch-T(3.1415926535/2))<1e-10){
                    yaw=0;
                    roll=atan2(2*(rr*kk-ii*jj),1-2*(jj*jj+kk*kk));
                }else{
                    if(abs(pitch+T(3.1415926535/2))<1e-10){
                        yaw=0;
                        roll=-atan2(2*(rr*kk-ii*jj),1-2*(jj*jj+kk*kk));
                    }else{
                        yaw=atan2(2*(rr*ii+jj*kk),1-2*(ii*ii+jj*jj));
                        roll=atan2(2*(rr*kk+ii*jj),1-2*(jj*jj+kk*kk));
                    }
                }
                return {roll,pitch,yaw};
            default:
                return {0,0,0};
        }
    }
    static Quaternion FromEulerAngles(T roll,T pitch,T yaw,EulerOrder order=EulerOrder::XYZ){
        T c1=cos(roll/2);
        T s1=sin(roll/2);
        T c2=cos(pitch/2);
        T s2=sin(pitch/2);
        T c3=cos(yaw/2);
        T s3=sin(yaw/2);
        switch(order){
            case EulerOrder::XYZ:
                Quaternion q(
                    c1*c2*c3-s1*s2*s3,
                    s1*c2*c3+c1*s2*s3,
                    c1*s2*c3-s1*c2*s3,
                    c1*c2*s3+s1*s2*c3
                );
                return q.normalized();
            case EulerOrder::XZY:
                Quaternion q1(
                    c1*c2*c3+s1*s2*s3,
                    s1*c2*c3-c1*s2*s3,
                    c1*c2*s3-s1*s2*c3,
                    c1*s2*c3+s1*c2*s3
                );
                return q1.normalized();
            case EulerOrder::YXZ:
                Quaternion q2(
                    c1*c2*c3+s1*s2*s3,
                    s1*c2*c3+c1*s2*s3,
                    c1*s2*c3-s1*c2*s3,
                    c1*c2*s3-s1*s2*c3
                );
                return q2.normalized();
            case EulerOrder::YZX:
                Quaternion q3(
                    c1*c2*c3-s1*s2*s3,
                    c1*s2*c3+s1*c2*s3,
                    s1*c2*c3+c1*s2*s3,
                    c1*c2*s3-s1*s2*c3
                );
                return q3.normalized();
            case EulerOrder::ZXY:
                Quaternion q4(
                    c1*c2*c3-s1*s2*s3,
                    c1*s2*c3-s1*c2*s3,
                    c1*c2*s3+s1*s2*c3,
                    s1*c2*c3+c1*s2*s3
                );
                return q4.normalized();
            case EulerOrder::ZYX:
                Quaternion q5(
                    c1*c2*c3+s1*s2*s3,
                    c1*c2*s3-s1*s2*c3,
                    c1*s2*c3+s1*c2*s3,
                    s1*c2*c3-c1*s2*s3
                );
                return q5.normalized();
            default:
                return Quaternion(1,0,0,0);
        }
    }
    array<T,3> RotateVector(T x,T y,T z)const{
        T rx=2*(j*z-k*y),ry=2*(k*x-i*z),rz=2*(i*y-j*x);
        return {x+r*rx+(j*rz-k*ry),y+r*ry+(k*rx-i*rz),z+r*rz+(i*ry-j*rx)};
    }
    array<T,3> RotateVector(const array<T,3> &v)const{
        return RotateVector(v[0],v[1],v[2]);
    }
    array<T,3> axis()const{
        T a=sqrt(1-r*r);
        if(a<1e-10){
            return {1,0,0};
        }
        return {i/a,j/a,k/a};
    }
    T angle()const{
        return 2*acos(r);
    }
    T angleDegrees()const{
        return angle()*180/3.14159265358979323846;
    }
    Quaternion inverse()const{
        return (this->conj())/(r*r+i*i+j*j+k*k);
    }
};
namespace std{
    template<typename T>
    struct tuple_size<Quaternion<T> >:integral_constant<size_t,4>{};
    template<typename T>
    struct tuple_element<0,Quaternion<T> >{using type=T;};
    template<typename T>
    struct tuple_element<1,Quaternion<T> >{using type=T;};
    template<typename T>
    struct tuple_element<2,Quaternion<T> >{using type=T;};
    template<typename T>
    struct tuple_element<3,Quaternion<T> >{using type=T;};
}
template<typename T>
Quaternion<T> operator +(Quaternion<T> a,T b){
    a.r+=b;
    return a;
} 
template<typename T>
Quaternion<T> operator +(T a,Quaternion<T> b){
    b.r+=a;
    return b;
}
template<typename T>
Quaternion<T> operator -(Quaternion<T> a,T b){
    a.r-=b;
    return a;
} 
template<typename T>
Quaternion<T> operator -(Quaternion<T> a){
    return {-a.r,-a.i,-a.j,-a.k};
}
template<typename T>
Quaternion<T> operator -(T a,Quaternion<T> b){
    b.r-=a;
    b=-b;
    return b;
}
template<typename T>
Quaternion<T> operator *(Quaternion<T> a,T b){
    a.r*=b;
    a.i*=b;
    a.j*=b;
    a.k*=b;
    return a;
}
template<typename T>
Quaternion<T> operator *(T a,Quaternion<T> b){
    b.r*=a;
    b.i*=a;
    b.j*=a;
    b.k*=a;
    return b;
}
template<typename T>
Quaternion<T> operator /(Quaternion<T> a,T b){
    a.r/=b;
    a.i/=b;
    a.j/=b;
    a.k/=b;
    return a;
}
template<typename T>
T abs(Quaternion<T> a){
    return sqrt(a.r*a.r+a.i*a.i+a.j*a.j+a.k*a.k);
}
template<typename T>
Quaternion<T> operator /(T a,Quaternion<T> b){
    return a*(b.conj()/(abs(b)*abs(b)));
}
template<typename T>
Quaternion<T> operator +(Quaternion<T> a,Quaternion<T> b){
    a.r+=b.r;
    a.i+=b.i;
    a.j+=b.j;
    a.k+=b.k;
    return a;
}
template<typename T>
Quaternion<T> operator -(Quaternion<T> a,Quaternion<T> b){
    a.r-=b.r;
    a.i-=b.i;
    a.j-=b.j;
    a.k-=b.k;
    return a;
}
template<typename T>
Quaternion<T> operator *(Quaternion<T> a,Quaternion<T> b){
    return Quaternion<T>(a.r*b.r-a.i*b.i-a.j*b.j-a.k*b.k,a.r*b.i+a.i*b.r+a.j*b.k-a.k*b.j,a.r*b.j+a.j*b.r+a.k*b.i-a.i*b.k,a.r*b.k+a.k*b.r+a.i*b.j-a.j*b.i);
}
template<typename T>
Quaternion<T> operator /(Quaternion<T> a,Quaternion<T> b){
    return a*(1.0/b);
}
template<typename T>
bool operator ==(Quaternion<T> a,Quaternion<T> b){
    if(isnan(a.r)||isnan(a.i)||isnan(a.j)||isnan(a.k)||isnan(b.r)||isnan(b.i)||isnan(b.j)||isnan(b.k)){
        return false;
    }
    if(isinf(a.r)||isinf(a.i)||isinf(a.j)||isinf(a.k)||isinf(b.r)||isinf(b.i)||isinf(b.j)||isinf(b.k)){
        return (a.r==b.r)&&(a.i==b.i)&&(a.j==b.j)&&(a.k==b.k);
    }
    return (abs(a.r-b.r)<1e-10)&&(abs(a.i-b.i)<1e-10)&&(abs(a.j-b.j)<1e-10)&&(abs(a.k-b.k)<1e-10);
}
template<typename T>
bool operator !=(Quaternion<T> a,Quaternion<T> b){
    return !(a==b);
}
template<typename T>
Quaternion<T>& operator +=(Quaternion<T> &a,const Quaternion<T> &b){
    a=a+b;
    return a;
}
template<typename T>
Quaternion<T>& operator -=(Quaternion<T> &a,const Quaternion<T> &b){
    a=a-b;
    return a;
}
template<typename T>
Quaternion<T>& operator *=(Quaternion<T> &a,const Quaternion<T> &b){
    a=a*b;
    return a;
}
template<typename T>
Quaternion<T>& operator /=(Quaternion<T> &a,const Quaternion<T> &b){
    a=a/b;
    return a;
}
template<typename T>
Quaternion<T> log(Quaternion<T> a){
    if(abs(a-a.r)<1e-10){
        return Quaternion<T>(log(a.r),0,0,0);
    }
    Quaternion<T> u(0.0,a.i/abs(a-a.r),a.j/abs(a-a.r),a.k/abs(a-a.r));
    return log(abs(a))+u*acos(a.r/abs(a));
}
template<typename T>
Quaternion<T> log10(Quaternion<T> a){
    return log(a)/log(10);
}
template<typename T>
Quaternion<T> log2(Quaternion<T> a){
    return log(a)/log(2);
}
template<typename T>
Quaternion<T> logp(Quaternion<T> a,Quaternion<T> b){
    return log(a)/log(b);
}
template<typename T>
Quaternion<T> exp(Quaternion<T> a){
    if(abs(a-a.r)<1e-10){
        return Quaternion<T>(exp(a.r),0,0,0);
    }
    Quaternion<T> u(0.0,a.i/abs(a-a.r),a.j/abs(a-a.r),a.k/abs(a-a.r));
    return exp(a.r)*(cos(abs(a-a.r))+u*sin(a-a.r));
}
template<typename T>
Quaternion<T> pow(Quaternion<T> a,Quaternion<T> b){
    return exp(log(a)*b);
}
template<typename T>
Quaternion<T> nthrt(Quaternion<T> a,Quaternion<T> b){
    return pow(a,1.0/b);
}
template<typename T>
Quaternion<T> sqrt(Quaternion<T> a){
    return nthrt(a,Quaternion<T>(2.0));
}
template<typename T>
Quaternion<T> cbrt(Quaternion<T> a){
    return nthrt(a,Quaternion<T>(3.0));
}
template<typename T>
T dot(Quaternion<T> a,Quaternion<T> b){
    return a.r*b.r+a.i*b.i+a.j*b.j+a.k*b.k;
}
template<typename T>
Quaternion<T> pure(Quaternion<T> a){
    return Quaternion<T>(0.0,a.i,a.j,a.k);
}
template <typename T>
Quaternion<T> unit(T b,T c,T d){
    return Quaternion<T>(0.0,b/sqrt(b*b+c*c+d*d),c/sqrt(b*b+c*c+d*d),d/sqrt(b*b+c*c+d*d));
}
template<typename T>
Quaternion<T> slerp(const Quaternion<T> &a,const Quaternion<T> &b,T t){
    T d=dot(a,b),sign=1;
    if(d<0){
        d=-d;
        sign=-1;
    }
    const T dt=T(0.9995);
    if(d>dt){
        Quaternion<T> q=a+(b*sign-a)*t;
        return q.normalized();
    }
    T theta=acos(d)*t;
    T sin_theta=sin(theta);
    T sin_theta0=sin(acos(d));
    T s2=sin_theta/sin_theta0;
    T s1=cos(theta)-d*s2;
    return (a*s1)+(b*sign*s2);
}
template<typename T>
Quaternion<T> sin(Quaternion<T> a){
    if(abs(a-a.r)<1e-10){
        return Quaternion<T>(sin(a.r),0,0,0);
    }
    Quaternion<T> u(0.0,a.i/abs(a-a.r),a.j/abs(a-a.r),a.k/abs(a-a.r));
    return sin(a.r)*cosh(abs(a-a.r))+u*cos(a.r)*sinh(abs(a-a.r));
}
template<typename T>
Quaternion<T> cos(Quaternion<T> a){
    if(abs(a-a.r)<1e-10){
        return Quaternion<T>(cos(a.r),0,0,0);
    }
    Quaternion<T> u(0.0,a.i/abs(a-a.r),a.j/abs(a-a.r),a.k/abs(a-a.r));
    return cos(a.r)*cosh(abs(a-a.r))-u*sin(a.r)*sinh(abs(a-a.r));
}
template<typename T>
Quaternion<T> tan(Quaternion<T> a){
    return sin(a)/cos(a);
}
template<typename T>
Quaternion<T> asin(Quaternion<T> a){
    if(abs(a-a.r)<1e-10){
        return Quaternion<T>(asin(a.r),0,0,0);
    }
    Quaternion<T> u(0.0,a.i/abs(a-a.r),a.j/abs(a-a.r),a.k/abs(a-a.r));
    return -u*log(u*a+sqrt(1.0));
}
template<typename T>
Quaternion<T> acos(Quaternion<T> a){
    if(abs(a-a.r)<1e-10){
        return Quaternion<T>(acos(a.r),0,0,0);
    }
    Quaternion<T> u(0.0,a.i/abs(a-a.r),a.j/abs(a-a.r),a.k/abs(a-a.r));
    return -u*log(a+u*sqrt(1.0-a*a));
}
template<typename T>
Quaternion<T> atan(Quaternion<T> a){
    if(abs(a-a.r)<1e-10){
        return Quaternion<T>(atan(a.r),0,0,0);
    }
    Quaternion<T> u(0.0,a.i/abs(a-a.r),a.j/abs(a-a.r),a.k/abs(a-a.r));
    return u/2*log((u+a)/(u-a));
}
template<typename T>
Quaternion<T> sinh(Quaternion<T> a){
    if(abs(a-a.r)<1e-10){
        return Quaternion<T>(sinh(a.r),0,0,0);
    }
    Quaternion<T> u(0.0,a.i/abs(a-a.r),a.j/abs(a-a.r),a.k/abs(a-a.r));
    return sinh(a.r)*cos(abs(a-a.r))+u*cosh(a.r)*sin(abs(a-a.r));
}
template<typename T>
Quaternion<T> cosh(Quaternion <T> a){
    if(abs(a-a.r)<1e-10){
        return Quaternion<T>(cosh(a.r),0,0,0);
    }
    Quaternion<T> u(0.0,a.i/abs(a-a.r),a.j/abs(a-a.r),a.k/abs(a-a.r));
    return cos(a.r)*cos(abs(a-a.r))+u*sinh(a.r)*sin(abs(a-a.r));
}
template<typename T>
Quaternion<T> tanh(Quaternion<T> a){
    return sinh(a)/cosh(a);
}
template<typename T>
Quaternion<T> asinh(Quaternion<T> a){
    return log(a+sqrt(a*a+1));
}
template<typename T>
Quaternion<T> acosh(Quaternion<T> a){
    return log(a+sqrt(a-1.0)*sqrt(a+1.0));
}
template<typename T>
Quaternion<T> atanh(Quaternion<T> a){
    return 0.5*log((1.0+a)/(1.0-a)); 
}
template<typename T>
string to_string(Quaternion<T> q){
    stringstream ss;
    ss<<q;
    return ss.str();
}
template<typename T>
istream &operator >>(istream &in,Quaternion<T> &q){
    string s;
    in>>s;
    s.erase(remove_if(s.begin(),s.end(),::isspace),s.end());
    if(s.empty()){
        q=Quaternion<T>(0.0,0.0,0.0,0.0);
        return in;
    }
    if(s.find("<")!=-1){
        string s1=s.substr(0,s.find("<"));
        string s2=s.substr(s.find("<")+1,s.size()-s.find("<")-1);
        s1.erase(0,1);
        s1.erase(s1.size()-1,1);
        string s3=s1.substr(0,s1.find(","));
        s1.erase(0,s3.size()+1);
        string s4=s1.substr(0,s1.find(","));
        s1.erase(0,s4.size()+1);
        Quaternion<T> u(0.0,stold(s3),stold(s4),stold(s1));
        T theta=stold(s2);
        q=cos(theta/2)+u*sin(theta/2);
        return in;
    }
    if(s[0]!='+'&&s[0]!='-'){
        s='+'+s;
    }
    vector<string>v;
    int start=0;
    for(int i=1;i<s.size();i++){
        if(s[i]=='+'||s[i]=='-'){
            v.push_back(s.substr(start,i-start));
            start=i;
        }
    }
    v.push_back(s.substr(start));
    for(const auto& t:v){
        if(t.empty()){
            continue;
        }
        char sign=t[0];
        string coe=t.substr(1);
        if(coe.find("i")!=-1){
            string nn=coe.substr(0,coe.size()-1);
            T val;
            try{
                val=nn.empty()?1.0L:stold(nn);
            }catch(const exception& e){
                in.setstate(ios::failbit);
                return in;
            }
            q.i=(sign=='+')?val:-val;
        }else{
            if(coe.find("j")!=-1){
                string nn=coe.substr(0,coe.size()-1);
                T val;
                try{
                    val=nn.empty()?1.0L:stold(nn);
                }catch(const exception& e){
                    in.setstate(ios::failbit);
                    return in;
                }
                q.j=(sign=='+')?val:-val;
            }else{
                if(coe.find("k")!=-1){
                    string nn=coe.substr(0,coe.size()-1);
                    T val;
                    try{
                        val=nn.empty()?1.0L:stold(nn);
                    }catch(const exception& e){
                        in.setstate(ios::failbit);
                        return in;
                    }
                    q.k=(sign=='+')?val:-val;
                }else{
                    T val=stold(coe);
                    q.r=(sign=='+')?val:-val;
                }
            }
        }
    }
    return in;
}
template<typename T>
ostream &operator <<(ostream &o,const Quaternion<T> &q){
    string s1=to_string(q.r),s2=to_string(q.i),s3=to_string(q.j),s4=to_string(q.k);
    s1=TrimTrailingZeros(s1);
    s2=TrimTrailingZeros(s2);
    s3=TrimTrailingZeros(s3);
    s4=TrimTrailingZeros(s4);
    bool flag=0;
    if(s1.find(".")==s1.size()-1){
        s1.erase(s1.size()-1,1);
    }
    if(s2.find(".")==s2.size()-1){
        s2.erase(s2.size()-1,1);
    }
    if(s3.find(".")==s3.size()-1){
        s3.erase(s3.size()-1,1);
    }
    if(s4.find(".")==s4.size()-1){
        s4.erase(s4.size()-1,1);
    }
    if(abs(q.r)>1e-10){
        o<<s1;
        flag=1;
    }
    if(q.i){
        if(abs(q.i)>1e-10){
            if((!flag)||q.i<0){
                flag=1;
            }else{
                o<<"+";
            }
        }
        if(abs(q.i-1)<1e-10){
            o<<"i";
        }else{
            if(abs(q.i+1)<1e-10){
                o<<"-i";
            }else{
                o<<s2<<"i";
            }
        }
    }
    if(q.j){
        if(abs(q.j)>1e-10){
            if((!flag)||q.j<0){
                flag=1;
            }else{
                o<<"+";
            }
        }
        if(abs(q.j-1)<1e-10){
            o<<"j";
        }else{
            if(abs(q.j+1)<1e-10){
                o<<"-j";
            }else{
                o<<s3<<"j";
            }
        }
    }
    if(q.k){
        if(abs(q.k)>1e-10){
            if((!flag)||q.k<0){
                flag=1;
            }else{
                o<<"+";
            }
        }
        if(abs(q.k-1)<1e-10){
            o<<"k";
        }else{
            if(abs(q.k+1)<1e-10){
                o<<"-k";
            }else{
                o<<s4<<"k";
            }
        }
    }
    return o;
}
#if __cplusplus>201103L
namespace QLiterals{
    Quaternion<float> operator"" _if(unsigned long long x){
        return Quaternion<float>(0,(float)x,0,0);
    }
    Quaternion<float> operator"" _if(long double x){
        return Quaternion<float>(0,(float)x,0,0);
    }
    Quaternion<float> operator"" _jf(unsigned long long x){
        return Quaternion<float>(0,0,(float)x,0);
    }
    Quaternion<float> operator"" _jf(long double x){
        return Quaternion<float>(0,0,(float)x,0);
    }
    Quaternion<float> operator"" _kf(unsigned long long x){
        return Quaternion<float>(0,0,0,(float)x);
    }
    Quaternion<float> operator"" _kf(long double x){
        return Quaternion<float>(0,0,0,(float)x);
    }
    Quaternion<float> operator"" _rf(unsigned long long x){
        return Quaternion<float>((float)x,0,0,0);
    }
    Quaternion<float> operator"" _rf(long double x){
        return Quaternion<float>((float)x,0,0,0);
    }
    Quaternion<float> operator"" _qf(const char* str,size_t len){
        string s(str,len);
        stringstream ss(s);
        Quaternion<float> c;
        ss>>c;
        return c;
    }
    Quaternion<float> operator"" _pf(const char* str,size_t len){
        string s(str,len);
        if(s.find("<")==s.npos){
            return Quaternion<float>(stof(s));
        }
        string s1=s.substr(0,s.find("<"));
        string s2=s.substr(s.find("<")+1,s.size()-s.find("<")-1);
        s1.erase(0,1);
        s1.erase(s1.size()-1,1);
        string s3=s1.substr(0,s1.find(","));
        s1.erase(0,s3.size()+1);
        string s4=s1.substr(0,s1.find(","));
        s1.erase(0,s4.size()+1);
        Quaternion<float> u(0.0,stof(s3),stof(s4),stof(s1));
        float theta=stof(s2);
        Quaternion<float> q=cos(theta/2)+u*sin(theta/2);
        return q;
    }
    Quaternion<double> operator"" _id(unsigned long long x){
        return Quaternion<double>(0,(double)x,0,0);
    }
    Quaternion<double> operator"" _id(long double x){
        return Quaternion<double>(0,(double)x,0,0);
    }
    Quaternion<double> operator"" _jd(unsigned long long x){
        return Quaternion<double>(0,0,(double)x,0);
    }
    Quaternion<double> operator"" _jd(long double x){
        return Quaternion<double>(0,0,(double)x,0);
    }
    Quaternion<double> operator"" _kd(unsigned long long x){
        return Quaternion<double>(0,0,0,(double)x);
    }
    Quaternion<double> operator"" _kd(long double x){
        return Quaternion<double>(0,0,0,(double)x);
    }
    Quaternion<double> operator"" _rd(unsigned long long x){
        return Quaternion<double>((double)x,0,0,0);
    }
    Quaternion<double> operator"" _rd(long double x){
        return Quaternion<double>((double)x,0,0,0);
    }
    Quaternion<double> operator"" _qd(const char* str,size_t len){
        string s(str,len);
        stringstream ss(s);
        Quaternion<double> c;
        ss>>c;
        return c;
    }
    Quaternion<double> operator"" _pd(const char* str,size_t len){
        string s(str,len);
        if(s.find("<")==s.npos){
            return Quaternion<double>(stod(s));
        }
        string s1=s.substr(0,s.find("<"));
        string s2=s.substr(s.find("<")+1,s.size()-s.find("<")-1);
        s1.erase(0,1);
        s1.erase(s1.size()-1,1);
        string s3=s1.substr(0,s1.find(","));
        s1.erase(0,s3.size()+1);
        string s4=s1.substr(0,s1.find(","));
        s1.erase(0,s4.size()+1);
        Quaternion<double> u(0.0,stod(s3),stod(s4),stod(s1));
        double theta=stod(s2);
        Quaternion<double> q=cos(theta/2)+u*sin(theta/2);
        return q;
    }
    Quaternion<long double> operator"" _ild(unsigned long long x){
        return Quaternion<long double>(0,(long double)x,0,0);
    }
    Quaternion<long double> operator"" _ild(long double x){
        return Quaternion<long double>(0,(long double)x,0,0);
    }
    Quaternion<long double> operator"" _jld(unsigned long long x){
        return Quaternion<long double>(0,0,(long double)x,0);
    }
    Quaternion<long double> operator"" _jld(long double x){
        return Quaternion<long double>(0,0,(long double)x,0);
    }
    Quaternion<long double> operator"" _kld(unsigned long long x){
        return Quaternion<long double>(0,0,0,(long double)x);
    }
    Quaternion<long double> operator"" _kld(long double x){
        return Quaternion<long double>(0,0,0,(long double)x);
    }
    Quaternion<long double> operator"" _rld(unsigned long long x){
        return Quaternion<long double>((long double)x,0,0,0);
    }
    Quaternion<long double> operator"" _rld(long double x){
        return Quaternion<long double>((long double)x,0,0,0);
    }
    Quaternion<long double> operator"" _qld(const char* str,size_t len){
        string s(str,len);
        stringstream ss(s);
        Quaternion<long double> c;
        ss>>c;
        return c;
    }
    Quaternion<long double> operator"" _pld(const char* str,size_t len){
        string s(str,len);
        if(s.find("<")==s.npos){
            return Quaternion<long double>(stold(s));
        }
        string s1=s.substr(0,s.find("<"));
        string s2=s.substr(s.find("<")+1,s.size()-s.find("<")-1);
        s1.erase(0,1);
        s1.erase(s1.size()-1,1);
        string s3=s1.substr(0,s1.find(","));
        s1.erase(0,s3.size()+1);
        string s4=s1.substr(0,s1.find(","));
        s1.erase(0,s4.size()+1);
        Quaternion<long double> u(0.0,stold(s3),stold(s4),stold(s1));
        long double theta=stold(s2);
        Quaternion<long double> q=cos(theta/2)+u*sin(theta/2);
        return q;
    }
}
namespace std{
    using namespace QLiterals;
}
#endif//C++14
#endif/*QUATERNION_HPP*/

:::