#include "AdvModelSpecular.hh"
#include "AdvModelParam.hh"
#include "AdvModelParamSet.hh"
#include "AdvModelLayer.hh"

#include <cfloat>

#include <gsl/gsl_blas.h>
#include <gsl/gsl_complex_math.h>
#include <gsl/gsl_linalg.h>


/** default constructor */
AdvModelSpecular::AdvModelSpecular() {
    this->set("AdvModelSpecular", "specular", 0);
    deltaQ = 0.0;
    efactor = m_paramset->NewParam("scale_factor");
    bkg = m_paramset->NewParam("background");

    efactor->SetVal(1.0);
    bkg->SetVal(0.0);

    efactor->SetScale(0.01);
    bkg->SetScale(1.0e-5);

    logFlag = 0;
}

/** destructor */
AdvModelSpecular::~AdvModelSpecular() {
}

void AdvModelSpecular::SetFitParam(const Double *p) {
    m_paramset->SetParamValues ( p );
}

void AdvModelSpecular::SetFitParam(const std::vector<Double> p) {
    m_paramset->SetParamValues ( p );
}

#ifdef USE_POINTER
/** evaluate the value of the function */
Double AdvModelSpecular::eval(const Double x, const Double *p) {
    Double d;
    m_paramset->SetParamValues ( p );
    d = ReflectAt ( x );
    if (logFlag==1 && d>0.0) d = log(d);
    return d;
}
 
#ifdef HAVE_DIFFERENTIAL_MEMBER
/** evaluate the value of the 1st derivative function */
Double AdvModelSpecular::der1st(const Double x, const Double *p) {
    std::cout << "AdvModelSpecular::der1st is not implemented" << std::endl;
    return DBL_MAX;
}

/** evaluate the value of the 2nd derivative function */
Double AdvModelSpecular::der2nd(const Double x, const Double *p) {
    std::cout << "AdvModelSpecular::der2nd is not implemented" << std::endl;
    return DBL_MAX;
}

/** evaluate the gradient of the function for parameters */
Double * AdvModelSpecular::gradient(const Double x, const Double *p) {
    std::cout << "AdvModelSpecular::gradient is not implemented" << std::endl;
    return NULL;
}

#endif //HAVE_DIFFERENTIAL_MEMBER
#endif //USE_POINTER

#ifdef USE_VECTOR
/** evaluate the value of the function */
Double AdvModelSpecular::eval(const Double x, const std::vector<Double> &p) {
    Double d;
    m_paramset->SetParamValues ( p );
    d = ReflectAt ( x );
    if (logFlag==1 && d>0.0) d = log(d);
    return d;
}

#ifdef HAVE_DIFFERENTIAL_MEMBER
/** evaluate the value of the 1st derivative function */
Double AdvModelSpecular::der1st(const Double x, const std::vector<Double> &p) {
    std::cout << "AdvModelSpecular::der1st is not implemented" << std::endl;
    return DBL_MAX;
}

/** evaluate the value of the 2nd derivative function */
Double AdvModelSpecular::der2nd(const Double x, const std::vector<Double> &p) {
    std::cout << "AdvModelSpecular::der2nd is not implemented" << std::endl;
    return DBL_MAX;
}

/** evaluate the gradient of the function for parameters */
std::vector<Double> AdvModelSpecular::gradient(const Double x, const std::vector<Double> &p) {
    std::cout << "AdvModelSpecular::gradient is not implemented" << std::endl;
    std::vector<Double> v;
    v.clear();
    return v;
}

#endif //HAVE_DIFFERENTIAL_MEMBER
#endif //USE_VECTOR

