inline void
  pcl::PolynomialCalculationsT<real>::solveCubicEquation (real a, real b, real c, real d, std::vector<real>& roots) const
{
  //cout << "Trying to solve "<<a<<"x^3 + "<<b<<"x^2 + "<<c<<"x + "<<d<<" = 0\n";

  if (isNearlyZero (a))
  {
    //cout << "Highest order element is 0 => Calling solveQuadraticEquation.\n";
    solveQuadraticEquation (b, c, d, roots);
    return;
  }

  if (isNearlyZero (d))
  {
    roots.push_back (0.0);
    //cout << "Constant element is 0 => Adding root 0 and calling solveQuadraticEquation.\n";
    std::vector<real> tmpRoots;
    solveQuadraticEquation (a, b, c, tmpRoots);
    for (unsigned int i=0; i<tmpRoots.size (); i++)
      if (!isNearlyZero (tmpRoots[i]))
        roots.push_back (tmpRoots[i]);
    return;
  }

  double a2 = a*a,
         a3 = a2*a,
         b2 = b*b,
         b3 = b2*b,
         alpha = ( (3.0*a*c-b2)/ (3.0*a2)),
         beta  = (2*b3/ (27.0*a3)) - ( (b*c)/ (3.0*a2)) + (d/a),
         alpha2 = alpha*alpha,
         alpha3 = alpha2*alpha,
         beta2 = beta*beta;
  
  // Value for resubstitution:
  double resubValue = b/ (3*a);

  //cout << "Trying to solve y^3 + "<<alpha<<"y + "<<beta<<"\n";

  double discriminant = (alpha3/27.0) + 0.25*beta2;

  //cout << "Discriminant is "<<discriminant<<"\n";

  if (isNearlyZero (discriminant))
  {
    if (!isNearlyZero (alpha) || !isNearlyZero (beta))
    {
      roots.push_back ( (-3.0*beta)/ (2.0*alpha) - resubValue);
      roots.push_back ( (3.0*beta)/alpha - resubValue);
    }
    else
    {
      roots.push_back (-resubValue);
    }
  }
  else if (discriminant > 0)
  {
    double sqrtDiscriminant = sqrt (discriminant);
    double d1 = -0.5*beta + sqrtDiscriminant,
           d2 = -0.5*beta - sqrtDiscriminant;
    if (d1 < 0)
      d1 = -pow (-d1, 1.0/3.0);
    else
      d1 = pow (d1, 1.0/3.0);

    if (d2 < 0)
      d2 = -pow (-d2, 1.0/3.0);
    else
      d2 = pow (d2, 1.0/3.0);

    //cout << PVAR (d1)<<", "<<PVAR (d2)<<"\n";
    roots.push_back (d1 + d2 - resubValue);
  }
  else
  {
    double tmp1 = sqrt (- (4.0/3.0)*alpha),
           tmp2 = acos (-sqrt (-27.0/alpha3)*0.5*beta)/3.0;
    roots.push_back (tmp1*cos (tmp2) - resubValue);
    roots.push_back (-tmp1*cos (tmp2 + M_PI/3.0) - resubValue);
    roots.push_back (-tmp1*cos (tmp2 - M_PI/3.0) - resubValue);
  }
 
#if 0
  cout << __PRETTY_FUNCTION__ << ": Found "<<roots.size ()<<" roots.\n";
  for (unsigned int i=0; i<roots.size (); i++)
  {
    real x=roots[i], x2=x*x, x3=x2*x;
    real result = a*x3 + b*x2 + c*x + d;
    if (fabs (result) > 1e-4)
    {
      cout << "Something went wrong:\n";
      //roots.clear ();
    }
    cout << "Root "<<i<<" = "<<roots[i]<<". ("<<a<<"x^3 + "<<b<<"x^2 + "<<c<<"x + "<<d<<" = "<<result<<")\n";
  }
  cout << "\n\n";
#endif
}
inline void
  pcl::PolynomialCalculationsT<real>::solveQuarticEquation (real a, real b, real c, real d, real e,
                                                            std::vector<real>& roots) const
{
  //cout << "Trying to solve "<<a<<"x^4 + "<<b<<"x^3 + "<<c<<"x^2 + "<<d<<"x + "<<e<<" = 0\n";

  if (isNearlyZero (a))
  {
    //cout << "Highest order element is 0 => Calling solveCubicEquation.\n";
    solveCubicEquation (b, c, d, e, roots);
    return;
  } 

  if (isNearlyZero (e))
  {
    roots.push_back (0.0);
    //cout << "Constant element is 0 => Adding root 0 and calling solveCubicEquation.\n";
    std::vector<real> tmpRoots;
    solveCubicEquation (a, b, c, d, tmpRoots);
    for (unsigned int i=0; i<tmpRoots.size (); i++)
      if (!isNearlyZero (tmpRoots[i]))
        roots.push_back (tmpRoots[i]);
    return;
  } 

  double root1, root2, root3, root4,
         a2 = a*a,
         a3 = a2*a,
         a4 = a2*a2,
         b2 = b*b,
         b3 = b2*b,
         b4 = b2*b2,
         alpha = ( (-3.0*b2)/ (8.0*a2)) + (c/a),
         beta  = (b3/ (8.0*a3)) - ( (b*c)/ (2.0*a2)) + (d/a),
         gamma = ( (-3.0*b4)/ (256.0*a4)) + ( (c*b2)/ (16.0*a3)) - ( (b*d)/ (4.0*a2)) + (e/a),
         alpha2 = alpha*alpha;
  
  // Value for resubstitution:
  double resubValue = b/ (4*a);

  //cout << "Trying to solve y^4 + "<<alpha<<"y^2 + "<<beta<<"y + "<<gamma<<"\n";
  
  if (isNearlyZero (beta))
  {  // y^4 + alpha*y^2 + gamma\n";
    //cout << "Using beta=0 condition\n";
    std::vector<real> tmpRoots;
    solveQuadraticEquation (1.0, alpha, gamma, tmpRoots);
    for (unsigned int i=0; i<tmpRoots.size (); i++)
    {
      double qudraticRoot = tmpRoots[i];
      if (sqrtIsNearlyZero (qudraticRoot))
      {
        roots.push_back (-resubValue);
      }
      else if (qudraticRoot > 0.0)
      {
        root1 = sqrt (qudraticRoot);
        roots.push_back (root1 - resubValue);
        roots.push_back (-root1 - resubValue);
      }
    }
  }
  else
  {
    //cout << "beta != 0\n";
    double alpha3 = alpha2*alpha,
           beta2 = beta*beta,
           p = (-alpha2/12.0)-gamma,
           q = (-alpha3/108.0)+ ( (alpha*gamma)/3.0)- (beta2/8.0),
           q2 = q*q,
           p3 = p*p*p,
           u = (0.5*q) + sqrt ( (0.25*q2)+ (p3/27.0));
    if (u > 0.0)
      u = pow (u, 1.0/3.0);
    else if (isNearlyZero (u))
      u = 0.0;
    else
      u = -pow (-u, 1.0/3.0);

    double y = (-5.0/6.0)*alpha - u;
    if (!isNearlyZero (u))
      y += p/ (3.0*u);

    double w = alpha + 2.0*y;
    
    if (w > 0)
    {
      w = sqrt (w);
    }
    else if (isNearlyZero (w))
    {
      w = 0;
    }
    else
    {
      //cout << "Found no roots\n";
      return;
    }

    double tmp1 = - (3.0*alpha + 2.0*y + 2.0* (beta/w)),
           tmp2 = - (3.0*alpha + 2.0*y - 2.0* (beta/w));
    
    if (tmp1 > 0)
    {
      tmp1 = sqrt (tmp1);
      root1 = - (b/ (4.0*a)) + 0.5* (w+tmp1);
      root2 = - (b/ (4.0*a)) + 0.5* (w-tmp1);
      roots.push_back (root1);
      roots.push_back (root2);
    }
    else if (isNearlyZero (tmp1))
    {
      root1 = - (b/ (4.0*a)) + 0.5*w;
      roots.push_back (root1);
    }

   if (tmp2 > 0)
   {
      tmp2 = sqrt (tmp2);
      root3 = - (b/ (4.0*a)) + 0.5* (-w+tmp2);
      root4 = - (b/ (4.0*a)) + 0.5* (-w-tmp2);
      roots.push_back (root3);
      roots.push_back (root4);
    }
    else if (isNearlyZero (tmp2))
    {
      root3 = - (b/ (4.0*a)) - 0.5*w;
      roots.push_back (root3);
    }
   
    //cout << "Test: " << alpha<<", "<<beta<<", "<<gamma<<", "<<p<<", "<<q<<", "<<u <<", "<<y<<", "<<w<<"\n";
  }
  
#if 0
  cout << __PRETTY_FUNCTION__ << ": Found "<<roots.size ()<<" roots.\n";
  for (unsigned int i=0; i<roots.size (); i++)
  {
    real x=roots[i], x2=x*x, x3=x2*x, x4=x2*x2;
    real result = a*x4 + b*x3 + c*x2 + d*x + e;
    if (fabs (result) > 1e-4)
    {
      cout << "Something went wrong:\n";
      //roots.clear ();
    }
    cout << "Root "<<i<<" = "<<roots[i]
         << ". ("<<a<<"x^4 + "<<b<<"x^3 + "<<c<<"x^2 + "<<d<<"x + "<<e<<" = "<<result<<")\n";
  }
  cout << "\n\n";
#endif
}
void
ExtendedHandEyeCalibration::solve(const std::vector<Eigen::Matrix4d, Eigen::aligned_allocator<Eigen::Matrix4d> >& H1,
                                  const std::vector<Eigen::Matrix4d, Eigen::aligned_allocator<Eigen::Matrix4d> >& H2,
                                  Eigen::Matrix4d& H_12,
                                  double& s) const
{
    int motionCount = H1.size();

    Eigen::MatrixXd T(motionCount * 6, 8);
    T.setZero();
    for (int i = 0; i < motionCount; ++i)
    {
        Eigen::AngleAxisd aa1(H1.at(i).block<3,3>(0,0));
        Eigen::Vector3d rvec1 = aa1.angle() * aa1.axis();
        Eigen::Vector3d tvec1 = H1.at(i).block<3,1>(0,3);

        Eigen::AngleAxisd aa2(H2.at(i).block<3,3>(0,0));
        Eigen::Vector3d rvec2 = aa2.angle() * aa2.axis();
        Eigen::Vector3d tvec2 = H2.at(i).block<3,1>(0,3);

        double theta1, d1;
        Eigen::Vector3d l1, m1;
        AngleAxisAndTranslationToScrew(rvec1, tvec1, theta1, d1, l1, m1);

        double theta2, d2;
        Eigen::Vector3d l2, m2;
        AngleAxisAndTranslationToScrew(rvec2, tvec2, theta2, d2, l2, m2);

        Eigen::Vector3d a = l1;
        Eigen::Vector3d a_prime = m1;
        Eigen::Vector3d b = l2;
        Eigen::Vector3d b_prime = m2;

        T.block<3,1>(i * 6, 0) = a - b;
        T.block<3,3>(i * 6, 1) = skew(Eigen::Vector3d(a + b));
        T.block<3,1>(i * 6 + 3, 0) = a_prime - b_prime;
        T.block<3,3>(i * 6 + 3, 1) = skew(Eigen::Vector3d(a_prime + b_prime));
        T.block<3,1>(i * 6 + 3, 4) = a - b;
        T.block<3,3>(i * 6 + 3, 5) = skew(Eigen::Vector3d(a + b));
    }

    Eigen::JacobiSVD<Eigen::MatrixXd> svd(T, Eigen::ComputeFullU | Eigen::ComputeFullV);

    // v7 and v8 span the null space of T, v6 may also be one
    // if rank = 5. 
    Eigen::Matrix<double, 8, 1> v6 = svd.matrixV().block<8,1>(0, 5);
    Eigen::Matrix<double, 8, 1> v7 = svd.matrixV().block<8,1>(0,6);
    Eigen::Matrix<double, 8, 1> v8 = svd.matrixV().block<8,1>(0,7);

    Eigen::Vector4d u1 = v7.block<4,1>(0,0);
    Eigen::Vector4d v1 = v7.block<4,1>(4,0);
    Eigen::Vector4d u2 = v8.block<4,1>(0,0);
    Eigen::Vector4d v2 = v8.block<4,1>(4,0);

    double lambda1 = 0;
    double lambda2 = 0.0;

    if (u1.dot(v1) == 0.0)
    {
        std::swap(u1, u2); 
        std::swap(v1, v2); 
    }
    if (u1.dot(v1) != 0.0)
    {
        double s[2];
        solveQuadraticEquation(u1.dot(v1), u1.dot(v2) + u2.dot(v1), u2.dot(v2), s[0], s[1]);

        // find better solution for s
        double t[2];
        for (int i = 0; i < 2; ++i)
        {
            t[i] = s[i] * s[i] * u1.dot(u1) + 2 * s[i] * u1.dot(u2) + u2.dot(u2);
        }

        int idx;
        if (t[0] > t[1])
        {
            idx = 0;
        }
        else
        {
            idx = 1;
        }

        lambda2 = sqrt(1.0 / t[idx]);
        lambda1 = s[idx] * lambda2;
    }
    else 
    {
        if (u1.norm() == 0.0 && u2.norm() > 0.0) 
        {
            lambda1 = 0.0;
            lambda2 = 1.0 / u2.norm();
        }
        else if (u2.norm() == 0.0 && u1.norm() > 0.0)
        {
            lambda1 = 1.0 / u1.norm();
            lambda2 = 0.0;
        }
    }

    // rotation
    Eigen::Vector4d q_coeffs = lambda1 * u1 + lambda2 * u2;
    Eigen::Vector4d q_prime_coeffs = lambda1 * v1 + lambda2 * v2;

    Eigen::Quaterniond q(q_coeffs(0), q_coeffs(1), q_coeffs(2), q_coeffs(3));
    Eigen::Quaterniond d(q_prime_coeffs(0), q_prime_coeffs(1), q_prime_coeffs(2), q_prime_coeffs(3));

    DualQuaterniond dq(q, d);

    H_12 = dq.toMatrix().inverse();

    s = 1.0;
    refine(H1, H2, H_12, s);
}