/**
 * @file RegRandomForests.h
 * @brief implementation of random set forest for regression
 * @author Sven Sickert
 * @date 06/19/2013

*/
#ifndef REGRANDOMFORESTSINCLUDE
#define REGRANDOMFORESTSINCLUDE

#include <vector>

#include "core/vector/VectorT.h"
#include "core/vector/MatrixT.h"

#include "vislearning/regression/regressionbase/RegressionAlgorithm.h"

#include "vislearning/regression/randomforest/RegressionTree.h"
#include "vislearning/regression/randomforest/RegressionTreeBuilder.h"


namespace OBJREC
{
  
/** implementation of random set forests for regression */
class RegRandomForests : public RegressionAlgorithm
{
  protected:
     /** vector containing all decision trees for regression */
    std::vector<RegressionTree *> forest;

    /** number of trees which will be generated during training */
    int number_of_trees;

    /** fraction of features used for each tree */
    double features_per_tree;

    /** fraction of training examples used for each tree */
    double samples_per_tree;

    /** if >0 then prune the trees using pruneTreeLeastSquares */
    double minimum_error_reduction;

    /** stored config to initialize a tree */
    const NICE::Config *conf;

    /** config section containing important config values */
    std::string confsection;

    /** pointer to the tree builder method */
    RegressionTreeBuilder *builder;

    /** calculate out-of-bag statistics or not */
    bool enableOutOfBagEstimates;
    
    /** out-of-bag statistics */
    std::vector<std::pair<double, double> > oobResults;

    /** predict using only a subset of all trees */
    double predict ( const NICE::Vector & x,
          const std::vector<int> & outofbagtrees );

    /** calculate out-of-bag statistics */
    void calcOutOfBagEstimates ( std::vector< std::vector<int> > & outofbagtrees,
          NICE::VVector x,
          NICE::Vector y );

    /** save example selection per tree */
    std::vector<std::vector<int> > exselection;
    
  public:
    
    /** initialize the regression method */
    RegRandomForests ( const NICE::Config *conf,
          std::string section );
    
    /** do nothing */
    RegRandomForests ();
    
    /** simple destructor */
    virtual ~RegRandomForests();
    
    /** learn parameters/models/whatever using a set of vectors and
     *  their corresponding function values
     */
    void teach ( const NICE::VVector & x, const NICE::Vector & y );
    
    /** main prediction function */
    double predict ( const NICE::Vector & x );

    /** get all leaf nodes for a given value (or inner nodes if depth is set to the level) */
    void getLeafNodes ( NICE::Vector x,
          std::vector<RegressionNode *> & leafNodes,
          int depth = 100000 );
    
    /** get all leaf nodes (or inner nodes if depth is set to the level) */
    void getAllLeafNodes ( std::vector<RegressionNode *> & leafNodes );

    /** enumerate all nodes within the trees */
    void indexDescendants ( std::map<RegressionNode *, std::pair<long, int> > & index ) const;

    /** reset all counters in all nodes contained in the forest */
    void resetCounters ();
    
    /** clone function */
    virtual RegRandomForests *clone ( void ) const
    {
      fthrow ( NICE::Exception, "clone() not yet implemented!\n" );
    }
    
    /** get out of bag estimates */
    std::vector<std::pair<double, double> > & getOutOfBagResults ()
    {
      return oobResults;
    };
    
    /** set the number of trees */
    void setComplexity ( int size );
    
    /** IO functions */
    void restore ( std::istream & is, int format = 0 );
    void store ( std::ostream & os, int format = 0 ) const;
    void clear ();
};
  
} // namespace

#endif