/* Fesslix - Stochastic Analysis
 * Copyright (C) 2010-2016 Wolfgang Betz
 *
 * Fesslix 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, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Fesslix 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 Fesslix.  If not, see <http://www.gnu.org/licenses/>. 
 */

#ifndef fesslix_flxrandomFEM_H
#define fesslix_flxrandomFEM_H


#include "flxfem_elements_kl.h"
#include "flxfem.h"
#include "flxdata.h"


FLXFEM_EXPORT FlxRFBox& RFbox_get();
/**
* @brief Current global index of the random variable to use
* -2: shape function of highest order (in case of random fields) ... random realization otherwise 
* -1: random realization
* 0: mean value
* 1 .. n_RV: Eigenmodes
*/
FLXFEM_EXPORT const int current_RF_mode_get();
FLXFEM_EXPORT void current_RF_mode_set(const int cRFm);


class FLXFEM_EXPORT RBRV_set_noise_RF : public RBRV_set_noise {
  protected:
    tuint RndBox_index;  
    
  public:
    RBRV_set_noise_RF(const bool internal, const tuint sRV, const std::string& name, const bool noID);
    virtual ~RBRV_set_noise_RF() {  }
    
    virtual void set_y(const tdouble* const y_vec);
    virtual void set_x(const tdouble* const x_vec);
    virtual void transform_y2x();
    virtual void transform_x2y() {}
    
    void set_RndBox_index(const tuint id) { RndBox_index = id; }
    const tuint get_RndBox_index() { return RndBox_index; }
};


//======================= Random Fields ============================================

class FlxRFsolveProp;
class FlxRFBox;
class FLXFEM_EXPORT FlxRandomField_base : public FlxBoxBase2 {
  protected:
    static tdouble *LX;
    static tdouble *GX;
    static tdouble *GY;
    static tdouble *GZ;
    static tdouble *GX2;
    static tdouble *GY2;
    static tdouble *GZ2;
    static tdouble *DELTAX;
    static tdouble *DELTAY;
    static tdouble *DELTAZ;
    static tdouble *DELTAP;
    /**
    * @brief true, if the random field has been assembled (solved)
    */
    bool is_assembled;
    /**
    * @brief the dimension of the random field - zero if is_assembled=false
    */
    tuint DIM;
    /**
    * @brief pointer to first random variable of the field in independent standard Normal space
    */
    const tdouble* y_smp;
    /**
    * @brief pointer to set of random variables
    */
    RBRV_set_noise_RF* ts;
    /**
    * @brief Eigenvalues
    */
    flxVec* Eigenvalues;	// stores the square roots of the eigenvalues
    /**
    * @brief index of random field
    */
    tuint RF_index;
    bool automatic_managed;
    /**
    * @brief stores the elements of the random field ... they define the domain !!!
    */
    std::map<flxDomain_base*, flxElement_KL_base*> boxEl;
      std::vector<flxElement_KL_base*> boxEL_vec;
    flxElement_KL_base* Current_Element_KL;
    /**
    * @brief Current Element (has to be updated before 'get_realization' is called)
    */
    static flxDomain_base* Current_Element;
    
    const tdouble get_domain() const;
    /**
    * @brief integrate the error over the domain of the current element
    */
    virtual const tdouble get_domain_error_el(const int errMeasure) const = 0;
    /**
    * @brief plot the random field within the current element
    */
    virtual void plot_el(std::ostream &sout, FlxMtxConstFun* ppdim, const int errMeasure, const int prec, const int fixW, const bool checkTOL) const = 0;
    /**
    * @brief plot the covariance function of the randomfield within the current element
    */
    virtual void plotcov_el(std::ostream &sout, const flxPoint& pp, FlxMtxConstFun* ppdim, const flxVec& tr, const int prec, const int fixW) const = 0;
    /**
    * @brief create a set of DIM independent standard Normal random variables with name rf_INDEX
    */
    void create_RBRVs();
    /**
    * @brief ... will be called by 'FlxRFBox'
    */
    static void set_Current_Element(flxDomain_base* El) { Current_Element = El; }
  public:
    FlxRandomField_base(tuint RF_index) : is_assembled(false), DIM(0), y_smp(NULL), ts(NULL), Eigenvalues(NULL), RF_index(RF_index), automatic_managed(false), Current_Element_KL(NULL) {};
    virtual ~FlxRandomField_base();
    const bool is_automatic() { return automatic_managed; }
    const bool is_Assembled() { return is_assembled; }
    void set_automatic();
    /**
    * @brief assign an element to the random field
    * @param pel the FEM-parent element
    * @param KLelProp element properties to assign
    * @param report true: log element info
    * @param rfindex if report=true - index of the random field
    */
    void set_element(flxDomain_base* pel, flxElement_KL_base* klb, const bool report=false, const tuint rfindex=0, const std::string& elID=0);
    /**
    * @brief get the current realization of the random field (based on current_element and global variables)
    */
    virtual const tdouble get_realization() const = 0;
    /**
    * @brief get the mean error
    * @param errMeasure	1: error variance; 2: covariance error
    */
    const tdouble get_domain_error(const int errMeasure);
    
