/** Set goniometer to matrix workspace and get its rotation matrix R (from
 * Q-sample to Q-lab
 * and output 1/R
 * @brief ConvertCWSDExpToMomentum::setupTransferMatrix
 * @param dataws :: matrix workspace containing sample rotation angles
 * @param rotationMatrix :: output as matrix 1/R to convert from Q-lab to
 * Q-sample
 */
void ConvertCWSDExpToMomentum::setupTransferMatrix(
    API::MatrixWorkspace_sptr dataws, Kernel::DblMatrix &rotationMatrix) {
  // Check sample logs
  if (!dataws->run().hasProperty("_omega") ||
      !dataws->run().hasProperty("_chi") || !dataws->run().hasProperty("_phi"))
    throw std::runtime_error(
        "Data workspace does not have sample log _phi, _chi or _omega. "
        "Unable to set goniometer and calcualte roation matrix R.");

  // Call algorithm SetGoniometer
  IAlgorithm_sptr setalg = createChildAlgorithm("SetGoniometer");
  setalg->initialize();
  setalg->setProperty("Workspace", dataws);
  setalg->setProperty("Axis0", "_omega,0,1,0,-1");
  setalg->setProperty("Axis1", "_chi,0,0,1,-1");
  setalg->setProperty("Axis2", "_phi,0,1,0,-1");
  setalg->execute();

  if (setalg->isExecuted()) {
    rotationMatrix = dataws->run().getGoniometer().getR();
    g_log.debug() << "Ratation matrix: " << rotationMatrix.str() << "\n";
    rotationMatrix.Invert();
    g_log.debug() << "Ratation matrix: " << rotationMatrix.str() << "\n";
  } else
    throw std::runtime_error("Unable to set Goniometer.");

  return;
}
/**
 * Fills in a column for each active hermite polynomial, starting at the given
 * index
 * @param cmatrix InOut matrix whose columns should be set to the mass profile
 * for each active hermite polynomial
 * @param start Index of the column to start on
 * @param errors Data errors array
 * @returns The number of columns filled
 */
size_t GramCharlierComptonProfile::fillConstraintMatrix(
    Kernel::DblMatrix &cmatrix, const size_t start,
    const std::vector<double> &errors) const {
  std::vector<double> profile(NFINE_Y, 0.0);
  const size_t nData(ySpace().size());
  std::vector<double> result(nData, 0.0);

  // If the FSE term is fixed by user then it's contribution is convoluted with
  // Voigt and summed with the first column
  // otherwise it gets a column of it's own at the end.
  // Either way it needs to be computed, so do this first

  std::vector<double> fse(NFINE_Y, 0.0);
  std::vector<double> convolvedFSE(nData, 0.0);
  addFSETerm(fse);
  convoluteVoigt(convolvedFSE.data(), nData, fse);

  size_t col(0);
  for (unsigned int i = 0; i < m_hermite.size(); ++i) {
    if (m_hermite[i] == 0)
      continue;

    const unsigned int npoly = 2 * i;
    addMassProfile(profile.data(), npoly);
    convoluteVoigt(result.data(), nData, profile);
    if (i == 0 && m_userFixedFSE) {
      std::transform(result.begin(), result.end(), convolvedFSE.begin(),
                     result.begin(), std::plus<double>());
    }
    std::transform(result.begin(), result.end(), errors.begin(), result.begin(),
                   std::divides<double>());
    cmatrix.setColumn(start + col, result);

    std::fill_n(profile.begin(), NFINE_Y, 0.0);
    std::fill_n(result.begin(), nData, 0.0);
    ++col;
  }

  if (!m_userFixedFSE) // Extra column for He3
  {
    std::transform(convolvedFSE.begin(), convolvedFSE.end(), errors.begin(),
                   convolvedFSE.begin(), std::divides<double>());
    cmatrix.setColumn(start + col, convolvedFSE);
    ++col;
  }
  return col;
}
/**
 * This function takes a square matrix and reduces its dimensionality by
 * one.
 *
 * @param mat : The matrix to strip dimensionality
 */
