
#if !defined(_HARKLIB_NODE_GHDSS_EXEC_H_INCLUDED_)
#define _HARKLIB_NODE_GHDSS_EXEC_H_INCLUDED_ 1

#include <string>
#include <iostream>
#include <vector>
#include <complex>
////////#include "GHDSS_SeparateMat.h"
#define _USE_MATH_DEFINES
#include <math.h>

////////#include "MICARYHA_struct.h"    // ClassView によって追加されました。
#include "HarkFileIO.h"

#include "hark-lib/matrix.h"
#include "hark-lib/tensor.h"
#include "hark-lib/tf.h"

#ifdef ENABLE_HARKIO3
extern "C"{
    #include "libharkio3.h"
}
#endif


namespace hark
{


    template <typename T>
    class FreqBins {
    public:
        FreqBins() : num_ch(0), lower_freq(0), upper_freq(0) { }
        FreqBins(const FreqBins<T> &bins) :
        num_ch(bins.num_ch), lower_freq(bins.lower_freq), upper_freq(bins.upper_freq) {
            this->bins.noalias() = bins.bins;
        }
        FreqBins(const hark::Matrix<T> &mat) :
        num_ch(mat.rows()), lower_freq(0), upper_freq(mat.cols()) {
            this->bins.noalias() = mat;
        }
        FreqBins(size_t _num_ch, size_t _lower_freq, size_t _upper_freq) :
                 num_ch(_num_ch), lower_freq(_lower_freq), upper_freq(_upper_freq) {
            if(this->upper_freq < this->lower_freq){
                std::stringstream ss;
                ss << "FreqBins: upper_freq < lower_freq ("
                   << this->upper_freq << " < " << this->lowe_freq << ")\n";
                throw hark::RuntimeError(ss.str());
            }
            this->bins.noalias() = hark::Matrix<T>(this->num_ch, (this->upper_freq - this->lower_freq + 1));
        }

        void resize(size_t _num_ch, size_t _lower_freq, size_t _upper_freq){
            this->num_ch = _num_ch;
            this->lower_freq = _lower_freq;
            this->upper_freq = _upper_freq;
            if(this->upper_freq < this->lower_freq){
                std::stringstream ss;
                ss << "FreqBins: upper_freq < lower_freq ("
                   << this->upper_freq << " < " << this->lower_freq << ")\n";
                throw hark::RuntimeError(ss.str());
            }
            this->bins.noalias() = hark::Matrix<T>(this->num_ch, (this->upper_freq - this->lower_freq + 1));
        }

