#include"main.hpp"
#include"Option.hpp"
#include"Mcmc.hpp"
#include"Pre.hpp"
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/normal_distribution.hpp>
#include <boost/random/uniform_real.hpp>
#include <boost/random/gamma_distribution.hpp>
#include"nlopt.hpp"

boost::mt19937 r;

const double sigma2_y0 = 10000;
const bool bounded=false;

void Mcmc::loc_RD()
{
    double protsum=0,mrnasum=0;
    int ncount=0;
    for (unsigned p=0;p<yH().size();p++) for (unsigned q=0;q<yH().at(p).size();q++) for (int j=0;j<op().nrep();j++) for (int t=0;t<op().nt();t++) {
        protsum+=exp(yH().at(p).at(q).at(j*op().nt()+t));
        if (op().Mbool()) protsum+=exp(yM().at(p).at(q).at(j*op().nt()+t));
        mrnasum+=exp(xx().at(p).at(j*op().nt()+t));
        ncount++;
    }
    d_mean_R=0;
    d_mean_D=0;
    d_sigma2_RD=op().Mbool() ? .25 : 1;
}

const double Mcmc::mean_RD(const char c) const
{
    if (c!='R' and c!='D') throw runtime_error("mean_RD");
    return (c=='R'?d_mean_R:d_mean_D);
}

double quantile(const vector<double>& data,const double f)
{
    vector<double> sorted_data(data);
    sort(sorted_data.begin(),sorted_data.end());
    const double index = f * (data.size() - 1) ;
    const size_t lhs = static_cast<size_t>(index) ;
    const double delta = index - lhs ;
    double result;
    if (data.size() == 0) throw runtime_error("quantile");
    if (lhs == data.size() - 1) {
        result = sorted_data.at(lhs) ;
    } else {
        result = (1 - delta) * sorted_data.at(lhs) + delta * sorted_data.at(lhs + 1) ;
    }
    return result ;
}

bool Mcmc::Ey_fn(const int p, const int j, const vector<double>& y0i,const vector<double>& Ri,const vector<double>& Di, vector<double>& Ey_ij,const Mcmc& mc,const char c)
{
    if (c!='M' and c!='H') throw runtime_error("Ey_fn");
    Ey_ij.at(0) = y0i.at(j);
    if (not op().Mbool()) for (int t=1;t<mc.op().nt();t++) {
        Ey_ij.at(t) = Ey_ij.at(t-1)
            + (mc.op().timep().at(t)-mc.op().timep().at(t-1))*( log(1.+exp(mc.xx().at(p).at(j*mc.op().nt()+t-1))) *Ri.at(t-1)-Ey_ij.at(t-1)*Di.at(t-1) );
        if (Ey_ij.at(t)<=0) return true;
    } else if (c=='M') for (int t=1;t<mc.op().nt();t++) {
        Ey_ij.at(t) = Ey_ij.at(t-1)
            + (mc.op().timep().at(t)-mc.op().timep().at(t-1))*( -Ey_ij.at(t-1)*Di.at(t-1) );
        if (Ey_ij.at(t)<=0) return true;
    } else for (int t=1;t<mc.op().nt();t++) {
        Ey_ij.at(t) = Ey_ij.at(t-1)
            + (mc.op().timep().at(t)-mc.op().timep().at(t-1))*( log(1.+exp(mc.xx().at(p).at(j*mc.op().nt()+t-1))) *Ri.at(t-1));
        if (Ey_ij.at(t)<=0) return true;
    }
    return false;
}

double Mcmc::sum_sq(const int p, const int j, const vector<double>& y0i, const vector<double>& Ri, const vector<double>& Di,const Mcmc& mc,const char c)
{
    if (c!='M' and c!='H') throw runtime_error("sum_sqj");
    double sum2=0;
    vector<double> Ey_ij(mc.op().nt()+0);
    if (Ey_fn(p,j,y0i,Ri,Di,Ey_ij,mc,c)) return NAN;
    const vector<vector<double> >& y_p=(c=='M'?mc.yM():mc.yH()).at(p);
    for (int t=0; t<mc.op().nt(); t++) for (unsigned q=0;q<y_p.size();q++) {
        sum2 += pow(y_p.at(q).at(j*mc.op().nt()+t)-log(Ey_ij.at(t)),2);
    }
    return sum2;
}

double Mcmc::sum_sq(const int p, const vector<double>& y0i, const vector<double>& Ri, const vector<double>& Di,const Mcmc& mc,const char c,const int s=0)
{
    if (c!='M' and c!='H') throw runtime_error("sum_sq");
    double sum2=0;
    const vector<vector<double> >& y_p=(c=='M'?mc.yM():mc.yH()).at(p);
    for (int j=0; j<mc.op().nrep(); j++) {
        bool flag=false;
        for (unsigned q=0;q<y_p.size();q++) if (obs(y_p.at(q).at(j*mc.op().nt()))) {
            flag=true;
            break;
        }
        if (flag) {
            vector<double> Ey_ij(mc.op().nt()+0);
            if (Ey_fn(p,j,y0i,Ri,Di,Ey_ij,mc,c)) return NAN;
            for (int t=s; t<mc.op().nt(); t++) for (unsigned q=0;q<y_p.size();q++) {
                sum2 += pow(y_p.at(q).at(j*mc.op().nt()+t)-log(Ey_ij.at(t)),2);
            }
        }
    }
    return sum2;
}

boost::random::normal_distribution<> ugaussian(0,1);
boost::uniform_real<> uniform(0,1);
void Mcmc::update_y0(const int p,const char c)
{
    if (c!='M' and c!='H') throw runtime_error("update_y0");
    const vector<vector<double> >& y_p=(c=='M'?yM():yH()).at(p);
    const double tau2p=(c=='M'?tau2M():tau2H()).at(p);
    const vector<double>& y0p=(c=='M'?M0():H0()).at(p);
    for (int j=0;j<op().nrep();j++) {
        bool flag=false;
        for (unsigned q=0;q<y_p.size();q++) if (obs(y_p.at(q).at(j*op().nt()))) {
            flag=true;
            break;
        }
        if (flag) {
            vector<double> newy0p(y0p);
            newy0p.at(j) *= exp(ugaussian(r));
            double logr = -sum_sq(p,j,newy0p,RR().at(p),DD().at(p),*this,c)/tau2p/2;
            logr -= -sum_sq(p,j,y0p,RR().at(p),DD().at(p),*this,c)/tau2p/2;
            logr += -pow(log(newy0p.at(j)),2)/2/sigma2_y0;
            logr -= -pow(log(y0p.at(j)),2)/2/sigma2_y0;
            if (logr>0 or uniform(r)<exp(logr)) {
                (c=='M'?d_M0: d_H0).at(p).at(j) = newy0p.at(j);
            }
        }
    }
}

void Mcmc::update_RD(const int p,const char c)
{
    if (c!='R' and c!='D') throw runtime_error("update_RD");
    const vector<int>& CPp=(c=='R'?CPR():CPD()).at(p);
    const vector<double>& RDp=(c=='R'?RR():DD()).at(p);
    const double& upl=upRD;
    vector<double> newRDp;
    int s=-1;
    for (int t=0;t<op().nt()-1;t++) {
        if (/*t==0 or */CPp.at(t)==1) {
            s = t+1;
            newRDp = RDp;
            if (bounded) {
                newRDp.at(t) = inv_logit(logit(RDp.at(t),upl)+ugaussian(r),upl);
            } else {
                newRDp.at(t) *= exp(ugaussian(r));
            }
        } else {
            newRDp.at(t) = newRDp.at(t-1);
        }
        if (/*t!=op().nt()-2 and */CPp.at(t+1)==0) continue;
        double logr = -sum_sq(p,H0().at(p),(c=='R'?newRDp:RR().at(p)),(c=='R'?DD().at(p):newRDp),*this,'H',s)/tau2H().at(p)/2;
        logr -= -sum_sq(p,H0().at(p),RR().at(p),DD().at(p),*this,'H',s)/tau2H().at(p)/2;
        if (op().Mbool()) {
            logr += -sum_sq(p,M0().at(p),(c=='R'?newRDp:RR().at(p)),(c=='R'?DD().at(p):newRDp),*this,'M',s)/tau2M().at(p)/2;
            logr -= -sum_sq(p,M0().at(p),RR().at(p),DD().at(p),*this,'M',s)/tau2M().at(p)/2;
        }
        if (bounded) {
            logr += log(pq(newRDp.at(t),upl))-log(pq(RDp.at(t),upl));
        } else {
            logr += -pow(log(newRDp.at(t))-mean_RD(c),2)/2/sigma2_RD();
            logr -= -pow(log(RDp.at(t))-mean_RD(c),2)/2/sigma2_RD();
        }
        if (logr>0 or uniform(r)<exp(logr)) {
            (c=='R'?d_RR:d_DD).at(p) = newRDp;
        }
    }
}

