/// Determines the Hermann-Mauguin symbol of the rotation-, rotoinversion- or
/// screw-axis.
std::string SymmetryElementRotationGenerator::determineSymbol(
    const SymmetryOperation &operation) const {
  const Kernel::IntMatrix &matrix = operation.matrix();

  int trace = matrix.Trace();
  int determinant = matrix.determinant();

  if (trace == 0 && determinant == -1) {
    return "-3";
  }

  std::string symbol;

  if (determinant < 0) {
    symbol += "-";
  }

  symbol += boost::lexical_cast<std::string>(operation.order());

  int translation =
      static_cast<int>(static_cast<double>(operation.order()) *
                       Kernel::V3D(determineTranslation(operation)).norm());

  if (translation != 0) {
    symbol += boost::lexical_cast<std::string>(translation);
  }

  return symbol;
}
/// Checks the trace and determinat of the matrix to determine if the matrix
/// belongs to a rotation.
bool SymmetryElementRotationGenerator::canProcess(
    const SymmetryOperation &operation) const {
  const Kernel::IntMatrix &matrix = operation.matrix();
  int determinant = matrix.determinant();
  int trace = matrix.Trace();

  return (abs(trace) != 3) && !(trace == 1 && determinant == -1);
}
/// Generates an instance of SymmetryElementMirror with the corresponding
/// symbol, axis and translation vector.
SymmetryElement_sptr SymmetryElementMirrorGenerator::generateElement(
    const SymmetryOperation &operation) const {
  const Kernel::IntMatrix &matrix = operation.matrix();

  V3R axis = determineAxis(matrix);
  V3R translation = determineTranslation(operation);
  std::string symbol = determineSymbol(operation);

  return boost::make_shared<SymmetryElementMirror>(symbol, axis, translation);
}
/**
 * Transforms the operation using the internally stored SymmetryOperation.
 *
 * This method returns the transformed symmetry operation, using the
 * following relation:
 *
 *  S' = O^-1 * S * O
 *
 * Where O is the internally stored operation.
 *
 * @param operation :: SymmetryOperation to transform.
 * @return Transformed symmetry operation.
 */
SymmetryOperation GroupTransformation::transformOperation(
    const SymmetryOperation &operation) const {
  MatrixVectorPair<double, V3R> op =
      m_inversePair *
      MatrixVectorPair<double, V3R>(convertMatrix<double>(operation.matrix()),
                                    operation.vector()) *
      m_matrixVectorPair;

  return SymmetryOperation(op.getMatrix(), op.getVector());
}
/// Determines the rotation sense according to the description in ITA 11.2.
SymmetryElementRotation::RotationSense
SymmetryElementRotationGenerator::determineRotationSense(
    const SymmetryOperation &operation, const V3R &rotationAxis) const {
  Kernel::V3D pointOnAxis1 = rotationAxis;
  Kernel::V3D pointOnAxis2 = rotationAxis * 2;
  Kernel::V3D pointOffAxis = rotationAxis + Kernel::V3D(2.1, 5.05, -1.1);
  Kernel::V3D generatedPoint = operation * pointOffAxis;

  Kernel::DblMatrix matrix(3, 3, false);
  matrix.setColumn(0, pointOnAxis2 - pointOnAxis1);
  matrix.setColumn(1, pointOffAxis - pointOnAxis1);
  matrix.setColumn(2, generatedPoint - pointOnAxis1);

  double determinant = matrix.determinant() * operation.matrix().determinant();

  if (determinant < 0) {
    return SymmetryElementRotation::Negative;
  } else {
    return SymmetryElementRotation::Positive;
  }
}
/// Checks that the trace of the matrix is 1 and the determinant is -1.
bool SymmetryElementMirrorGenerator::canProcess(
    const SymmetryOperation &operation) const {
  const Kernel::IntMatrix &matrix = operation.matrix();

  return matrix.Trace() == 1 && matrix.determinant() == -1;
}