void vtkDataSetToNonOrthogonalDataSet::stripMatrix(Kernel::DblMatrix &mat) {
  std::size_t dim = mat.Ssize() - 1;
  Kernel::DblMatrix temp(dim, dim);
  for (std::size_t i = 0; i < dim; i++) {
    for (std::size_t j = 0; j < dim; j++) {
      temp[i][j] = mat[i][j];
    }
  }
  mat = temp;
}
/**
 * Fills in a column of the matrix with this mass profile, starting at the given
 * index
 * @param cmatrix InOut matrix whose column should be set to the mass profile
 *                for each active hermite polynomial
 * @param start Index of the column to start on
 * @param errors The data errors
 * @returns The number of columns filled
 */
size_t MultivariateGaussianComptonProfile::fillConstraintMatrix(
    Kernel::DblMatrix &cmatrix, const size_t start,
    const std::vector<double> &errors) const {
  std::vector<double> result(ySpace().size());
  this->massProfile(result.data(), ySpace().size());
  std::transform(result.begin(), result.end(), errors.begin(), result.begin(),
                 std::divides<double>());
  cmatrix.setColumn(start, result);
  return 1;
}
/**
 * This function will create the skew matrix and basis for a non-orthogonal
 * representation.
 *
 * @param ol : The oriented lattice containing B matrix and crystal basis
 *vectors
 * @param w : The tranform requested when MDworkspace was created
 * @param aff : The affine matrix taking care of coordinate transformations
 */
void vtkDataSetToNonOrthogonalDataSet::createSkewInformation(
    Geometry::OrientedLattice &ol, Kernel::DblMatrix &w,
    Kernel::Matrix<coord_t> &aff) {
  // Get the B matrix
  Kernel::DblMatrix bMat = ol.getB();
  // Apply the W tranform matrix
  bMat *= w;
  // Create G*
  Kernel::DblMatrix gStar = bMat.Tprime() * bMat;
  Geometry::UnitCell uc(ol);
  uc.recalculateFromGstar(gStar);
  m_skewMat = uc.getB();
  // Calculate the column normalisation
  std::vector<double> bNorm;
  for (std::size_t i = 0; i < m_skewMat.numCols(); i++) {
    double sum = 0.0;
    for (std::size_t j = 0; j < m_skewMat.numRows(); j++) {
      sum += m_skewMat[j][i] * m_skewMat[j][i];
    }
    bNorm.push_back(std::sqrt(sum));
  }
  // Apply column normalisation to skew matrix
  Kernel::DblMatrix scaleMat(3, 3, true);
  scaleMat[0][0] /= bNorm[0];
  scaleMat[1][1] /= bNorm[1];
  scaleMat[2][2] /= bNorm[2];
  m_skewMat *= scaleMat;

  // Setup basis normalisation array
  // Intel and MSBuild can't handle this
  // m_basisNorm = {ol.astar(), ol.bstar(), ol.cstar()};
  m_basisNorm.push_back(ol.astar());
  m_basisNorm.push_back(ol.bstar());
  m_basisNorm.push_back(ol.cstar());

  // Expand matrix to 4 dimensions if necessary
  if (4 == m_numDims) {
    m_basisNorm.push_back(1.0);
    Kernel::DblMatrix temp(4, 4, true);
    for (std::size_t i = 0; i < 3; i++) {
      for (std::size_t j = 0; j < 3; j++) {
        temp[i][j] = m_skewMat[i][j];
      }
    }
    m_skewMat = temp;
  }

  // Convert affine matrix to similar type as others
  Kernel::DblMatrix affMat(aff.numRows(), aff.numCols());
  for (std::size_t i = 0; i < aff.numRows(); i++) {
    for (std::size_t j = 0; j < aff.numCols(); j++) {
      affMat[i][j] = aff[i][j];
    }
  }
  // Strip affine matrix down to correct dimensions
  this->stripMatrix(affMat);

  // Perform similarity transform to get coordinate orientation correct
  m_skewMat = affMat.Tprime() * (m_skewMat * affMat);
  m_basisNorm = affMat * m_basisNorm;
  if (4 == m_numDims) {
    this->stripMatrix(m_skewMat);
  }

  this->findSkewBasis(m_basisX, m_basisNorm[0]);
  this->findSkewBasis(m_basisY, m_basisNorm[1]);
  this->findSkewBasis(m_basisZ, m_basisNorm[2]);
}