TEST(FunctionFactoryTest, CreateCompositeFunctionTest)
{
  std::string ini = "CompositeFunction("
    "FunctionFactoryTestFunction(A=10,B=20),"
    "FunctionFactoryTestFunction(A=30,B=40)"
    ")";
  IFunction_sptr fun = FunctionFactory::instance().createFitFunction(ini);
  EXPECT_TRUE( fun.get() != nullptr );
  CompositeFunction_sptr cf = boost::dynamic_pointer_cast<CompositeFunction>(fun);
  EXPECT_TRUE( cf.get() != nullptr );
  EXPECT_EQ( cf->nFunctions(), 2 );
  auto f1 = cf->getFunction(0);
  auto f2 = cf->getFunction(1);
  EXPECT_EQ(f1->getParameter("A"),10);
  EXPECT_EQ(f1->getParameter("B"),20);
  EXPECT_EQ(f2->getParameter("A"),30);
  EXPECT_EQ(f2->getParameter("B"),40);
  EXPECT_EQ(cf->getParameter("f0.A"),10);
  EXPECT_EQ(cf->getParameter("f0.B"),20);
  EXPECT_EQ(cf->getParameter("f1.A"),30);
  EXPECT_EQ(cf->getParameter("f1.B"),40);

  std::cerr << '\n' << fun->asString(true) << '\n' << std::endl;

  IFunction_sptr fun1 = FunctionFactory::instance().createFitFunction(ini);
  EXPECT_TRUE( fun1.get() != nullptr );
  cf = boost::dynamic_pointer_cast<CompositeFunction>(fun1);
  EXPECT_TRUE( cf.get() != nullptr );
  EXPECT_EQ( cf->nFunctions(), 2 );
  EXPECT_EQ(cf->getParameter("f0.A"),10);
  EXPECT_EQ(cf->getParameter("f0.B"),20);
  EXPECT_EQ(cf->getParameter("f1.A"),30);
  EXPECT_EQ(cf->getParameter("f1.B"),40);

}
  /** Set parameter fitting setup (boundary, fix or unfix) to function from Parameter map
   */
  void RefinePowderInstrumentParameters2::setFunctionParameterFitSetups(IFunction_sptr function,
                                                                        map<string, Parameter> params)
  {
    // 1. Prepare
    vector<string> funparamnames = m_positionFunc->getParameterNames();

    // 2. Set up
    std::map<std::string, Parameter>::iterator paramiter;
    for (size_t i = 0; i < funparamnames.size(); ++i)
    {
      string parname = funparamnames[i];
      paramiter = params.find(parname);

      if (paramiter != params.end())
      {
        // Found, set up the parameter
        Parameter& param = paramiter->second;
        if (param.fit)
        {
          // If fit.  Unfix it and set up constraint
          function->unfix(i);

          double lowerbound = param.minvalue;
          double upperbound = param.maxvalue;
          if (lowerbound >= -DBL_MAX*0.1 || upperbound <= DBL_MAX*0.1)
          {
            // If there is a boundary
            BoundaryConstraint *bc = new BoundaryConstraint(function.get(), parname, lowerbound,
                                                            upperbound, false);
            function->addConstraint(bc);
          }
        }
        else
        {
          // If fix.
          function->fix(i);
        }
      }
      else
      {
        // Not found and thus quit
        stringstream errss;
        errss << "Peak profile parameter " << parname << " is not found in input parameters. ";
        g_log.error(errss.str());
        throw runtime_error(errss.str());
      }
    } // ENDFOR parameter name

    g_log.notice() << "Fit function:\n" << function->asString() << "\n";

    return;
  }
  /**
   * Start fitting process.
   */
  void MuonSequentialFitDialog::startFit()
  {
    if ( m_state != Stopped )
      throw std::runtime_error("Couln't start: already running");

    setState(Preparing);

    // Explicitly run the file search. This might be needed when Start is clicked straigh after 
    // editing the run box. In that case, lost focus event might not be processed yet and search
    // might not have been started yet. Otherwise, search is not done as the widget sees that it
    // has not been changed. Taken from LoadDialog.cpp:124.
    m_ui.runs->findFiles();

    // Wait for file search to finish.
    while ( m_ui.runs->isSearching() )
    {
      QApplication::processEvents();
    }

    // To process events from the finished thread
    QApplication::processEvents();

    // Validate input fields
    if ( ! isInputValid() )
    {
      QMessageBox::critical(this, "Input is not valid", 
        "One or more input fields are invalid.\n\nInvalid fields are marked with a '*'.");
      setState(Stopped);
      return;
    }

    QStringList runFilenames = m_ui.runs->getFilenames();

    const std::string label = m_ui.labelInput->text().toStdString();
    const std::string labelGroupName = SEQUENTIAL_PREFIX + label;

    AnalysisDataServiceImpl& ads = AnalysisDataService::Instance();

    if ( ads.doesExist(labelGroupName) )
    {
      QMessageBox::StandardButton answer = QMessageBox::question(this, "Label already exists", 
          "Label you specified was used for one of the previous fits. Do you want to overwrite it?", 
          QMessageBox::Yes | QMessageBox::Cancel);

      if ( answer != QMessageBox::Yes )
      {
        setState(Stopped);
        return;
      }

      ads.deepRemoveGroup(labelGroupName);
    }
   
    // Create a group for label
    ads.add(labelGroupName, boost::make_shared<WorkspaceGroup>());

    // Tell progress bar how many iterations we will need to make and reset it
    m_ui.progress->setRange( 0, runFilenames.size() );
    m_ui.progress->setFormat("%p%");
    m_ui.progress->setValue(0);

    // Clear diagnosis table for new fit
    m_ui.diagnosisTable->setRowCount(0);

    // Get fit function as specified by user in the fit browser
    IFunction_sptr fitFunction = FunctionFactory::Instance().createInitialized(
        m_fitPropBrowser->getFittingFunction()->asString() );

    // Whether we should use initial function for every fit
    bool useInitFitFunction = (m_ui.paramTypeGroup->checkedButton() == m_ui.paramTypeInitial);

    setState(Running);
    m_stopRequested = false;

    for ( auto fileIt = runFilenames.constBegin(); fileIt != runFilenames.constEnd(); ++fileIt )
    {
      // Process events (so that Stop button press is processed)
      QApplication::processEvents();

      // Stop if requested by user
      if ( m_stopRequested )
        break;

      MatrixWorkspace_sptr ws;

      try {
        auto load = AlgorithmManager::Instance().create("MuonLoad");
        load->initialize();
        load->setChild(true);
        load->setRethrows(true);
        load->updatePropertyValues(*m_loadAlg);
        load->setPropertyValue("Filename", fileIt->toStdString());
        load->setPropertyValue("OutputWorkspace", "__YouDontSeeMeIAmNinja");
        if (m_fitPropBrowser->rawData()) // TODO: or vice verca?
          load->setPropertyValue("RebinParams", "");

        load->execute();

        ws = load->getProperty("OutputWorkspace");
      } catch (...) {
        QMessageBox::critical(
            this, "Loading failed",
            "Unable to load one of the files.\n\nCheck log for details");
        break;
      }

      const std::string runTitle = getRunTitle(ws);
      const std::string wsBaseName = labelGroupName + "_" + runTitle; 

      IFunction_sptr functionToFit;

      if ( useInitFitFunction )
        // Create a copy so that the original function is not changed
        functionToFit = FunctionFactory::Instance().createInitialized( fitFunction->asString() );
      else
        // Use the same function over and over, so that previous fitted params are used for the next fit
        functionToFit = fitFunction;

      IAlgorithm_sptr fit = AlgorithmManager::Instance().create("Fit");
      fit->setRethrows(true);

      try 
      {

        // Set function. Gets updated when fit is done. 
        fit->setProperty("Function", functionToFit);

        fit->setProperty("InputWorkspace", ws);
        fit->setProperty("Output", wsBaseName);

        // We should have one spectra only in the workspace, so use the first one.
        fit->setProperty("WorkspaceIndex", 0);

        // Various properties from the fit prop. browser
        fit->setProperty("StartX", m_fitPropBrowser->startX());
        fit->setProperty("EndX", m_fitPropBrowser->endX());
        fit->setProperty("Minimizer", m_fitPropBrowser->minimizer());
        fit->setProperty("CostFunction", m_fitPropBrowser->costFunction());

        fit->execute();
      }
      catch(...)
      {
        QMessageBox::critical(this, "Fitting failed", 
            "Unable to fit one of the files.\n\nCheck log for details");
        break;
      }

      // Make sure created fit workspaces end-up in the group
      // TODO: this really should use loop
      ads.addToGroup(labelGroupName, wsBaseName + "_NormalisedCovarianceMatrix");
      ads.addToGroup(labelGroupName, wsBaseName + "_Parameters");
      ads.addToGroup(labelGroupName, wsBaseName + "_Workspace");

      // Copy log values
      auto fitWs = ads.retrieveWS<MatrixWorkspace>(wsBaseName + "_Workspace");
      fitWs->copyExperimentInfoFrom(ws.get());

      // Add information about the fit to the diagnosis table
      addDiagnosisEntry(runTitle, fit->getProperty("OutputChi2OverDof"), functionToFit); 

      // Update progress
      m_ui.progress->setFormat("%p% - " + QString::fromStdString(runTitle) );
      m_ui.progress->setValue( m_ui.progress->value() + 1 );
    }

    setState(Stopped);
  }