/// Derivatives of function with respect to active parameters
void MultiDomainFunction::functionDeriv(const FunctionDomain &domain,
                                        Jacobian &jacobian) {
  // works only on CompositeDomain
  if (!dynamic_cast<const CompositeDomain *>(&domain)) {
    throw std::invalid_argument(
        "Non-CompositeDomain passed to MultiDomainFunction.");
  }
  const CompositeDomain &cd = dynamic_cast<const CompositeDomain &>(domain);
  // domain must not have less parts than m_maxIndex
  if (cd.getNParts() < m_maxIndex) {
    throw std::invalid_argument(
        "CompositeDomain has too few parts (" +
        boost::lexical_cast<std::string>(cd.getNParts()) +
        ") for MultiDomainFunction (max index " +
        boost::lexical_cast<std::string>(m_maxIndex) + ").");
  }

  countValueOffsets(cd);
  // evaluate member functions derivatives
  for (size_t iFun = 0; iFun < nFunctions(); ++iFun) {
    // find the domains member function must be applied to
    std::vector<size_t> domains;
    getDomainIndices(iFun, cd.getNParts(), domains);

    for (auto i = domains.begin(); i != domains.end(); ++i) {
      const FunctionDomain &d = cd.getDomain(*i);
      PartialJacobian J(&jacobian, m_valueOffsets[*i], paramOffset(iFun));
      getFunction(iFun)->functionDeriv(d, J);
    }
  }
}
/**
 * Initialize the function with the workspace(s).
 * @param function :: A function to initialize.
 */
void MultiDomainCreator::initFunction(API::IFunction_sptr function) {
  auto mdFunction =
      boost::dynamic_pointer_cast<API::MultiDomainFunction>(function);
  if (mdFunction) {
    // loop over member functions and init them
    for (size_t iFun = 0; iFun < mdFunction->nFunctions(); ++iFun) {
      std::vector<size_t> domainIndices;
      // get domain indices for this function
      mdFunction->getDomainIndices(iFun, m_creators.size(), domainIndices);
      if (!domainIndices.empty()) {
        /*
        if ( domainIndices.size() != 1 )
        {
          std::stringstream msg;
          msg << "Function #" << iFun << " applies to multiple domains.\n"
              << "Only one of the domains is used to set workspace.";
          g_log.warning(msg.str());
        }
        */
        size_t index = domainIndices[0];
        if (index >= m_creators.size()) {
          std::stringstream msg;
          msg << "Domain index is out of range. (Function #" << iFun << ")";
          throw std::runtime_error(msg.str());
        }
        m_creators[index]->initFunction(mdFunction->getFunction(iFun));
      } else {
        g_log.warning() << "Function #" << iFun
                        << " doesn't apply to any domain" << std::endl;
      }
    }
  } else {
    IDomainCreator::initFunction(function);
  }
}
/**
 * Split this function into independent functions. The number of functions in
 * the
 * returned vector must be equal to the number
 * of domains. The result of evaluation of the i-th function on the i-th domain
 * must be
 * the same as if this MultiDomainFunction was evaluated.
 */
std::vector<IFunction_sptr>
MultiDomainFunction::createEquivalentFunctions() const {
  size_t nDomains = m_maxIndex + 1;
  std::vector<CompositeFunction_sptr> compositeFunctions(nDomains);
  for (size_t iFun = 0; iFun < nFunctions(); ++iFun) {
    // find the domains member function must be applied to
    std::vector<size_t> domains;
    getDomainIndices(iFun, nDomains, domains);

    for (auto i = domains.begin(); i != domains.end(); ++i) {
      size_t j = *i;
      CompositeFunction_sptr cf = compositeFunctions[j];
      if (!cf) {
        // create a composite function for each domain
        cf = CompositeFunction_sptr(new CompositeFunction());
        compositeFunctions[j] = cf;
      }
      // add copies of all functions applied to j-th domain to a single
      // compositefunction
      cf->addFunction(FunctionFactory::Instance().createInitialized(
          getFunction(iFun)->asString()));
    }
  }
  std::vector<IFunction_sptr> outFunctions(nDomains);
  // fill in the output vector
  // check functions containing a single member and take it out of the composite
  for (size_t i = 0; i < compositeFunctions.size(); ++i) {
    auto fun = compositeFunctions[i];
    if (!fun || fun->nFunctions() == 0) {
      throw std::runtime_error("There is no function for domain " +
                               boost::lexical_cast<std::string>(i));
    }
    if (fun->nFunctions() > 1) {
      outFunctions[i] = fun;
    } else {
      outFunctions[i] = fun->getFunction(0);
    }
  }
  return outFunctions;
}
/// Function you want to fit to.
/// @param domain :: The buffer for writing the calculated values. Must be big
/// enough to accept dataSize() values
void MultiDomainFunction::function(const FunctionDomain &domain,
                                   FunctionValues &values) const {
  // works only on CompositeDomain
  if (!dynamic_cast<const CompositeDomain *>(&domain)) {
    throw std::invalid_argument(
        "Non-CompositeDomain passed to MultiDomainFunction.");
  }
  const CompositeDomain &cd = dynamic_cast<const CompositeDomain &>(domain);
  // domain must not have less parts than m_maxIndex
  if (cd.getNParts() <= m_maxIndex) {
    throw std::invalid_argument(
        "CompositeDomain has too few parts (" +
        boost::lexical_cast<std::string>(cd.getNParts()) +
        ") for MultiDomainFunction (max index " +
        boost::lexical_cast<std::string>(m_maxIndex) + ").");
  }
  // domain and values must be consistent
  if (cd.size() != values.size()) {
    throw std::invalid_argument(
        "MultiDomainFunction: domain and values have different sizes.");
  }

  countValueOffsets(cd);
  // evaluate member functions
  values.zeroCalculated();
  for (size_t iFun = 0; iFun < nFunctions(); ++iFun) {
    // find the domains member function must be applied to
    std::vector<size_t> domains;
    getDomainIndices(iFun, cd.getNParts(), domains);

    for (auto i = domains.begin(); i != domains.end(); ++i) {
      const FunctionDomain &d = cd.getDomain(*i);
      FunctionValues tmp(d);
      getFunction(iFun)->function(d, tmp);
      values.addToCalculated(m_valueOffsets[*i], tmp);
    }
  }
}