void Mcmc::update_CP(const int p,const char c)
{
    if (c!='R' and c!='D') throw runtime_error("update_CP");
    const vector<double>& RDp=(c=='R'?RR():DD()).at(p);
    const vector<int>& CPp=(c=='R'?CPR():CPD()).at(p);
    const vector<vector<double> >& d_varphi=(c=='R'?d_varphiR:d_varphiD);
    const double& upl=upRD;
    for (int cp=1;cp<op().nt()-1;cp++) {
        vector<double> newRDp=RDp;
        int l_time_index=-1, r_time_index=-1;
        for (int t=cp-1;l_time_index<0;t--) if (/*t==0 or */CPp.at(t)==1) l_time_index=t;
        for (int t=cp+1;r_time_index<0;t++) if (/*t==op().nt()-1 or */CPp.at(t)==1) r_time_index=t;
        const double l_time = op().timep().at(cp)-op().timep().at(l_time_index);
        const double r_time = op().timep().at(r_time_index)-op().timep().at(cp);
        const double t_time = op().timep().at(r_time_index)-op().timep().at(l_time_index);
        double logr=0;
        if (CPp.at(cp)==0) {
            const double u = uniform(r);
            const double newRleft = (bounded ? inv_logit(logit(RDp.at(cp),upl)+r_time/t_time*logit(u,1),upl) : exp(log(RDp.at(cp))+r_time/t_time*logit(u,1)) );
            const double newRright= (bounded ? inv_logit(logit(RDp.at(cp),upl)-l_time/t_time*logit(u,1),upl) : exp(log(RDp.at(cp))-l_time/t_time*logit(u,1)) );
            for (int t=l_time_index;t<cp;t++) newRDp.at(t) = newRleft;
            for (int t=cp;t<r_time_index;t++) newRDp.at(t) = newRright;
            if (d_prior) logr += d_varphi.at(p).at(cp);
            if (bounded) {
                logr += -log(upl);
                logr += 2*log(newRleft*(upl-newRright)+newRright*(upl-newRleft))-log(pq(RDp.at(cp),upl))-log(upl);
            } else {
                logr -= -log(RDp.at(cp))-pow(log(RDp.at(cp))-mean_RD(c),2)/2/sigma2_RD();
                logr += -log(newRleft)-pow(log(newRleft)-mean_RD(c),2)/2/sigma2_RD();
                logr += -log(newRright)-pow(log(newRright)-mean_RD(c),2)/2/sigma2_RD();
                logr += -log(sigma2_RD())-.5*log(2*M_PI);
                logr += 2*log(newRleft+newRright)-log(RDp.at(cp));
            }
        } else {
            const double mergedR=(bounded ? inv_logit((l_time*logit(RDp.at(cp-1),upl)+r_time*logit(RDp.at(cp),upl))/t_time,upl) : exp((l_time*log(RDp.at(cp-1))+r_time*log(RDp.at(cp)))/t_time) );
            for (int t=l_time_index;t<r_time_index;t++) newRDp.at(t)=mergedR;
            if (d_prior) logr -= d_varphi.at(p).at(cp);
            //if ((not op().Mbool()) and c=='D') logr-=logit(.1,1);
            if (bounded) {
                logr -= -log(upl);
                logr -= 2*log(RDp.at(cp-1)*(upl-RDp.at(cp))+RDp.at(cp)*(upl-RDp.at(cp-1)))-log(pq(mergedR,upl))-log(upl);
            } else {
                logr += -log(mergedR)-pow(log(mergedR)-mean_RD(c),2)/2/sigma2_RD();
                logr -= -log(RDp.at(cp-1))-pow(log(RDp.at(cp-1))-mean_RD(c),2)/2/sigma2_RD();
                logr -= -log(RDp.at(cp))-pow(log(RDp.at(cp))-mean_RD(c),2)/2/sigma2_RD();
                logr -= -log(sigma2_RD())-.5*log(2*M_PI);
                logr -= 2*log(RDp.at(cp-1)+RDp.at(cp))-log(mergedR);
            }
        }
        logr += -sum_sq(p,H0().at(p),(c=='R'?newRDp:RR().at(p)),(c=='R'?DD().at(p):newRDp),*this,'H',l_time_index+1)/tau2H().at(p)/2;
        logr -= -sum_sq(p,H0().at(p),RR().at(p),DD().at(p),*this,'H',l_time_index+1)/tau2H().at(p)/2;
        if (op().Mbool()) {
            logr += -sum_sq(p,M0().at(p),(c=='R'?newRDp:RR().at(p)),(c=='R'?DD().at(p):newRDp),*this,'M',l_time_index+1)/tau2M().at(p)/2;
            logr -= -sum_sq(p,M0().at(p),RR().at(p),DD().at(p),*this,'M',l_time_index+1)/tau2M().at(p)/2;
        }
        if (logr>0 or uniform(r)<exp(logr)) {
            if (c=='R') {
                d_RR.at(p) = newRDp;
                d_CPR.at(p).at(cp)=1-CPp.at(cp);
            } else {
                d_DD.at(p) = newRDp;
                d_CPD.at(p).at(cp)=1-CPp.at(cp);
            }
        }
    }
}

void Mcmc::gibbs_tau2(const int p,const char c)
{
    if (c!='M' and c!='H') throw runtime_error("gibbs_tau2");
    const double sumsq=sum_sq(p,(c=='M'?M0():H0()).at(p),RR().at(p),DD().at(p),*this,c);
    if (obs(sumsq)) {
        (c=='M'?d_tau2M:d_tau2H).at(p) = 1 / boost::random::gamma_distribution<> ((c=='M'?a_tauM():a_tauH())+.5*d_nobs.at(p)*op().nt(),1/((c=='M'?b_tauM():b_tauH())+sumsq/2))(r);
    }
}

void Mcmc::tau_hyperparams(const char c)
{
    if (c!='M' and c!='H') throw runtime_error("tau_hyperparams");
    double& d_a_tau=(c=='M'?d_a_tauM:d_a_tauH);
    double& d_b_tau=(c=='M'?d_b_tauM:d_b_tauH);
    vector<double>& d_tau2=(c=='M'?d_tau2M:d_tau2H);
    const vector<double>& tau2=d_tau2;

    vector<vector<vector<double> > > yy(yH());
    if (op().Mbool()) for (unsigned p=0;p<yH().size();p++) for (unsigned q=0;q<yH().at(p).size();q++) for (int j=0;j<op().nrep();j++) for (int t=0;t<op().nt();t++) {
        yy.at(p).at(q).at(j*op().nt()+t)=log(exp(yM().at(p).at(q).at(j*op().nt()+t))+exp(yH().at(p).at(q).at(j*op().nt()+t)));
    }

    d_tau2.assign(yy.size(),0);
    for (unsigned p=0;p<yy.size();p++) {
        for (unsigned q=0; q<yy.at(p).size(); q++) {
            for (int j=0;j<op().nrep();j++) {
                vector<double> mu(op().nt()-1);
                for (int t=0;t<op().nt()-1;t++) {
                    mu.at(t)=yy.at(p).at(q).at(j*op().nt()+t)+yy.at(p).at(q).at(j*op().nt()+t+1);
                    mu.at(t)/=2;
                }
                for (int t=1;t<op().nt()-1;t++) {
                    d_tau2.at(p)+=.5*pow(yy.at(p).at(q).at(j*op().nt()+t)-mu.at(t-1),2);
                    d_tau2.at(p)+=.5*pow(yy.at(p).at(q).at(j*op().nt()+t)-mu.at(t),2);
                }
                d_tau2.at(p)+=pow(yy.at(p).at(q).at(j*op().nt()+0)-mu.at(0),2);
                const int t=op().nt()-2;
                d_tau2.at(p)+=pow(yy.at(p).at(q).at(j*op().nt()+t)-mu.at(t),2);
            }
        }
        d_tau2.at(p) /= op().nc()*yy.at(p).size();
    }
    const double m=quantile(d_tau2,.1);
    for (unsigned p=0;p<yy.size();p++) d_tau2.at(p) += m;
    ofstream var_ofs("varc"+boost::lexical_cast<string>(c)+".txt");

    //std::transform(tau2.begin(),tau2.end(),d_tau2.begin(),std::bind1st(std::multiplies<double>(),2.));

    copy(tau2.begin(),tau2.end(),ostream_iterator<double>(var_ofs,"\n"));
    const double mom1=accumulate(tau2.begin(),tau2.end(),0.)/tau2.size();
    double mom2=0;
    for (vector<double>::const_iterator it=tau2.begin();it!=tau2.end();it++) mom2 += (*it)*(*it);
    mom2 /= tau2.size();
    d_a_tau=(2*mom2-mom1*mom1)/(mom2-mom1*mom1);//MOM estimates
    d_b_tau=mom1*mom2/(mom2-mom1*mom1);         //MOM estimates
}

Mcmc::Mcmc(const Pre& pr) : d_op(pr.op()),d_prior(false),d_xx(pr.xx()),d_yM(pr.yM()),d_yH(pr.yH()),d_nobs(pr.nobs())
{
    Construct();
}

Mcmc::Mcmc(const Pre& pr,const vector<vector<double> >& varphiR,const vector<vector<double> >& varphiD) : d_op(pr.op()),d_prior(true),d_varphiR(varphiR),d_varphiD(varphiD),d_xx(pr.xx()),d_yM(pr.yM()),d_yH(pr.yH()),d_nobs(pr.nobs())
{
    Construct();
}

void Mcmc::Construct()
{
    d_M0.assign(yM().size(),vector<double>(op().nrep()));
    d_H0.assign(yH().size(),vector<double>(op().nrep()));
    for (unsigned p=0;p<yM().size();p++) for (int j=0;j<op().nrep();j++) {
        double sum=0;
        for (unsigned q=0;q<yM().at(p).size();q++) sum+=yM().at(p).at(q).at(j*op().nt());
        d_M0.at(p).at(j)=exp(sum/yM().at(p).size());
    }
    for (unsigned p=0;p<yH().size();p++) for (int j=0;j<op().nrep();j++) {
        double sum=0;
        for (unsigned q=0;q<yH().at(p).size();q++) sum+=yH().at(p).at(q).at(j*op().nt());
        d_H0.at(p).at(j)=exp(sum/yH().at(p).size());
    }
    d_RR.assign(yH().size(),vector<double> (op().nt(),1e-5));
    d_DD.assign(yH().size(),vector<double> (op().nt(),1e-5));

    loc_RD();

    vector<int> setcp(op().nt(),1);setcp.front()=1;setcp.back()=1;
    d_CPR.assign(yH().size(),setcp);
    d_CPRum.assign(yH().size(),vector<int> (op().nt()-1));
    d_CPD.assign(yH().size(),setcp);
    d_CPDum.assign(yH().size(),vector<int> (op().nt()-1));

    if (op().Mbool()) tau_hyperparams('M');
    tau_hyperparams('H');
    ofstream M0_ofs("s_M0");
    ofstream H0_ofs("s_H0");
    ofstream RR_ofs("s_RR");
    ofstream DD_ofs("s_DD");
    ofstream tau2M_ofs("s_tau2M");
    ofstream tau2H_ofs("s_tau2H");
    if (not tau2M_ofs) throw runtime_error("can't open s_tau2M");
    ofstream CPR_ofs("s_CPR");
    ofstream CPD_ofs("s_CPD");
    ofstream etaM_ofs("s_etaM");
    ofstream etaH_ofs("s_etaH");
    ofstream loglike_ofs("s_loglike");

    upRD=10;

    for (int iter=0,ns=0; ns<op().ns(); iter++) {

        if (iter%100==0) cout<<'\r'<<iter<<flush;
        for (unsigned p=0; p<yH().size(); p++) {
            if (op().Mbool()) update_y0(p,'M');
            update_y0(p,'H');
            update_RD(p,'R');
            update_RD(p,'D');
            update_CP(p,'R');
            update_CP(p,'D');
        }

        if (iter>op().nburn() and iter%op().nthin()==0) {
            ns++;

            for (unsigned p=0;p<yH().size();p++) {
                if (op().Mbool()) tau2M_ofs<<d_tau2M.at(p)<<'\t';
                tau2H_ofs<<d_tau2H.at(p)<<'\t';
                for (int j=0;j<op().nrep();j++) {
                    if (op().Mbool()) M0_ofs<<d_M0.at(p).at(j)<<'\t';
                    H0_ofs<<d_H0.at(p).at(j)<<'\t';
                }
                for (int t=0;t<op().nt()-1;t++) {
                    RR_ofs<<d_RR.at(p).at(t)<<'\t';
                    DD_ofs<<d_DD.at(p).at(t)<<'\t';
                }
                for (int t=1;t<op().nt()-1;t++) {
                    CPR_ofs<<d_CPR.at(p).at(t)<<'\t';
                    CPD_ofs<<d_CPD.at(p).at(t)<<'\t';
                    d_CPRum.at(p).at(t)+=d_CPR.at(p).at(t);
                    d_CPDum.at(p).at(t)+=d_CPD.at(p).at(t);
                }

            }

            if (op().Mbool()) {
                M0_ofs<<'\n';
                tau2M_ofs<<'\n';
            }
            H0_ofs<<'\n';
            tau2H_ofs<<'\n';
            RR_ofs<<'\n';
            DD_ofs<<'\n';
            CPR_ofs<<'\n';
            CPD_ofs<<'\n';

            double loglike = 0;
            for (unsigned p=0;p<yH().size();p++) for (unsigned q=0;q<yH().at(p).size();q++) for (int j=0;j<op().nrep();j++) {
                if (op().Mbool()) {
                    if (obs(yM().at(p).at(q).at(j*op().nt()))) {
                        vector<double> Ey_ij(op().nt());
                        if (Ey_fn(p,j,d_M0.at(p),d_RR.at(p),d_DD.at(p),Ey_ij,*this,'M')) throw runtime_error("fit");
                        for (int t=0;t<op().nt();t++) {
                            etaM_ofs << log(Ey_ij.at(t)) << '\t';
                            loglike += -log(d_tau2M.at(p))-pow(yM().at(p).at(q).at(j*op().nt()+t)-log(Ey_ij.at(t)),2)/d_tau2M.at(p)/2;
                        }
                    } else {
                        for (int t=0;t<op().nt();t++) etaM_ofs << "NA\t";
                    }
                }
                if (obs(yH().at(p).at(q).at(j*op().nt()))) {
                    vector<double> Ey_ij(op().nt());
                    if (Ey_fn(p,j,d_H0.at(p),d_RR.at(p),d_DD.at(p),Ey_ij,*this,'H')) throw runtime_error("fit");
                    for (int t=0;t<op().nt();t++) {
                        etaH_ofs << log(Ey_ij.at(t)) << '\t';
                        loglike += -log(d_tau2H.at(p))-pow(yH().at(p).at(q).at(j*op().nt()+t)-log(Ey_ij.at(t)),2)/d_tau2H.at(p)/2;
                    }
                } else {
                    for (int t=0;t<op().nt();t++) etaH_ofs << "NA\t";
                }
            }
            if (op().Mbool()) etaM_ofs<<'\n';
            etaH_ofs<<'\n';
            loglike_ofs << loglike << '\n';
        }

    }
    cout<<'\n';
}