Double AdvModelSpecular::ReflectAt ( const Double Q ) {
    if (m_layers.empty()) {
    	std::printf("No layer\n");
    	return 0.0;
    }

    Double dQ = this->deltaQ * Q;  //  ⊿Q : 正規分布の分散

    //  convolution 装置分解能
    Double sum = 0.0;
    Double weight = 0.0;
    for (Int4 i = -4; i<=4; i++)
    {
        if (dQ <= 0.0 && i != 0) continue;

        Double qval = Q + (i * 0.5) * dQ;
        if (qval < 0.0) continue;

        Double w;
        if (dQ > 0.0) {
            w = 1.0 / sqrt ( 2.0 * PI ) / dQ * exp ( - 0.5 * ( qval-Q ) * ( qval-Q ) / dQ / dQ );
        } else {
            w = 1.0;
        }
        Double r[4], t[4];
        Double reflec = 0.0;
        Double kz = qval * 0.5;
        CalcReflectionAt(kz, r, t);

        switch (m_mode) {
        case UnPol2UnPol:
            reflec = ( r[0] + r[1] + r[2] + r[3] ) * 0.5;
            break;
        case UpPol2UpPol:
            reflec = r[0];
            break;
        case UpPol2DownPol:
            reflec = r[1];
            break;
        case UpPol2UnPol:
            reflec = r[0] + r[1];
            break;
        case DownPol2DownPol:
            reflec = r[3];
            break;
        case DownPol2UpPol:
            reflec = r[2];
            break;
        case DownPol2UnPol:
            reflec = r[2] + r[3];
            break;
        }

        sum += reflec * w;
        weight += w;
    }
    if (weight > 0.0) { sum /= weight; }
    sum = sum * efactor->GetVal() + bkg->GetVal();
    return sum;
}

/**
    Int4 flag:
      flag = 1   unpolarized neutron reflectivity
          out[0] : in(up+down) -> out(up+down)
      flag = 2   polarized neutron reflectivity
          out[0] : in(up)      -> out(up+down)
          out[1] : in(down)    -> out(up+down)
      flag = 3   polarized neutron reflectivity with polarization analysis
          out[0] : in(up)      -> out(up)
          out[1] : in(down)    -> out(down)
          out[2] : in(up)      -> out(down)  !  spin flip
                or in(down)    -> out(up)    !  spin flip
*/
std::vector<Double> AdvModelSpecular::ReflectAt ( const Double Q, Int4 flag ) {
    if (m_layers.empty()) {
    	std::printf("No layer\n");
    	std::vector<Double> out(3,0.0);
    	return out;
    }

	Double r[4];
    Double dQ = this->deltaQ * Q;  //  ⊿Q : 正規分布の分散

    //  convolution 装置分解能
    Double weight = 0.0;
    r[0] = 0.0;
    r[1] = 0.0;
    r[2] = 0.0;
    r[3] = 0.0;

    for (Int4 i = -4; i<=4; i++)
    {
        if (dQ <= 0.0 && i != 0) continue;

        Double qval = Q + (i * 0.5) * dQ;
        if (qval < 0.0) continue;

        Double w;
        if (dQ > 0.0) {
            w = 1.0 / sqrt ( 2.0 * PI ) / dQ * exp ( - 0.5 * ( qval-Q ) * ( qval-Q ) / dQ / dQ );
        } else {
            w = 1.0;
        }
        Double r0[4], t0[4];
        Double kz = qval * 0.5;
        CalcReflectionAt(kz, r0, t0);

        r[0] += w * r0[0];
        r[1] += w * r0[1];
        r[2] += w * r0[2];
        r[3] += w * r0[3];
        weight += w;
    }

    if (weight <= 0.0) { weight = 1.0; }
    r[0] /= weight;
    r[1] /= weight;
    r[2] /= weight;
    r[3] /= weight;

    std::vector<Double> out;
    out.clear();

    if (flag==1) {
        out.resize(1);
        out[0] = ( r[0] + r[1] + r[2] + r[3] ) * 0.5;
        out[0] = out[0] * efactor->GetVal() + bkg->GetVal();
    } else if (flag==2) {
        out.resize(2);
        out[0] = r[0] + r[1];
        out[1] = r[2] + r[3];
        out[0] = out[0] * efactor->GetVal() + bkg->GetVal();
        out[1] = out[1] * efactor->GetVal() + bkg->GetVal();
    } else if (flag==3) {
        out.resize(3);
        out[0] = r[0];
        out[1] = r[3];
        out[2] = r[1];
        out[0] = out[0] * efactor->GetVal() + bkg->GetVal();
        out[1] = out[1] * efactor->GetVal() + bkg->GetVal();
        out[2] = out[2] * efactor->GetVal() + bkg->GetVal();
    }
    return out;
}


/** 反射率計算 */
void AdvModelSpecular::CalcReflection ( ElementContainer& ein, ElementContainer& eout ) {

    std::vector<Double> x, y;

    eout = ein;
    x = ein.Put(ein.PutXKey());

    for (UInt4 i=0; i<x.size()-1; i++) {
        std::vector<Double> v = ReflectAt ( (x[i]+x[i+1])*0.5, 1 );
        eout.SetValue ( eout.PutYKey(), i, v[0] );
    }
    
}