    /**
    * @brief returns a string describing the RF
    */
    virtual void print_para(std::ostream &sout) const = 0;
    /**
    * @brief assembles the random field
    * @return number of DOFs in the system
    */
    virtual const tnlong solve(FlxRFsolveProp* RFsolveProp,std::ostream& sout) = 0;
    /**
    * @brief plots the random field
    */
    void plot(std::ostream &sout, FlxMtxConstFun* ppdim, const int errMeasure, const int prec, const int fixW, const bool checkTOL);
    /**
    * @brief plots the covariance function of the random field
    */
    void plotcov(std::ostream &sout, const flxPoint& pp, FlxMtxConstFun* ppdim, const int prec, const int fixW);    
    
    /**
    * @brief returns a function describing the mean - memory has to be deallocated !!!
    */
    virtual FlxFunction* get_mean_fun();
    /**
    * @brief calculates a term of the linear expansion - without the random variable
    * @param i the index 
    *		-2: shape function with highest order
    * 		-1: not allowed here
    * 		0: mean value
    * 		>1: mode -> if globalIndex=true: global mode; otherwise  local mode
    * @note uses global local coordinates (LX,LY,LZ)
    */
    virtual const tdouble calc_Hi(int i, bool globalIndex);
    /**
    * @brief returns the ith eigenvalue (counting starts with 0)
    */
    virtual const tdouble get_Eigenvalue(const int i, bool errSerious=true) = 0;
    
    const tuint get_DIM() { return DIM; }
    /**
    * @brief set the current KL-element
    */
    void set_Current_Element_KL();
    
    void assignElementIDs(flxVec_ptr& vptr, const flxDomain_base::geoel_type typeEL, flxFEM* femptr);

    virtual flxElement_KL_base::rftype get_Type() const = 0;
    static FlxData* data;
    static void set_Const();
    
    friend class FlxRFBox;
};


class FLXFEM_EXPORT FlxRandomField_KL_FEM_Gauss : public FlxRandomField_base {
  protected:
    /**
    * @brief mean value
    */
    FlxFunction* mu;
    /**
    * @brief standard deviation
    */
    FlxFunction* sD;
    /**
    * @brief correlation
    */
    FlxFunction* rho;
    /**
    * @brief Eigenvectors (DIM*Ndof)
    */
    std::vector<flxVec> Eigenvectors;
    /**
    * @brief Number of DOFs of the problem
    */
    tnlong Ndof;
    /**
    * @brief specifies the type used for solving the eigenvalue problem
    */
    const int ev_solver;
    /**
    * @brief stores topological information - only required for projection methods!!!
    */
    niMap nodalM;
    eiMap edgeM;
    fiMap faceM;

    virtual const tdouble get_domain_error_el(const int errMeasure) const;
    virtual void plot_el(std::ostream &sout, FlxMtxConstFun* ppdim, const int errMeasure, const int prec, const int fixW, const bool checkTOL) const;
    virtual void plotcov_el(std::ostream &sout, const flxPoint& pp, FlxMtxConstFun* ppdim, const flxVec& tr, const int prec, const int fixW) const;
    /**
    * @brief Assembles the DOFs of the elements in the field
    */
    void assembleElementDOFs();
  public:
    FlxRandomField_KL_FEM_Gauss(tuint RF_index, FlxFunction* mu, FlxFunction* sD, FlxFunction* rho, const int ev_solver);
    virtual ~FlxRandomField_KL_FEM_Gauss();
    virtual const tdouble get_realization() const;
    virtual void print_para(std::ostream &sout) const;
    virtual flxElement_KL_base::rftype get_Type() const {return flxElement_KL_base::rfKLfemGauss;}
    virtual const tnlong solve(FlxRFsolveProp* RFsolveProp,std::ostream& sout);
    virtual FlxFunction* get_mean_fun();
    virtual const tdouble calc_Hi(int i, bool globalIndex);
    virtual const tdouble get_Eigenvalue(const int i, bool errSerious=true);
    
    FlxFunction* get_sD() { return sD; }
};