#include <iostream>
#include <fstream>
#include <iomanip>
#include <vector>
#include <string>
#include <exception>
#include <iterator>

#include <boost/algorithm/string.hpp>

#include "biu/OptionParser.hh"

#include <sgm/GM_vf2.hh>
#include <sgm/MR_Storing.hh>
#include <sgm/SubGraph.hh>
#include <sgm/RP_Hanser96.hh>

#include <ggl/Graph.hh>
#include <ggl/Graph_GML_writer.hh>
#include <ggl/Graph_GMLparser.hh>

#include "version.hh"

	using namespace ggl;

//////////////////////////////////////////////////////////////////////////
#ifndef ARGEXCEPTION_
#define ARGEXCEPTION_

	  /*! Exception class for exeptions thrown during argument and input parsing.
	   */
	class ArgException : public std::exception {
	public:
		  //! the error message
		std::string errorMsg;
		ArgException( std::string errorMsg_ ) : errorMsg(errorMsg_) {}
		virtual ~ArgException() throw (){}
		virtual const char* what() const throw() {
			return errorMsg.c_str();
		}
	};

#endif
//////////////////////////////////////////////////////////////////////////

	typedef std::vector< Graph > Graph_container;

/////////////////////////////////////////////////////////////////////////////

void
initAllowedArguments( biu::OptionMap & allowedArgs, std::string &infoText );

//////////////////////////////////////////////////////////////////////////

void
parseGraphs(	std::istream & in
				, Graph_container & toFill
				, const size_t linesRead ) throw(std::exception);

//////////////////////////////////////////////////////////////////////////

/*!
 *
 * Dummy aromaticity perception to access rings to predict
 *
 */
class RR_list
	: public sgm::RingReporter
{
public:

	typedef std::vector< RingList > RingListVec;

protected:

	  //! container that will hold all rings of the current graph
	RingListVec allRings;

	  /*!
	   * Comparison of ring lists based on the ring size.
	   */
	class RingSizeLess {
	public:

		  /*!
		   * less comparison of two ring descriptors.
		   * @param x the ring to be checked if smaller
		   * @param y the ring to compare to
		   * @return true if x.size < y.size;
		   * 		false otherwise
		   */
		bool operator () ( const RingList & x
						  , const RingList & y )
		{
			return x.size() < y.size();
		}
	};

public:

	RR_list()
	 : allRings()
	{}

	virtual ~RR_list()
	{}

	void
	reportRing(	const sgm::Graph_Interface& graph
				, const RingList & ringList )
	{
		  // store description
		allRings.push_back( ringList );
	}

	RingListVec &
	getAllRings()
	{
		return allRings;
	}

	void
	pruneFusedRings( RR_list::RingListVec & rings );

	//////////////////////////////////////////////////////////////////////
	//////////////////////////////////////////////////////////////////////
};

//////////////////////////////////////////////////////////////////////////

int main( int argc, char** argv ) {

	//////////////////////////////////////////////////////////////
	// variables
	//////////////////////////////////////////////////////////////

	std::istream* in = NULL;
	std::ifstream* inFile = NULL;
	size_t inLine = 0;

	std::ostream* out = &std::cout;
	std::ofstream* outFile = NULL;

	//////////////////////////////////////////////////////////////
	// parameter parsing and checking
	//////////////////////////////////////////////////////////////

	biu::OptionMap allowedArgs;	//< map of allowed arguments
	std::string infoText;		//< info string of the program
	initAllowedArguments(allowedArgs,infoText);	// init

		// parse programm arguments
	biu::COptionParser opts = biu::COptionParser(	allowedArgs, argc,
													argv, infoText);
		// check arguments parseable and all mandatory arguments given
		// + help output if needed
	if (opts.argExist("help")) {
		opts.coutUsage();
		return 0;
	}
	if (opts.argExist("version")) {
		giveVersion();
		return 0;
	}
	if (!opts.noErrors()) {
		return -1;
	}

	int exitValue = 0;

	try {

		  // set graph input stream
		if (boost::iequals(opts.getStrVal("graph"),"STDIN")) {
			  // read from STDIN
			in = &std::cin;
		} else {
			  // read from file
			inFile = new std::ifstream( opts.getStrVal("graph").c_str()
										, std::ifstream::in );
			if (!inFile->is_open()) {
				std::ostringstream oss;
				oss	<<"cannot open input file '" <<opts.getStrVal("graph") <<"'";
				throw ArgException(oss.str());
			}
			in = inFile;
		}
		  // set output stream
		if (opts.getStrVal("out").size() == 0) {
			throw ArgException("no output file given");
		} else if ( !boost::iequals(opts.getStrVal("out"),"STDOUT")) {
			outFile = new std::ofstream(	opts.getStrVal("out").c_str()
											, std::ofstream::out );
			if (!outFile->is_open()) {
				std::ostringstream oss;
				oss	<<"cannot open output file '" <<opts.getStrVal("out") <<"'";
				throw ArgException(oss.str());
			}
			out = outFile;
		}

		const int maxRingSize = opts.getIntVal("maxRingSize");
		if (maxRingSize < 3) {
			throw ArgException("'maxRingSize' has to be >= 3");
		}

		//////////////////////////////////////////////////////////////
		// parse graphs from input
		//////////////////////////////////////////////////////////////

		  // get master graph to match onto
		Graph_container parsedGraphs;
		parseGraphs( *in, parsedGraphs, inLine );
		if (parsedGraphs.size() != 1)  {
			throw ArgException("could not parse only one graph from 'graph'");
		}
		Graph & graph = *(parsedGraphs.begin());

		//////////////////////   RING ENUMERATION  /////////////////////////////////////

		  // store all rings up to the maximally predictable size
		sgm::RP_Hanser96 ringFinder;
		  // setup graph interface
		Graph_boost graphWrapper(graph);
		  // setup ring container
		RR_list reporter;
		  // run ring perception
		ringFinder.findRings( graphWrapper, reporter, maxRingSize );

		  // prune rings if needed
		if (opts.argExist("pruneFusedRings")) {
			  // prune rings to remove fused representation
			reporter.pruneFusedRings( reporter.getAllRings() );
		}

		//////////////////////   OUTPUT  /////////////////////////////////////

		const RR_list::RingListVec & allRings = reporter.getAllRings();

		for (size_t i=0; i< allRings.size(); ++i) {

			const sgm::RingReporter::RingList & nodes = allRings.at(i);

			for (sgm::RingReporter::RingList::const_iterator n = nodes.begin(); n!= nodes.end(); ++n) {
				*out <<" "<<*n;
			}
			*out <<std::endl;

		}

	} catch (std::exception& ex) {
		std::cerr <<"\n\n ERROR : " <<ex.what() <<"\n"<<std::endl;
		exitValue = -1;
	}

	//////////////////////////////////////////////////////////////
	// final stream handling
	//////////////////////////////////////////////////////////////

	in = &std::cin;
	out = &std::cout;
	if (inFile != NULL)		{ inFile->close(); delete inFile; }
	if (outFile != NULL)	{ outFile->close(); delete outFile; }

	return exitValue;
}

//////////////////////////////////////////////////////////////////////////

/*!
 * Initialises allowed parameters and their default values and prepares an
 * information string for the tool.
 */
void
initAllowedArguments(biu::OptionMap & allowedArgs, std::string &infoText )
{
	infoText = "\n"
		"Enumerates all rings for a given graph.\n"
		;

	allowedArgs.push_back(biu::COption(
							"graph", true, biu::COption::STRING,
							"The name of the file that holds the graph encoding in GML or 'STDIN' when to read GML from standard input",
							"STDIN"));
	allowedArgs.push_back(biu::COption(
							"maxRingSize", true, biu::COption::INT,
							"The maximal ring size to be considered",
							"15"));
	allowedArgs.push_back(biu::COption(
							"pruneFusedRings", true, biu::COption::BOOL,
							"prunes the outer rings that emerge if two rings share a single edge"));
	allowedArgs.push_back(biu::COption(
							"out", true, biu::COption::STRING,
							"Output file name or 'STDOUT' when to write to standard output",
							"STDOUT"));
	allowedArgs.push_back(biu::COption(
							"help", true, biu::COption::BOOL,
							"Displays help on all parameters"));
	allowedArgs.push_back(biu::COption(
							"version", true, biu::COption::BOOL,
							"Version information"));
}