/** 反射率計算 */
void AdvModelSpecular::CalcReflection ( ElementContainer& ein, ElementContainer& eout1, ElementContainer& eout2 ) {

    std::vector<Double> x, y;

    eout1 = ein;
    eout2 = ein;
    x = ein.Put(ein.PutXKey());

    for (UInt4 i=0; i<x.size()-1; i++) {
        std::vector<Double> v = ReflectAt ( (x[i]+x[i+1])*0.5, 2 );
        eout1.SetValue ( eout1.PutYKey(), i, v[0] );
        eout2.SetValue ( eout2.PutYKey(), i, v[1]);
    }
    
}

/** 反射率計算 */
void AdvModelSpecular::CalcReflection ( ElementContainer& ein, ElementContainer& eout1, ElementContainer& eout2, ElementContainer& eout3 ) {

    std::vector<Double> x, y;

    eout1 = ein;
    eout2 = ein;
    eout3 = ein;
    x = ein.Put(ein.PutXKey());

    for (UInt4 i=0; i<x.size()-1; i++) {
        std::vector<Double> v = ReflectAt ( (x[i]+x[i+1])*0.5, 3 );
        eout1.SetValue ( eout1.PutYKey(), i, v[0] );
        eout2.SetValue ( eout2.PutYKey(), i, v[1]);
        eout3.SetValue ( eout3.PutYKey(), i, v[2]);
    }
}

