//                                               -*- C++ -*-
/**
 *  @file  ExpertMixture.cxx
 *  @brief The class building chaos expansions
 *
 *  (C) Copyright 2005-2012 EDF-EADS-Phimeca
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License.
 *
 *  This library is distributed in the hope that it will be useful
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 *
 *  @author dutka
 *  @date   2008-05-21 11:21:38 +0200 (Wed, 21 May 2008)
 */
#include "ExpertMixture.hxx"
#include "PersistentObjectFactory.hxx"

BEGIN_NAMESPACE_OPENTURNS

CLASSNAMEINIT(ExpertMixture);

static Factory<ExpertMixture> RegisteredFactory("ExpertMixture");


/* Default constructor */
ExpertMixture::ExpertMixture()
  : NumericalMathEvaluationImplementation()
  , experts_()
  , classifier_()
{
  // Nothing to do
}


/* Constructor */
ExpertMixture::ExpertMixture(const Basis & experts,
                             const Classifier & classifier)
  : NumericalMathEvaluationImplementation()
  , experts_(experts)
  , classifier_(classifier)
{
  // Check that the experts and the classifier are compatible
}

/* Virtual constructor */
ExpertMixture * ExpertMixture::clone() const
{
  return new ExpertMixture(*this);
}

/* Comparison operator */
Bool ExpertMixture::operator ==(const ExpertMixture & other) const
{
  if (this == &other) return true;
  return (experts_ == other.experts_) && (classifier_ == other.classifier_);
}

/* String converter */
String ExpertMixture::__repr__() const
{
  return OSS() << "class=" << getClassName()
         << ", experts=" << experts_
         << ", classifier=" << classifier_;
}

String ExpertMixture::__str__(const String & offset) const
{
  return OSS(false) << "experts=" << experts_.__str__(offset)
         << "\nclassifier=" << classifier_.__str__(offset);
}


/* Local experts accessor */
void ExpertMixture::setExperts(const Basis & experts)
{
  if (experts.getDimension() != classifier_.getDimension()) throw InvalidArgumentException(HERE) << "Error: the local experts must have the same input dimension as the classifier dimension. Here, experts input dimension=" << experts.getDimension() << " and classifier dimension=" << classifier_.getDimension();
  experts_ = experts;
}

Basis ExpertMixture::getExperts() const
{
  return experts_;
}

/* Classifier accessor */
void ExpertMixture::setClassifier(const Classifier & classifier)
{
  if (experts_.getDimension() != classifier.getDimension()) throw InvalidArgumentException(HERE) << "Error: the local experts must have the same input dimension as the classifier dimension. Here, experts input dimension=" << experts_.getDimension() << " and classifier dimension=" << classifier.getDimension();
  classifier_ = classifier;
}

Classifier ExpertMixture::getClassifier() const
{
  return classifier_;
}


/* Operator () */
NumericalPoint ExpertMixture::operator() (const NumericalPoint & inP) const
{
  return experts_[classifier_.classify(inP)](inP);
}

/* Get the i-th marginal function */
ExpertMixture::Implementation ExpertMixture::getMarginal(const UnsignedLong i) const
{
  const Indices index(1, i);
  return getMarginal(index);
}

/* Get the function corresponding to indices components */
ExpertMixture::Implementation ExpertMixture::getMarginal(const Indices & indices) const
{
  const UnsignedLong expertsSize(experts_.getSize());
  Basis marginalExperts(expertsSize);
  for (UnsignedLong i = 0; i < expertsSize; ++i) marginalExperts[i] = experts_[i].getMarginal(indices);
  return ExpertMixture(marginalExperts, classifier_).clone();
}


/* Method save() stores the object through the StorageManager */
void ExpertMixture::save(Advocate & adv) const
{
  NumericalMathEvaluationImplementation::save(adv);
  adv.saveAttribute( "experts_", experts_ );
  adv.saveAttribute( "classifier_", classifier_ );
}


/* Method load() reloads the object from the StorageManager */
void ExpertMixture::load(Advocate & adv)
{
  NumericalMathEvaluationImplementation::load(adv);
  adv.loadAttribute( "experts_", experts_ );
  adv.loadAttribute( "classifier_", classifier_ );
}

END_NAMESPACE_OPENTURNS