    public:
        hark::Matrix<T> bins;
        size_t num_ch;
        size_t lower_freq;
        size_t upper_freq;
    };


namespace node
{

/*
struct GHDSS_Params {
    int ss_method; // ha_iSSMethod : SS step size update method (0:Fix,1:myu_LC,2:Adaptive)
    float ss_myu; // ha_fSSMyu : SS step size parameter
    float ss_scale; // ha_fSSScal : SS scaling factor
    float noise_floor; // ha_fNoiseFloor : noise floor
    int lc_const; // ha_iLCConst : lc_const method (0:Full ,1:Diag)
    int lc_method; // ha_iLCMethod : LC step size update method (0:Fix,1:Adaptive)
    float lc_myu; // ha_fLCMyu : LC step size parameter
}; // MICARY_PrmSeparation_GHDSS ;
*/


//constexpr int __INVALID_SRC_ID = -32768;

// ---------------------------------------------------------------------
// GHDSS処理実行クラス
/*
class Cexec{
public:
    Cexec();
    ~Cexec();
    // 初期化
    // return error code (0 is error ,1 is success)
    //   filename_tf 伝達関数データファイル名
    //   filename_sm 分離行列初期値データファイル名
    //   select_tf_type TF_INPUT_TYPE パラメータ (__TF_INPUT_FILE/__TF_INPUT_ONLINE)

    // iNch マイクチャンネル数
    // iNpos 音源数
    // iLower_freq 下限周波数インデックス
    // iUpper_freq 上限周波数インデックス
    // filename マイク位置座標ファイル
    // fspeed_sound 音速
    // bFlgFixedNoise 固定雑音源の分離フラグ(true 分離 ,false 分離なし)
    // filename_FixedNoise 固定雑音源位置座標ファイル名

    // フレームパラメータの設定
    // return: error code (0 is error ,1 is success) always 1
    // arg1: prm_frame: Frame parameters
    // arg2: sampling_rate: Sampling Frequency [Hz]
    // arg3: length: Frame length
    // arg4: overlap: Overlap samples
    // arg5: enumWin: Time window (0: Hanning window / 1: CONJ window)
    // 伝達関数の初期化
    // return error code (0 is error ,1 is success)
    //   psPrmFrame フレームパラメータのポインタ
    // TFデータベース、分離行列初期値データベースの初期値に値を更新
    // return error code (0 is error ,1 is success)
    //   psPrmFrame フレームパラメータのポインタ
    //   iSrcIndex 読みだす音源番号
    // 音源位置データをクラス内部保存値に代入する
    // return error code (0 is error ,1 is success)
    //   loc_src_pos 音源位置座標データ
    // 固定雑音源位置座標の作成
    // return error code (0 is error ,1 is success)
    //   loc_src 固定雑音源を含めたサイズを持つ音源位置座標データ
    ////int MakeFixedNoise_Pos(std::vector<Location> &loc_src);
    // 固定雑音源の検出チェック
    // return error code (true is include ,false is no include)
    //   loc_src 音源位置座標データ
    ////bool CheckFixedNoise_noise(std::vector<Location> &loc_src);
    // 分離行列の初期化
    // return error code (0 is error ,1 is success)
    //   psPrmFrame フレームパラメータ
    // 2012/01/13 added
    // 音源インデックスで分離行列初期値を読み込み
    // return error code (0 is error ,1 is success)
    //   psPrmFrame フレームパラメータ
    //   iSrcIndex 読み込む音源IDインデックス

    //--------------------------------------------------------------------------------
    // return: Output index
    // arg1: cur: Current sound source coordinate.
    // arg2: DB: Sound source position coordinate DB to be searched.
    // arg3: level: Search level.
    //       0 searches all neighbors, >=1 sets the number of neighbors to search.
    // 最近傍位置のデータインデックスを取得(Neighbor利用)
    // 最近傍位置のデータインデックスを取得(座標計算)
    // TFデータベース中で最近傍位置の位置IDを取得(座標計算による)

    //--------------------------------------------------------------------------------
    // return: error code (true: success / false: fail)
    // arg1: prm_frame: Frame parameters
    // arg2: filename: Output filename
    // 更新された分離行列をファイルに出力
    // 更新された全ての分離行列をファイルに出力
    // 更新された分離行列をファイルに追加出力
    // 更新用分離行列保存値の更新

    //--------------------------------------------------------------------------------
    // return: check code (0: no change / 1: changed num of src / 2: changed only pos of src)
    // arg1: src_pos_cur: Current sound source position's coordinate data
    // arg2: prm_frame: Frame parameters
    // TF_CONJとWの引き継ぎ
    // POSによる伝達関数の変更確認
    // POSによる分離行列の変更確認

    // 更新用分離行列保存値の初期化
    // return error code (0 is error ,1 is success)
    // 分離行列出力ファイルの初期化
    // return error code (0 is error ,1 is success)
    //   filename 分離行列出力ファイル名
    //Changed by N.Tanaka 2014/08/27

    // マイク座標のシフトパラメータ設定
    //   ivalue shiftの実行フラグ
    ////void SetiMicPosShift(int ivalue);
    // 初期化
    // return error code (true is success ,false is error)
    // args: num_ch, num_pos, lower, upper (num of mic, num of src, lower freq index, upper freq index)
    // TransferFunction データ 設定
    //   tfData TransferFunction データ
    // TransferFunction 伝達関数が全部か一部か
    // return __TF_ONLINE_ALL/__TF_ONLINE_PARTIAL
    // TransferFunction データ読み出し
    // return error code (0 is error, 1 is success)
private:
    // HarkFileFormat Class
    // 音源の出現・存在・消滅を確認
    // return Check current source if it creats, exists or remove.
    //   lcCurLoc 音源位置
    //   iUpdateMethodW 更新方法(ID / POS / ID_POS).
    //int _CheckFrontSrcLoc(MICARY_Location lcCurLoc,int iUpdateMethodW);
    // 消滅した音源の確認
    // 更新用分離行列保存値のうち，更新履歴のある有効サイズを取得
    // return \~English Valid source number

    // 内積の計算
    // 位置座標 [x,y,z系] から
    //HA_Float _InnerProductLoc(MICARY_Location micary_Loc1,MICARY_Location micary_Loc2);

    //HA_Float _InnerProductVec(MICARY_FloatArray micary_faVec1,MICARY_FloatArray micary_faVec2);

    //HA_Float _InnerProductVec(float fVec1[3],float fVec2[3]);


    // 角度[angle]からPosition[x,y,z]に変換
    // return location position[x,y,z]
    //   pos 位置座標[水平角度が設定されている]
    ////void _Ang2Pos(Location &pos); // *plocPos

    //--------------------------------------------------------------------------------
    // return: Calicurate euclidean distance
    // arg1: ref_pos: 音源ベクトル
    // arg2: obj_pos: 音源ベクトル
    // 2点のユークリッド距離を求める

    //--------------------------------------------------------------------------------
    // return: check code(true is in range ,false is out of range)
    // arg1: ref_pos: 参照位置
    // arg2: obj_pos: 目的位置
    // 2点の位置がThreshold_lengthで設定された範囲内であるかの判定(Neighbor利用)
    // 2点の位置がThreshold_lengthで設定された範囲内であるかの判定(座標計算)

    //--------------------------------------------------------------------------------
    // return check code(true is no change ,false is change)
    // arg: id_cur 対象となる現在の音源位置データのインデックス
    // 分離行列の音源位置による変更の確認と引継ぎ
    // 分離行列の音源IDによる変更の確認と引継ぎ
    // 伝達関数の音源IDによる変更の確認と引継ぎ
    // return check code(true is no change ,false is change)
    //   caTFDBInit 1フレーム前のTF_CONJデータ
    //   laSrcPosInit 1フレーム前の音源位置データ

    // (共役定義)伝達関数初期値の計算
    // return error code(0 is error ,1 is success)
    //   iUpper_freq 上限周波数インデックス
    //   psPrmFrame 現フレームのパラメータ構造体ポインタ
    ////int MakeTFMatrix(int upper, PrmFrame &prm_frame);
    // 2012/01/13 added
    // (共役定義)伝達関数初期値の計算
    // return error code(0 is error ,1 is success)
    //   psPrmFrame 現フレームのパラメータ構造体ポインタ
    //   idsrc TFデータを作成する音源IDインデックス
    ////int MakeTFMatrix2(PrmFrame &prm_frame, int idsrc);

    // 伝達関数のファイル読み込み
    // return error code(0 is error, 1 is success)
    //   psPrmFrame 現フレームのパラメータ構造体
    //int ReadTFMatrix(MICARY_PrmFrame psPrmFrame);
    // マイク位置座標のファイル読み込み
    // return error code(0 is error ,1 is success)
    ////int ReadMicPos();
    // 分離行列のファイル読み込み
    // return error code(0 is error, 1 is success)
    //   psPrmFrame 現フレームのパラメータ構造体
    //int ReadSepMatrix(MICARY_PrmFrame psPrmFrame);
    // 保存値伝達関数データベースから最近傍位置音源データの読み込み
    // return The saved data index
    //   OutTF 読み込まれた伝達関数データ
    //   TFDB 保存されている伝達関数データベース
    // DBからデータの取得(最も近い座標を読む)
    // return The saved data index
    //   OutTF 読み込まれた伝達関数データ
    //   TFDB 保存されている伝達関数データベース
    //   idsrc 読み込む音源インデックス
    // 保存値分離行列データベースから最近傍位置音源データの読み込み
    // return The saved data index
    //   OutSM 読み込まれた分離行列
    //   SMDB 保存されている分離行列データベース
    // 保存値分離行列データベースから最近傍位置音源データの読み込み
    // return The saved data index
    //   OutSM 読み込まれた分離行列
    //   SMDB 保存されている分離行列データベース
    //   idsrc 読み込む音源インデックス
    ///////////////UpdateSeparationMatrix update_w; // *m_updateW; 更新用分離行列保存値

    ////std::vector<Location> loc_src_fixed_noise; // locaSrcFixedNoise 固定雑音源位置


    //std::vector<Location> loc_mic; // locaMic マイク位置
    ////int mic_pos_shift; // iMicPosShift マイク位置の重心シフト(0 シフト ,1 固定)

    // 現在のTFからidを指定してposindexを取得
};
*/






// ---------------------------------------------------------------------
// GHDSS class
class GHDSS_exec {
public:
    //constexpr double M_PI = 3.14159265358979323846;