void AdvModelSpecular::SetFuncParam(const char* fname) {

    // Read XML file
    FILE *fp;
    mxml_node_t *tree;
    fp = fopen(fname, "r");
    if (!fp) {
        std::printf("file open error %s\n", fname);
        return;
    }
#if (MXML_MAJOR_VERSION < 4)  // mxml version
    tree = mxmlLoadFile(NULL, fp, MXML_TEXT_CALLBACK);
#else
    tree = mxmlLoadFile(NULL, NULL, fp);
#endif
    fclose(fp);

    mxml_node_t *model_top;
    model_top = mxmlFindElement(tree, tree, "SimulationModel", "type", "specular", MXML_DESCEND);
    if (!model_top) {
        std::cout << fname << " is not adequate XML file." << std::endl;
        return;
    }

    mxml_node_t *layer_top;
    layer_top = mxmlFindElement(model_top, model_top, "Layers", NULL, NULL, MXML_DESCEND);
    if (!layer_top) {
        std::cout << "Cannot find [Layers] in XML." << std::endl;
        return;
    }

    const char* cp;
    cp = mxmlElementGetAttr(layer_top, "TotalNumber");
    if (!cp) {
        std::cout << "TotalNumber of Layers is not given." << std::endl;
        return;
    }
    Int4 nlayer = atoi(cp);

    if (nlayer < 2) {
        std::cout << "At least, two layers ( air, substrate ) are needed." << std::endl;
        return;
    }

    //  clear m_layers
    if (!m_layers.empty()) {
        for (Int4 i = 0; i < (Int4)m_layers.size(); i++) {
            if (m_layers[i]) delete m_layers[i];
        }
        m_layers.resize(0);
    }

    //  resize m_layers
    //  initialize with NULL pointer
    m_layers.resize(nlayer, NULL);

    char id[10];
    mxml_node_t *node1, *node2;
    for (size_t i = 0; i < m_layers.size(); i++) {
        std::snprintf(id, sizeof(id), "%d", (Int4) i);
        node1 = mxmlFindElement(layer_top, layer_top, "Layer", "id", id, MXML_DESCEND);
        if (node1 == NULL) {
            std::cout << "Cannot find [Layer id=" << id << "] in XML." << std::endl;
            return;
        }
        m_layers[i] = new AdvModelLayer(this);

        node2 = mxmlFindElement(node1, node1, "angle", NULL, NULL, MXML_DESCEND);
        if (node2) {
            m_layers[i]->GetParamAngle()->SetXML( node2 );
        }

        node2 = mxmlFindElement(node1, node1, "SLDreal", NULL, NULL, MXML_DESCEND);
        if (node2) {
            m_layers[i]->GetParamSLDr()->SetXML( node2 );
        }

        node2 = mxmlFindElement(node1, node1, "SLDimag", NULL, NULL, MXML_DESCEND);
        if (node2) {
            m_layers[i]->GetParamSLDi()->SetXML( node2 );
        }

        node2 = mxmlFindElement(node1, node1, "SLDMagreal", NULL, NULL, MXML_DESCEND);
        if (node2) {
            m_layers[i]->GetParamSLDMr()->SetXML( node2 );
        }

        node2 = mxmlFindElement(node1, node1, "SLDMagimag", NULL, NULL, MXML_DESCEND);
        if (node2) {
            m_layers[i]->GetParamSLDMi()->SetXML( node2 );
        }

        node2 = mxmlFindElement(node1, node1, "width", NULL, NULL, MXML_DESCEND);
        if (node2) {
            m_layers[i]->GetParamWidth()->SetXML( node2 );
        }

    }
    node1 = mxmlFindElement(model_top, model_top, "Polarization", NULL, NULL, MXML_DESCEND);
    Int4 iIn, iRef;
    iIn = 0;
    iRef = 0;
    if (node1) {
        cp = mxmlElementGetAttr(node1, "incident");
        if (cp) {
            if ((cp[0]=='U'||cp[0]=='u')&&(cp[1]=='N'||cp[1]=='n')) iIn = 0;
            else if ((cp[0]=='U'||cp[0]=='u')&&(cp[1]=='P'||cp[1]=='p')) iIn = 1;
            else if ((cp[0]=='D'||cp[0]=='d')&&(cp[1]=='O'||cp[1]=='o')) iIn = -1;
            else  std::printf("unknown keyword for polarization value: %s\n",cp);
        }

        cp = mxmlElementGetAttr(node1, "reflected");
        if (cp) {
            if ((cp[0]=='U'||cp[0]=='u')&&(cp[1]=='N'||cp[1]=='n')) iRef = 0;
            else if ((cp[0]=='U'||cp[0]=='u')&&(cp[1]=='P'||cp[1]=='p')) iRef = 1;
            else if ((cp[0]=='D'||cp[0]=='d')&&(cp[1]=='O'||cp[1]=='o')) iRef = -1;
            else  std::printf("unknown keyword for polarization value: %s\n",cp);
        }

    }
    if (iIn==0 && iRef==0)        m_mode = UnPol2UnPol;
    else if (iIn==1 && iRef==0)   m_mode = UpPol2UnPol;
    else if (iIn==1 && iRef==1)   m_mode = UpPol2UpPol;
    else if (iIn==1 && iRef==-1)  m_mode = UpPol2DownPol;
    else if (iIn==-1 && iRef==0)  m_mode = DownPol2UnPol;
    else if (iIn==-1 && iRef==1)  m_mode = DownPol2UpPol;
    else if (iIn==-1 && iRef==-1) m_mode = DownPol2DownPol;
    else std::printf("unsupported polarization pattern %d %d\n",iIn,iRef);

    node1 = mxmlFindElement(model_top, model_top, "ScaleFactor", NULL, NULL, MXML_DESCEND);
    if (node1) efactor->SetXML( node1 );

    node1 = mxmlFindElement(model_top, model_top, "BackGround", NULL, NULL, MXML_DESCEND);
    if (node1) bkg->SetXML( node1 );

    node1 = mxmlFindElement(model_top, model_top, "Resolution", NULL, NULL, MXML_DESCEND);
    if (node1) {
      cp = mxmlElementGetAttr(node1, "deltaQ");
      if (cp) {
        deltaQ = atof(cp);
      }
    }

    this->set("AdvModelSpecular", "specular", m_paramset->GetNumFitParam());

    std::cout << "Set Parameters OK." << std::endl;
}

