o
    oh                     @   s  d dl mZ d dlmZ d dlmZ d dlmZmZm	Z	m
Z
 d dlmZmZ d dlmZ d dlmZmZmZ d dlmZmZ d d	lmZ d d
lmZ d dlmZ d dlmZm Z  d dl!m"Z" d dl#m$Z$m%Z% d dl&m'Z' d dl(m)Z) dd Z*dd Z+G dd de"Z,dS )    )Rational)S)is_eq)	conjugateimresign)explog)sqrt)acosasinatan2)cossin)trigsimp	integrate)MutableDenseMatrix)sympify_sympify)Expr)	fuzzy_notfuzzy_or)as_int)prec_to_dpsc                 C   sn   |dur/|j r1|jdu rtdtdd | D }|r3t|d tdd | D du r5tddS dS dS dS )	z$validate if input norm is consistentNFzInput norm must be positive.c                 s   s     | ]}|j o|jd u V  qdS )TN)	is_numberis_real.0i r!   m/var/www/html/construction_image-detection-poc/venv/lib/python3.10/site-packages/sympy/algebras/quaternion.py	<genexpr>   s    z_check_norm.<locals>.<genexpr>   c                 s   s    | ]}|d  V  qdS )r$   Nr!   r   r!   r!   r"   r#      s    zIncompatible value for norm.)r   is_positive
ValueErrorallr   sum)elementsnorm	numericalr!   r!   r"   _check_norm   s   
$r,   c                 C   s   t | tkr
tdt| dkrtd| |  }|  }|s'|s'td|  \}}}||ks6||kr:tdt| td }|rNtdd	||S )	zGvalidate seq and return True if seq is lowercase and False if uppercasezExpected seq to be a string.   zExpected 3 axes, got `{}`.zkseq must either be fully uppercase (for extrinsic rotations), or fully lowercase, for intrinsic rotations).z"Consecutive axes must be differentxyzXYZzNExpected axes from `seq` to be from ['x', 'y', 'z'] or ['X', 'Y', 'Z'], got {} )
typestrr&   lenformatisupperislowerlowersetjoin)seq	intrinsic	extrinsicr    jkbadr!   r!   r"   _is_extrinsic   s"   r?   c                       s  e Zd ZdZdZdZdu fdd	Zd	d
 Zedd Z	edd Z
edd Zedd Zedd Zedd Zedd ZdvddZedd Zedd Zdwdd Zed!d" Zed#d$ Zd%d& Zd'd( Zd)d* Zd+d, Zd-d. Zd/d0 Zd1d2 Zd3d4 Zd5d6 Zd7d8 Z d9d: Z!d;d< Z"d=d> Z#e$d?d@ Z%dAdB Z&dCdD Z'dEdF Z(dGdH Z)dIdJ Z*dKdL Z+dMdN Z,dOdP Z-dQdR Z.dSdT Z/dUdV Z0e$dWdX Z1dYdZ Z2dxd[d\Z3d]d^ Z4d_d` Z5dadb Z6dcdd Z7dedf Z8dgdh Z9didj Z:edkdl Z;dmdn Z<dodp Z=dqdr Z>dsdt Z?  Z@S )y
Quaterniona  Provides basic quaternion operations.
    Quaternion objects can be instantiated as ``Quaternion(a, b, c, d)``
    as in $q = a + bi + cj + dk$.

    Parameters
    ==========

    norm : None or number
        Pre-defined quaternion norm. If a value is given, Quaternion.norm
        returns this pre-defined value instead of calculating the norm

    Examples
    ========

    >>> from sympy import Quaternion
    >>> q = Quaternion(1, 2, 3, 4)
    >>> q
    1 + 2*i + 3*j + 4*k

    Quaternions over complex fields can be defined as:

    >>> from sympy import Quaternion
    >>> from sympy import symbols, I
    >>> x = symbols('x')
    >>> q1 = Quaternion(x, x**3, x, x**2, real_field = False)
    >>> q2 = Quaternion(3 + 4*I, 2 + 5*I, 0, 7 + 8*I, real_field = False)
    >>> q1
    x + x**3*i + x*j + x**2*k
    >>> q2
    (3 + 4*I) + (2 + 5*I)*i + 0*j + (7 + 8*I)*k

    Defining symbolic unit quaternions:

    >>> from sympy import Quaternion
    >>> from sympy.abc import w, x, y, z
    >>> q = Quaternion(w, x, y, z, norm=1)
    >>> q
    w + x*i + y*j + z*k
    >>> q.norm()
    1

    References
    ==========

    .. [1] https://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/
    .. [2] https://en.wikipedia.org/wiki/Quaternion

    g      &@Fr   TNc                    sd   t t||||f\}}}}tdd ||||fD rtdt | ||||}||_|| |S )Nc                 s   s    | ]}|j d u V  qdS )FN)is_commutativer   r!   r!   r"   r#   r   s    z%Quaternion.__new__.<locals>.<genexpr>z arguments have to be commutative)mapr   anyr&   super__new___real_fieldset_norm)clsabcd
real_fieldr*   obj	__class__r!   r"   rE   o   s   
zQuaternion.__new__c                 C   s   t |}t| j| || _dS )a  Sets norm of an already instantiated quaternion.

        Parameters
        ==========

        norm : None or number
            Pre-defined quaternion norm. If a value is given, Quaternion.norm
            returns this pre-defined value instead of calculating the norm

        Examples
        ========

        >>> from sympy import Quaternion
        >>> from sympy.abc import a, b, c, d
        >>> q = Quaternion(a, b, c, d)
        >>> q.norm()
        sqrt(a**2 + b**2 + c**2 + d**2)

        Setting the norm:

        >>> q.set_norm(1)
        >>> q.norm()
        1

        Removing set norm:

        >>> q.set_norm(None)
        >>> q.norm()
        sqrt(a**2 + b**2 + c**2 + d**2)

        N)r   r,   args_norm)selfr*   r!   r!   r"   rG   y   s    
zQuaternion.set_normc                 C   
   | j d S )Nr   rQ   rS   r!   r!   r"   rI         