    enum class TF_INPUT_TYPE: int64_t {
        FILE   = 0,
        ONLINE = 1
    };
    enum class ONLINE_TF_UPDATE_TYPE: int64_t {
        INVALID = -1,
        ALL     = 0,
        PARTIAL = 1
    };
    enum class SS_METHOD: int64_t { // How to calculate SS step size.
        FIX      = 0, // use a fixed value for ss_myu.
        LCMYU    = 1, // ss_myu uses the same value as lc_myu.
        ADAPTIVE = 2  // use adaptive ss_myu.
    };
    enum class LC_CONST: int64_t { // Decide on a geometric constraint method.
        FULL = 0, // uses all elements of matrix.
        DIAG = 1  // only uses diagonal elements of matrix.
    };
    enum class LC_METHOD: int64_t { // How to calculate LC step size.
        FIX      = 0, // use a fixed value for lc_myu.
        ADAPTIVE = 1  // use adaptive lc_myu.
    };
    enum class UPDATE_TYPE: int64_t { // How to update the separation matrix.
        STEP  = 0, // performs update based on geometric constraints
                   // after updating based on higher-order decorrelation.
        TOTAL = 1  // updating based on higher-order decorrelation and
                   // updating based on geometric constraints are
                   // performed simulaneeously. 
    };
    enum class UPDATE_METHOD: int64_t {
        ID     = 0,
        POS    = 1,
        ID_POS = 2
    };
    static constexpr int INVALID_SRCID = std::numeric_limits<int>::min();
    enum class SRCID_PREVIOUS_STATE: int {
        INVALID = INVALID_SRCID,
        REMOVE = -1,
        EXISTS = 0,
        CREATE = 1
    };