void AdvModelSpecular::GetFuncParam(const char* fname) {
    char s[250];

    mxml_node_t *tree;
    tree = mxmlNewXML("1.0");

    mxml_node_t *model_top;
    model_top = mxmlNewElement(tree, "SimulationModel");

    mxmlElementSetAttr(model_top, "type", "specular");

    mxml_node_t *layer_top;
    layer_top = mxmlNewElement(model_top, "Layers");

    std::snprintf(s,sizeof(s),"%d",(Int4)m_layers.size());
    mxmlElementSetAttr(layer_top, "TotalNumber", s);

    mxml_node_t *node1, *node2;
 
    for (size_t i = 0; i < m_layers.size(); i++) {
        node1 = mxmlNewElement(layer_top, "Layer");

        std::snprintf(s, sizeof(s), "%d", (Int4) i);
        mxmlElementSetAttr(node1, "id", s);

        node2 = mxmlNewElement(node1, "angle");
        m_layers[i]->GetParamAngle()->GetXML( node2 );

        node2 = mxmlNewElement(node1, "SLDreal");
        m_layers[i]->GetParamSLDr()->GetXML( node2 );

        node2 = mxmlNewElement(node1, "SLDimag");
        m_layers[i]->GetParamSLDi()->GetXML( node2 );

        node2 = mxmlNewElement(node1, "SLDMagreal");
        m_layers[i]->GetParamSLDMr()->GetXML( node2 );

        node2 = mxmlNewElement(node1, "SLDMagimag");
        m_layers[i]->GetParamSLDMi()->GetXML( node2 );

        node2 = mxmlNewElement(node1, "width");
        m_layers[i]->GetParamWidth()->GetXML( node2 );
    }

    node1 = mxmlNewElement(model_top, "Polarization");
    switch(m_mode) {
      case UnPol2UnPol:
        mxmlElementSetAttr(node1, "incident" , "unpol");
        mxmlElementSetAttr(node1, "reflected", "unpol");
        break;
      case UpPol2UnPol:
        mxmlElementSetAttr(node1, "incident" , "up");
        mxmlElementSetAttr(node1, "reflected", "unpol");
        break;
      case UpPol2UpPol:
        mxmlElementSetAttr(node1, "incident" , "up");
        mxmlElementSetAttr(node1, "reflected", "up");
        break;
      case UpPol2DownPol:
        mxmlElementSetAttr(node1, "incident" , "up");
        mxmlElementSetAttr(node1, "reflected", "down");
        break;
      case DownPol2UnPol:
        mxmlElementSetAttr(node1, "incident" , "down");
        mxmlElementSetAttr(node1, "reflected", "unpol");
        break;
      case DownPol2UpPol:
        mxmlElementSetAttr(node1, "incident" , "down");
        mxmlElementSetAttr(node1, "reflected", "up");
        break;
      case DownPol2DownPol:
        mxmlElementSetAttr(node1, "incident" , "down");
        mxmlElementSetAttr(node1, "reflected", "down");
        break;
    }

    node2 = mxmlNewElement(model_top, "ScaleFactor");
    efactor->GetXML( node2 );

    node2 = mxmlNewElement(model_top, "BackGround");
    bkg->GetXML( node2 );

    node2 = mxmlNewElement(model_top, "Resolution");
    std::snprintf(s, sizeof(s), "%g", deltaQ);
    mxmlElementSetAttr(node2 , "deltaQ", s);

    // Save XML file
    FILE *fp;
    fp = fopen(fname, "w");
    if (!fp) {
        std::printf("file open error %s\n", fname);
        return;
    }

#if (MXML_MAJOR_VERSION < 4)  // mxml version
    mxmlSaveFile ( tree, fp, MXML_NO_CALLBACK );
#else
    mxmlSaveFile ( tree, NULL, fp );
#endif

    fclose(fp);
}