zQuaternion.ac                 C   rT   )N   rU   rV   r!   r!   r"   rJ      rW   zQuaternion.bc                 C   rT   )Nr$   rU   rV   r!   r!   r"   rK      rW   zQuaternion.cc                 C   rT   )Nr-   rU   rV   r!   r!   r"   rL      rW   zQuaternion.dc                 C      | j S N)rF   rV   r!   r!   r"   rM      s   zQuaternion.real_fieldc                 C   s\   t | j| j | j | j g| j| j| j | jg| j| j| j| j g| j| j | j| jggS )a  Returns 4 x 4 Matrix equivalent to a Hamilton product from the
        left. This can be useful when treating quaternion elements as column
        vectors. Given a quaternion $q = a + bi + cj + dk$ where a, b, c and d
        are real numbers, the product matrix from the left is:

        .. math::

            M  =  \begin{bmatrix} a  &-b  &-c  &-d \\
                                  b  & a  &-d  & c \\
                                  c  & d  & a  &-b \\
                                  d  &-c  & b  & a \end{bmatrix}

        Examples
        ========

        >>> from sympy import Quaternion
        >>> from sympy.abc import a, b, c, d
        >>> q1 = Quaternion(1, 0, 0, 1)
        >>> q2 = Quaternion(a, b, c, d)
        >>> q1.product_matrix_left
        Matrix([
        [1, 0,  0, -1],
        [0, 1, -1,  0],
        [0, 1,  1,  0],
        [1, 0,  0,  1]])

        >>> q1.product_matrix_left * q2.to_Matrix()
        Matrix([
        [a - d],
        [b - c],
        [b + c],
        [a + d]])

        This is equivalent to:

        >>> (q1 * q2).to_Matrix()
        Matrix([
        [a - d],
        [b - c],
        [b + c],
        [a + d]])
        MatrixrI   rJ   rK   rL   rV   r!   r!   r"   product_matrix_left   s   ,zQuaternion.product_matrix_leftc                 C   s\   t | j| j | j | j g| j| j| j| j g| j| j | j| jg| j| j| j | jggS )aM  Returns 4 x 4 Matrix equivalent to a Hamilton product from the
        right. This can be useful when treating quaternion elements as column
        vectors. Given a quaternion $q = a + bi + cj + dk$ where a, b, c and d
        are real numbers, the product matrix from the left is:

        .. math::

            M  =  \begin{bmatrix} a  &-b  &-c  &-d \\
                                  b  & a  & d  &-c \\
                                  c  &-d  & a  & b \\
                                  d  & c  &-b  & a \end{bmatrix}


        Examples
        ========

        >>> from sympy import Quaternion
        >>> from sympy.abc import a, b, c, d
        >>> q1 = Quaternion(a, b, c, d)
        >>> q2 = Quaternion(1, 0, 0, 1)
        >>> q2.product_matrix_right
        Matrix([
        [1, 0, 0, -1],
        [0, 1, 1, 0],
        [0, -1, 1, 0],
        [1, 0, 0, 1]])

        Note the switched arguments: the matrix represents the quaternion on
        the right, but is still considered as a matrix multiplication from the
        left.

        >>> q2.product_matrix_right * q1.to_Matrix()
        Matrix([
        [ a - d],
        [ b + c],
        [-b + c],
        [ a + d]])

        This is equivalent to:

        >>> (q1 * q2).to_Matrix()
        Matrix([
        [ a - d],
        [ b + c],
        [-b + c],
        [ a + d]])
        r[   rV   r!   r!   r"   product_matrix_right   s   1zQuaternion.product_matrix_rightc                 C   s    |rt | jdd S t | jS )a  Returns elements of quaternion as a column vector.
        By default, a ``Matrix`` of length 4 is returned, with the real part as the
        first element.
        If ``vector_only`` is ``True``, returns only imaginary part as a Matrix of
        length 3.

        Parameters
        ==========

        vector_only : bool
            If True, only imaginary part is returned.
            Default value: False

        Returns
        =======

        Matrix
            A column vector constructed by the elements of the quaternion.

        Examples
        ========

        >>> from sympy import Quaternion
        >>> from sympy.abc import a, b, c, d
        >>> q = Quaternion(a, b, c, d)
        >>> q
        a + b*i + c*j + d*k

        >>> q.to_Matrix()
        Matrix([
        [a],
        [b],
        [c],
        [d]])


        >>> q.to_Matrix(vector_only=True)
        Matrix([
        [b],
        [c],
        [d]])

        rX   N)r\   rQ   )rS   vector_onlyr!   r!   r"   	to_Matrix  s   ,