//////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////

	void
	RR_list::
	pruneFusedRings( RR_list::RingListVec & rings )
	{

		  // sort rings by size
		RingSizeLess ringSizeLess;

		std::sort( rings.begin(), rings.end(), ringSizeLess );

		  //! defines an edge; NOTE: use as ordered pairs, i.e. first <= second
		typedef std::pair< size_t, size_t > Edge;
		  //! container to maintain aromatic edges without duplicates
		typedef std::set< Edge > EdgeSet;

		typedef std::vector< EdgeSet > EdgeSetVec;

		EdgeSetVec edges(rings.size());

		  // generate edge list description for each ring
		for (size_t i=0; i<rings.size(); ++i) {
			RingList::const_iterator cur=rings.at(i).begin(), last=rings.at(i).begin();
			for (cur++; cur!=rings.at(i).end(); ++cur,++last) {
				if (*cur<*last) {
					edges[i].insert( Edge(*cur,*last) );
				} else {
					edges[i].insert( Edge(*last,*cur) );
				}
			}
		}

//std::cerr <<"\nNEXT\n";
		  // prune rings
		size_t numOfprunedRings = 0;
		for (int i=rings.size()-1; i>=0; i--) {
//std::cerr <<"next ring = ";
//for (RingList::const_iterator x=rings.at(i).begin(); x!=rings.at(i).end(); ++x) {
//	std::cerr <<*x <<" ";
//}
//std::cerr <<"\n";
			bool canBePruned = false;
			for( size_t c=0; !canBePruned && c < (size_t)i; ++c ) {
//std::cerr <<"  compare to ring = ";
//for (RingList::const_iterator x=rings.at(c).begin(); x!=rings.at(c).end(); ++x) {
//	std::cerr <<*x <<" ";
//}
//std::cerr <<"\n";
				  // skip already deleted rings
				if (edges.at(c).empty()) {
					continue;
				}
				  // stop if ring is of equal size or larger
				if (edges.at(c).size() >= edges.at(i).size()) {
					break;
				}
				  // check for containment of this ring c within ring i

				  // get iterators on edge sets to compare
				EdgeSet::const_iterator largeEdge = edges.at(i).begin(), largeEnd = edges.at(i).end();
				EdgeSet::const_iterator curEdge = edges.at(c).begin(), curEnd = edges.at(c).end();
				  // sequential check
				size_t curOverlap = 0;
				  // check only until up to two differences have been found
				while(largeEdge != largeEnd && curEdge != curEnd) {
					  // check if overlap
					if (*largeEdge == *curEdge) {
						++curOverlap;
						++largeEdge;
						++curEdge;
					  // check which counter to increase
					} else if (*largeEdge < *curEdge) {
						++largeEdge;
					} else {
						++curEdge;
					}
				}
				  // check if smaller current ring is contained excluding one edge
				  // if only one edge is left : ring is contained
				canBePruned = (curOverlap+1 == edges.at(c).size());

			}
			  // prune ring if obsolete
			if (canBePruned) {
				  // remove edge information for ring at position i
				edges[i].clear();
				  // count pruning
				numOfprunedRings++;
//std::cerr <<"==> pruned\n";
			}
		}

		  // shift remaining rings to the front
		if (numOfprunedRings>0) {
			size_t fillPos = 0;
			for( size_t i=0; i<rings.size(); ++i ) {
				if (!edges.at(i).empty()) {
					  // check if overwrite is needed
					if (fillPos < i) {
						rings[fillPos] = rings[i];
					}
					  // increase overwrite counter
					fillPos++;
				}
			}
			  // drop deleted elements
			rings.resize(fillPos);
		}

	}

//////////////////////////////////////////////////////////////////////////

/*!
 * Parses the given input stream for graphs in GML format and adds each graph
 * to the given container.
 *
 * Each graph has to be headed by a comment line starting with a '#' character
 * that specifies the name of the graph.
 *
 * @param in the input stream to read from
 * @param toFill the container to add the graphs to
 * @param linesRead the number of lines already read from input (needed for
 *                  error reporting)
 *
 */
void
parseGraphs(	std::istream & in
				, Graph_container & toFill
				, const size_t linesRead = 0 )  throw(std::exception)
{
	std::string line;
	size_t lineNumber = linesRead;
	std::string graphString = std::string("");
	  // parse whole input stream
	while (in.good()) {
		  // read next line
		std::getline( in, line );
		lineNumber++;

		  // check if next rule header or end of file was reached
		if (line[0] == '#' || in.eof()) {
			  // convert rule string to rule object
			if (graphString.size() > 0) {
				std::pair<ggl::Graph, int>
						ret = ggl::Graph_GMLparser::parseGraph( graphString );
				  // check for parsing error
				if (ret.second != -1) {
					std::ostringstream oss;
					oss	<<" Graph parsing error in graph specification BEFORE line "
						<<line
						<<" at graph string position " <<ret.second;
					throw ArgException(oss.str());
				}
				toFill.push_back(ret.first);
			}
			  // clear processed rule
			graphString.clear();
		} else {
			  // append line content to rule string
			graphString.append(line);
		}
	}
}

//////////////////////////////////////////////////////////////////////////


// ====================================================================
// This file is part of FlexibleSUSY.
//
// FlexibleSUSY 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.
//
// FlexibleSUSY 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 FlexibleSUSY.  If not, see
// <http://www.gnu.org/licenses/>.
// ====================================================================

// File generated at Sat 27 Aug 2016 12:40:25

#include "NSM_slha_io.hpp"
#include "NSM_input_parameters.hpp"
#include "NSM_info.hpp"
#include "logger.hpp"
#include "wrappers.hpp"
#include "numerics2.hpp"
#include "config.h"

#include <fstream>
#include <sstream>
#include <iostream>
#include <boost/bind.hpp>

#define Pole(p) physical.p
#define PHYSICAL(p) model.get_physical().p
#define PHYSICAL_SLHA(p) model.get_physical_slha().p
#define LOCALPHYSICAL(p) physical.p
#define MODELPARAMETER(p) model.get_##p()
#define DEFINE_PHYSICAL_PARAMETER(p) decltype(LOCALPHYSICAL(p)) p;
#define LowEnergyConstant(p) Electroweak_constants::p

using namespace softsusy;