    class Location {
    public:
        Location() : valid(UPDATE_METHOD::ID), idpos(0), iddir(0), tfpos(0) {
            this->x[0] = 0.0f; this->x[1] = 0.0f; this->x[2] = 0.0f;
            this->p[0] = 0.0f; this->p[1] = 0.0f; this->p[2] = 0.0f;
        }
        Location(hark::Position &pos) : valid(UPDATE_METHOD::ID), iddir(0) {
            if(pos.sys == hark::TF_Coordinate::Cartesian){
                this->x[0] = pos.coord[0];
                this->x[1] = pos.coord[1];
                this->x[2] = pos.coord[2];
            }
            else if(pos.sys == hark::TF_Coordinate::Polar){
                this->p[0] = pos.coord[0];
                this->p[1] = pos.coord[1];
                this->p[2] = pos.coord[2];
            }
            this->idpos = pos.id;
            this->tfpos = pos.id;
        }
        Location(hark::Position &a, hark::Position &b) : valid(UPDATE_METHOD::ID), iddir(0) {
            if(a.id == b.id){
                if(a.sys == hark::TF_Coordinate::Cartesian
                && b.sys == hark::TF_Coordinate::Polar){
                    this->x[0] = a.coord[0];
                    this->x[1] = a.coord[1];
                    this->x[2] = a.coord[2];
                    this->p[0] = b.coord[0];
                    this->p[1] = b.coord[1];
                    this->p[2] = b.coord[2];
                }
                else if(a.sys == hark::TF_Coordinate::Polar
                     && b.sys == hark::TF_Coordinate::Cartesian){
                    this->x[0] = b.coord[0];
                    this->x[1] = b.coord[1];
                    this->x[2] = b.coord[2];
                    this->p[0] = a.coord[0];
                    this->p[1] = a.coord[1];
                    this->p[2] = a.coord[2];
                }
                this->idpos = a.id;
                this->tfpos = a.id;
            }
        }

        void set_from_position(hark::Position &pos){
            if(pos.sys == hark::TF_Coordinate::Cartesian){
                this->x[0] = pos.coord[0];
                this->x[1] = pos.coord[1];
                this->x[2] = pos.coord[2];
            }
            else if(pos.sys == hark::TF_Coordinate::Polar){
                this->p[0] = pos.coord[0];
                this->p[1] = pos.coord[1];
                this->p[2] = pos.coord[2];
            }
            else{
                std::stringstream ss;
                ss << "pos.sys value is unknown coordinates.\n"
                   << "@GHDSS_exec::Location::set_from_position\n";
                throw RuntimeError(ss.str());
            }
        }
        void set_from_positions(hark::Position &a, hark::Position &b){
            if(a.id == b.id){
                if(a.sys == hark::TF_Coordinate::Cartesian
                && b.sys == hark::TF_Coordinate::Polar){
                    this->x[0] = a.coord[0];
                    this->x[1] = a.coord[1];
                    this->x[2] = a.coord[2];
                    this->p[0] = b.coord[0];
                    this->p[1] = b.coord[1];
                    this->p[2] = b.coord[2];
                }
                else if(a.sys == hark::TF_Coordinate::Polar
                     && b.sys == hark::TF_Coordinate::Cartesian){
                    this->x[0] = b.coord[0];
                    this->x[1] = b.coord[1];
                    this->x[2] = b.coord[2];
                    this->p[0] = a.coord[0];
                    this->p[1] = a.coord[1];
                    this->p[2] = a.coord[2];
                }
                else{
                    std::stringstream ss;
                    ss << "One of a and b must be Cartesian and the other Polar.\n"
                       << "@GHDSS_exec::Location::set_from_position\n";
                    throw RuntimeError(ss.str());
                }
                this->idpos = a.id;
                this->tfpos = a.id;
            }
            else{
                std::stringstream ss;
                ss << "The a and b indices are different.\n"
                   << "@GHDSS_exec::Location::set_from_positions\n";
                throw RuntimeError(ss.str());
            }
        }