void AdvModelSpecular::CalcReflectionAt(Double kz, Double r[], Double t[]) {

    //  レイヤーのパラメータチェック
//    CheckLayers();

    //  gsl の複素数型行列
    gsl_matrix_complex *ma, *mb, *mc;
    ma = gsl_matrix_complex_alloc(4, 4);
    mb = gsl_matrix_complex_alloc(4, 4);
    mc = gsl_matrix_complex_alloc(4, 4);

    //  mb を単位行列へ
    gsl_matrix_complex_set_identity(mb);
    if (m_layers.size() < 2) {
      std::printf("Error: number of layer < 2\n");
      return;
    }

    //  各レイヤーの行列をかける
    Double width_sum = 0.0;
    for (size_t i = 1; i < m_layers.size(); i++) {
        m_layers[i]->GetMatrix(kz, ma);

        //  行列の掛け算 mc = ma × mb, 左からレイヤー行列をかける
        gsl_blas_zgemm(CblasNoTrans, CblasNoTrans,
                GSL_COMPLEX_ONE, ma, mb, GSL_COMPLEX_ZERO, mc);

        //  mc と mb を入れ替える
        std::swap(mb, mc);

        //  全体の厚さ
        width_sum += m_layers[i]->GetWidth();
    }


    //  substrate layer number
    Int4 ns = (Int4) m_layers.size() - 1;

    //  set matrix for reflect coef. and transmit coef. calculation
    Complex c1, c2, c3;
    Complex im = Complex(0.0, 1.0);
    gsl_complex gc;

    Complex kplus_0, kminus_0;
    Complex kplus_n, kminus_n;
    m_layers[0]->GetWavenumber(kz, kplus_0, kminus_0);
    m_layers[ns]->GetWavenumber(kz, kplus_n, kminus_n);
    Complex ea = exp(im * kplus_n * width_sum);
    Complex eb = exp(im * kminus_n * width_sum);

    gsl_matrix_complex_set_zero(ma);

    for (Int4 i = 0; i < 4; i++) {
        gc = gsl_matrix_complex_get(mb, i, 0);
        c1 = gsl2Complex(gc);
        gc = gsl_matrix_complex_get(mb, i, 1);
        c2 = gsl2Complex(gc);
        c3 = -c1 + im * kplus_0 * c2;
        gc = Complex2gsl(c3);

        gsl_matrix_complex_set(ma, i, 0, gc);

        gc = gsl_matrix_complex_get(mb, i, 2);
        c1 = gsl2Complex(gc);
        gc = gsl_matrix_complex_get(mb, i, 3);
        c2 = gsl2Complex(gc);
        c3 = -c1 + im * kminus_0 * c2;
        gc = Complex2gsl(c3);

        gsl_matrix_complex_set(ma, i, 2, gc);
    }
    c3 = ea;
    gc = Complex2gsl(c3);
    gsl_matrix_complex_set(ma, 0, 1, gc);
    c3 = ea * im * kplus_n;
    gc = Complex2gsl(c3);
    gsl_matrix_complex_set(ma, 1, 1, gc);
    c3 = eb;
    gc = Complex2gsl(c3);
    gsl_matrix_complex_set(ma, 2, 3, gc);
    c3 = eb * im * kminus_n;
    gc = Complex2gsl(c3);
    gsl_matrix_complex_set(ma, 3, 3, gc);

    gsl_vector_complex *x = gsl_vector_complex_alloc(4);
    gsl_vector_complex *b = gsl_vector_complex_alloc(4);

    Int4 s;
    gsl_permutation *p = gsl_permutation_alloc(4);

    gsl_linalg_complex_LU_decomp(ma, p, &s);

    Int4 id;
    Complex k0;
    for ( id=0; id<=2; id+=2) {
        if (id==0) {
            //  plus (+) polarization
            k0 = kplus_0;
        } else if (id==2) {
            //  minus (-) polarization
            k0 = kminus_0;
        }

        for (Int4 i = 0; i < 4; i++) {
            gc = gsl_matrix_complex_get(mb, i, id);
            c1 = gsl2Complex(gc);
            gc = gsl_matrix_complex_get(mb, i, id + 1);
            c2 = gsl2Complex(gc);
            c3 = c1 + im * k0 * c2;
            gc = Complex2gsl(c3);
            gsl_vector_complex_set(b, i, gc);
        }

        gsl_linalg_complex_LU_solve(ma, p, b, x);

        gc = gsl_vector_complex_get(x, 0);
        r[id] = gsl_complex_abs2(gc);
        gc = gsl_vector_complex_get(x, 2);
        r[id+1] = gsl_complex_abs2(gc);

        gc = gsl_vector_complex_get(x, 1);
        t[id] = gsl_complex_abs2(gc);
        gc = gsl_vector_complex_get(x, 3);
        t[id+1] = gsl_complex_abs2(gc);
    }

    //debug
//    std::cout << "reflect  " << r[0] << " " << r[1] << " " << std::endl;
//    std::cout << "reflect  " << r[2] << " " << r[3] << " " << std::endl;
//    std::cout << "transmit " << t[0] << " " << t[1] << " " << std::endl;
//    std::cout << "transmit " << t[2] << " " << t[3] << " " << std::endl;
    //debug

    gsl_matrix_complex_free(ma);
    gsl_matrix_complex_free(mb);
    gsl_matrix_complex_free(mc);
    gsl_vector_complex_free(b);
    gsl_vector_complex_free(x);
    gsl_permutation_free(p);
}

Complex AdvModelSpecular::gsl2Complex(gsl_complex c) {
    return Complex(GSL_REAL(c), GSL_IMAG(c));
}