namespace flexiblesusy {

char const * const NSM_slha_io::drbar_blocks[NUMBER_OF_DRBAR_BLOCKS] =
   { "gauge", "Yu", "Yd", "Ye", "SM", "HMIX", "NMSSMRUN" }
;

NSM_slha_io::NSM_slha_io()
   : slha_io()
   , print_imaginary_parts_of_majorana_mixings(false)
{
}

void NSM_slha_io::clear()
{
   slha_io.clear();
}

void NSM_slha_io::set_print_imaginary_parts_of_majorana_mixings(bool flag)
{
   print_imaginary_parts_of_majorana_mixings = flag;
}

/**
 * Stores the EXTPAR input parameters in the SLHA object.
 *
 * @param input struct of input parameters
 */
void NSM_slha_io::set_extpar(const NSM_input_parameters& input)
{
   std::ostringstream extpar;

   extpar << "Block EXTPAR\n";
   extpar << FORMAT_ELEMENT(0, input.Qin, "Qin");
   slha_io.set_block(extpar);

}

/**
 * Stores the MINPAR input parameters in the SLHA object.
 *
 * @param input struct of input parameters
 */
void NSM_slha_io::set_minpar(const NSM_input_parameters& input)
{
   std::ostringstream minpar;

   minpar << "Block MINPAR\n";
   minpar << FORMAT_ELEMENT(1, input.Lambda1Input, "Lambda1Input");
   minpar << FORMAT_ELEMENT(2, input.Lambda2Input, "Lambda2Input");
   minpar << FORMAT_ELEMENT(3, input.Lambda3Input, "Lambda3Input");
   minpar << FORMAT_ELEMENT(4, input.Lambda4Input, "Lambda4Input");
   minpar << FORMAT_ELEMENT(5, input.Lambda5Input, "Lambda5Input");
   minpar << FORMAT_ELEMENT(6, input.vSInput, "vSInput");
   slha_io.set_block(minpar);

}

/**
 * Stores the SMINPUTS input parameters in the SLHA object.
 *
 * @param qedqcd class of Standard Model parameters
 */
void NSM_slha_io::set_sminputs(const softsusy::QedQcd& qedqcd)
{
   slha_io.set_sminputs(qedqcd);
}

/**
 * Stores the spectrum generator information in the SPINFO block in
 * the SLHA object.
 *
 * @param problems struct with parameter point problems
 */
void NSM_slha_io::set_spinfo(const Problems<NSM_info::NUMBER_OF_PARTICLES>& problems)
{
   std::ostringstream spinfo;
   spinfo << "Block SPINFO\n"
          << FORMAT_SPINFO(1, PKGNAME)
          << FORMAT_SPINFO(2, FLEXIBLESUSY_VERSION);

   if (problems.have_warning()) {
      std::ostringstream warnings;
      problems.print_warnings(warnings);
      spinfo << FORMAT_SPINFO(3, warnings.str());
   }

   if (problems.have_problem()) {
      std::ostringstream problems_str;
      problems.print_problems(problems_str);
      spinfo << FORMAT_SPINFO(4, problems_str.str());
   }

   spinfo << FORMAT_SPINFO(5, NSM_info::model_name)
          << FORMAT_SPINFO(9, SARAH_VERSION);

   slha_io.set_block(spinfo, SLHA_io::front);
}

/**
 * Stores the particle masses in the SLHA object.
 *
 * @param physical struct of physical parameters
 *
 * @param write_sm_masses flag to indicate if Standard Model
 *    particle masses should be written as well
 */
void NSM_slha_io::set_mass(const NSM_physical& physical,
                                   bool write_sm_masses)
{
   std::ostringstream mass;

   mass << "Block MASS\n"
      << FORMAT_MASS(24, LOCALPHYSICAL(MVWp), "VWp")
      << FORMAT_MASS(25, LOCALPHYSICAL(Mhh(0)), "hh(1)")
      << FORMAT_MASS(35, LOCALPHYSICAL(Mhh(1)), "hh(2)")
   ;

   if (write_sm_masses) {
      mass
         << FORMAT_MASS(21, LOCALPHYSICAL(MVG), "VG")
         << FORMAT_MASS(12, LOCALPHYSICAL(MFv(0)), "Fv(1)")
         << FORMAT_MASS(14, LOCALPHYSICAL(MFv(1)), "Fv(2)")
         << FORMAT_MASS(16, LOCALPHYSICAL(MFv(2)), "Fv(3)")
         << FORMAT_MASS(1, LOCALPHYSICAL(MFd(0)), "Fd(1)")
         << FORMAT_MASS(3, LOCALPHYSICAL(MFd(1)), "Fd(2)")
         << FORMAT_MASS(5, LOCALPHYSICAL(MFd(2)), "Fd(3)")
         << FORMAT_MASS(2, LOCALPHYSICAL(MFu(0)), "Fu(1)")
         << FORMAT_MASS(4, LOCALPHYSICAL(MFu(1)), "Fu(2)")
         << FORMAT_MASS(6, LOCALPHYSICAL(MFu(2)), "Fu(3)")
         << FORMAT_MASS(11, LOCALPHYSICAL(MFe(0)), "Fe(1)")
         << FORMAT_MASS(13, LOCALPHYSICAL(MFe(1)), "Fe(2)")
         << FORMAT_MASS(15, LOCALPHYSICAL(MFe(2)), "Fe(3)")
         << FORMAT_MASS(22, LOCALPHYSICAL(MVP), "VP")
         << FORMAT_MASS(23, LOCALPHYSICAL(MVZ), "VZ")
      ;
   }

   slha_io.set_block(mass);

}

/**
 * Stores the mixing matrices in the SLHA object.
 *
 * @param physical struct of physical parameters
 *
 * @param write_sm_mixing_matrics flag to indicate if Standard Model
 *    particle mixing matrices should be written as well
 */
void NSM_slha_io::set_mixing_matrices(const NSM_physical& physical,
                                              bool write_sm_mixing_matrics)
{

   if (write_sm_mixing_matrics) {
      slha_io.set_block("UULMIX", LOCALPHYSICAL(Vu), "Vu");
      slha_io.set_block("UDLMIX", LOCALPHYSICAL(Vd), "Vd");
      slha_io.set_block("UURMIX", LOCALPHYSICAL(Uu), "Uu");
      slha_io.set_block("UDRMIX", LOCALPHYSICAL(Ud), "Ud");
      slha_io.set_block("UELMIX", LOCALPHYSICAL(Ve), "Ve");
      slha_io.set_block("UERMIX", LOCALPHYSICAL(Ue), "Ue");
   }

   if (print_imaginary_parts_of_majorana_mixings) {
   }

}

void NSM_slha_io::set_ckm(
   const Eigen::Matrix<std::complex<double>,3,3>& ckm_matrix,
   double scale)
{
   slha_io.set_block("VCKM"  , ckm_matrix.real(), "Re(CKM)", scale);
   slha_io.set_block("IMVCKM", ckm_matrix.imag(), "Im(CKM)", scale);
}

void NSM_slha_io::set_pmns(
   const Eigen::Matrix<std::complex<double>,3,3>& pmns_matrix,
   double scale)
{
   slha_io.set_block("VPMNS"  , pmns_matrix.real(), "Re(PMNS)", scale);
   slha_io.set_block("IMVPMNS", pmns_matrix.imag(), "Im(PMNS)", scale);
}

/**
 * Write SLHA object to file.
 *
 * @param file_name file name
 */
void NSM_slha_io::write_to_file(const std::string& file_name)
{
   slha_io.write_to_file(file_name);
}

/**
 * Read (DR-bar) model parameter output scale from MODSEL entry 12
 */
double NSM_slha_io::get_parameter_output_scale() const
{
   return slha_io.get_modsel().parameter_output_scale;
}

/**
 * Read SLHA object from file
 *
 * @param file_name file name
 */
void NSM_slha_io::read_from_file(const std::string& file_name)
{
   slha_io.read_from_file(file_name);
   slha_io.read_modsel();
}

/**
 * Read SLHA object from source
 *
 * calls SLHA_io::read_from_source()
 *
 * @param source source name
 */
void NSM_slha_io::read_from_source(const std::string& source)
{
   slha_io.read_from_source(source);
   slha_io.read_modsel();
}

/**
 * Read SLHA object from stream
 *
 * @param istr stream name
 */
void NSM_slha_io::read_from_stream(std::istream& istr)
{
   slha_io.read_from_stream(istr);
}

/**
 * Fill struct of model input parameters from SLHA object (MINPAR and
 * EXTPAR blocks)
 *
 * @param input struct of model input parameters
 */
void NSM_slha_io::fill(NSM_input_parameters& input) const
{
   SLHA_io::Tuple_processor minpar_processor
      = boost::bind(&NSM_slha_io::fill_minpar_tuple, boost::ref(input), _1, _2);
   SLHA_io::Tuple_processor extpar_processor
      = boost::bind(&NSM_slha_io::fill_extpar_tuple, boost::ref(input), _1, _2);

   slha_io.read_block("MINPAR", minpar_processor);
   slha_io.read_block("EXTPAR", extpar_processor);

}

/**
 * Reads DR-bar parameters from a SLHA output file.
 */
void NSM_slha_io::fill_drbar_parameters(NSM_mass_eigenstates& model) const
{
   model.set_g1(slha_io.read_entry("gauge", 1) * 1);
   model.set_g2(slha_io.read_entry("gauge", 2));
   model.set_g3(slha_io.read_entry("gauge", 3));
   {
      Eigen::Matrix<double,3,3> Yu;
      slha_io.read_block("Yu", Yu);
      model.set_Yu(Yu);
   }
   {
      Eigen::Matrix<double,3,3> Yd;
      slha_io.read_block("Yd", Yd);
      model.set_Yd(Yd);
   }
   {
      Eigen::Matrix<double,3,3> Ye;
      slha_io.read_block("Ye", Ye);
      model.set_Ye(Ye);
   }
   model.set_mH2(slha_io.read_entry("SM", 1));
   model.set_Lambda1(slha_io.read_entry("SM", 2));
   model.set_vH(slha_io.read_entry("HMIX", 3));
   model.set_vS(slha_io.read_entry("NMSSMRUN", 5));

   model.set_scale(read_scale());
}

/**
 * Reads DR-bar parameters, pole masses and mixing matrices (in
 * Haber-Kane convention) from a SLHA output file.
 */
void NSM_slha_io::fill(NSM_mass_eigenstates& model) const
{
   fill_drbar_parameters(model);

   NSM_physical physical_hk;
   fill_physical(physical_hk);
   physical_hk.convert_to_hk();
   model.get_physical() = physical_hk;
}

/**
 * Fill struct of extra physical input parameters from SLHA object
 * (FlexibleSUSYInput block)
 *
 * @param settings struct of physical input parameters
 */
void NSM_slha_io::fill(Physical_input& input) const
{
   slha_io.fill(input);
}

/**
 * Fill struct of spectrum generator settings from SLHA object
 * (FlexibleSUSY block)
 *
 * @param settings struct of spectrum generator settings
 */
void NSM_slha_io::fill(Spectrum_generator_settings& settings) const
{
   slha_io.fill(settings);
}

void NSM_slha_io::fill_minpar_tuple(NSM_input_parameters& input,
                                                int key, double value)
{
   switch (key) {
   case 1: input.Lambda1Input = value; break;
   case 2: input.Lambda2Input = value; break;
   case 3: input.Lambda3Input = value; break;
   case 4: input.Lambda4Input = value; break;
   case 5: input.Lambda5Input = value; break;
   case 6: input.vSInput = value; break;
   default: WARNING("Unrecognized entry in block MINPAR: " << key); break;
   }

}

void NSM_slha_io::fill_extpar_tuple(NSM_input_parameters& input,
                                                int key, double value)
{
   switch (key) {
   case 0: input.Qin = value; break;
   default: WARNING("Unrecognized entry in block EXTPAR: " << key); break;
   }

}

/**
 * Reads pole masses and mixing matrices from a SLHA output file.
 */
void NSM_slha_io::fill_physical(NSM_physical& physical) const
{
   {
      DEFINE_PHYSICAL_PARAMETER(Vu);
      slha_io.read_block("UULMIX", Vu);
      LOCALPHYSICAL(Vu) = Vu;
   }
   {
      DEFINE_PHYSICAL_PARAMETER(Vd);
      slha_io.read_block("UDLMIX", Vd);
      LOCALPHYSICAL(Vd) = Vd;
   }
   {
      DEFINE_PHYSICAL_PARAMETER(Uu);
      slha_io.read_block("UURMIX", Uu);
      LOCALPHYSICAL(Uu) = Uu;
   }
   {
      DEFINE_PHYSICAL_PARAMETER(Ud);
      slha_io.read_block("UDRMIX", Ud);
      LOCALPHYSICAL(Ud) = Ud;
   }
   {
      DEFINE_PHYSICAL_PARAMETER(Ve);
      slha_io.read_block("UELMIX", Ve);
      LOCALPHYSICAL(Ve) = Ve;
   }
   {
      DEFINE_PHYSICAL_PARAMETER(Ue);
      slha_io.read_block("UERMIX", Ue);
      LOCALPHYSICAL(Ue) = Ue;
   }

   LOCALPHYSICAL(MVG) = slha_io.read_entry("MASS", 21);
   LOCALPHYSICAL(MFv)(0) = slha_io.read_entry("MASS", 12);
   LOCALPHYSICAL(MFv)(1) = slha_io.read_entry("MASS", 14);
   LOCALPHYSICAL(MFv)(2) = slha_io.read_entry("MASS", 16);
   LOCALPHYSICAL(MVP) = slha_io.read_entry("MASS", 22);
   LOCALPHYSICAL(MVZ) = slha_io.read_entry("MASS", 23);
   LOCALPHYSICAL(MFd)(0) = slha_io.read_entry("MASS", 1);
   LOCALPHYSICAL(MFd)(1) = slha_io.read_entry("MASS", 3);
   LOCALPHYSICAL(MFd)(2) = slha_io.read_entry("MASS", 5);
   LOCALPHYSICAL(MFu)(0) = slha_io.read_entry("MASS", 2);
   LOCALPHYSICAL(MFu)(1) = slha_io.read_entry("MASS", 4);
   LOCALPHYSICAL(MFu)(2) = slha_io.read_entry("MASS", 6);
   LOCALPHYSICAL(MFe)(0) = slha_io.read_entry("MASS", 11);
   LOCALPHYSICAL(MFe)(1) = slha_io.read_entry("MASS", 13);
   LOCALPHYSICAL(MFe)(2) = slha_io.read_entry("MASS", 15);
   LOCALPHYSICAL(Mhh)(0) = slha_io.read_entry("MASS", 25);
   LOCALPHYSICAL(Mhh)(1) = slha_io.read_entry("MASS", 35);
   LOCALPHYSICAL(MVWp) = slha_io.read_entry("MASS", 24);

}

/**
 * Reads the renormalization scales from all DR-bar parameter blocks.
 * If blocks with different scales are found the last scale is
 * returned and a warning is printed.
 *
 * @return common renormalization scale
 */
double NSM_slha_io::read_scale() const
{
   double scale = 0.;

   for (unsigned i = 0; i < NUMBER_OF_DRBAR_BLOCKS; i++) {
      const double block_scale = slha_io.read_scale(drbar_blocks[i]);
      if (!is_zero(block_scale)) {
         if (!is_zero(scale) && !is_equal(scale, block_scale))
            WARNING("DR-bar parameters defined at different scales");
         scale = block_scale;
      }
   }

   return scale;
}

} // namespace flexiblesusy


//---------------------------------------------------------------------------//
// Copyright (c) 2019 Mikhail Komarov <nemo@nil.foundation>
//
// MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//---------------------------------------------------------------------------//

#ifndef CRYPTO3_BLOCK_MODE_CIPHER_BLOCK_CHAINING_HPP
#define CRYPTO3_BLOCK_MODE_CIPHER_BLOCK_CHAINING_HPP

#include <boost/integer.hpp>

#include <nil/crypto3/modes/mode.hpp>
#include <nil/crypto3/modes/cts.hpp>
#include <nil/crypto3/modes/padding.hpp>

#include <nil/crypto3/codec/algorithm/encode.hpp>

//#include <nil/crypto3/codec/logic.hpp>

namespace nil {
    namespace crypto3 {
        namespace block {
            namespace modes {
                namespace detail {
                    template<typename Cipher, typename Padding, template<typename> class Allocator = std::allocator>
                    struct ctr_policy {
                        typedef std::size_t size_type;

                        typedef Cipher cipher_type;
                        typedef Padding padding_type;

                        constexpr static const size_type block_bits = cipher_type::block_bits;
                        constexpr static const size_type block_words = cipher_type::block_words;
                        typedef typename cipher_type::block_type block_type;

                        typedef std::vector<boost::uint_t<CHAR_BIT>, Allocator<boost::uint_t<CHAR_BIT>>> iv_type;
                    };

                    template<typename Cipher, typename Padding, typename CiphertextStealingMode>
                    struct ctr_encryption_policy : public ctr_policy<Cipher, Padding> {};