        hark::Position get_cartesian_position(){
            hark::Position pos;
            pos.sys = hark::TF_Coordinate::Cartesian;
            pos.coord[0] = this->x[0];
            pos.coord[1] = this->x[1];
            pos.coord[2] = this->x[2];
            pos.id = this->idpos;
            //pos.id = this->tfpos;
            return pos;
        }
        hark::Position get_polar_position(){
            hark::Position pos;
            pos.sys = hark::TF_Coordinate::Polar;
            pos.coord[0] = this->p[0];
            pos.coord[1] = this->p[1];
            pos.coord[2] = this->p[2];
            pos.id = this->idpos;
            //pos.id = this->tfpos;
            return pos;
        }

    public:
        float x[3]; // ha_fX, ha_fY, ha_fZ,  X[m], Y[m], Z[m]
        float p[3]; // (ha_fR, ha_fTheta, ha_fPhi, radius, azimuth[deg], elevation[deg]
        UPDATE_METHOD valid; // MICARY_LocationFlg micary_LocationFlag; 有効な座標を示すフラグ
        int idpos; // ha_idPos
        int iddir; // ha_idDir
        int tfindex[3];
        int tfindex_max[3];
        int tfindex_min[3];
        int tfpos; // tfposid TFDB中の位置ID // ha_idPosが異なる使われ方をしているため追加
    };


    std::vector<Location> PositionsToLocations(std::vector<hark::Position> &pos){
        std::vector<Location> out;
        out.reserve(pos.size());
        for(size_t i = 0; i < pos.size(); i++){ out.emplace_back(pos[i]); }
        return out;
    };
    std::vector<Location> PositionsToLocations(std::vector<hark::Position> &a,
                                               std::vector<hark::Position> &b){
        std::vector<Location> out;
        out.reserve(a.size());
        for(size_t i = 0; i < a.size(); i++){ out.emplace_back(a[i], b[i]); }
        return out;
    };

    std::vector<hark::Position> LocationsToCartesianPositions(std::vector<Location> &loc){
        std::vector<hark::Position> out;
        out.reserve(loc.size());
        for(size_t i = 0; i < loc.size(); i++){
            out.emplace_back(loc[i].get_cartesian_position());
        }
        return out;
    };
    std::vector<hark::Position> LocationsToPolarPositions(std::vector<Location> &loc){
        std::vector<hark::Position> out;
        out.reserve(loc.size());
        for(size_t i = 0; i < loc.size(); i++){
            out.emplace_back(loc[i].get_polar_position());
        }
        return out;
    };


    class SeparationMatrix {
    public:
        SeparationMatrix() : num_inputs(0), num_outputs(0) {}
        SeparationMatrix(size_t _num_freq, size_t _num_src, size_t _num_mic) :
                num_inputs(0), num_outputs(0) {
            if(_num_freq * _num_src * _num_mic == 0){ return; }
            this->num_inputs = _num_mic;
            this->num_outputs = _num_src;
            this->freq_index.resize(_num_freq);
            this->sep_mat.resize(_num_freq * _num_src * _num_mic);
        }
        SeparationMatrix(std::vector<std::complex<float> > &sm,
                         std::vector<int> &freqs, size_t _num_src, size_t _num_mic) :
                num_inputs(0), num_outputs(0) {
            if(sm.size() != freqs.size() * _num_src * _num_mic){
                std::cout << "Argument object size given to initialization do not match.\n"
                          << "sm.size()=" << sm.size()
                          << " != (freqs.size()=" << freqs.size()
                          << " * _num_src=" << _num_src
                          << " * _num_mic=" << _num_mic << ")\n"
                          << "@GHDSS_exec::SeparationMatrix(tf, freqs, srcs, mics)"
                          << std::endl;
                return;
            }
            this->num_inputs = _num_mic;
            this->num_outputs = _num_src;
            this->freq_index = freqs;
            this->sep_mat = sm;
        }

