/**
 * @file bob/trainer/PCATrainer.h
 * @date Tue Jan 18 17:07:26 2011 +0100
 * @author André Anjos <andre.anjos@idiap.ch>
 * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch>
 *
 * @brief Principal Component Analysis implemented with Singular Value
 * Decomposition or using the Covariance Method. Both are implemented using
 * LAPACK.
 *
 * Copyright (C) 2011-2013 Idiap Research Institute, Martigny, Switzerland
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, version 3 of the License.
 * 
 * This program 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 General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef BOB_TRAINER_PCA_TRAINER_H
#define BOB_TRAINER_PCA_TRAINER_H

#include <blitz/array.h>
#include <bob/machine/LinearMachine.h>

namespace bob { namespace trainer {

  /**
   * @ingroup TRAINER
   * @{
   */

  /**
   * @brief Sets a linear machine to perform the Karhunen-Loève Transform 
   * (KLT) on a given dataset using either Singular Value Decomposition (SVD),
   * the default, or the Covariance Method.
   *
   * References:
   * 1. Eigenfaces for Recognition, Turk & Pentland, Journal of Cognitive
   *    Neuroscience (1991) Volume: 3, Issue: 1, Publisher: MIT Press, 
   *    Pages: 71-86
   * 2. http://en.wikipedia.org/wiki/Singular_value_decomposition
   * 3. http://en.wikipedia.org/wiki/Principal_component_analysis
   */
  class PCATrainer {

    public: //api

      /**
       * @brief Initializes a new PCA trainer using SVD or, as an option, the
       * Covariance Matrix method. The training stage will place the resulting
       * principal components in the linear machine and set it up to extract
       * the variable means automatically. As an option, you may preset the
       * trainer so that the normalization performed by the resulting linear
       * machine also divides the variables by the standard deviation of each
       * variable ensemble.
       */
      PCATrainer(bool use_svd=true);

      /**
       * @brief Copy constructor
       */
      PCATrainer(const PCATrainer& other);

      /**
       * @brief Destructor
       */
      virtual ~PCATrainer();

      /**
       * @brief Assignment operator
       */
      PCATrainer& operator=(const PCATrainer& other);

      /**
       * @brief Equal to
       */
      bool operator==(const PCATrainer& other) const;

      /**
       * @brief Not equal to
       */
      bool operator!=(const PCATrainer& other) const;

      /**
       * @brief Gets the SVD/CovMat flag. <code>true</code> means use SVD
       * method.
       */
      bool getUseSVD () const { return m_use_svd; }

      /**
       * @brief Sets the SVD/CovMat flag. <code>true</code> means use SVD
       * method.
       */
      void setUseSVD (bool value) { m_use_svd = value; }

      /**
       * @brief Similar to
       */
      bool is_similar_to(const PCATrainer& other,
          const double r_epsilon=1e-5, const double a_epsilon=1e-8) const;

      /**
       * @brief Trains the LinearMachine to perform the KLT. The resulting
       * machine will have the eigen-vectors of the covariance matrix arranged
       * by decreasing energy automatically. You don't need to sort the results.
       */
      virtual void train(bob::machine::LinearMachine& machine, 
          const blitz::Array<double,2>& X) const;

      /**
       * @brief Trains the LinearMachine to perform the KLT. The resulting
       * machien will have the eigen-vectors of the covariance matrix arranged
       * by decreasing energy automatically. You don't need to sort the results.
       * Also returns the eigen values of the covariance matrix so you can use
       * that to choose which components to keep.
       */
      virtual void train(bob::machine::LinearMachine& machine,
          blitz::Array<double,1>& eigen_values,
          const blitz::Array<double,2>& X) const;

      /**
       * @brief Calculates the maximum possible rank for the covariance matrix
       * of X, given X.
       *
       * This determines what is the maximum number of non-zero eigen values
       * that can be generated by this trainer. It should be used to setup
       * Machines and input vectors prior to feeding them into this trainer.
       */
      size_t output_size(const blitz::Array<double,2>& X) const;

    private: //representation

      bool m_use_svd; ///< if this trainer should be using SVD or Covariance

  };

  /**
   * @}
   */
}}

#endif /* BOB_TRAINER_PCA_TRAINER_H */