                    template<typename Cipher, typename Padding>
                    struct ctr_encryption_policy<Cipher, Padding, cts<0, Cipher, Padding>>
                        : public ctr_policy<Cipher, Padding> {
                        typedef typename ctr_policy<Cipher, Padding>::size_type size_type;

                        typedef typename ctr_policy<Cipher, Padding>::cipher_type cipher_type;
                        typedef typename ctr_policy<Cipher, Padding>::padding_type padding_type;

                        constexpr static const size_type block_bits = ctr_policy<Cipher, Padding>::block_bits;
                        constexpr static const size_type block_words = ctr_policy<Cipher, Padding>::block_words;
                        typedef typename ctr_policy<Cipher, Padding>::block_type block_type;

                        typedef typename ctr_policy<Cipher, Padding>::iv_type iv_type;

                        inline static block_type begin_message(const cipher_type &cipher, const block_type &plaintext,
                                                               const iv_type &iv = iv_type()) {
                            block_type block = {0};

                            //                        codec::encode<codec::encoder::logic_xor>(plaintext, iv,
                            //                        block.begin());

                            return cipher.encrypt(block);
                        }

                        inline static block_type process_block(const cipher_type &cipher, const block_type &plaintext,
                                                               const block_type &previous = block_type()) {
                            block_type block = {0};

                            //                        codec::encode<codec::encoder::logic_xor>(plaintext, previous,
                            //                        block.begin());

                            return cipher.encrypt(block);
                        }

                        inline static block_type end_message(const cipher_type &cipher, const block_type &plaintext,
                                                             const block_type &previous = block_type(),
                                                             const iv_type &iv = iv_type()) {
                            block_type result = {0};
                            std::array<uint8_t, block_bits / CHAR_BIT> byte_array = {0};

                            pack(plaintext, byte_array);

                            size_type rem = std::count(byte_array.begin(), byte_array.end(), block_type::value_type()) %
                                            (block_bits / CHAR_BIT);
                            if (rem || padding_type::always_pad) {
                                block_type padbuffer = padding_type::pad(plaintext, rem), block;
                                //                            codec::encode<codec::encoder::logic_xor>(!iv.empty() ? iv
                                //                            : previous, padbuffer, block .begin());
                                result = cipher.encrypt(block);
                            }

                            return result;
                        }
                    };

                    template<typename Cipher, typename Padding>
                    struct ctr_encryption_policy<Cipher, Padding, cts<1, Cipher, Padding>>
                        : public ctr_policy<Cipher, Padding> {
                        typedef typename ctr_policy<Cipher, Padding>::size_type size_type;

                        typedef typename ctr_policy<Cipher, Padding>::cipher_type cipher_type;
                        typedef typename ctr_policy<Cipher, Padding>::padding_type padding_type;

                        constexpr static const size_type block_bits = ctr_policy<Cipher, Padding>::block_bits;
                        constexpr static const size_type block_words = ctr_policy<Cipher, Padding>::block_words;
                        typedef typename ctr_policy<Cipher, Padding>::block_type block_type;

                        typedef typename ctr_policy<Cipher, Padding>::iv_type iv_type;

                        inline static block_type begin_message(const cipher_type &cipher, const block_type &plaintext,
                                                               const iv_type &iv = iv_type()) {
                            block_type block = {0};

                            //                        codec::encode<codec::encoder::logic_xor>(plaintext, iv,
                            //                        block.begin());

                            return cipher.encrypt(block);
                        }

                        inline static block_type process_block(const cipher_type &cipher, const block_type &plaintext,
                                                               const block_type &previous = block_type()) {
                            block_type block = {0};

                            //                        codec::encode<codec::encoder::logic_xor>(plaintext, previous,
                            //                        block.begin());

                            return cipher.encrypt(block);
                        }

                        inline static block_type end_message(const cipher_type &cipher, const block_type &plaintext,
                                                             const block_type &previous = block_type(),
                                                             const iv_type &iv = iv_type()) {
                            block_type result = {0};
                            std::array<uint8_t, block_bits / CHAR_BIT> byte_array = {0};

                            pack(plaintext, byte_array);

                            size_type rem = std::count(byte_array.begin(), byte_array.end(), block_type::value_type()) %
                                            (block_bits / CHAR_BIT);
                            if (rem || padding_type::always_pad) {
                                block_type padbuffer = padding_type::pad(plaintext, rem), block;
                                //                            codec::encode<codec::encoder::logic_xor>(!iv.empty() ? iv
                                //                            : previous, padbuffer, block .begin());
                                result = cipher.encrypt(block);
                            }

                            return result;
                        }
                    };

                    template<typename Cipher, typename Padding>
                    struct ctr_encryption_policy<Cipher, Padding, cts<2, Cipher, Padding>>
                        : public ctr_policy<Cipher, Padding> {
                        typedef typename ctr_policy<Cipher, Padding>::size_type size_type;

                        typedef typename ctr_policy<Cipher, Padding>::cipher_type cipher_type;
                        typedef typename ctr_policy<Cipher, Padding>::padding_type padding_type;

                        constexpr static const size_type block_bits = ctr_policy<Cipher, Padding>::block_bits;
                        constexpr static const size_type block_words = ctr_policy<Cipher, Padding>::block_words;
                        typedef typename ctr_policy<Cipher, Padding>::block_type block_type;

                        typedef typename ctr_policy<Cipher, Padding>::iv_type iv_type;

                        inline static block_type begin_message(const cipher_type &cipher, const block_type &plaintext,
                                                               const iv_type &iv = iv_type()) {
                            block_type block = {0};

                            //                        codec::encode<codec::encoder::logic_xor>(plaintext, iv,
                            //                        block.begin());

                            return cipher.encrypt(block);
                        }

                        inline static block_type process_block(const cipher_type &cipher, const block_type &plaintext,
                                                               const block_type &previous = block_type()) {
                            block_type block = {0};

                            //                        codec::encode<codec::encoder::logic_xor>(plaintext, previous,
                            //                        block.begin());

                            return cipher.encrypt(block);
                        }

                        inline static block_type end_message(const cipher_type &cipher, const block_type &plaintext,
                                                             const block_type &previous = block_type(),
                                                             const iv_type &iv = iv_type()) {
                            block_type result = {0};
                            std::array<uint8_t, block_bits / CHAR_BIT> byte_array = {0};

                            pack(plaintext, byte_array);

                            size_type rem = std::count(byte_array.begin(), byte_array.end(), block_type::value_type()) %
                                            (block_bits / CHAR_BIT);
                            if (rem || padding_type::always_pad) {
                                block_type padbuffer = padding_type::pad(plaintext, rem), block;
                                //                            codec::encode<codec::encoder::logic_xor>(!iv.empty() ? iv
                                //                            : previous, padbuffer, block .begin());
                                result = cipher.encrypt(block);
                            }

                            return result;
                        }
                    };

                    template<typename Cipher, typename Padding>
                    struct ctr_encryption_policy<Cipher, Padding, cts<3, Cipher, Padding>>
                        : public ctr_policy<Cipher, Padding> {
                        typedef typename ctr_policy<Cipher, Padding>::size_type size_type;

                        typedef typename ctr_policy<Cipher, Padding>::cipher_type cipher_type;
                        typedef typename ctr_policy<Cipher, Padding>::padding_type padding_type;

                        constexpr static const size_type block_bits = ctr_policy<Cipher, Padding>::block_bits;
                        constexpr static const size_type block_words = ctr_policy<Cipher, Padding>::block_words;
                        typedef typename ctr_policy<Cipher, Padding>::block_type block_type;

                        typedef typename ctr_policy<Cipher, Padding>::iv_type iv_type;

                        inline static block_type begin_message(const cipher_type &cipher, const block_type &plaintext,
                                                               const iv_type &iv = iv_type()) {
                            block_type block = {0};

                            //                        codec::encode<codec::encoder::logic_xor>(plaintext, iv,
                            //                        block.begin());

                            return cipher.encrypt(block);
                        }

                        inline static block_type process_block(const cipher_type &cipher, const block_type &plaintext,
                                                               const block_type &previous = block_type()) {
                            block_type block = {0};

                            //                        codec::encode<codec::encoder::logic_xor>(plaintext, previous,
                            //                        block.begin());

                            return cipher.encrypt(block);
                        }

                        inline static block_type end_message(const cipher_type &cipher, const block_type &plaintext,
                                                             const block_type &previous = block_type(),
                                                             const iv_type &iv = iv_type()) {
                            block_type result = {0};
                            std::array<uint8_t, block_bits / CHAR_BIT> byte_array = {0};

                            pack(plaintext, byte_array);

                            size_type rem = std::count(byte_array.begin(), byte_array.end(), block_type::value_type()) %
                                            (block_bits / CHAR_BIT);
                            if (rem || padding_type::always_pad) {
                                block_type padbuffer = padding_type::pad(plaintext, rem), block;
                                //                            codec::encode<codec::encoder::logic_xor>(!iv.empty() ? iv
                                //                            : previous, padbuffer, block .begin());
                                result = cipher.encrypt(block);
                            }

                            return result;
                        }
                    };

                    template<typename Cipher, typename Padding, typename CiphertextStealingMode>
                    struct ctr_decryption_policy : public ctr_policy<Cipher, Padding> {};

                    template<typename Cipher, typename Padding>
                    struct ctr_decryption_policy<Cipher, Padding, cts<0, Cipher, Padding>>
                        : public ctr_policy<Cipher, Padding> {
                        typedef typename ctr_policy<Cipher, Padding>::size_type size_type;

                        typedef typename ctr_policy<Cipher, Padding>::cipher_type cipher_type;
                        typedef typename ctr_policy<Cipher, Padding>::padding_type padding_type;

                        constexpr static const std::size_t block_bits = ctr_policy<Cipher, Padding>::block_bits;
                        constexpr static const std::size_t block_words = ctr_policy<Cipher, Padding>::block_words;
                        typedef typename ctr_policy<Cipher, Padding>::block_type block_type;

                        typedef typename ctr_policy<Cipher, Padding>::iv_type iv_type;

                        inline static block_type begin_message(const cipher_type &cipher, const block_type &plaintext,
                                                               const iv_type &iv = iv_type()) {
                            block_type block = cipher.decrypt(plaintext);

                            //                        codec::encode<codec::encoder::logic_xor>(block, iv);

                            return block;
                        }

                        inline static block_type process_block(const cipher_type &cipher, const block_type &plaintext,
                                                               const block_type &previous = block_type()) {
                            block_type result = cipher.decrypt(plaintext);

                            //                        codec::encode<codec::encoder::logic_xor>(result, previous);

                            return result;
                        }

                        inline static block_type end_message(const cipher_type &cipher, const block_type &plaintext,
                                                             const iv_type &iv = iv_type()) {
                            block_type result = cipher.decrypt(plaintext);

                            //                        codec::encode<codec::encoder::logic_xor>(result, iv);

                            return result;
                        }
                    };

