//                                               -*- C++ -*-
/**
 *  @file  JacobiFactory.cxx
 *  @brief Jacobi polynomial factory
 *
 *  (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 schueller
 *  @date   2008-05-21 11:21:38 +0200 (Wed, 21 May 2008)
 */
#include "JacobiFactory.hxx"
#include "PersistentObjectFactory.hxx"
#include "Beta.hxx"
#include "Exception.hxx"

BEGIN_NAMESPACE_OPENTURNS



CLASSNAMEINIT(JacobiFactory);

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



/* Default constructor: (1, 1) order Jacobi polynomial associated with the default Beta() = Beta(2, 4, -1, 1) distribution which is equal to the Epanechnikov distribution */
JacobiFactory::JacobiFactory()
  : OrthogonalUniVariatePolynomialFactory(Beta()),
    alpha_(1.0),
    beta_(1.0)
{
  initializeCache();
}


/* Parameter constructor: (alpha, beta) is the order of the Jacobi polynomial, associated with the Beta(beta + 1, alpha + beta + 2, -1, 1) distribution in the ANALYSIS parameter set or to the Beta(alpha, beta, -1, 1) distribution in the PROBABILITY parameter set */
JacobiFactory::JacobiFactory(const NumericalScalar alpha,
                             const NumericalScalar beta,
                             const ParameterSet parameterization)
  : OrthogonalUniVariatePolynomialFactory((parameterization == ANALYSIS ? Beta(beta + 1.0, alpha + beta + 2.0, -1.0, 1.0) : Beta(alpha, beta, -1.0, 1.0))),
    alpha_(alpha),
    beta_(beta)
{
  initializeCache();
}

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


/* Calculate the coefficients of recurrence a0n, a1n, a2n such that
   Pn+1(x) = (a0n * x + a1n) * Pn(x) + a2n * Pn-1(x) */
JacobiFactory::Coefficients JacobiFactory::getRecurrenceCoefficients(const UnsignedLong n) const
{
  Coefficients recurrenceCoefficients(3, 0.0);
  if (n == 0)
  {
    const NumericalScalar factor(0.5 * sqrt((alpha_ + beta_ + 3.0) / ((alpha_ + 1.0) * (beta_ + 1.0))));
    recurrenceCoefficients[0] = (alpha_ + beta_ + 2.0) * factor;
    recurrenceCoefficients[1] = (alpha_ - beta_) * factor;
    // Conventional value of 0.0 for recurrenceCoefficients[2]
    return recurrenceCoefficients;
  }
  const NumericalScalar nAlphaP1(n + alpha_ + 1);
  const NumericalScalar nBetaP1(n + beta_ + 1);
  const NumericalScalar twoNAlphaBetaP2(nAlphaP1 + nBetaP1);
  const NumericalScalar factor1((twoNAlphaBetaP2 + 1) / ((n + 1) * nAlphaP1 * nBetaP1 * (nAlphaP1 + beta_)));
  const NumericalScalar factor2(0.5 * sqrt((twoNAlphaBetaP2 - 1) * factor1));
  recurrenceCoefficients[0] = factor2 * twoNAlphaBetaP2;
  recurrenceCoefficients[1] = factor2 * (alpha_ - beta_) * (alpha_ + beta_) / (twoNAlphaBetaP2 - 2);
  recurrenceCoefficients[2] = -twoNAlphaBetaP2 / (twoNAlphaBetaP2 - 2) * sqrt((nAlphaP1 - 1) * (nBetaP1 - 1) * (nAlphaP1 + beta_ - 1) * n * factor1 / (twoNAlphaBetaP2 - 3));
  return recurrenceCoefficients;
}

/* Alpha accessor */
NumericalScalar JacobiFactory::getAlpha() const
{
  return alpha_;
}

/* Beta accessor */
NumericalScalar JacobiFactory::getBeta() const
{
  return beta_;
}

/* String converter */
String JacobiFactory::__repr__() const
{
  return OSS() << "class=" << getClassName()
         << " alpha=" << alpha_
         << " beta=" << beta_
         << " measure=" << measure_;
}


/* Method save() stores the object through the StorageManager */
void JacobiFactory::save(Advocate & adv) const
{
  OrthogonalUniVariatePolynomialFactory::save(adv);
  adv.saveAttribute( "alpha_", alpha_ );
  adv.saveAttribute( "beta_", beta_ );
}


/* Method load() reloads the object from the StorageManager */
void JacobiFactory::load(Advocate & adv)
{
  OrthogonalUniVariatePolynomialFactory::load(adv);
  adv.loadAttribute( "alpha_", alpha_ );
  adv.loadAttribute( "beta_", beta_ );
}

END_NAMESPACE_OPENTURNS
