text: """ run with: python3 -m sophus.quaternion """
  
  import sophus
  import sympy
  import sys
  import unittest
  
  
  class Quaternion:
      """ Quaternion class """
  
      def __init__(self, real, vec):
          """ Quaternion consists of a real scalar, and an imaginary 3-vector """
          assert isinstance(vec, sophus.Vector3)
          self.real = real
          self.vec = vec
  
      def __mul__(self, right):
          """ quaternion multiplication """
          return Quaternion(self[3] * right[3] - self.vec.dot(right.vec),
                            self[3] * right.vec + right[3] * self.vec +
                            self.vec.cross(right.vec))
  
      def __add__(self, right):
          """ quaternion multiplication """
          return Quaternion(self[3] + right[3], self.vec + right.vec)
  
      def __neg__(self):
          return Quaternion(-self[3], -self.vec)
  
      def __truediv__(self, scalar):
          """ scalar division """
          return Quaternion(self.real / scalar, self.vec / scalar)
  
      def __repr__(self):
          return "( " + repr(self[3]) + " + " + repr(self.vec) + "i )"
  
      def __getitem__(self, key):
          """ We use the following convention [vec0, vec1, vec2, real] """
          assert (key >= 0 and key < 4)
          if key == 3:
              return self.real
          else:
              return self.vec[key]
  
      def squared_norm(self):
          """ squared norm when considering the quaternion as 4-tuple """
          return self.vec.squared_norm() + self.real**2
  
      def conj(self):
          """ quaternion conjugate """
          return Quaternion(self.real, -self.vec)
  
      def inv(self):
          """ quaternion inverse """
          return self.conj() / self.squared_norm()
  
      @staticmethod
      def identity():
          return Quaternion(1, sophus.Vector3(0, 0, 0))
  
      @staticmethod
      def zero():
          return Quaternion(0, sophus.Vector3(0, 0, 0))
  
      def subs(self, x, y):
          return Quaternion(self.real.subs(x, y), self.vec.subs(x, y))
  
      def simplify(self):
          v = sympy.simplify(self.vec)
          return Quaternion(sympy.simplify(self.real),
                            sophus.Vector3(v[0], v[1], v[2]))
  
      def __eq__(self, other):
          if isinstance(self, other.__class__):
              return self.real == other.real and self.vec == other.vec
          return False
  
      @staticmethod
      def Da_a_mul_b(a, b):
          """ derivatice of quaternion muliplication wrt left multiplier a """
          v0 = b.vec[0]
          v1 = b.vec[1]
          v2 = b.vec[2]
          y = b.real
          return sophus.Matrix([[y, v2, -v1, v0],
                                [-v2, y, v0, v1],
                                [v1, -v0, y, v2],
                                [-v0, -v1, -v2, y]])
  
      @staticmethod
      def Db_a_mul_b(a, b):
          """ derivatice of quaternion muliplication wrt right multiplicand b """
          u0 = a.vec[0]
          u1 = a.vec[1]
          u2 = a.vec[2]
          x = a.real
          return sophus.Matrix([[x, -u2, u1, u0],
                                [u2, x, -u0, u1],
                                [-u1, u0, x, u2],
                                [-u0, -u1, -u2, x]])
  
  
  class TestQuaternion(unittest.TestCase):
      def setUp(self):
          x, u0, u1, u2 = sympy.symbols('x u0 u1 u2', real=True)
          y, v0, v1, v2 = sympy.symbols('y v0 v1 v2', real=True)
          u = sophus.Vector3(u0, u1, u2)
          v = sophus.Vector3(v0, v1, v2)
          self.a = Quaternion(x, u)
          self.b = Quaternion(y, v)
  
      def test_muliplications(self):
          product = self.a * self.a.inv()
          self.assertEqual(product.simplify(),
                           Quaternion.identity())
          product = self.a.inv() * self.a
          self.assertEqual(product.simplify(),
                           Quaternion.identity())
  
      def test_derivatives(self):
          d = sophus.Matrix(4, 4, lambda r, c: sympy.diff(
              (self.a * self.b)[r], self.a[c]))
          self.assertEqual(d,
                           Quaternion.Da_a_mul_b(self.a, self.b))
          d = sophus.Matrix(4, 4, lambda r, c: sympy.diff(
              (self.a * self.b)[r], self.b[c]))
          self.assertEqual(d,
                           Quaternion.Db_a_mul_b(self.a, self.b))
  
  
  if __name__ == '__main__':
      unittest.main()
      print('hello')
  