class FLXFEM_EXPORT FlxRandomField_KL_FEM_Gauss_proj : public FlxRandomField_KL_FEM_Gauss {
  private:
    const bool proj_h12;
    const bool solve_full;
  protected:
    virtual const tdouble get_domain_error_el(const int errMeasure) const;
    virtual void plot_el(std::ostream &sout, FlxMtxConstFun* ppdim, const int errMeasure, const int prec, const int fixW, const bool checkTOL) const;
  public:
    FlxRandomField_KL_FEM_Gauss_proj(tuint RF_index, FlxFunction* mu, FlxFunction* sD, FlxFunction* rho, const int ev_solver, const bool proj_h12, const bool solve_full);
    virtual ~FlxRandomField_KL_FEM_Gauss_proj();
    virtual void print_para(std::ostream &sout) const;
    virtual flxElement_KL_base::rftype get_Type() const {return flxElement_KL_base::rfKLfemGaussProj;}
    virtual const tnlong solve(FlxRFsolveProp* RFsolveProp,std::ostream& sout);
};

class FLXFEM_EXPORT FlxRandomField_KL_Collo_Gauss : public FlxRandomField_KL_FEM_Gauss {
  protected:
  public:
    FlxRandomField_KL_Collo_Gauss(tuint RF_index, FlxFunction* mu, FlxFunction* sD, FlxFunction* rho, const int ev_solver);
    virtual ~FlxRandomField_KL_Collo_Gauss();
    virtual void print_para(std::ostream &sout) const;
    virtual flxElement_KL_base::rftype get_Type() const {return flxElement_KL_base::rfColloGauss;}
    virtual const tnlong solve(FlxRFsolveProp* RFsolveProp,std::ostream& sout);
};

class FLXFEM_EXPORT FlxRandomField_EOLE_Gauss : public FlxRandomField_KL_FEM_Gauss {
  protected:
    std::vector<flxPoint> pointVec;
    /**
    * @brief standard deviation of points in pointVec
    */
    flxVec* sdVec;
    /**
    * @brief vector needed to compute the eigenfunctions ... depends on current x !!!
    */
    flxVec* Hx;
    
    virtual const tdouble get_domain_error_el(const int errMeasure) const;
    /**
    * @brief get the error at a point within the field (based on current_element and global variables)
    */
    const tdouble get_error(int errMeasure) const;
    virtual void plot_el(std::ostream &sout, FlxMtxConstFun* ppdim, const int errMeasure, const int prec, const int fixW, const bool checkTOL) const;
    virtual void plotcov_el(std::ostream &sout, const flxPoint& pp, FlxMtxConstFun* ppdim, const flxVec& tr, const int prec, const int fixW) const;
    /**
    * @brief Assembles the DOFs of the elements in the field ... meaning a LOT of points from each element
    */
    void assembleElementDOFs();
    /**
    * @brief Updates the Hx-vector ... note GX,GY and GZ have to be set !!!
    */
    const tdouble assemble_Hx() const;
  public:
    FlxRandomField_EOLE_Gauss(tuint RF_index, FlxFunction* mu, FlxFunction* sD, FlxFunction* rho, const int ev_solver);
    ~FlxRandomField_EOLE_Gauss();
    virtual const tdouble get_realization() const;
    virtual void print_para(std::ostream &sout) const;
    virtual flxElement_KL_base::rftype get_Type() const {return flxElement_KL_base::rfEOLEgauss;}
    virtual const tnlong solve(FlxRFsolveProp* RFsolveProp,std::ostream& sout);
    virtual const tdouble calc_Hi(int i, bool globalIndex);
    virtual const tdouble get_Eigenvalue(const int i, bool errSerious=true);
};

class FLXFEM_EXPORT FlxRandomField_KL_Analytical_ExpCorr_Gauss_1D : public FlxRandomField_base {
  protected:
    /**
    * @brief mean value
    */
    FlxFunction* mu;
    /**
    * @brief standard deviation
    */
    tdouble sD;
    /**
    * @brief correlation length
    */
    tdouble corrL;
    /**
    * @brief length of the domain over 2 - after the field has been assembled
    */
    tdouble domain_l;
    
    flxVec* alpha_i;
    flxVec* omega_i;
    
    flxElement_KL_1D* etkl;
    
    virtual const tdouble get_domain_error_el(const int errMeasure) const;
    /**
    * @brief get the error at a point within the field (based on current_element and global variables)
    */
    const tdouble get_error(const int errMeasure) const;
    /**
    * @brief calculates the cov_error by integartion. LX has to be set (LX will be conserved)
    */
    const tdouble get_error_cov(const std::vector<tdouble> &iVec, const tuint GN, const tdouble* GP, const tdouble* GW) const;
    virtual void plot_el(std::ostream &sout, FlxMtxConstFun* ppdim, const int errMeasure, const int prec, const int fixW, const bool checkTOL) const;
    virtual void plotcov_el(std::ostream &sout, const flxPoint& pp, FlxMtxConstFun* ppdim, const flxVec& tr, const int prec, const int fixW) const;
  public:
    FlxRandomField_KL_Analytical_ExpCorr_Gauss_1D(tuint RF_index, FlxFunction* mu, tdouble sD, tdouble corrL);
    ~FlxRandomField_KL_Analytical_ExpCorr_Gauss_1D();
    virtual const tdouble get_realization() const;
    virtual void print_para(std::ostream &sout) const;
    virtual flxElement_KL_base::rftype get_Type() const {return flxElement_KL_base::rfKLAnaExpCorrGauss1D;}
    virtual const tnlong solve(FlxRFsolveProp* RFsolveProp,std::ostream& sout);
    virtual FlxFunction* get_mean_fun();
    virtual const tdouble calc_Hi(int i, bool globalIndex);
    virtual const tdouble get_Eigenvalue(const int i, bool errSerious=true);
};