        void resize(size_t _num_freq, size_t _num_src, size_t _num_mic){
            if(_num_freq * _num_src * _num_mic == 0){
                this->num_inputs = 0;
                this->num_outputs = 0;
                this->freq_index.clear();
                this->sep_mat.clear();
                return;
            }
            this->num_inputs = _num_mic;
            this->num_outputs = _num_src;
            this->freq_index.resize(_num_freq);
            this->sep_mat.resize(_num_freq * _num_src * _num_mic);
        }

        void clear(){ this->resize(0, 0, 0); }

        void shrink_to_fit(){
            this->freq_index.shrink_to_fit();
            this->sep_mat.shrink_to_fit();
        }

    public:
        std::vector<int> freq_index;
        int num_inputs;
        int num_outputs;
        std::vector<std::complex<float> > sep_mat;
    };


    class SMDatabase {
    public:
        SMDatabase(){}
        SMDatabase(size_t _num_freq, size_t _num_src, size_t _num_mic){
            if(_num_freq * _num_src * _num_mic == 0){ return; }
            this->sm.resize(_num_freq, _num_src, _num_mic);
            this->loc_src.resize(_num_src);
            this->loc_mic.resize(_num_mic);
        }
        SMDatabase(SeparationMatrix &_sm,
                   std::vector<Location> srcs,
                   std::vector<Location> mics){
            if(_sm.sep_mat.size() != _sm.freq_index.size() * srcs.size() * mics.size()){
                std::cout << "Argument object size given to initialization do not match.\n"
                          << "_sm.sep_mat.size()=" << _sm.sep_mat.size()
                          << " != (_sm.freq_index.size()=" << _sm.freq_index.size()
                          << " * srcs.size()=" << srcs.size()
                          << " * mics.size()=" << mics.size() << ")\n"
                          << "@GHDSS_exec::SMDatabase(sm, srcs, mics)" << std::endl;
                return;
            }
            this->sm = _sm;
            this->loc_src = srcs;
            this->loc_mic = mics;
        }

        void resize(size_t _num_freq, size_t _num_src, size_t _num_mic){
            this->sm.resize(_num_freq, _num_src, _num_mic);
            this->loc_src.resize(_num_src);
            this->loc_mic.resize(_num_mic);
        }
        void clear(){ this->resize(0, 0, 0); }

    public:
        SeparationMatrix sm;
        std::vector<Location> loc_src;
        std::vector<Location> loc_mic;
    };


    class TFDatabase {
    public:
        TFDatabase(){}
        TFDatabase(size_t _num_freq, size_t _num_src, size_t _num_mic){
            if(_num_freq * _num_src * _num_mic == 0){ return; }
            this->tf.resize(_num_freq * _num_src * _num_mic);
            this->freq_bin.resize(_num_freq);
            this->loc_src.resize(_num_src);
            this->loc_mic.resize(_num_mic);
        }
        TFDatabase(std::vector<std::complex<float> > &tf,
                   std::vector<int> &freqs,
                   std::vector<Location> &srcs,
                   std::vector<Location> &mics){
            if(tf.size() != freqs.size() * srcs.size() * mics.size()){
                std::cout << "Argument object size given to initialization do not match.\n"
                          << "tf.size()=" << tf.size()
                          << " != (freqs.size()=" << freqs.size()
                          << " * srcs.size()=" << srcs.size()
                          << " * mics.size()=" << mics.size() << ")\n"
                          << "@GHDSS_exec::TFDatabase(tf, freqs, srcs, mics)" << std::endl;
                return;
            }
            this->tf = tf;
            this->freq_bin = freqs;
            this->loc_src = srcs;
            this->loc_mic = mics;
        }

        void resize(size_t _num_freq, size_t _num_src, size_t _num_mic){
            this->tf.resize(_num_freq * _num_src * _num_mic);
            this->freq_bin.resize(_num_freq);
            this->loc_src.resize(_num_src);
            this->loc_mic.resize(_num_mic);
        }

        void clear(){ this->resize(0, 0, 0); }

    public:
        std::vector<std::complex<float> > tf;
        std::vector<int> freq_bin;
        std::vector<Location> loc_src;
        std::vector<Location> loc_mic;
    };


    struct SavedSeparation {
        TFDatabase TFDB;       // Saved internal TF reading values
        SeparationMatrix sm;  // Separation Matrix
    };

    struct UpdateSeparationMatrix {
        std::vector<SRCID_PREVIOUS_STATE> Update;
        SMDatabase SMDB;
    };

    TF_INPUT_TYPE tf_type;
    SS_METHOD ss_method;
    LC_CONST lc_const;
    LC_METHOD lc_method;
    UPDATE_TYPE update;
    UPDATE_METHOD update_method_tf; // How to update the transfer function.
    UPDATE_METHOD update_method_w; // How to update the separation matrix.
    std::vector<UPDATE_METHOD> updated_methods_w; // How the separation matrix is actually updated.