                    template<typename Cipher, typename Padding>
                    struct ctr_decryption_policy<Cipher, Padding, cts<1, Cipher, Padding>>
                        : public ctr_policy<Cipher, Padding> {
                        typedef typename ctr_policy<Cipher, Padding>::size_type size_type;

                        typedef typename ctr_policy<Cipher, Padding>::cipher_type cipher_type;
                        typedef typename ctr_policy<Cipher, Padding>::padding_type padding_type;

                        constexpr static const std::size_t block_bits = ctr_policy<Cipher, Padding>::block_bits;
                        constexpr static const std::size_t block_words = ctr_policy<Cipher, Padding>::block_words;
                        typedef typename ctr_policy<Cipher, Padding>::block_type block_type;

                        typedef typename ctr_policy<Cipher, Padding>::iv_type iv_type;

                        inline static block_type begin_message(const cipher_type &cipher, const block_type &plaintext,
                                                               const iv_type &iv = iv_type()) {
                            block_type block = cipher.decrypt(plaintext);

                            //                        codec::encode<codec::encoder::logic_xor>(block, iv);

                            return block;
                        }

                        inline static block_type process_block(const cipher_type &cipher, const block_type &plaintext,
                                                               const block_type &previous = block_type()) {
                            block_type result = cipher.decrypt(plaintext);

                            //                        codec::encode<codec::encoder::logic_xor>(result, previous);

                            return result;
                        }

                        inline static block_type end_message(const cipher_type &cipher, const block_type &plaintext,
                                                             const iv_type &iv = iv_type()) {
                            block_type result = cipher.decrypt(plaintext);

                            //                        codec::encode<codec::encoder::logic_xor>(result, iv);

                            return result;
                        }
                    };

                    template<typename Cipher, typename Padding>
                    struct ctr_decryption_policy<Cipher, Padding, cts<2, Cipher, Padding>>
                        : public ctr_policy<Cipher, Padding> {
                        typedef typename ctr_policy<Cipher, Padding>::size_type size_type;

                        typedef typename ctr_policy<Cipher, Padding>::cipher_type cipher_type;
                        typedef typename ctr_policy<Cipher, Padding>::padding_type padding_type;

                        constexpr static const std::size_t block_bits = ctr_policy<Cipher, Padding>::block_bits;
                        constexpr static const std::size_t block_words = ctr_policy<Cipher, Padding>::block_words;
                        typedef typename ctr_policy<Cipher, Padding>::block_type block_type;

                        typedef typename ctr_policy<Cipher, Padding>::iv_type iv_type;

                        inline static block_type begin_message(const cipher_type &cipher, const block_type &plaintext,
                                                               const iv_type &iv = iv_type()) {
                            block_type block = cipher.decrypt(plaintext);

                            //                        codec::encode<codec::encoder::logic_xor>(block, iv);

                            return block;
                        }

                        inline static block_type process_block(const cipher_type &cipher, const block_type &plaintext,
                                                               const block_type &previous = block_type()) {
                            block_type result = cipher.decrypt(plaintext);

                            //                        codec::encode<codec::encoder::logic_xor>(result, previous);

                            return result;
                        }

                        inline static block_type end_message(const cipher_type &cipher, const block_type &plaintext,
                                                             const iv_type &iv = iv_type()) {
                            block_type result = cipher.decrypt(plaintext);

                            //                        codec::encode<codec::encoder::logic_xor>(result, iv);

                            return result;
                        }
                    };

                    template<typename Cipher, typename Padding>
                    struct ctr_decryption_policy<Cipher, Padding, cts<3, Cipher, Padding>>
                        : public ctr_policy<Cipher, Padding> {
                        typedef typename ctr_policy<Cipher, Padding>::size_type size_type;

                        typedef typename ctr_policy<Cipher, Padding>::cipher_type cipher_type;
                        typedef typename ctr_policy<Cipher, Padding>::padding_type padding_type;

                        constexpr static const std::size_t block_bits = ctr_policy<Cipher, Padding>::block_bits;
                        constexpr static const std::size_t block_words = ctr_policy<Cipher, Padding>::block_words;
                        typedef typename ctr_policy<Cipher, Padding>::block_type block_type;

                        typedef typename ctr_policy<Cipher, Padding>::iv_type iv_type;

                        inline static block_type begin_message(const cipher_type &cipher, const block_type &plaintext,
                                                               const iv_type &iv = iv_type()) {
                            block_type block = cipher.decrypt(plaintext);

                            //                        codec::encode<codec::encoder::logic_xor>(block, iv);

                            return block;
                        }

                        inline static block_type process_block(const cipher_type &cipher, const block_type &plaintext,
                                                               const block_type &previous = block_type()) {
                            block_type result = cipher.decrypt(plaintext);

                            //                        codec::encode<codec::encoder::logic_xor>(result, previous);

                            return result;
                        }

                        inline static block_type end_message(const cipher_type &cipher, const block_type &plaintext,
                                                             const iv_type &iv = iv_type()) {
                            block_type result = cipher.decrypt(plaintext);

                            //                        codec::encode<codec::encoder::logic_xor>(result, iv);

                            return result;
                        }
                    };

                    template<typename Policy>
                    class counter {
                        typedef Policy policy_type;

                    public:
                        typedef typename policy_type::cipher_type cipher_type;
                        typedef typename policy_type::padding_type padding_type;

                        typedef typename policy_type::size_type size_type;

                        typedef typename cipher_type::key_type key_type;
                        typedef typename policy_type::iv_type iv_type;

                        constexpr static const size_type block_bits = policy_type::block_bits;
                        constexpr static const size_type block_words = policy_type::block_words;
                        typedef typename cipher_type::block_type block_type;

                        counter(const cipher_type &cipher) : cipher(cipher) {
                        }

                        block_type begin_message(const block_type &plaintext, const iv_type &iv = iv_type()) {
                            previous = policy_type::begin_message(cipher, plaintext, iv);
                            return previous;
                        }

                        block_type process_block(const block_type &plaintext) {
                            previous = policy_type::process_block(cipher, plaintext, previous);
                            return previous;
                        }

                        block_type end_message(const block_type &plaintext, const iv_type &iv = iv_type()) {
                            return policy_type::end_message(cipher, plaintext, iv);
                        }

                        inline static size_type required_output_size(size_type inputlen) {
                            return padding_type::required_output_size(inputlen);
                        }

                    protected:
                        block_type previous;
                        cipher_type cipher;
                    };
                }    // namespace detail

                /*!
                 * @brief Counter Mode (CTR).
                 * @tparam Cipher
                 * @tparam Padding
                 *
                 * @ingroup block_modes
                 */
                template<typename Cipher, template<typename> class Padding,
                         template<typename, template<typename> class> class CiphertextStealingMode = cts0>
                struct counter {
                    typedef Cipher cipher_type;
                    typedef Padding<Cipher> padding_type;
                    typedef CiphertextStealingMode<Cipher, Padding> ciphertext_stealing_type;

                    typedef detail::ctr_encryption_policy<cipher_type, padding_type, ciphertext_stealing_type>
                        encryption_policy;
                    typedef detail::ctr_decryption_policy<cipher_type, padding_type, ciphertext_stealing_type>
                        decryption_policy;

                    template<template<typename, typename> class Policy>
                    struct bind {
                        typedef detail::counter<Policy<cipher_type, padding_type>> type;
                    };
                };

                /*!
                 * @brief
                 *
                 * @tparam Cipher
                 * @tparam Padding
                 * @tparam CiphertextStealingMode
                 *
                 * @ingroup block_modes
                 */
                template<typename Cipher, template<typename> class Padding,
                         template<typename, template<typename> class> class CiphertextStealingMode>
                using ctr = counter<Cipher, Padding, CiphertextStealingMode>;
            }    // namespace modes
        }        // namespace block
    }            // namespace crypto3
}    // namespace nil

#endif


/*! \file readinputfile.cpp
    \brief インプットファイルの読み込みを行うクラスの実装

    Copyright ©  2015 @dc1394 All Rights Reserved.
    This software is released under the BSD 2-Clause License.
*/

#include "readinputfile.h"
#include <iostream>                     // for std::cerr
#include <stdexcept>                    // for std::runtime_error
#include <boost/algorithm/string.hpp>   // for boost::algorithm
#include <boost/assert.hpp>             // for BPOOST_ASSERT
#include <boost/cast.hpp>               // for boost::numeric_cast
#include <boost/range/algorithm.hpp>    // for boost::find

namespace schrac {
    // #region staticメンバ変数

    ci_string const ReadInputFile::CHEMICAL_SYMBOL = "chemical.symbol";
	ci_string const ReadInputFile::EQ_TYPE_DEFAULT = "sch";
	ci_string const ReadInputFile::EQ_TYPE = "eq.type";
    std::array<ci_string, 4> const ReadInputFile::EQ_TYPE_ARRAY =
    {
        ci_string("sch"),
        ci_string("sdirac"),
        ci_string("dirac")
    };
    ci_string const ReadInputFile::ORBITAL = "orbital";
    const std::array<ci_string, 4> ReadInputFile::SOLVER_TYPE_ARRAY =
    {
        ci_string("adams_bashforth_moulton"),
        ci_string("bulirsch_stoer"),
        ci_string("controlled_runge_kutta")
    };
    ci_string const ReadInputFile::SOLVER_TYPE_DEFAULT = "controlled_runge_kutta";
	ci_string const ReadInputFile::SPIN_ORBITAL = "spin.orbital";
	ci_string const ReadInputFile::SPIN_ORBITAL_DEFAULT = "alpha";

    // #endregion staticメンバ変数

    // #region コンストラクタ

    ReadInputFile::ReadInputFile(std::pair<std::string, bool> const & arg) :
        PData([this]() { return std::cref(pdata_); }, nullptr),
        ifs_(std::get<0>(arg).c_str()),
        lineindex_(1),
        pdata_(std::make_shared<Data>())
    {
        pdata_->usetbb_ = std::get<1>(arg);
    }

    // #endregion コンストラクタ

    // #region publicメンバ関数

    void ReadInputFile::readFile()
    {
        if (!ifs_.is_open())
            throw std::runtime_error("インプットファイルが開けませんでした");

        auto const errorendfunc = []() {
            throw std::runtime_error("インプットファイルが異常です");
        };

        if (!readAtom()) {
            errorendfunc();
        }

        if (!readEq()) {
            errorendfunc();
        }

        // グリッドの最小値を読み込む
        readValue("grid.xmin", XMIN_DEFAULT, pdata_->xmin_);

        // グリッドの最大値を読み込む
        readValue("grid.xmax", XMAX_DEFAULT, pdata_->xmax_);

        // グリッドのサイズを読み込む
        readValue("grid.num", GRID_NUM_DEFAULT, pdata_->grid_num_);

        // 許容誤差を読み込む
        readValue("eps", EPS_DEFAULT, pdata_->eps_);

        // 解く方程式のタイプを読み込む
        if (!readSolverType()) {
            errorendfunc();
        }

        // 固有値探索をはじめる値を読み込む
        if (!readValueAuto("search.LowerE", pdata_->search_lowerE_)) {
            errorendfunc();
        }

        // 固有値検索の間隔を読み込む
        readValue("num.of.partition", NUM_OF_PARTITION_DEFAULT, pdata_->num_of_partition_);

        // マッチングポイントを読み込む
        readValue("matching.point.ratio", MAT_PO_RATIO_DEFAULT, pdata_->mat_po_ratio_);

        // 密度の初期値ρ0(r)のための係数cを読み込む
        if (!readValueAuto("rho0.c", pdata_->rho0_c_)) {
            errorendfunc();
        }

        // 密度の初期値ρ0(r)のための係数alphaを読み込む
        if (!readValueAuto("rho0.alpha", pdata_->rho0_alpha_)) {
            errorendfunc();
        }

        // SCFの最大ループ回数を読み込む
        readValue("scf.maxIter", SCF_MAXITER_DEFAULT, pdata_->scf_maxiter_);

        // SCFの一次混合の重みを読み込む
        if (!readScfMixingWeight()) {
            errorendfunc();
        }

        // SCFの収束判定条件の値を読み込む
        readValue("scf.criterion", SCF_CRITERION_DEFAULT, pdata_->scf_criterion_);
    }