gsl_complex AdvModelSpecular::Complex2gsl(Complex c) {
    gsl_complex gc;
    GSL_SET_COMPLEX(&gc, c.real(), c.imag());
    return gc;
}

std::vector<Double> AdvModelSpecular::GetParamVals() {
    return m_paramset->GetParamValues();    
}

std::vector<Double> AdvModelSpecular::GetUpperVals() {
    return m_paramset->GetUpperValues();    
}

std::vector<Double> AdvModelSpecular::GetLowerVals() {
    return m_paramset->GetLowerValues();    
}

void AdvModelSpecular::toLogScale ( ElementContainer& el ) {

    if(logFlag != 1) return;

    std::vector<Double> x, y, e;

    y = el.Put(el.PutYKey());
    e = el.Put(el.PutEKey());

    for (UInt4 i=0; i<y.size(); i++) {
        el.SetValue ( el.PutYKey(), i, y[i]>0.0? log(y[i]) : 0.0 );
        el.SetValue ( el.PutEKey(), i, e[i]>0.0? log(e[i]) : 0.0 );
    }

}

void AdvModelSpecular::DebugPrintMatrix ( gsl_matrix_complex *mb ) {
        gsl_complex gc;
        gc = gsl_matrix_complex_get(mb, 0, 0);
        std::printf("matrix[0][0] = ( %g , %g )\n",GSL_REAL(gc),GSL_IMAG(gc));
        gc = gsl_matrix_complex_get(mb, 0, 1);
        std::printf("matrix[0][1] = ( %g , %g )\n",GSL_REAL(gc),GSL_IMAG(gc));
        gc = gsl_matrix_complex_get(mb, 0, 2);
        std::printf("matrix[0][2] = ( %g , %g )\n",GSL_REAL(gc),GSL_IMAG(gc));
        gc = gsl_matrix_complex_get(mb, 0, 3);
        std::printf("matrix[0][3] = ( %g , %g )\n",GSL_REAL(gc),GSL_IMAG(gc));

        gc = gsl_matrix_complex_get(mb, 1, 0);
        std::printf("matrix[1][0] = ( %g , %g )\n",GSL_REAL(gc),GSL_IMAG(gc));
        gc = gsl_matrix_complex_get(mb, 1, 1);
        std::printf("matrix[1][1] = ( %g , %g )\n",GSL_REAL(gc),GSL_IMAG(gc));
        gc = gsl_matrix_complex_get(mb, 1, 2);
        std::printf("matrix[1][2] = ( %g , %g )\n",GSL_REAL(gc),GSL_IMAG(gc));
        gc = gsl_matrix_complex_get(mb, 1, 3);
        std::printf("matrix[1][3] = ( %g , %g )\n",GSL_REAL(gc),GSL_IMAG(gc));

        gc = gsl_matrix_complex_get(mb, 2, 0);
        std::printf("matrix[2][0] = ( %g , %g )\n",GSL_REAL(gc),GSL_IMAG(gc));
        gc = gsl_matrix_complex_get(mb, 2, 1);
        std::printf("matrix[2][1] = ( %g , %g )\n",GSL_REAL(gc),GSL_IMAG(gc));
        gc = gsl_matrix_complex_get(mb, 2, 2);
        std::printf("matrix[2][2] = ( %g , %g )\n",GSL_REAL(gc),GSL_IMAG(gc));
        gc = gsl_matrix_complex_get(mb, 2, 3);
        std::printf("matrix[2][3] = ( %g , %g )\n",GSL_REAL(gc),GSL_IMAG(gc));

        gc = gsl_matrix_complex_get(mb, 3, 0);
        std::printf("matrix[3][0] = ( %g , %g )\n",GSL_REAL(gc),GSL_IMAG(gc));
        gc = gsl_matrix_complex_get(mb, 3, 1);
        std::printf("matrix[3][1] = ( %g , %g )\n",GSL_REAL(gc),GSL_IMAG(gc));
        gc = gsl_matrix_complex_get(mb, 3, 2);
        std::printf("matrix[3][2] = ( %g , %g )\n",GSL_REAL(gc),GSL_IMAG(gc));
        gc = gsl_matrix_complex_get(mb, 3, 3);
        std::printf("matrix[3][3] = ( %g , %g )\n",GSL_REAL(gc),GSL_IMAG(gc));
}