    UpdateSeparationMatrix update_w; // Separation Matrix saved for update.

    float ss_myu; // Step size when updating the sepration matrix based on higher-order decorrelation.
    float ss_scale; // Scale factor in higher-order correlation matrix calulation.
    float noise_floor; // Amplitude threshold (upper limit) for considering the input signal as noise.
    float lc_myu; // Step size when updating the sepration matrix based on geometric constraints.

    //float w_myu; // Step size for updating the separation matrix. Currently unused.
    float threshold; //Threshold[db] for determining whether to update the separation matrix.

    float acceptable_distance; // Acceptable distance [m] as confirmed by POS.

    int sampling_rate;
    int length;
    int advance;
    int window_length;

    GHDSS_exec();
    ~GHDSS_exec();

    void SetbIni(bool _initialized){ this->initialized = _initialized; }

    void Initialize_GHDSS(int num_ch, int num_pos, int lower_freq, int upper_freq);

    void ExecuteGHDSS(hark::FreqBins<std::complex<float> > &InputSpec,
                      hark::FreqBins<std::complex<float> > &OutputSpec);




    SavedSeparation SepMat;  // Saved Separation Matrix
    void Initialize(/*int iFlgTf, const std::string &filename_tf,
                   int iFlgSm, const std::string &filename_sm,
                   int select_tf_type*/);
    //void SetPrmFrame(PrmFrame &prm_frame, int sampling_rate,
    //                 int length, int overlap, int enumWin);
    int InitTFMatrix();
    int LoadInitialTFMatrix(int src_index);
    int AssignSrcPos(std::vector<Location> &loc_src_pos);
    int InitSepMatrix();
    int LoadInitialSepMatrix(int src_index);
    int GetNearestPos(Location &cur, std::vector<Location> &DB, int level=0);
    int GetNearestPosByCoord(Location &cur, std::vector<Location> &DB);
    int GetPosIdFromTFDBByCoord(Location &cur);
    int GetPosIdFromTFDBByCoord(hark::Position &pos);
    void ExportUpdateW(const std::string &filename);
    void ExportAllUpdateW(const std::string &filename);
    void AddUpdateW(const std::string &filename, bool export_W);
    void SetUpdateW();
    std::complex<float> GetUpdateW(int isrc, int imic, int ifreq);
    int ChangeSrcPos_TF_CONJ_W(std::vector<Location> &src_pos_cur);
    int ChangeSrcPos_TF_CONJ(std::vector<Location> &src_pos_cur);
    int ChangeSrcPos_W(std::vector<Location> &src_pos_cur);
    int InitUpdateW();
    int InitExportW(const std::string &filename);
    bool Initialize_Change(size_t num_ch, size_t num_pos, size_t lower, size_t upper);
    bool SetTfData(const hark::TransferFunction &tfData);
    ONLINE_TF_UPDATE_TYPE IsTfMatPartial();
    int SetTransferFunction();

private:
    int _CheckFrontSrcLoc(Location &cur_loc, UPDATE_METHOD update_method_W);
    void _SetRemoveSrc();
    size_t _GetNvalidUpdateW();
    float inner_product(Location &loc1, Location &loc2){
        float result;
        float vec1[3], vec2[3];

        result = loc1.x[0] * loc1.x[0] + loc1.x[1] * loc1.x[1] + loc1.x[2] * loc1.x[2];
        result = std::sqrt(result);
        result = 1.0f / result;
        vec1[0] = loc1.x[0] * result;
        vec1[1] = loc1.x[1] * result;
        vec1[2] = loc1.x[2] * result;

        result = loc2.x[0] * loc2.x[0] + loc2.x[1] * loc2.x[1] + loc2.x[2] * loc2.x[2];
        result = std::sqrt(result);
        result = 1.0f / result;
        vec2[0] = loc2.x[0] * result;
        vec2[1] = loc2.x[1] * result;
        vec2[2] = loc2.x[2] * result;

        result = this->inner_product(vec1, vec2) ;
        return result;
    }
    float inner_product(std::vector<float> &v1, std::vector<float> &v2){
        float result = 0.0f;
        size_t vsize = (v1.size() <= v2.size()) ? v1.size() : v2.size();
        for(size_t i = 0; i < vsize; i++){ result += v1[i] * v2[i]; }
        return result;
    }
    float inner_product(float a1[3], float a2[3]){
        float result = 0.0f;
        for(size_t i = 0; i < 3; i++){ result += a1[i] * a2[i]; }
        return result;
    }
    float _EuclidDistance(Location &ref_pos, Location &obj_pos);
    bool _IsThreshPos(Location &ref_pos, Location &obj_pos);
    bool _IsThreshPosByCoord(Location &ref_pos, Location &obj_pos);
    bool _CheckSrcPos_W(int id_cur);
    bool _CheckSrcID_W(int id_cur);
    bool _CheckSrcID_TF_CONJ(std::vector<std::complex<float> > &TFDBInit, std::vector<Location> &src_pos_ini);
    void ReadTFMatrix();
    void ReadSepMatrix();
    int GetNearestTFMatrixFromDB(TFDatabase &OutTF, TFDatabase &TFDB);
    int GetNearestTFMatrixFromDB2(TFDatabase &OutTF, TFDatabase &TFDB, int idsrc);
    int GetNearestSepMatrixFromDB(SavedSeparation &OutSM, SMDatabase &SMDB);
    int GetNearestSepMatrixFromDB2(SavedSeparation &OutSM, SMDatabase &SMDB, int idsrc);
    int GetPosIndex(int id);