    // #endregion publicメンバ関数

    // #region privateメンバ関数

    void ReadInputFile::errorMessage(ci_string const & s) const
    {
        std::cerr << "インプットファイルに" << s.c_str() << "の行が見つかりませんでした。\n";
    }

    void ReadInputFile::errorMessage(std::int32_t line, ci_string const & s1, ci_string const & s2) const
    {
        std::cerr << "インプットファイルの[" << s1.c_str() << "]の行が正しくありません。\n";
        std::cerr << line << "行目, 未知のトークン:" << s2.c_str() << std::endl;
    }

    std::pair<std::int32_t, std::optional<ReadInputFile::strvec>> ReadInputFile::getToken(ci_string const & article)
    {
        using namespace boost::algorithm;

        std::array<char, BUFSIZE> buf;
        ifs_.getline(buf.data(), BUFSIZE);
        ci_string const line(buf.data());

        // もし一文字も読めなかったら
        if (!ifs_.gcount()) {
            errorMessage(article);
            return std::make_pair(-1, std::nullopt);
        }

        // 読み込んだ行が空、あるいはコメント行でないなら
        if (!line.empty() && (line[0] != '#')) {
            // トークン分割
            strvec tokens;
            split(tokens, line, is_any_of(" \t"), token_compress_on);

            auto const itr(tokens.begin());

            if (*itr != article) {
                errorMessage(lineindex_, article, *itr);
                return std::make_pair(-1, std::nullopt);
            }

            return std::make_pair(0, std::make_optional<strvec>(std::move(tokens)));
        }
        else {
            return std::make_pair(1, std::nullopt);
        }
    }

    bool ReadInputFile::readAtom()
    {
        // 原子の種類を読み込む
        auto const chemsym(readData(ReadInputFile::CHEMICAL_SYMBOL));
        if (!chemsym) {
            return false;
        }

        try {
            auto const itr(boost::find(Data::Chemical_Symbol, chemsym->c_str()));
            if (itr == Data::Chemical_Symbol.end()) {
                throw std::invalid_argument("");
            }

            pdata_->chemical_symbol_ = *itr;
            pdata_->Z_ = static_cast<double>(std::distance(Data::Chemical_Symbol.begin(), itr)) + 1.0;
        }
        catch (std::invalid_argument const &) {
            errorMessage(lineindex_ - 1, ReadInputFile::CHEMICAL_SYMBOL, *chemsym);
            return false;
        }

        // 軌道を読み込む
        auto const porbital(readData(ReadInputFile::ORBITAL));
        if (!porbital) {
            return false;
        }

        auto const orbital(*porbital);
        if (orbital.length() != 2) {
            errorMessage(lineindex_ - 1, ReadInputFile::ORBITAL, orbital);
            return false;
        }

        if (!std::isdigit(orbital[0])) {
            errorMessage(lineindex_ - 1, ReadInputFile::ORBITAL, orbital);
            return false;
        }
        pdata_->orbital_ = orbital[0];
        pdata_->n_ = boost::numeric_cast<std::uint8_t>(orbital[0] - '0');

        switch (orbital[1]) {
        case 's':
            pdata_->l_ = 0;
            pdata_->orbital_ += 's';
            break;

        case 'p':
            pdata_->l_ = 1;
            pdata_->orbital_ += 'p';
            break;

        case 'd':
            pdata_->l_ = 2;
            pdata_->orbital_ += 'd';
            break;

        case 'f':
            pdata_->l_ = 3;
            pdata_->orbital_ += 'f';
            break;

        case 'g':
            pdata_->l_ = 4;
            pdata_->orbital_ += 'g';
            break;

        default:
            errorMessage(lineindex_ - 1, ReadInputFile::ORBITAL, orbital);
            return false;
            break;
        }

        if (pdata_->n_ - pdata_->l_ < 1) {
            std::cerr << "量子数の指定が異常です。\n";
            return false;
        }

        // スピン軌道を読み込む
        auto const pspin_orbital(readData(ReadInputFile::SPIN_ORBITAL, ReadInputFile::SPIN_ORBITAL_DEFAULT));
        if (!pspin_orbital) {
            return false;
        }

        pdata_->spin_orbital_ = *pspin_orbital;
        if (pdata_->spin_orbital_ != Data::ALPHA && pdata_->spin_orbital_ != Data::BETA) {
            errorMessage(lineindex_ - 1, ReadInputFile::SPIN_ORBITAL, pdata_->spin_orbital_);
            return false;
        }

        if (!pdata_->l_) {
            // s軌道は特別なケース
            // j = l_ + 1/2
            pdata_->j_ = 0.5;
            pdata_->kappa_ = -1.0;
        }
        else if (pdata_->spin_orbital_ == Data::ALPHA) {
            // j = l_ + 1/2に対して
            pdata_->j_ = static_cast<double>(pdata_->l_) + 0.5;
            pdata_->kappa_ = -static_cast<double>(pdata_->l_) - 1.0;
        }
        else {
            // j = l_ - 1/2に対して
            pdata_->j_ = static_cast<double>(pdata_->l_) - 0.5;
            pdata_->kappa_ = static_cast<double>(pdata_->l_);
        }

        return true;
    }

    std::optional<ci_string> ReadInputFile::readData(ci_string const & article)
    {
        for (; true; lineindex_++) {
            auto const ret(getToken(article));

            switch (std::get<0>(ret))
            {
            case -1:
                return std::nullopt;
                break;

            case 0:
            {
                auto const tokens(*(std::get<1>(ret)));

                // 読み込んだトークンの数がもし2個以外だったら
                if (tokens.size() != 2 || tokens[1].empty()) {
                    std::cerr << "インプットファイル" << lineindex_ << "行目の、[" << article.c_str() << "]の行が正しくありません。\n";
                    return std::nullopt;
                }

                ++lineindex_;
                return *(++tokens.begin());
            }
            case 1:
                break;

            default:
                BOOST_ASSERT(!"何かがおかしい!");
                break;
            }
        }
    }

    std::optional<ci_string> ReadInputFile::readData(ci_string const & article, ci_string const & def)
    {
        // グリッドを読み込む
        for (; true; lineindex_++) {
            auto const ret(getToken(article));

            switch (std::get<0>(ret))
            {
            case -1:
                return std::nullopt;
                break;

            case 0:
            {
                auto const tokens(*(std::get<1>(ret)));
                auto itr(++tokens.begin());
                ++lineindex_;

                // 読み込んだトークンの数をはかる
                switch (tokens.size()) {
                case 1:
                    // デフォルト値を返す
                    return def;
                    break;

                case 2:
                    return *itr == "DEFAULT" ? def : *itr;
                    break;

                default:
                    {
                        auto val(*itr);

                        if (val == "DEFAULT" || val[0] == '#') {
                            // デフォルト値を返す
                            return def;
                        }
                        else if ((*(++itr))[0] != '#') {
                            errorMessage(lineindex_ - 1, article, *itr);
                            return std::nullopt;
                        }

                        return val;
                    }
                }
            }
                break;

            case 1:
                break;

            default:
                BOOST_ASSERT(!"何かがおかしい!");
                break;
            }
        }
    }

    std::optional<ci_string> ReadInputFile::readDataAuto(ci_string const & article)
    {
        for (; true; lineindex_++) {
            auto const ret(getToken(article));

            switch (std::get<0>(ret))
            {
            case -1:
                return std::nullopt;
                break;

            case 0:
            {
                auto const tokens(*(std::get<1>(ret)));
                auto itr(++tokens.begin());
                ++lineindex_;

                // 読み込んだトークンの数をはかる
                switch (tokens.size()) {
                case 1:
                    return std::nullopt;
                    break;

                case 2:
                    return (*itr == "DEFAULT" || *itr == "AUTO") ?
                        std::make_optional<ci_string>(ci_string()) : std::make_optional<ci_string>(*itr);
                    break;

                default:
                    {
                        auto val = *itr;

                        if (val == "DEFAULT" || val == "AUTO" || val[0] == '#') {
                            // デフォルト値を返す
                            return std::make_optional<ci_string>(ci_string());
                        } else if ((*(++itr))[0] != '#') {
                            errorMessage(lineindex_ - 1, article, *itr);

                            // エラー
                            return std::nullopt;
                        }

                        return std::make_optional<ci_string>(std::move(val));
                    }
                }
            }
                break;

            case 1:
                break;

            default:
                BOOST_ASSERT(!"何かがおかしい!");
                break;
            }
        }
    }

    bool ReadInputFile::readEq()
    {
        auto const peqtype(readData(ReadInputFile::EQ_TYPE, ReadInputFile::EQ_TYPE_DEFAULT));
        if (!peqtype) {
            return false;
        }

        auto const eqtype(*peqtype);
        auto const itr(boost::find(ReadInputFile::EQ_TYPE_ARRAY, eqtype));

        if (itr == ReadInputFile::EQ_TYPE_ARRAY.end()) {
            errorMessage(lineindex_ - 1, ReadInputFile::EQ_TYPE, eqtype);
            return false;
        }
        else if (itr != ReadInputFile::EQ_TYPE_ARRAY.begin()) {
            pdata_->eq_type_ = boost::numeric_cast<Data::Eq_type>(std::distance(ReadInputFile::EQ_TYPE_ARRAY.begin(), itr));
        }

        if (pdata_->eq_type_ == Data::Eq_type::SDIRAC) {
            // scalar Dirac方程式の場合
            // j = l_ + 1/2
            pdata_->j_ = 0.5;
            pdata_->kappa_ = -1.0;
        }

        return true;
    }

    bool ReadInputFile::readScfMixingWeight()
    {
        readValue("scf.Mixing.Weight", SCF_MIXING_WEIGHT_DEFAULT, pdata_->scf_mixing_weight_);
        if (pdata_->scf_mixing_weight_ <= 0.0 || pdata_->scf_mixing_weight_ > 1.0) {
            std::cerr << "インプットファイルの[scf.Mixing.Weight]の行が正しくありません。\n";
            return false;
        }
        return true;
    }

    bool ReadInputFile::readSolverType()
    {
        auto const psolvetype(readData("solver.type", ReadInputFile::SOLVER_TYPE_DEFAULT));
        if (!psolvetype) {
            return false;
        }

        auto const solvertype = *psolvetype;
        auto const itr(boost::find(ReadInputFile::SOLVER_TYPE_ARRAY, solvertype));
        if (itr == ReadInputFile::SOLVER_TYPE_ARRAY.end()) {
            errorMessage(lineindex_ - 1, "solver.type", solvertype);
            return false;
        } else {
            pdata_->solver_type_ = boost::numeric_cast<Data::Solver_type>(
                std::distance(ReadInputFile::SOLVER_TYPE_ARRAY.begin(), itr));
        }

        return true;
    }

    // #endregion privateメンバ関数
}