//======================= FlxRFBox ============================================

class FLXFEM_EXPORT FlxRFBox {
  protected:
    /**
    * @brief stores all random fields
    */
    std::map<tuint, FlxRandomField_base*> boxM;
  public:
    FlxRFBox();
    ~FlxRFBox();
    /**
    * @brief insert an user defined random variable
    */
    void insert (const tuint index, FlxRandomField_base* value);
    
    FlxRandomField_base* get(const tuint index, const bool errSerious=true);
    
    /**
    * @brief set the current KL-element of all random fields
    */
    void set_Current_Element_KL(flxDomain_base* El);
};


//======================= Generators of Random Fields ============================================

class FLXFEM_EXPORT FlxRF_Generator_base : public FlxReaderBase2, public FlxBoxBase2 {
  public:
    virtual ~FlxRF_Generator_base() {};
    /**
    * @brief create an instance of a random field
    */
    virtual FlxRandomField_base* generate_RF(tuint RF_index, bool errSerious=true) = 0;
    
    // Types of Random Fields
      static flxElement_KL_base::rftype get_rft(std::string strrft, bool errSerious = true);
      static std::string get_rft(flxElement_KL_base::rftype rft);
    /**
    * @brief create an instance of a random field generator
    */
    static FlxRF_Generator_base* createRF(flxElement_KL_base::rftype rft);   
};

class FLXFEM_EXPORT FlxRF_Generator_KL_FEM_Gauss : public FlxRF_Generator_base {
  protected:
    FlxFunction* mu;
    FlxFunction* sD;
    FlxFunction* rho;
    FlxFunction* evtype;
  public:
    FlxRF_Generator_KL_FEM_Gauss();
    virtual ~FlxRF_Generator_KL_FEM_Gauss();
    FlxRandomField_base* generate_RF(tuint RF_index, bool errSerious=true);
};

class FLXFEM_EXPORT FlxRF_Generator_KL_FEM_Gauss_proj : public FlxRF_Generator_KL_FEM_Gauss {
  private:
    bool proj_h12;
    bool solve_full;
  public:
    FlxRF_Generator_KL_FEM_Gauss_proj();
    FlxRandomField_base* generate_RF(tuint RF_index, bool errSerious=true);
};

class FLXFEM_EXPORT FlxRF_Generator_KL_FEM_Gauss_Collo : public FlxRF_Generator_KL_FEM_Gauss {
  public:
    FlxRandomField_base* generate_RF(tuint RF_index, bool errSerious=true);
};

class FLXFEM_EXPORT FlxRF_Generator_EOLE_Gauss : public FlxRF_Generator_KL_FEM_Gauss {
  protected:
    
  public:
    FlxRandomField_base* generate_RF(tuint RF_index, bool errSerious=true);
};

class FLXFEM_EXPORT FlxRF_Generator_KL_Analytical_ExpCorr_Gauss_1D : public FlxRF_Generator_base {
  protected:
    FlxFunction* mu;
    FlxFunction* sD;
    FlxFunction* corrL;
  public:
    FlxRF_Generator_KL_Analytical_ExpCorr_Gauss_1D();
    ~FlxRF_Generator_KL_Analytical_ExpCorr_Gauss_1D() { delete mu; delete sD; delete corrL; }
    FlxRandomField_base* generate_RF(tuint RF_index, bool errSerious=true);
};


/**
* @brief stores properties to create KL-elements
*/
class FLXFEM_EXPORT FlxRFsolveProp : public FlxReaderBase2 {
  private:
    FlxFunction* dim;
    bool octaveOutput;
    FlxFunction* calcerr;
    FlxString* file;
    bool load;   // true: load; false: store
  public:
    FlxRFsolveProp(bool errSerious=true);
    ~FlxRFsolveProp();
    const tuint get_dim();
    const bool get_octaveOutput() { return octaveOutput; }
    const bool get_calcerr();
    const bool is_load() { return (file && load); }
    const bool is_store() { return (file && !load); }
    const std::string get_file_name();
};






#endif // fesslix_flxrandomFEM_H