zQuaternion.to_Matrixc                 C   sF   t |}|dkr|dkrtd||dkrtdg|R  S t| S )a  Returns quaternion from elements of a column vector`.
        If vector_only is True, returns only imaginary part as a Matrix of
        length 3.

        Parameters
        ==========

        elements : Matrix, list or tuple of length 3 or 4. If length is 3,
            assume real part is zero.
            Default value: False

        Returns
        =======

        Quaternion
            A quaternion created from the input elements.

        Examples
        ========

        >>> from sympy import Quaternion
        >>> from sympy.abc import a, b, c, d
        >>> q = Quaternion.from_Matrix([a, b, c, d])
        >>> q
        a + b*i + c*j + d*k

        >>> q = Quaternion.from_Matrix([b, c, d])
        >>> q
        0 + b*i + c*j + d*k

        r-      z7Input elements must have length 3 or 4, got {} elementsr   )r2   r&   r3   r@   )rH   r)   lengthr!   r!   r"   from_MatrixK  s   !zQuaternion.from_Matrixc           
         s   t |dkr
tdt|}| \  fdddD }fdddD }fdddD }| ||d }| ||d	 }| ||d
 }	|rRt|	| | S t|| |	 S )a  Returns quaternion equivalent to rotation represented by the Euler
        angles, in the sequence defined by ``seq``.

        Parameters
        ==========

        angles : list, tuple or Matrix of 3 numbers
            The Euler angles (in radians).
        seq : string of length 3
            Represents the sequence of rotations.
            For extrinsic rotations, seq must be all lowercase and its elements
            must be from the set ``{'x', 'y', 'z'}``
            For intrinsic rotations, seq must be all uppercase and its elements
            must be from the set ``{'X', 'Y', 'Z'}``

        Returns
        =======

        Quaternion
            The normalized rotation quaternion calculated from the Euler angles
            in the given sequence.

        Examples
        ========

        >>> from sympy import Quaternion
        >>> from sympy import pi
        >>> q = Quaternion.from_euler([pi/2, 0, 0], 'xyz')
        >>> q
        sqrt(2)/2 + sqrt(2)/2*i + 0*j + 0*k

        >>> q = Quaternion.from_euler([0, pi/2, pi] , 'zyz')
        >>> q
        0 + (-sqrt(2)/2)*i + 0*j + sqrt(2)/2*k

        >>> q = Quaternion.from_euler([0, pi/2, pi] , 'ZYZ')
        >>> q
        0 + sqrt(2)/2*i + 0*j + sqrt(2)/2*k

        r-   z3 angles must be given.c                       g | ]
}| kr
d ndqS rX   r   r!   r   n)r    r!   r"   
<listcomp>      z)Quaternion.from_euler.<locals>.<listcomp>xyzc                    rd   re   r!   rf   )r<   r!   r"   rh     ri   c                    rd   re   r!   rf   )r=   r!   r"   rh     ri   r   rX   r$   )r2   r&   r?   r6   from_axis_angler   )
rH   anglesr9   r;   eiejekqiqjqkr!   )r    r<   r=   r"   
from_eulerv  s   +zQuaternion.from_eulerc                 C   s2  |   rtdg d}t|}| \}}}d|d }d|d }d|d }|s3||}}||k}	|	r?d| | }|| ||  ||  d }
| j| j| j| jg}|d }|| }|| }|| |
 }|	s}|| || || || f\}}}}|r|	r| 	 d }t
|| ||  ||  ||  | |d< nEd| 	 d  }t|| ||  ||  ||  | |d< n&dtt|| ||  t|| ||   |d< |	s|d  tjd 8  < d}t|tjrt|tjrd}t|tjrt|tjrd}|dkrK|r$t||t|| |d< t||t|| |d< nZt|| ||  || ||  |d< t|| ||  || ||  |d< n3tj|d|  < |dkrddt|| |d| < ndt|| |d| < |d|   |rzdnd9  < |	s|d  |
9  < |rt|d	d	d S t|S )
a}  Returns Euler angles representing same rotation as the quaternion,
        in the sequence given by ``seq``. This implements the method described
        in [1]_.

        For degenerate cases (gymbal lock cases), the third angle is
        set to zero.

        Parameters
        ==========

        seq : string of length 3
            Represents the sequence of rotations.
            For extrinsic rotations, seq must be all lowercase and its elements
            must be from the set ``{'x', 'y', 'z'}``
            For intrinsic rotations, seq must be all uppercase and its elements
            must be from the set ``{'X', 'Y', 'Z'}``

        angle_addition : bool
            When True, first and third angles are given as an addition and
            subtraction of two simpler ``atan2`` expressions. When False, the
            first and third angles are each given by a single more complicated
            ``atan2`` expression. This equivalent expression is given by:

            .. math::

                \operatorname{atan_2} (b,a) \pm \operatorname{atan_2} (d,c) =
                \operatorname{atan_2} (bc\pm ad, ac\mp bd)

            Default value: True

        avoid_square_root : bool
            When True, the second angle is calculated with an expression based
            on ``acos``, which is slightly more complicated but avoids a square
            root. When False, second angle is calculated with ``atan2``, which
            is simpler and can be better for numerical reasons (some
            numerical implementations of ``acos`` have problems near zero).
            Default value: False


        Returns
        =======

        Tuple
            The Euler angles calculated from the quaternion

        Examples
        ========

        >>> from sympy import Quaternion
        >>> from sympy.abc import a, b, c, d
        >>> euler = Quaternion(a, b, c, d).to_euler('zyz')
        >>> euler
        (-atan2(-b, c) + atan2(d, a),
         2*atan2(sqrt(b**2 + c**2), sqrt(a**2 + d**2)),
         atan2(-b, c) + atan2(d, a))


        References
        ==========

        .. [1] https://doi.org/10.1371/journal.pone.0276302

        z(Cannot convert a quaternion with norm 0.)r   r   r   rj   rX      r$   r   N)is_zero_quaternionr&   r?   r6   indexrI   rJ   rK   rL   r*   r   r   r   r   r   Pir   Zerotuple)rS   r9   angle_additionavoid_square_rootrl   r;   r    r<   r=   	symmetricr   r)   rI   rJ   rK   rL   n2caser!   r!   r"   to_euler  sf   @
$..2
&(
zQuaternion.to_eulerc                 C   s   |\}}}t |d |d  |d  }|| || || }}}t|tj }t|tj }|| }	|| }
|| }| ||	|
|S )a  Returns a rotation quaternion given the axis and the angle of rotation.

        Parameters
        ==========

        vector : tuple of three numbers
            The vector representation of the given axis.
        angle : number
            The angle by which axis is rotated (in radians).

        Returns
        =======

        Quaternion
            The normalized rotation quaternion calculated from the given axis and the angle of rotation.

        Examples
        ========

        >>> from sympy import Quaternion
        >>> from sympy import pi, sqrt
        >>> q = Quaternion.from_axis_angle((sqrt(3)/3, sqrt(3)/3, sqrt(3)/3), 2*pi/3)
        >>> q
        1/2 + 1/2*i + 1/2*j + 1/2*k

        r$   )r   r   r   Halfr   )rH   vectoranglexyzr*   srI   rJ   rK   rL   r!   r!   r"   rk   D  s   
zQuaternion.from_axis_anglec                 C   s   |  tdd }t||d  |d  |d  d }t||d  |d  |d  d }t||d  |d  |d  d }t||d  |d  |d  d }|t|d |d   }|t|d	 |d
   }|t|d |d   }t||||S )a  Returns the equivalent quaternion of a matrix. The quaternion will be normalized
        only if the matrix is special orthogonal (orthogonal and det(M) = 1).

        Parameters
        ==========

        M : Matrix
            Input matrix to be converted to equivalent quaternion. M must be special
            orthogonal (orthogonal and det(M) = 1) for the quaternion to be normalized.

        Returns
        =======

        Quaternion
            The quaternion equivalent to given matrix.

        Examples
        ========

        >>> from sympy import Quaternion
        >>> from sympy import Matrix, symbols, cos, sin, trigsimp
        >>> x = symbols('x')
        >>> M = Matrix([[cos(x), -sin(x), 0], [sin(x), cos(x), 0], [0, 0, 1]])
        >>> q = trigsimp(Quaternion.from_rotation_matrix(M))
        >>> q
        sqrt(2)*sqrt(cos(x) + 1)/2 + 0*i + 0*j + sqrt(2 - 2*cos(x))*sign(sin(x))/2*k

        rX   r-   )r   r   )rX   rX   )r$   r$   r$   )r$   rX   )rX   r$   )r   r$   )r$   r   re   )r   rX   )detr   r   r   r@   )rH   MabsQrI   rJ   rK   rL   r!   r!   r"   from_rotation_matrixo  s   $$$$zQuaternion.from_rotation_matrixc                 C   
   |  |S rZ   addrS   otherr!   r!   r"   __add__     
zQuaternion.__add__c                 C   r   rZ   r   r   r!   r!   r"   __radd__  r   zQuaternion.__radd__c                 C   s   |  |d S Nru   r   r   r!   r!   r"   __sub__  s   zQuaternion.__sub__c                 C      |  | t|S rZ   _generic_mulr   r   r!   r!   r"   __mul__     zQuaternion.__mul__c                 C   s   |  t|| S rZ   r   r   r!   r!   r"   __rmul__  r   zQuaternion.__rmul__c                 C   r   rZ   )pow)rS   pr!   r!   r"   __pow__  r   zQuaternion.__pow__c                 C   s   t | j | j | j | j S rZ   )r@   rI   rJ   rK   rL   rV   r!   r!   r"   __neg__  s   zQuaternion.__neg__c                 C   s   | t |d  S r   r   r   r!   r!   r"   __truediv__  r   zQuaternion.__truediv__c                 C   s   t || d  S r   r   r   r!   r!   r"   __rtruediv__  r   zQuaternion.__rtruediv__c                 G   s
   | j | S rZ   r   rS   rQ   r!   r!   r"   _eval_Integral  r   zQuaternion._eval_Integralc                    s(     dd | j fdd| jD  S )NevaluateTc                    s   g | ]
}|j i  qS r!   )diff)r   rI   kwargssymbolsr!   r"   rh     ri   z#Quaternion.diff.<locals>.<listcomp>)
setdefaultfuncrQ   )rS   r   r   r!   r   r"   r     s   zQuaternion.diffc                 C   s   | }t |}t|ts8|jr$|jr$tt||j t||j |j	|j
S |jr4t|j| |j|j	|j
S tdt|j|j |j|j |j	|j	 |j
|j
 S )a  Adds quaternions.

        Parameters
        ==========

        other : Quaternion
            The quaternion to add to current (self) quaternion.

        Returns
        =======

        Quaternion
            The resultant quaternion after adding self to other

        Examples
        ========

        >>> from sympy import Quaternion
        >>> from sympy import symbols
        >>> q1 = Quaternion(1, 2, 3, 4)
        >>> q2 = Quaternion(5, 6, 7, 8)
        >>> q1.add(q2)
        6 + 8*i + 10*j + 12*k
        >>> q1 + 5
        6 + 2*i + 3*j + 4*k
        >>> x = symbols('x', real = True)
        >>> q1.add(x)
        (x + 1) + 2*i + 3*j + 4*k

        Quaternions over complex fields :

        >>> from sympy import Quaternion
        >>> from sympy import I
        >>> q3 = Quaternion(3 + 4*I, 2 + 5*I, 0, 7 + 8*I, real_field = False)
        >>> q3.add(2 + 3*I)
        (5 + 7*I) + (2 + 5*I)*i + 0*j + (7 + 8*I)*k

        z<Only commutative expressions can be added with a Quaternion.)r   
isinstancer@   rM   
is_complexr   rI   r   rJ   rK   rL   rA   r&   )rS   r   q1q2r!   r!   r"   r     s   '
&$zQuaternion.addc                 C   r   )a  Multiplies quaternions.

        Parameters
        ==========

        other : Quaternion or symbol
            The quaternion to multiply to current (self) quaternion.

        Returns
        =======

        Quaternion
            The resultant quaternion after multiplying self with other

        Examples
        ========

        >>> from sympy import Quaternion
        >>> from sympy import symbols
        >>> q1 = Quaternion(1, 2, 3, 4)
        >>> q2 = Quaternion(5, 6, 7, 8)
        >>> q1.mul(q2)
        (-60) + 12*i + 30*j + 24*k
        >>> q1.mul(2)
        2 + 4*i + 6*j + 8*k
        >>> x = symbols('x', real = True)
        >>> q1.mul(x)
        x + 2*x*i + 3*x*j + 4*x*k

        Quaternions over complex fields :

        >>> from sympy import Quaternion
        >>> from sympy import I
        >>> q3 = Quaternion(3 + 4*I, 2 + 5*I, 0, 7 + 8*I, real_field = False)
        >>> q3.mul(2 + 3*I)
        (2 + 3*I)*(3 + 4*I) + (2 + 3*I)*(2 + 5*I)*i + 0*j + (2 + 3*I)*(7 + 8*I)*k

        r   r   r!   r!   r"   mul  s   'zQuaternion.mulc                 C   s  t | tst |ts| | S t | ts@|jr&| jr&tt| t| dd| S | jr<t| |j | |j | |j	 | |j
 S tdt |tsr| jrX|jrX| tt|t|dd S |jrnt|| j || j || j	 || j
 S td| jdu r|jdu rd}n|  |  }t| j |j | j	|j	  | j
|j
  | j|j  | j|j | j	|j
  | j
|j	  | j|j  | j |j
 | j	|j  | j
|j  | j|j	  | j|j	 | j	|j  | j
|j  | j|j
  |dS )an  Generic multiplication.

        Parameters
        ==========

        q1 : Quaternion or symbol
        q2 : Quaternion or symbol

        It is important to note that if neither q1 nor q2 is a Quaternion,
        this function simply returns q1 * q2.

        Returns
        =======

        Quaternion
            The resultant quaternion after multiplying q1 and q2

        Examples
        ========

        >>> from sympy import Quaternion
        >>> from sympy import Symbol, S
        >>> q1 = Quaternion(1, 2, 3, 4)
        >>> q2 = Quaternion(5, 6, 7, 8)
        >>> Quaternion._generic_mul(q1, q2)
        (-60) + 12*i + 30*j + 24*k
        >>> Quaternion._generic_mul(q1, S(2))
        2 + 4*i + 6*j + 8*k
        >>> x = Symbol('x', real = True)
        >>> Quaternion._generic_mul(q1, x)
        x + 2*x*i + 3*x*j + 4*x*k

        Quaternions over complex fields :

        >>> from sympy import I
        >>> q3 = Quaternion(3 + 4*I, 2 + 5*I, 0, 7 + 8*I, real_field = False)
        >>> Quaternion._generic_mul(q3, 2 + 3*I)
        (2 + 3*I)*(3 + 4*I) + (2 + 3*I)*(2 + 5*I)*i + 0*j + (2 + 3*I)*(7 + 8*I)*k

        r   zAOnly commutative expressions can be multiplied with a Quaternion.Nr*   )r   r@   rM   r   r   r   rA   rI   rJ   rK   rL   r&   rR   r*   )r   r   r*   r!   r!   r"   r     s.   +
&
&2.0.zQuaternion._generic_mulc                 C   s&   | }t |j|j |j |j |jdS )z(Returns the conjugate of the quaternion.r   )r@   rI   rJ   rK   rL   rR   rS   qr!   r!   r"   _eval_conjugateh  s   "zQuaternion._eval_conjugatec                 C   sD   | j du r| }tt|jd |jd  |jd  |jd  S | j S )z#Returns the norm of the quaternion.Nr$   )rR   r   r   rI   rJ   rK   rL   r   r!   r!   r"   r*   m  s   
0zQuaternion.normc                 C   s   | }|d|    S )z.Returns the normalized form of the quaternion.rX   r   r   r!   r!   r"   	normalizew  s   zQuaternion.normalizec                 C   s,   | }|  s
tdt|d|  d   S )z&Returns the inverse of the quaternion.z6Cannot compute inverse for a quaternion with zero normrX   r$   )r*   r&   r   r   r!   r!   r"   inverse|  s   zQuaternion.inversec                 C   s   z	| t |}}W n ty   t Y S w |dk r!| | }}|dkr'|S tdddd}|dkrF|d@ r:||9 }||9 }|dL }|dks2|S )a  Finds the pth power of the quaternion.

        Parameters
        ==========

        p : int
            Power to be applied on quaternion.

        Returns
        =======

        Quaternion
            Returns the p-th power of the current quaternion.
            Returns the inverse if p = -1.

        Examples
        ========

        >>> from sympy import Quaternion
        >>> q = Quaternion(1, 2, 3, 4)
        >>> q.pow(4)
        668 + (-224)*i + (-336)*j + (-448)*k

        r   rX   )r   r&   NotImplementedr   r@   )rS   r   r   resr!   r!   r"   r     s"   zQuaternion.powc                 C   s   | }t |jd |jd  |jd  }t|jt| }t|jt| |j | }t|jt| |j | }t|jt| |j | }t||||S )a  Returns the exponential of $q$, given by $e^q$.

        Returns
        =======

        Quaternion
            The exponential of the quaternion.

        Examples
        ========

        >>> from sympy import Quaternion
        >>> q = Quaternion(1, 2, 3, 4)
        >>> q.exp()
        E*cos(sqrt(29))
        + 2*sqrt(29)*E*sin(sqrt(29))/29*i
        + 3*sqrt(29)*E*sin(sqrt(29))/29*j
        + 4*sqrt(29)*E*sin(sqrt(29))/29*k

        r$   )	r   rJ   rK   rL   r	   rI   r   r   r@   )rS   r   vector_normrI   rJ   rK   rL   r!   r!   r"   r	     s   "zQuaternion.expc                 C   s   | }t |jd |jd  |jd  }| }t|}|jt|j|  | }|jt|j|  | }|jt|j|  | }t||||S )ag  Returns the logarithm of the quaternion, given by $\log q$.

        Examples
        ========

        >>> from sympy import Quaternion
        >>> q = Quaternion(1, 2, 3, 4)
        >>> q.log()
        log(sqrt(30))
        + 2*sqrt(29)*acos(sqrt(30)/30)/29*i
        + 3*sqrt(29)*acos(sqrt(30)/30)/29*j
        + 4*sqrt(29)*acos(sqrt(30)/30)/29*k

        r$   )	r   rJ   rK   rL   r*   lnr   rI   r@   )rS   r   r   q_normrI   rJ   rK   rL   r!   r!   r"   r
     s   "zQuaternion.logc                    sD    fdd| j D }| j}|d ur|j  }t|| t|d|iS )Nc                    s   g | ]}|j   qS r!   )subsr   rU   r!   r"   rh     s    z)Quaternion._eval_subs.<locals>.<listcomp>r*   )rQ   rR   r   r,   r@   )rS   rQ   r)   r*   r!   rU   r"   
_eval_subs  s   

zQuaternion._eval_subsc                    s    t | t fdd| jD  S )a  Returns the floating point approximations (decimal numbers) of the quaternion.

        Returns
        =======

        Quaternion
            Floating point approximations of quaternion(self)

        Examples
        ========

        >>> from sympy import Quaternion
        >>> from sympy import sqrt
        >>> q = Quaternion(1/sqrt(1), 1/sqrt(2), 1/sqrt(3), 1/sqrt(4))
        >>> q.evalf()
        1.00000000000000
        + 0.707106781186547*i
        + 0.577350269189626*j
        + 0.500000000000000*k

        c                    s   g | ]}|j  d qS ))rg   )evalf)r   argnprecr!   r"   rh     s    z*Quaternion._eval_evalf.<locals>.<listcomp>)r   r@   rQ   )rS   precr!   r   r"   _eval_evalf  s   zQuaternion._eval_evalfc                 C   s0   | }|  \}}t||| }|| |  S )aY  Computes the pth power in the cos-sin form.

        Parameters
        ==========

        p : int
            Power to be applied on quaternion.

        Returns
        =======

        Quaternion
            The p-th power in the cos-sin form.

        Examples
        ========

        >>> from sympy import Quaternion
        >>> q = Quaternion(1, 2, 3, 4)
        >>> q.pow_cos_sin(4)
        900*cos(4*acos(sqrt(30)/30))
        + 1800*sqrt(29)*sin(4*acos(sqrt(30)/30))/29*i
        + 2700*sqrt(29)*sin(4*acos(sqrt(30)/30))/29*j
        + 3600*sqrt(29)*sin(4*acos(sqrt(30)/30))/29*k

        )to_axis_angler@   rk   r*   )rS   r   r   vr   r   r!   r!   r"   pow_cos_sin
  s   zQuaternion.pow_cos_sinc                 G   sF   t t| jg|R  t| jg|R  t| jg|R  t| jg|R  S )a  Computes integration of quaternion.

        Returns
        =======

        Quaternion
            Integration of the quaternion(self) with the given variable.

        Examples
        ========

        Indefinite Integral of quaternion :

        >>> from sympy import Quaternion
        >>> from sympy.abc import x
        >>> q = Quaternion(1, 2, 3, 4)
        >>> q.integrate(x)
        x + 2*x*i + 3*x*j + 4*x*k

        Definite integral of quaternion :

        >>> from sympy import Quaternion
        >>> from sympy.abc import x
        >>> q = Quaternion(1, 2, 3, 4)
        >>> q.integrate((x, 1, 5))
        4 + 8*i + 12*j + 16*k

        )r@   r   rI   rJ   rK   rL   r   r!   r!   r"   r   -  s   " zQuaternion.integratec                 C   s^   t |trt|d |d }n| }|td| d | d | d  t| }|j|j|jfS )a  Returns the coordinates of the point pin (a 3 tuple) after rotation.

        Parameters
        ==========

        pin : tuple
            A 3-element tuple of coordinates of a point which needs to be
            rotated.
        r : Quaternion or tuple
            Axis and angle of rotation.

            It's important to note that when r is a tuple, it must be of the form
            (axis, angle)

        Returns
        =======

        tuple
            The coordinates of the point after rotation.

        Examples
        ========

        >>> from sympy import Quaternion
        >>> from sympy import symbols, trigsimp, cos, sin
        >>> x = symbols('x')
        >>> q = Quaternion(cos(x/2), 0, 0, sin(x/2))
        >>> trigsimp(Quaternion.rotate_point((1, 1, 1), q))
        (sqrt(2)*cos(x + pi/4), sqrt(2)*sin(x + pi/4), 1)
        >>> (axis, angle) = q.to_axis_angle()
        >>> trigsimp(Quaternion.rotate_point((1, 1, 1), (axis, angle)))
        (sqrt(2)*cos(x + pi/4), sqrt(2)*sin(x + pi/4), 1)

        r   rX   r$   )	r   rz   r@   rk   r   r   rJ   rK   rL   )pinrr   poutr!   r!   r"   rotate_pointN  s
   
$&zQuaternion.rotate_pointc           	      C   s   | }|j jr
|d }| }tdt|j  }td|j |j   }t|j| }t|j| }t|j| }|||f}||f}|S )a  Returns the axis and angle of rotation of a quaternion.

        Returns
        =======

        tuple
            Tuple of (axis, angle)

        Examples
        ========

        >>> from sympy import Quaternion
        >>> q = Quaternion(1, 1, 1, 1)
        >>> (axis, angle) = q.to_axis_angle()
        >>> axis
        (sqrt(3)/3, sqrt(3)/3, sqrt(3)/3)
        >>> angle
        2*pi/3

        ru   r$   rX   )	rI   is_negativer   r   r   r   rJ   rK   rL   )	rS   r   r   r   r   r   r   r   tr!   r!   r"   r   {  s   
zQuaternion.to_axis_anglec                 C   s|  | }|  d }|rM||jd |jd  |jd  |jd   }||jd |jd  |jd  |jd   }||jd |jd  |jd  |jd   }n0dd| |jd |jd    }dd| |jd |jd    }dd| |jd |jd    }d| |j|j |j|j   }d| |j|j |j|j   }	d| |j|j |j|j   }
d| |j|j |j|j   }d| |j|j |j|j   }d| |j|j |j|j   }|st|||	g|
||g|||ggS |\}}}|||  ||  ||	  }|||
  ||  ||  }|||  ||  ||  }d } }}d}t|||	|g|
|||g||||g||||ggS )a  Returns the equivalent rotation transformation matrix of the quaternion
        which represents rotation about the origin if ``v`` is not passed.

        Parameters
        ==========

        v : tuple or None
            Default value: None
        homogeneous : bool
            When True, gives an expression that may be more efficient for
            symbolic calculations but less so for direct evaluation. Both
            formulas are mathematically equivalent.
            Default value: True

        Returns
        =======

        tuple
            Returns the equivalent rotation transformation matrix of the quaternion
            which represents rotation about the origin if v is not passed.

        Examples
        ========

        >>> from sympy import Quaternion
        >>> from sympy import symbols, trigsimp, cos, sin
        >>> x = symbols('x')
        >>> q = Quaternion(cos(x/2), 0, 0, sin(x/2))
        >>> trigsimp(q.to_rotation_matrix())
        Matrix([
        [cos(x), -sin(x), 0],
        [sin(x),  cos(x), 0],
        [     0,       0, 1]])

        Generates a 4x4 transformation matrix (used for rotation about a point
        other than the origin) if the point(v) is passed as an argument.
        r$   rX   r   )r*   rI   rJ   rK   rL   r\   )rS   r   homogeneousr   r   m00m11m22m01m02m10m12m20m21r   r   r   m03m13m23m30m31m32m33r!   r!   r"   to_rotation_matrix  s4   ',,.          
zQuaternion.to_rotation_matrixc                 C   rY   )am  Returns scalar part($\mathbf{S}(q)$) of the quaternion q.

        Explanation
        ===========

        Given a quaternion $q = a + bi + cj + dk$, returns $\mathbf{S}(q) = a$.

        Examples
        ========

        >>> from sympy.algebras.quaternion import Quaternion
        >>> q = Quaternion(4, 8, 13, 12)
        >>> q.scalar_part()
        4

        )rI   rV   r!   r!   r"   scalar_part  s   zQuaternion.scalar_partc                 C   s   t d| j| j| jS )a  
        Returns $\mathbf{V}(q)$, the vector part of the quaternion $q$.

        Explanation
        ===========

        Given a quaternion $q = a + bi + cj + dk$, returns $\mathbf{V}(q) = bi + cj + dk$.

        Examples
        ========

        >>> from sympy.algebras.quaternion import Quaternion
        >>> q = Quaternion(1, 1, 1, 1)
        >>> q.vector_part()
        0 + 1*i + 1*j + 1*k

        >>> q = Quaternion(4, 8, 13, 12)
        >>> q.vector_part()
        0 + 8*i + 13*j + 12*k

        r   )r@   rJ   rK   rL   rV   r!   r!   r"   vector_part  s   zQuaternion.vector_partc                 C   s    |    }td|j|j|jS )a  
        Returns $\mathbf{Ax}(q)$, the axis of the quaternion $q$.

        Explanation
        ===========

        Given a quaternion $q = a + bi + cj + dk$, returns $\mathbf{Ax}(q)$  i.e., the versor of the vector part of that quaternion
        equal to $\mathbf{U}[\mathbf{V}(q)]$.
        The axis is always an imaginary unit with square equal to $-1 + 0i + 0j + 0k$.

        Examples
        ========

        >>> from sympy.algebras.quaternion import Quaternion
        >>> q = Quaternion(1, 1, 1, 1)
        >>> q.axis()
        0 + sqrt(3)/3*i + sqrt(3)/3*j + sqrt(3)/3*k

        See Also
        ========

        vector_part

        r   )r   r   r@   rJ   rK   rL   )rS   axisr!   r!   r"   r     s   zQuaternion.axisc                 C   s   | j jS )a  
        Returns true if the quaternion is pure, false if the quaternion is not pure
        or returns none if it is unknown.

        Explanation
        ===========

        A pure quaternion (also a vector quaternion) is a quaternion with scalar
        part equal to 0.

        Examples
        ========

        >>> from sympy.algebras.quaternion import Quaternion
        >>> q = Quaternion(0, 8, 13, 12)
        >>> q.is_pure()
        True

        See Also
        ========
        scalar_part

        )rI   is_zerorV   r!   r!   r"   is_pure9  s   zQuaternion.is_purec                 C   s
   |   jS )a  
        Returns true if the quaternion is a zero quaternion or false if it is not a zero quaternion
        and None if the value is unknown.

        Explanation
        ===========

        A zero quaternion is a quaternion with both scalar part and
        vector part equal to 0.

        Examples
        ========

        >>> from sympy.algebras.quaternion import Quaternion
        >>> q = Quaternion(1, 0, 0, 0)
        >>> q.is_zero_quaternion()
        False

        >>> q = Quaternion(0, 0, 0, 0)
        >>> q.is_zero_quaternion()
        True

        See Also
        ========
        scalar_part
        vector_part

        )r*   r   rV   r!   r!   r"   rv   T  s   
zQuaternion.is_zero_quaternionc                 C   s   dt |   |   S )a7  
        Returns the angle of the quaternion measured in the real-axis plane.

        Explanation
        ===========

        Given a quaternion $q = a + bi + cj + dk$ where $a$, $b$, $c$ and $d$
        are real numbers, returns the angle of the quaternion given by

        .. math::
            \theta := 2 \operatorname{atan_2}\left(\sqrt{b^2 + c^2 + d^2}, {a}\right)

        Examples
        ========

        >>> from sympy.algebras.quaternion import Quaternion
        >>> q = Quaternion(1, 4, 4, 4)
        >>> q.angle()
        2*atan(4*sqrt(3))

        r$   )r   r   r*   r   rV   r!   r!   r"   r   t  s   zQuaternion.anglec                 C   sD   |   s|  rtdt|  |    |  |    gS )aS  
        Returns True if the transformation arcs represented by the input quaternions happen in the same plane.

        Explanation
        ===========

        Two quaternions are said to be coplanar (in this arc sense) when their axes are parallel.
        The plane of a quaternion is the one normal to its axis.

        Parameters
        ==========

        other : a Quaternion

        Returns
        =======

        True : if the planes of the two quaternions are the same, apart from its orientation/sign.
        False : if the planes of the two quaternions are not the same, apart from its orientation/sign.
        None : if plane of either of the quaternion is unknown.

        Examples
        ========

        >>> from sympy.algebras.quaternion import Quaternion
        >>> q1 = Quaternion(1, 4, 4, 4)
        >>> q2 = Quaternion(3, 8, 8, 8)
        >>> Quaternion.arc_coplanar(q1, q2)
        True

        >>> q1 = Quaternion(2, 8, 13, 12)
        >>> Quaternion.arc_coplanar(q1, q2)
        False

        See Also
        ========

        vector_coplanar
        is_pure

        z)Neither of the given quaternions can be 0)rv   r&   r   r   r   r!   r!   r"   arc_coplanar  s   *,zQuaternion.arc_coplanarc                 C   sh   t | st | st | rtdt|j|j|jg|j|j|jg|j|j|jgg }|jS )a"  
        Returns True if the axis of the pure quaternions seen as 3D vectors
        ``q1``, ``q2``, and ``q3`` are coplanar.

        Explanation
        ===========

        Three pure quaternions are vector coplanar if the quaternions seen as 3D vectors are coplanar.

        Parameters
        ==========

        q1
            A pure Quaternion.
        q2
            A pure Quaternion.
        q3
            A pure Quaternion.

        Returns
        =======

        True : if the axis of the pure quaternions seen as 3D vectors
        q1, q2, and q3 are coplanar.
        False : if the axis of the pure quaternions seen as 3D vectors
        q1, q2, and q3 are not coplanar.
        None : if the axis of the pure quaternions seen as 3D vectors
        q1, q2, and q3 are coplanar is unknown.

        Examples
        ========

        >>> from sympy.algebras.quaternion import Quaternion
        >>> q1 = Quaternion(0, 4, 4, 4)
        >>> q2 = Quaternion(0, 8, 8, 8)
        >>> q3 = Quaternion(0, 24, 24, 24)
        >>> Quaternion.vector_coplanar(q1, q2, q3)
        True

        >>> q1 = Quaternion(0, 8, 16, 8)
        >>> q2 = Quaternion(0, 8, 3, 12)
        >>> Quaternion.vector_coplanar(q1, q2, q3)
        False

        See Also
        ========

        axis
        is_pure

        "The given quaternions must be pure)	r   r   r&   r\   rJ   rK   rL   r   r   )rH   r   r   q3r   r!   r!   r"   vector_coplanar  s   $66zQuaternion.vector_coplanarc                 C   s4   t |  st | rtd| | ||    S )a  
        Returns True if the two pure quaternions seen as 3D vectors are parallel.

        Explanation
        ===========

        Two pure quaternions are called parallel when their vector product is commutative which
        implies that the quaternions seen as 3D vectors have same direction.

        Parameters
        ==========

        other : a Quaternion

        Returns
        =======

        True : if the two pure quaternions seen as 3D vectors are parallel.
        False : if the two pure quaternions seen as 3D vectors are not parallel.
        None : if the two pure quaternions seen as 3D vectors are parallel is unknown.

        Examples
        ========

        >>> from sympy.algebras.quaternion import Quaternion
        >>> q = Quaternion(0, 4, 4, 4)
        >>> q1 = Quaternion(0, 8, 8, 8)
        >>> q.parallel(q1)
        True

        >>> q1 = Quaternion(0, 8, 13, 12)
        >>> q.parallel(q1)
        False

        z%The provided quaternions must be purer   r   r&   rv   r   r!   r!   r"   parallel     %zQuaternion.parallelc                 C   s4   t |  st | rtd| | ||    S )a|  
        Returns the orthogonality of two quaternions.

        Explanation
        ===========

        Two pure quaternions are called orthogonal when their product is anti-commutative.

        Parameters
        ==========

        other : a Quaternion

        Returns
        =======

        True : if the two pure quaternions seen as 3D vectors are orthogonal.
        False : if the two pure quaternions seen as 3D vectors are not orthogonal.
        None : if the two pure quaternions seen as 3D vectors are orthogonal is unknown.

        Examples
        ========

        >>> from sympy.algebras.quaternion import Quaternion
        >>> q = Quaternion(0, 4, 4, 4)
        >>> q1 = Quaternion(0, 8, 8, 8)
        >>> q.orthogonal(q1)
        False

        >>> q1 = Quaternion(0, 2, 2, 0)
        >>> q = Quaternion(0, 2, -2, 0)
        >>> q.orthogonal(q1)
        True

        r   r   r   r!   r!   r"   
orthogonal#  r   zQuaternion.orthogonalc                 C   s   |   |   S )a  
        Returns the index vector of the quaternion.

        Explanation
        ===========

        The index vector is given by $\mathbf{T}(q)$, the norm (or magnitude) of
        the quaternion $q$, multiplied by $\mathbf{Ax}(q)$, the axis of $q$.

        Returns
        =======

        Quaternion: representing index vector of the provided quaternion.

        Examples
        ========

        >>> from sympy.algebras.quaternion import Quaternion
        >>> q = Quaternion(2, 4, 2, 4)
        >>> q.index_vector()
        0 + 4*sqrt(10)/3*i + 2*sqrt(10)/3*j + 4*sqrt(10)/3*k

        See Also
        ========

        axis
        norm

        )r*   r   rV   r!   r!   r"   index_vectorM  s   zQuaternion.index_vectorc                 C   s   t |  S )aj  
        Returns the natural logarithm of the norm(magnitude) of the quaternion.

        Examples
        ========

        >>> from sympy.algebras.quaternion import Quaternion
        >>> q = Quaternion(2, 4, 2, 4)
        >>> q.mensor()
        log(2*sqrt(10))
        >>> q.norm()
        2*sqrt(10)

        See Also
        ========

        norm

        )r   r*   rV   r!   r!   r"   mensorn  s   zQuaternion.mensor)r   r   r   r   TN)F)TF)NT)A__name__
__module____qualname____doc___op_priorityrA   rE   rG   propertyrI   rJ   rK   rL   rM   r]   r^   r`   classmethodrc   rs   r   rk   r   r   r   r   r   r   r   r   r   r   r   r   r   r   staticmethodr   r   r*   r   r   r   r	   r
   r   r   r   r   r   r   r   r   r   r   r   rv   r   r   r   r   r   r   r   __classcell__r!   r!   rO   r"   r@   :   s    0
$





1

61
*

? 
*
+6)
K
-#!
,
(L /
;**!r@   N)-sympy.core.numbersr   sympy.core.singletonr   sympy.core.relationalr   $sympy.functions.elementary.complexesr   r   r   r   &sympy.functions.elementary.exponentialr	   r
   r   (sympy.functions.elementary.miscellaneousr   (sympy.functions.elementary.trigonometricr   r   r   r   r   sympy.simplify.trigsimpr   sympy.integrals.integralsr   sympy.matrices.denser   r\   sympy.core.sympifyr   r   sympy.core.exprr   sympy.core.logicr   r   sympy.utilities.miscr   mpmath.libmp.libmpfr   r,   r?   r@   r!   r!   r!   r"   <module>   s&    