# Path: pyem/geom/quat_numba.py
# @numba.jit(cache=False, nopython=True, nogil=True)
# def distq(q1, q2):
#     pi_half = np.pi / 2
#     v = np.abs(np.sum(q1 * q2))
#     v = np.clip(0, 1.0, v)
#     v = np.arccos(v)
#     v *= 2
#     # msk = v > pi_half
#     # v[msk] = np.pi - v[msk]
#     return v
# 
# Path: pyem/geom/quat_numba.py
# @numba.jit(cache=True, nopython=True, nogil=True)
# def qslerp(q1, q2, t, longest=False):
#     cos_half_theta = np.dot(q1, q2)
#     if cos_half_theta >= 1.0:
#         return q1.copy()
#     if longest:
#         if cos_half_theta > 0:
#             cos_half_theta = -cos_half_theta
#             q1 = -q1
#     elif cos_half_theta < 0:
#         cos_half_theta = -cos_half_theta
#         q1 = -q1
#     half_theta = np.arccos(cos_half_theta)
#     sin_half_theta = np.sqrt(1 - cos_half_theta * cos_half_theta)
#     if np.abs(sin_half_theta) < 1E-12:
#         return (q1 + q2) / 2
#     a = np.sin((1 - t) * half_theta)
#     b = np.sin(t * half_theta)
#     return (q1 * a + q2 * b) / sin_half_theta
# 
# Path: pyem/geom/quat_numba.py
# @numba.guvectorize(["void(float64[:], float64[:], float64[:])"],
#                    "(m),(m)->(m)", nopython=True, cache=False)
# def qtimes(q1, q2, q3):
#     _qtimes(q1, q2, q3)
# 
# Path: pyem/geom/quat.py
# def meanq(q, w=None):
#     if w is None:
#         return np.linalg.eigh(np.einsum('ij,ik->...jk', q, q))[1][:, -1]
#     else:
#         return np.linalg.eigh(np.einsum('ij,ik,i->...jk', q, q, w))[1][:, -1]


import numpy as np
from .quat_numba import distq
from .quat_numba import qslerp
from .quat_numba import qtimes
from .quat import meanq
        mu0 = np.mean(arr, axis=0, keepdims=True)
        mu = np.mean(arr)
    else:
        mu0 = np.mean(reference, axis=0, keepdims=True)
        mu = np.mean(reference)
    mu1 = np.mean(arr, axis=1, keepdims=True)
    if inplace:
        arr -= mu0
        arr -= mu1
        arr += mu
    else:
        arr = arr - mu0
        arr -= mu1
        arr += mu
    return arr


def isrotation(r, tol=1e-6):
    """Check for valid rotation matrix"""
    check = np.identity(3, dtype=r.dtype) - np.dot(r.T, r)
    if tol is None:
        return check
    return np.linalg.norm(check) < tol


def qslerp_mult_balanced(keyq, steps_per_deg=10):
    qexp = []
    for i in range(0, len(keyq) - 1):
        ninterp = np.round(np.rad2deg(2 * distq(keyq[i], keyq[i + 1])) * steps_per_deg)
        for t in np.arange(0, 1, 1 / ninterp):

# Path: pyem/geom/quat_numba.py
# @numba.jit(cache=False, nopython=True, nogil=True)
# def distq(q1, q2):
#     pi_half = np.pi / 2
#     v = np.abs(np.sum(q1 * q2))
#     v = np.clip(0, 1.0, v)
#     v = np.arccos(v)
#     v *= 2
#     # msk = v > pi_half
#     # v[msk] = np.pi - v[msk]
#     return v
# 
# Path: pyem/geom/quat_numba.py
# @numba.jit(cache=True, nopython=True, nogil=True)
# def qslerp(q1, q2, t, longest=False):
#     cos_half_theta = np.dot(q1, q2)
#     if cos_half_theta >= 1.0:
#         return q1.copy()
#     if longest:
#         if cos_half_theta > 0:
#             cos_half_theta = -cos_half_theta
#             q1 = -q1
#     elif cos_half_theta < 0:
#         cos_half_theta = -cos_half_theta
#         q1 = -q1
#     half_theta = np.arccos(cos_half_theta)
#     sin_half_theta = np.sqrt(1 - cos_half_theta * cos_half_theta)
#     if np.abs(sin_half_theta) < 1E-12:
#         return (q1 + q2) / 2
#     a = np.sin((1 - t) * half_theta)
#     b = np.sin(t * half_theta)
#     return (q1 * a + q2 * b) / sin_half_theta
# 
# Path: pyem/geom/quat_numba.py
# @numba.guvectorize(["void(float64[:], float64[:], float64[:])"],
#                    "(m),(m)->(m)", nopython=True, cache=False)
# def qtimes(q1, q2, q3):
#     _qtimes(q1, q2, q3)
# 
# Path: pyem/geom/quat.py
# def meanq(q, w=None):
#     if w is None:
#         return np.linalg.eigh(np.einsum('ij,ik->...jk', q, q))[1][:, -1]
#     else:
#         return np.linalg.eigh(np.einsum('ij,ik,i->...jk', q, q, w))[1][:, -1]


# Path: pyem/geom/geom.py
import numpy as np
from .quat_numba import distq
from .quat_numba import qslerp
from .quat_numba import qtimes
from .quat import meanq

        mu0 = np.mean(arr, axis=0, keepdims=True)
        mu = np.mean(arr)
    else:
        mu0 = np.mean(reference, axis=0, keepdims=True)
        mu = np.mean(reference)
    mu1 = np.mean(arr, axis=1, keepdims=True)
    if inplace:
        arr -= mu0
        arr -= mu1
        arr += mu
    else:
        arr = arr - mu0
        arr -= mu1
        arr += mu
    return arr


def isrotation(r, tol=1e-6):
    """Check for valid rotation matrix"""
    check = np.identity(3, dtype=r.dtype) - np.dot(r.T, r)
    if tol is None:
        return check
    return np.linalg.norm(check) < tol


def qslerp_mult_balanced(keyq, steps_per_deg=10):
    qexp = []
    for i in range(0, len(keyq) - 1):
        ninterp = np.round(np.rad2deg(2 * distq(keyq[i], keyq[i + 1])) * steps_per_deg)
        for t in np.arange(0, 1, 1 / ninterp):

            qexp.append(qslerp(keyq[i], keyq[i + 1], t))