    void MakeWMatGHDSS();
    void MakeWMatGHDSS2(size_t i);

    void dUpdateSeparationMatGHDSS(hark::FreqBins<std::complex<float> > &InputSpec,
                                   hark::FreqBins<std::complex<float> > &OutputSpec);

private:
    void _dCalcSSStepSize_GHDSS();
    void _dCalcLCStepSize_GHDSS(size_t num_src, size_t num_mic);


    HarkFileIO harkio_tf; // for Read TF
    HarkFileIO harkio_w; // for Write W
    HarkFileIO harkio_tmp; // Temporary

public:
    std::string filename_mic; // Microphone location coordinates filename.
    std::string filename_tf; // (Conjugated) Transfer Function filename.
    std::string filename_sm; // Separation Matrix filename.
    std::string filename_fixed_noise; // Fixed Noise location coordinates filename.

private:
    float sound_speed; // Sound Speed
    std::vector<Location> previous_loc_src; // Sound source locations before 1 frame.
    TFDatabase TFDB; // (Conjugated) TransferFunction data read from file.
    SMDatabase SMDB; // Separation Matrix data read from file.
    hark::TransferFunction m_tfData; // TransgerFunction Data
    hark::Neighbor m_tfNeighbor; // TransgerFunction Neighbor


    hark::Matrix<std::complex<double> > g_SS_Ryy;
    //hark::Matrix<std::complex<double> > g_SS_Zns1;
    hark::Matrix<std::complex<double> > g_SS_E;
    hark::Matrix<std::complex<double> > g_SS_Jss;
    hark::Matrix<std::complex<double> > g_SS_Jpri;
    //hark::Matrix<std::complex<double> > g_SS_Znm1;
    //hark::Matrix<std::complex<double> > g_SS_Znsns;
    hark::Matrix<std::complex<double> > g_SS_Znsnm;
    hark::Matrix<std::complex<double> > g_SS_Z11;

    hark::Matrix<std::complex<float> > g_SS_PY;
    hark::Matrix<std::complex<float> > g_SS_QY;

    hark::Matrix<std::complex<double> > g_LC_E;
    //hark::Matrix<std::complex<double> > g_LC_Znsns;
    //hark::Matrix<std::complex<double> > g_LC_Znsnm;
    hark::Matrix<std::complex<double> > g_LC_Jlc;
    //hark::Matrix<std::complex<double> > g_LC_Atmp;

    hark::Matrix<std::complex<float> > g_Up_A;
    hark::Matrix<std::complex<float> > g_Up_X;
    hark::Matrix<std::complex<float> > g_Up_W;
    hark::Matrix<std::complex<float> > g_Up_Y;
    hark::Matrix<std::complex<float> > g_Up_J_SS;
    hark::Matrix<std::complex<float> > g_Up_J_LC;

    hark::Matrix<std::complex<double> > g_Up_Rxx;
    hark::Matrix<std::complex<double> > g_Up_Ryy;

    size_t g_SepMat_Nsrc;
    size_t g_SepMat_Nmic;

    bool initialized;

    hark::FreqBins<std::complex<double> > Rxx, Ryy; // double precision is required.
    //GHDSS_Params params; //m_prmSep ;

    // Parameters for divergence prevention.
    float prev_threshold;
    float x_threshold;
};
// ---------------------------------------------------------------------

}
}

#endif // _HARKLIB_NODE_GHDSS_EXEC_H_INCLUDED_

