#include <iostream>
//#include <iomanip>
//#include <fstream>

//#include <boost/numeric/ublas/matrix.hpp>
//#include <boost/numeric/ublas/vector.hpp>
//#include <boost/numeric/ublas/io.hpp>

#include <vector>
//#include <cmath>
//#include <cstring>

//#include <unistd.h>

#include "NRGclasses.hpp"
#include "NRGfunctions.hpp"
#include "NRGOpMatRules.hpp"
#include "NRG_main.hpp"
#include "TwoChQS.hpp"

#ifndef pi
#define pi 3.141592653589793238462643383279502884197169
#endif

void CNRGCodeHandler::ModelSwitch(  vector<int> &CommonQNs,
				    vector<int> &totSpos,
				    CNRGarray* pAeig,
				    CNRGbasisarray* pSingleSite,
				    CNRGmatrix* pHN,
				    vector<CNRGmatrix> &STLMatArray,
				    CNRGthermo* ThermoArray,
				    vector<double> &chi_m1){

  // Ok, this can be changed later to reduce the number of parameters
  // Leave as it is for now.

  // List of Non CNRGcodehandler objects that needs to be set/defined

  // ALL of these have to be DEFINED as fields of CNRGCodeHandler
  // STLMatArray -> MatArray (but routines need an STL vector...
  //                          SHOULD DEFINE MatArray as an STL vector in
  //                          CNRGcodehandler
  //                          and adapt in the other routines.
  //                          But leave it for now)
  //                MatArray enters with 4 elements. Add if you need more.
  // CommonQNs   -> (input)
  // totSpos     -> (input)
  // HN          -> (input)
  // NumNRGarrays (not really, just set it as NumNRGmats)
  // ThermoArray -> ThermoSTLArray (ok)

  // Defined here:

  CNRGmatrix auxNRGMat;
  vector <double> Params;

  double chi2_m1=0.0; // Second coupling into the same channel!

  // To do: getrid of ThermoArray and STLMatArray:
  // define them in codehandler class.
  // get rid of all parameters?

  ///////////////////////////////////
  ////   Model specific hamiltonians ///
  ///////////////////////////////////

// SymNo:
//  0 - OneChQS
//  1 - TwoChQS
//  2 - OneChQSz
//  3 - OneChQ
//  4 - TwoChQSP
//  5 - TwoChQSz
//  6 - OneChNupPdn
//  7 - OneChS
//  8 - OneChSz
//  9 - OneChPupPdn

// ModelNo:
//  0 - Anderson
//  1 - Kondo
//  2 - Anderson w Holstein phonons
//  3 - Chain only
//  4 - CM phonons (2ch only)
//  5 - SMM (1chQ only)
//  6 - DQD
//  7 - Anderson+Local Majorana (1ch NupPdn only)
//

// NEW: Add zEQ(z) to SuscepImp names.
//

    char zstring[30];
    char zvalue[8];
    strcpy(zstring,"_zEQ");
    if (dEqual(code_z_twist,1.0))
      strcpy(zvalue,"1");
    else
      sprintf(zvalue,"%4.2f",code_z_twist);
    strcat(zstring,zvalue);
    cout << "Zstring = " << zstring << endl;

    switch(SymNo){
    case 0: // OneChQS
      ////////////////////////////
      /////                   ////
      ///// Symmetry: OneChQS ////
      /////                   ////
      ////////////////////////////
      /// Models so far:       ///
      ///  0 - Anderson        ///
      ///  1 - Kondo           ///
      ///  3 - Chain           ///
      ///  6 - DQD             ///
      ////////////////////////////
      switch(ModelNo){
      case 0: // Single-impurity Anderson model Q,S symmetry

	// Initialize matrices (OneChQ routines work here!)
	// Jul 09: using STMatArray

	//NumThermoMats=2;

	// fN (reduced)
	STLMatArray[0].NeedOld=false;
	STLMatArray[0].UpperTriangular=false;
	STLMatArray[0].CheckForMatEl=OneChQS_cd_check;
	STLMatArray[0].CalcMatEl=OneChQS_fN_MatEl;
	STLMatArray[0].SaveMatYN=false;

	switch (calcdens){
	case 0: // OpAvg: n_d(T), n^2_d(T), S^2(T) - NEW (2012)
	  auxNRGMat.NeedOld=true;
	  auxNRGMat.UpperTriangular=false; // update procedure only works for 'false'
	  auxNRGMat.CalcAvg=true;  // CalcAvg
	  auxNRGMat.CheckForMatEl=Diag_check;
	  auxNRGMat.CalcMatEl=ImpOnly_MatEl;
	  STLMatArray[1]=auxNRGMat; // 1 - ndot (there already)
	  STLMatArray[2]=auxNRGMat; // 2 - ndot^2 (there already)
	  STLMatArray[3]=auxNRGMat; // 3 - Sdot^2 (there already)
	  strcpy(STLMatArray[1].MatName,"Ndot");
	  strcpy(STLMatArray[2].MatName,"NdSq");
	  strcpy(STLMatArray[3].MatName,"SdSq");
	  NumNRGmats=STLMatArray.size();
	  MatArray=&STLMatArray[0]; // Need to do this after
	  // changes in STLMatArray!
	  SaveData=false;
// 	  STLMatArray.pop_back(); // 1 Matrices only
// 	  STLMatArray.pop_back(); // 1 Matrices only
// 	  STLMatArray.pop_back(); // 1 Matrices only
	  // No Thermo
	  NumThermoMats=0;
	  break;
	case 1: //Spectral density
	  STLMatArray.pop_back(); // 2 Matrices
	  STLMatArray.pop_back(); // 2 Matrices
	  NumThermoMats=0;
	  // cd operator
	  STLMatArray[1].NeedOld=true;
	  STLMatArray[1].UpperTriangular=false;
	  STLMatArray[1].CheckForMatEl=OneChQS_cd_check;
	  STLMatArray[1].CalcMatEl=OneChQS_cd_MatEl;
	  STLMatArray[1].SaveMatYN=true;
	  strcpy(STLMatArray[1].MatName,"cdot1");
	  NumNRGmats=STLMatArray.size();
	  // changes in STLMatArray!
	  SaveData=true;
	  strcpy(SaveArraysFileName,"1chQSAnderson");
	  break;
	case 2: // Thermodynamics ONLY
//	  STLMatArray.pop_back(); // 1 Matrix only
//	  STLMatArray.pop_back(); // 1 Matrix only
//	  STLMatArray.pop_back(); // 1 Matrix only
// If you want <n_d>(T), use calcdens=0
// June 2013: I'm changing this. Want to calculate ndot toghether with chi_imp
//

 	  auxNRGMat.NeedOld=true;
 	  auxNRGMat.UpperTriangular=false;
	  // update procedure only works for 'false'
 	  auxNRGMat.CalcAvg=true;  // CalcAvg
 	  auxNRGMat.CheckForMatEl=Diag_check;
 	  auxNRGMat.CalcMatEl=ImpOnly_MatEl;
 	  STLMatArray[1]=auxNRGMat; // 1 - ndot (there already)
 	  STLMatArray[2]=auxNRGMat; // 2 - ndot^2 (not right now)
 	  STLMatArray[3]=auxNRGMat; // 3 - Sdot^2 (not right now)
 	  strcpy(STLMatArray[1].MatName,"Ndot");
 	  strcpy(STLMatArray[2].MatName,"NdSq");
 	  strcpy(STLMatArray[3].MatName,"SdSq");
////////////
	  NumNRGmats=STLMatArray.size();
	  MatArray=&STLMatArray[0]; // Need to do this after
	  // changes in STLMatArray!
	  SaveData=false;
	  // Thermo
	  // Z trick in file name.
	  // Changinf chain arq names for 1chQS too (Sep 2010)
	  strcpy(ThermoArray[0].ArqName,"SuscepImp1ChQS_Anderson");
	  strcat(ThermoArray[0].ArqName,zstring);
	  strcat(ThermoArray[0].ArqName,".dat");
	  //
	  strcpy(ThermoArray[0].ChainArqName,"SuscepChain1Ch_QS");
	  strcat(ThermoArray[0].ChainArqName,zstring);
	  strcat(ThermoArray[0].ChainArqName,".dat");
	  //
	  strcpy(ThermoArray[1].ArqName,"EntropyImp1ChQS_Anderson");
	  strcat(ThermoArray[1].ArqName,zstring);
	  strcat(ThermoArray[1].ArqName,".dat");
	  //
	  strcpy(ThermoArray[1].ChainArqName,"EntropyChain1Ch_QS");
	  strcat(ThermoArray[1].ChainArqName,zstring);
	  strcat(ThermoArray[1].ChainArqName,".dat");
	  ThermoArray[0].Calc=CalcSuscep;
	  ThermoArray[0].dImpValue=1.0/8.0;
	  ThermoArray[1].Calc=CalcEntropy;
	  ThermoArray[1].dImpValue=2.0*log(2.0);
	  NumThermoMats=2;
	  break;
	case 4: // Dynamical spin susceptibility and <Sz> and <Sz^2> (2015)
	  // 5 Matrices
	  STLMatArray.push_back(auxNRGMat);
	  NumThermoMats=0;
	  // <Sz> and <Sz2> operator
	  auxNRGMat.NeedOld=true;
	  auxNRGMat.UpperTriangular=false;
	  // update procedure only works for 'false'
	  auxNRGMat.CalcAvg=true;  // CalcAvg
	  auxNRGMat.CheckForMatEl=Diag_check;
	  auxNRGMat.CalcMatEl=ImpOnly_MatEl;

	  STLMatArray[1]=auxNRGMat; // 1 - Sz dynamical
	  STLMatArray[1].SaveMatYN=true;
	  STLMatArray[1].CalcAvg=false;
	  STLMatArray[1].CalcMatEl=OneChQS_Sz_MatEl;
	  strcpy(STLMatArray[1].MatName,"Szomega");
	  STLMatArray[1].WignerEckartL=1.0; // Needed by DM_NRG later

	  STLMatArray[2]=auxNRGMat; // 2 - Nd dynamical
	  STLMatArray[2].SaveMatYN=true;
	  STLMatArray[2].CalcAvg=false;
	  strcpy(STLMatArray[2].MatName,"Ndomega");

	  STLMatArray[3]=auxNRGMat; // 3 - <Sz2>(T) static
	  STLMatArray[3].SaveMatYN=false;
	  strcpy(STLMatArray[3].MatName,"Sz2dot");

	  STLMatArray[4]=auxNRGMat; // 4 - <Nd2>(T) static
	  STLMatArray[4].SaveMatYN=false;
	  strcpy(STLMatArray[4].MatName,"Nd2dot");

	  NumNRGmats=STLMatArray.size();
	  MatArray=&STLMatArray[0]; // Need to do this after
	  // changes in STLMatArray!
	  SaveData=true;
	  strcpy(SaveArraysFileName,"1chQSAnderson");
	  break;
	default:
	  cout << " calcdens = " << calcdens << " not implemented. Exiting. " << endl;
	  exit(0);

	}
	// end switch calcdens

	// Param for H0
	Params.push_back(Lambda);
	Params.push_back(HalfLambdaFactor);
	Params.push_back(dInitParams[0]); // U
	Params.push_back(dInitParams[2]); // ed
	Params.push_back(chi_m1[0]); // gamma

	OneChQS_SetAnderson_Hm1(Params,pAeig,STLMatArray);

	// BuildBasis params
	CommonQNs.push_back(2); // Q and S and commont QNs
	CommonQNs.push_back(0); // position of Q in old basis
	CommonQNs.push_back(1); // position of S in old basis
	CommonQNs.push_back(0); // position of Q in SingleSite
	CommonQNs.push_back(1); // position of S in SingleSite
	totSpos.push_back(1);   // SU(2) symmetry in position 1
	// HN params
	pHN->CalcHNMatEl=OneChQS_HN_MatEl;
	NumChannels=1;

	break;
	// end OneChQS Anderson model set up

      case 1: // Single-impurity Kondo model Q,S symmetry

	Nsites0=1;

	// fN (reduced)
	STLMatArray[0].NeedOld=false;
	STLMatArray[0].UpperTriangular=false;
	STLMatArray[0].CheckForMatEl=OneChQS_cd_check;
	STLMatArray[0].CalcMatEl=OneChQS_fN_MatEl;
	STLMatArray[0].SaveMatYN=false;

	STLMatArray.pop_back(); // 1 Matrix only
	STLMatArray.pop_back(); // 1 Matrix only
	STLMatArray.pop_back(); // 1 Matrix only

	switch (calcdens){
	case 0: // Levels only
	  NumThermoMats=0;
	  break;
	case 2: // Thermodynamics
	  // Z trick in file name.
	  strcpy(ThermoArray[0].ArqName,"SuscepImp1ChQS_Kondo");
	  strcat(ThermoArray[0].ArqName,zstring);
	  strcat(ThermoArray[0].ArqName,".dat");
	  strcpy(ThermoArray[0].ChainArqName,"SuscepChain1Ch_QS");
	  strcat(ThermoArray[0].ChainArqName,zstring);
	  strcat(ThermoArray[0].ChainArqName,".dat");
	  strcpy(ThermoArray[1].ArqName,"EntropyImp1ChQS_Kondo");
	  strcat(ThermoArray[1].ArqName,zstring);
	  strcat(ThermoArray[1].ArqName,".dat");
	  strcpy(ThermoArray[1].ChainArqName,"EntropyChain1Ch_QS");
	  strcat(ThermoArray[1].ChainArqName,zstring);
	  strcat(ThermoArray[1].ChainArqName,".dat");
	  // Values for the single-impurity Kondo model
	  ThermoArray[0].Calc=CalcSuscep;
	  ThermoArray[0].dImpValue=1.0/4.0;
	  ThermoArray[1].Calc=CalcEntropy;
	  ThermoArray[1].dImpValue=log(2.0);
	  NumThermoMats=2;
	  break;

	default:
	  cout << " Calculating levels only " << endl;

	}
	// end switch calcdens

	// Param for H0
	Params.push_back(Lambda);
	Params.push_back(HalfLambdaFactor);
	Params.push_back(chi_m1[0]); // will become Jtilde

	//"chi_m1[0]"= sqrt(Fsq*rho_0*J_0/pi)/(sqrt(Lambda)*HalfLambdaFactor);

	cout << " Fsq = " << Chains[0].Fsq
	     << " rho0 J0  = " << Lambda*HalfLambdaFactor*HalfLambdaFactor*chi_m1[0]*chi_m1[0]*pi/Chains[0].Fsq << endl;

	OneChQS_SetKondoH0(Params,pAeig,pSingleSite,STLMatArray);

	// BuildBasis params
	CommonQNs.push_back(2); // Q and S and commont QNs
	CommonQNs.push_back(0); // position of Q in old basis
	CommonQNs.push_back(1); // position of S in old basis
	CommonQNs.push_back(0); // position of Q in SingleSite
	CommonQNs.push_back(1); // position of S in SingleSite
	totSpos.push_back(1);   // SU(2) symmetry in position 1
	// HN params
	pHN->CalcHNMatEl=OneChQS_HN_MatEl;
	NumChannels=1;
	// Set chi_0 to enter HN!
	chi_m1[0]=Chains[0].GetChin(0); // Should be EQUAL to chiN(0,Lambda) if Square
	cout << " chi_0= " << chi_m1[0]
	     << " Square band chi_0 : " << chiN(0,Lambda)
	     << endl;

	break;
	// end OneChQS Kondo model set up

      case 3: // Chain only

	ZeroParams();
	Nsites0=1;
	NumThermoMats=2;

	// fN (reduced)
	STLMatArray[0].NeedOld=false;
	STLMatArray[0].UpperTriangular=false;
	STLMatArray[0].CheckForMatEl=OneChQS_cd_check;
	STLMatArray[0].CalcMatEl=OneChQS_fN_MatEl;
	STLMatArray.pop_back(); // Only ONE array
	STLMatArray.pop_back(); // Only ONE array
	STLMatArray.pop_back(); // Only ONE array
	// Set ChainH0: SingleSite is already set, just get Aeig and STLMatArray.
	OneChQS_SetChainH0(pAeig,STLMatArray);
	NumNRGmats=STLMatArray.size();
	MatArray=&STLMatArray[0]; // Need to do this after
	SaveData=false;
	// BuildBasis params
	CommonQNs.push_back(2); // Q and S and commont QNs
	CommonQNs.push_back(0); // position of Q in old basis
	CommonQNs.push_back(1); // position of S in old basis
	CommonQNs.push_back(0); // position of Q in SingleSite
	CommonQNs.push_back(1); // position of S in SingleSite
	totSpos.push_back(1);   // SU(2) symmetry in position 1
	// HN params
	pHN->CalcHNMatEl=OneChQS_HN_MatEl;
	NumChannels=1;
	if (BandNo==0){
	  chi_m1[0]=chiN(0,Lambda); // is actually chi0 // chiN is a codehandler func
	// Valid ONLY for SquareBand, z_twist=1! Check side dot calcs...
	}
	else{chi_m1[0]=Chains[0].GetChin(0);}
	// Need to change chain files for ALL Models!!
	//strcpy(ThermoArray[0].ChainArqName,"SuscepChain1Ch_QS.dat");
	strcpy(ThermoArray[0].ChainArqName,"SuscepChain1Ch_QS");
	strcat(ThermoArray[0].ChainArqName,zstring);
	strcat(ThermoArray[0].ChainArqName,".dat");
	//strcpy(ThermoArray[1].ChainArqName,"EntropyChain1Ch_QS.dat");
	strcpy(ThermoArray[1].ChainArqName,"EntropyChain1Ch_QS");
	strcat(ThermoArray[1].ChainArqName,zstring);
	strcat(ThermoArray[1].ChainArqName,".dat");
	break;

      case 6: // 1chQS Double Quantum dot

	cout << " Implementing DQD case... " << endl;

	chi2_m1=sqrt(2.0*dInitParams[4]/pi)/(sqrt(Lambda)*HalfLambdaFactor);

	Params.push_back(Lambda);
	Params.push_back(HalfLambdaFactor);
	Params.push_back(dInitParams[0]); // U1
	Params.push_back(dInitParams[2]); // ed1
	Params.push_back(chi_m1[0]); // gamma1
	Params.push_back(dInitParams[3]); // U2
	Params.push_back(dInitParams[5]); // ed2
	Params.push_back(chi2_m1); // gamma2

	// Question: Can these things be implemented within the subroutine??

	// fN (reduced)
	STLMatArray[0].NeedOld=false;
	STLMatArray[0].UpperTriangular=false;
	STLMatArray[0].CheckForMatEl=OneChQS_cd_check;
	STLMatArray[0].CalcMatEl=OneChQS_fN_MatEl;

	switch (calcdens){
	case 0: // Calculates Op averages only
	  auxNRGMat.NeedOld=true;
	  auxNRGMat.UpperTriangular=false; // update procedure only works for 'false'
	  auxNRGMat.CalcAvg=true;  // CalcAvg
	  auxNRGMat.CheckForMatEl=Diag_check;
	  auxNRGMat.CalcMatEl=ImpOnly_MatEl;
	  STLMatArray[1]=auxNRGMat; // 1 - ndot (there already)
	  STLMatArray[2]=auxNRGMat; // 2 - ndot^2 (there already)
	  STLMatArray[3]=auxNRGMat; // 3 - Sdot^2 (there already)
	  STLMatArray.push_back(auxNRGMat); // 4 - S1 dot S2
	  strcpy(STLMatArray[1].MatName,"Ndqd");
	  strcpy(STLMatArray[2].MatName,"NdqdSq");
	  strcpy(STLMatArray[3].MatName,"SdqdSq");
	  strcpy(STLMatArray[4].MatName,"S1dotS2");
	  NumNRGmats=STLMatArray.size();
	  MatArray=&STLMatArray[0]; // Need to do this after
	  // changes in STLMatArray!
	  SaveData=false;
	  break;
	case 1: // Calc Spectral functions
	  // All operators have the same Checks/MatEls in |Q> basis
	  // what changes is the initial set-up
	  auxNRGMat.NeedOld=true;
	  auxNRGMat.CheckForMatEl=OneChQS_cd_check;
	  auxNRGMat.CalcMatEl=OneChQS_cd_MatEl;
	  auxNRGMat.SaveMatYN=true;
	  STLMatArray[1]=auxNRGMat; // 1 - cd1
	  STLMatArray[2]=auxNRGMat; // 2 - cd2
	  strcpy(STLMatArray[1].MatName,"cdot1");
	  strcpy(STLMatArray[2].MatName,"cdot2");
	  STLMatArray.pop_back(); // 3 elements only
	  NumNRGmats=STLMatArray.size();
	  MatArray=&STLMatArray[0]; // Need to do this after
	  // changes in STLMatArray!
	  SaveData=true;
	  strcpy(SaveArraysFileName,"DQD");
	  break;
	case 2: // Thermodynamics
	  STLMatArray.pop_back(); // 1 Matrix only
	  STLMatArray.pop_back(); // 1 Matrix only
	  STLMatArray.pop_back(); // 1 Matrix only
	  //strcpy(ThermoArray[0].ArqName,"SuscepImp1ChQS_DQD.dat");
	  //strcpy(ThermoArray[0].ChainArqName,"SuscepChain1Ch_QS.dat");
	  strcpy(ThermoArray[0].ArqName,"SuscepImp1ChQS_DQD");
	  strcat(ThermoArray[0].ArqName,zstring);
	  strcat(ThermoArray[0].ArqName,".dat");
	  strcpy(ThermoArray[0].ChainArqName,"SuscepChain1Ch_QS");
	  strcat(ThermoArray[0].ChainArqName,zstring);
	  strcat(ThermoArray[0].ChainArqName,".dat");
	  //strcpy(ThermoArray[1].ArqName,"EntropyImp1ChQS_DQD.dat");
	  //strcpy(ThermoArray[1].ChainArqName,"EntropyChain1Ch_QS.dat");
	  strcpy(ThermoArray[1].ArqName,"EntropyImp1ChQS_DQD");
	  strcat(ThermoArray[1].ArqName,zstring);
	  strcat(ThermoArray[1].ArqName,".dat");
	  strcpy(ThermoArray[1].ChainArqName,"EntropyChain1Ch_QS");
	  strcat(ThermoArray[1].ChainArqName,zstring);
	  strcat(ThermoArray[1].ChainArqName,".dat");

	  ThermoArray[0].Calc=CalcSuscep;
	  ThermoArray[0].dImpValue=1.0/4.0; // check. Irrelevant, actually.
	  ThermoArray[1].Calc=CalcEntropy;
	  ThermoArray[1].dImpValue=4.0*log(2.0);
	  NumThermoMats=2;
	  break;

	default:
	  cout << " Calculating levels only " << endl;

	}
	// end switch calcdens

	OneChQS_SetH0_DQD(Params,pAeig,pSingleSite,STLMatArray);

	// BuildBasis params
	CommonQNs.push_back(2); // Q and S and commont QNs
	CommonQNs.push_back(0); // position of Q in old basis
	CommonQNs.push_back(1); // position of S in old basis
	CommonQNs.push_back(0); // position of Q in SingleSite
	CommonQNs.push_back(1); // position of S in SingleSite
	totSpos.push_back(1);   // SU(2) symmetry in position 1
	// HN params
	pHN->CalcHNMatEl=OneChQS_HN_MatEl;
	Nsites0=1;
	NumChannels=1;
	chi_m1[0]=chiN(0,Lambda);

	break;

      default:
	cout << " Model not implemented for this symmetry. Exiting... " << endl;
	exit(0);
      }
      // end switch ModelNo
      break;

    case 1: // TwoChQS
      ////////////////////////////
      /////                   ////
      ///// Symmetry: TwoChQS ////
      /////                   ////
      ////////////////////////////
      /// Models so far:       ///
      ///  1 - Kondo           ///
      ///  3 - Chain           ///
      ////////////////////////////
      switch(ModelNo){
      case 1:
	// Single-impurity Kondo model Q,S symmetry
	cout << " Implementing the two-Channel Kondo model " << endl;

	Nsites0=1;
	NumThermoMats=2;

	Params.clear();

	//Params.push_back(Lambda);
	//Params.push_back(HalfLambdaFactor); Do we need this??

	// Coupling to second channel
// 	chi2_m1=sqrt(2.0*dInitParams[4]/pi)/(sqrt(Lambda)*HalfLambdaFactor);
	chi_m1[1]=sqrt(2.0*dInitParams[4]/pi)/(sqrt(Lambda)*HalfLambdaFactor);

	cout << " FOR NOW, not including the Lambda factor in J1,J2 " << endl;

	Params.push_back(dInitParams[1]); // J1
	Params.push_back(dInitParams[4]); // J2

	switch (calcdens){
	case 0: // Levels only
	  NumThermoMats=0;
	  break;
	case 2:
	  // Z trick in file name.
	  strcpy(ThermoArray[0].ArqName,"SuscepImp2ChQS_Kondo");
	  strcat(ThermoArray[0].ArqName,zstring);
	  strcat(ThermoArray[0].ArqName,".dat");
	  strcpy(ThermoArray[0].ChainArqName,"SuscepChain2Ch_QS");
	  strcat(ThermoArray[0].ChainArqName,zstring);
	  strcat(ThermoArray[0].ChainArqName,".dat");
	  strcpy(ThermoArray[1].ArqName,"EntropyImp2ChQS_Kondo");
	  strcat(ThermoArray[1].ArqName,zstring);
	  strcat(ThermoArray[1].ArqName,".dat");
	  strcpy(ThermoArray[1].ChainArqName,"EntropyChain2Ch_QS");
	  strcat(ThermoArray[1].ChainArqName,zstring);
	  strcat(ThermoArray[1].ChainArqName,".dat");
	  // Values for the single-impurity Kondo model
	  ThermoArray[0].Calc=CalcSuscep;
	  ThermoArray[0].dImpValue=1.0/4.0;
	  ThermoArray[1].Calc=CalcEntropy;
	  ThermoArray[1].dImpValue=log(2.0);
	  NumThermoMats=2;
	  break;
// 	case 3:
// 	  break;
	default:
	  cout << " Please select calcdens=0 or 2 for TwoChQS_Kondo " << endl;
	  exit(0);
	}
	// end switch calcdens

	STLMatArray.pop_back(); // 2 Matrices
	STLMatArray.pop_back(); // 2 Matrices

	// fN_ch1 (reduced)
	STLMatArray[0].NeedOld=false;
	STLMatArray[0].UpperTriangular=false; // WHY? Not symmetric
	STLMatArray[0].SaveMatYN=false;
	STLMatArray[0].CheckForMatEl=TwoChQS_cd_check;
	STLMatArray[0].CalcMatEl=TwoChQS_fNch1_MatEl;

	// fN_ch2 (reduced)
	STLMatArray[1].NeedOld=false;
	STLMatArray[1].UpperTriangular=false;  // WHY? Not symmetric
	STLMatArray[1].SaveMatYN=false;
	STLMatArray[1].CheckForMatEl=TwoChQS_cd_check;
	STLMatArray[1].CalcMatEl=TwoChQS_fNch2_MatEl;

	TwoChQS_SetKondoH0(Params,pSingleSite,pAeig,STLMatArray);
	// Do I need pAbasis?? Nope...

	// BuildBasis params
	CommonQNs.push_back(2); // Q and S and commont QNs
	CommonQNs.push_back(0); // position of Q in old basis
	CommonQNs.push_back(1); // position of S in old basis
	CommonQNs.push_back(0); // position of Q in SingleSite
	CommonQNs.push_back(1); // position of S in SingleSite
	totSpos.push_back(1);   // SU(2) symmetry in position 1
	// Hamiltonian/Code params
	// Need to check these things. (May 8/2011)
	pHN->CalcHNMatEl=TwoChQS_HN_MatEl;
	Nsites0=1;
	NumNRGmats=2;
	NumChannels=2; // Not needed...

	// Is this ok? What if there is a pseudogap?? Solved! (2019)
	chi_m1[0]=chiN(0,Lambda);
// 	chi2_m1=chiN(0,Lambda);
	chi_m1[1]=chiN(0,Lambda);
	if (BandNo==0){
	  chi_m1[0]=chiN(0,Lambda); // is actually chi0 // chiN is a codehandler func
// 	  chi2_m1=chiN(0,Lambda);
	  chi_m1[1]=chiN(0,Lambda);
	}
	// Valid ONLY for SquareBand, z_twist=1! Check side dot calcs...
	else{
	  chi_m1[0]=Chains[0].GetChin(0);
// 	  chi2_m1=Chains[1].GetChin(0);
	  chi_m1[1]=Chains[1].GetChin(0);
	}
	///////////

	// Debugging: Print and Exit
	pAeig->PrintEn();
	//exit(0);
	break;
      case 3: // TwoChQS Chain only

	ZeroParams();
	Nsites0=1;
	NumNRGmats=2;
	NumChannels=2;
	NumThermoMats=2;

	strcpy(ThermoArray[0].ChainArqName,"SuscepChain2Ch_QS");
	strcat(ThermoArray[0].ChainArqName,zstring);
	strcat(ThermoArray[0].ChainArqName,".dat");
	strcpy(ThermoArray[1].ChainArqName,"EntropyChain2Ch_QS");
	strcat(ThermoArray[1].ChainArqName,zstring);
	strcat(ThermoArray[1].ChainArqName,".dat");

	STLMatArray.pop_back(); // 2 Matrices
	STLMatArray.pop_back(); // 2 Matrices
	// fN_ch1 (reduced)
	STLMatArray[0].NeedOld=false;
	STLMatArray[0].UpperTriangular=false; // WHY? Not symmetric
	STLMatArray[0].SaveMatYN=false;
	STLMatArray[0].CheckForMatEl=TwoChQS_cd_check;
	STLMatArray[0].CalcMatEl=TwoChQS_fNch1_MatEl;
	// fN_ch2 (reduced)
	STLMatArray[1].NeedOld=false;
	STLMatArray[1].UpperTriangular=false;  // WHY? Not symmetric
	STLMatArray[1].SaveMatYN=false;
	STLMatArray[1].CheckForMatEl=TwoChQS_cd_check;
	STLMatArray[1].CalcMatEl=TwoChQS_fNch2_MatEl;

	TwoChQS_SetH0Chain(pAeig,pSingleSite,STLMatArray);
	// Debugging: Print and Exit
	pAeig->PrintEn();

	// BuildBasis params
	CommonQNs.push_back(2); // Q and S and commont QNs
	CommonQNs.push_back(0); // position of Q in old basis
	CommonQNs.push_back(1); // position of S in old basis
	CommonQNs.push_back(0); // position of Q in SingleSite
	CommonQNs.push_back(1); // position of S in SingleSite
	totSpos.push_back(1);   // SU(2) symmetry in position 1
	// Hamiltonian/Code params
	pHN->CalcHNMatEl=TwoChQS_HN_MatEl;
	if (BandNo==0){
	  chi_m1[0]=chiN(0,Lambda); // is actually chi0 // chiN is a codehandler func
	  chi_m1[1]=chiN(0,Lambda);
	}
	// Valid ONLY for SquareBand, z_twist=1! Check side dot calcs...
	else{
	  chi_m1[0]=Chains[0].GetChin(0);
	  chi_m1[1]=Chains[1].GetChin(0);
	}
	///////////
	break;
      default:
	cout << " Model not implemented for this symmetry. Exiting... " << endl;
	exit(0);
      }
      // end switch ModelNo
      break;

    case 2: // OneChQSz
      ////////////////////////////
      /////                    ////
      ///// Symmetry: OneChQSz ////
      /////                    ////
      //////////////////////////////////////
      /// Models so far:                 ///
      ///  0 - Anderson (Bfield)         ///
      ///  3 - Chain                     ///
      ///  6 - DQD (Zeeman in both dots) ///
      //////////////////////////////////////

      switch(ModelNo){
      case 0: // Anderson

	// Initialize matrices (OneChQ routines work here!)
	// Jul 09: using STMatArray

	STLMatArray[0].NeedOld=false;
	STLMatArray[0].CheckForMatEl=OneChQ_cd_check; //WHY???
	STLMatArray[0].CalcMatEl=OneChQ_fNup_MatEl;
	STLMatArray[0].SaveMatYN=false;

	STLMatArray[1].NeedOld=false;
	STLMatArray[1].CheckForMatEl=OneChQ_cd_check; //WHY???
	STLMatArray[1].CalcMatEl=OneChQ_fNdn_MatEl;
	STLMatArray[1].SaveMatYN=false;

	// Param for H0
	Params.push_back(dInitParams[0]); // U
	Params.push_back(dInitParams[2]); // ed
	Params.push_back(Lambda);
	Params.push_back(HalfLambdaFactor);
	Params.push_back(dInitParams[3]);        // Mag Field

	switch (calcdens){
	case 0: // OpAvg: n_d(T), S_z(T) -(2013)
	  auxNRGMat.NeedOld=true;
	  auxNRGMat.UpperTriangular=false; // update procedure only works for 'false'
	  auxNRGMat.CalcAvg=true;  // CalcAvg
	  auxNRGMat.CheckForMatEl=Diag_check;
	  auxNRGMat.CalcMatEl=ImpOnly_MatEl;
	  STLMatArray[2]=auxNRGMat; // 2 - ndot (there already)
	  STLMatArray[3]=auxNRGMat; // 3 - Sz (there already)
	  strcpy(STLMatArray[2].MatName,"Ndot");
	  strcpy(STLMatArray[3].MatName,"Szdot");
	  // changes in STLMatArray!
	  SaveData=false;
	  // No Thermo
	  NumThermoMats=0;
	  break;
 	case 1: //Spectral density
	  // cd_up and cd_dn (OneChQSz_SetAnderson equals
	  // MatArray[2,3] to MatArray[0,1]
	  STLMatArray[2].NeedOld=true;
	  STLMatArray[2].CheckForMatEl=OneChQSz_cdup_check;
	  STLMatArray[2].CalcMatEl=OneChQSz_cdup_MatEl;
	  STLMatArray[2].SaveMatYN=true;

	  STLMatArray[3].NeedOld=true;
	  STLMatArray[3].CheckForMatEl=OneChQSz_cddn_check;
	  STLMatArray[3].CalcMatEl=OneChQSz_cddn_MatEl;
	  STLMatArray[3].SaveMatYN=true;

	  strcpy(STLMatArray[2].MatName,"cdup");
	  strcpy(STLMatArray[3].MatName,"cddn");

	  cout << " N = -1" << endl;
	  cout << " cdN_up : " << endl;
	  STLMatArray[2].PrintAllBlocks();
	  cout << " cdN_dn : " << endl;
	  STLMatArray[3].PrintAllBlocks();

	  SaveData=true;

 	  break;
 	case 2: // Thermodynamics ONLY

	  STLMatArray.pop_back(); // 2 Matrices only
	  STLMatArray.pop_back(); // 2 Matrices only
	  strcpy(ThermoArray[0].ArqName,"SuscepImp1ChQSz");
	  strcat(ThermoArray[0].ArqName,zstring);
	  strcat(ThermoArray[0].ArqName,".dat");
	  strcpy(ThermoArray[0].ChainArqName,"SuscepChain1Ch_QSz");
	  strcat(ThermoArray[0].ChainArqName,zstring);
	  strcat(ThermoArray[0].ChainArqName,".dat");

	  strcpy(ThermoArray[1].ArqName,"EntropyImp1ChQSz");
	  strcat(ThermoArray[1].ArqName,zstring);
	  strcat(ThermoArray[1].ArqName,".dat");
	  strcpy(ThermoArray[1].ChainArqName,"EntropyChain1Ch_QSz");
	  strcat(ThermoArray[1].ChainArqName,zstring);
	  strcat(ThermoArray[1].ChainArqName,".dat");

	  ThermoArray[0].Calc=CalcSuscep;
	  ThermoArray[0].dImpValue=1.0/8.0; // check
	  ThermoArray[1].Calc=CalcEntropy;
	  ThermoArray[1].dImpValue=2.0*log(2.0);
	  NumThermoMats=2;
	  SaveData=false;

 	  break;

	case 4: // Dynamical spin susceptibility and <Sz> and <Sz^2> (2013)
	  // 6 Matrices
	  STLMatArray.push_back(auxNRGMat);
	  STLMatArray.push_back(auxNRGMat);
	  NumThermoMats=0;
	  // <Sz> and <Sz2> operator
	  auxNRGMat.NeedOld=true;
	  auxNRGMat.UpperTriangular=false;
	  // update procedure only works for 'false'
	  auxNRGMat.CalcAvg=true;  // CalcAvg
	  auxNRGMat.CheckForMatEl=Diag_check;
	  auxNRGMat.CalcMatEl=ImpOnly_MatEl;

	  STLMatArray[2]=auxNRGMat; // 2 - Sz dynamical
	  STLMatArray[2].SaveMatYN=true;
	  STLMatArray[2].CalcAvg=false;

	  STLMatArray[3]=auxNRGMat; // 3 - Nd dynamical
	  STLMatArray[3].SaveMatYN=true;
	  STLMatArray[3].CalcAvg=false;

	  STLMatArray[4]=auxNRGMat; // 4 - <Sz2>(T) static
	  STLMatArray[4].SaveMatYN=false;

	  STLMatArray[5]=auxNRGMat; // 5 - <Nd2>(T) static
	  STLMatArray[5].SaveMatYN=false;

	  strcpy(STLMatArray[2].MatName,"Szomega");
	  strcpy(STLMatArray[3].MatName,"Ndomega");
	  strcpy(STLMatArray[4].MatName,"Sz2dot");
	  strcpy(STLMatArray[5].MatName,"Nd2dot");

	  NumNRGmats=STLMatArray.size();
	  // changes in STLMatArray!
	  SaveData=true;
	  strcpy(SaveArraysFileName,"1chQSzAnderson");
	  break;

 	case 5: //Spectral density with Self-energy trick
	  // 6 Matrices
	  STLMatArray.push_back(auxNRGMat);
	  STLMatArray.push_back(auxNRGMat);
	  // cd_up and cd_dn
	  STLMatArray[2].NeedOld=true;
	  STLMatArray[2].CheckForMatEl=OneChQSz_cdup_check;
	  STLMatArray[2].CalcMatEl=OneChQSz_cdup_MatEl;
	  STLMatArray[2].SaveMatYN=true;

	  STLMatArray[3].NeedOld=true;
	  STLMatArray[3].CheckForMatEl=OneChQSz_cddn_check;
	  STLMatArray[3].CalcMatEl=OneChQSz_cddn_MatEl;
	  STLMatArray[3].SaveMatYN=true;

	  // <cd_up N_dn> and <cd_dn N_up>
	  STLMatArray[4].NeedOld=true;
	  STLMatArray[4].CheckForMatEl=OneChQSz_cdup_check;
	  STLMatArray[4].CalcMatEl=OneChQSz_cdup_MatEl; // Same Fermi Factor
	  STLMatArray[4].SaveMatYN=true;

	  STLMatArray[5].NeedOld=true;
	  STLMatArray[5].CheckForMatEl=OneChQSz_cddn_check;
	  STLMatArray[5].CalcMatEl=OneChQSz_cddn_MatEl; // Same Fermi Factor
	  STLMatArray[5].SaveMatYN=true;

	  strcpy(STLMatArray[2].MatName,"cdup");
	  strcpy(STLMatArray[3].MatName,"cddn");
	  strcpy(STLMatArray[4].MatName,"cdupNdn");
	  strcpy(STLMatArray[5].MatName,"cddnNup");

	  SaveData=true;

 	  break;

	default:
	  cout << " calcdens = " << calcdens << " not implemented. Exiting. " << endl;
	  exit(0);

	}
	// end switch calcdens

	OneChQSz_SetAnderson_Hm1(Params,pAeig,STLMatArray);

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

	// BuildBasis params
	CommonQNs.push_back(2); // No of common QNs
	CommonQNs.push_back(0); // pos of QN 1 in old
	CommonQNs.push_back(1); // pos of QN 2 in old

	CommonQNs.push_back(0); // pos of QN 1 in SingleSite
	CommonQNs.push_back(1); // pos of QN 1 in SingleSite

	// No total S variables. Leave totSpos empty
	pHN->CalcHNMatEl=OneChQSz_HN_MatEl;

	// Wrap up
	NumChannels=1;
	Nsites0=0;
	NumNRGmats=STLMatArray.size();
	MatArray=&STLMatArray[0]; // Need to do this after
	// changes in STLMatArray!

	break;

      case 3: // OneChQSz Chain only

	ZeroParams();
	Nsites0=1;
	NumThermoMats=2;
	NumChannels=1;

	// fN_up and fN_dn
	// fd_up
	STLMatArray[0].NeedOld=false;
	STLMatArray[0].UpperTriangular=false;
	STLMatArray[0].CheckForMatEl=OneChQSz_cdup_check; //This should work
	STLMatArray[0].CalcMatEl=OneChQSz_fNup_MatEl;
	STLMatArray[0].SaveMatYN=false;

	// fd_dn
	STLMatArray[1].NeedOld=false;
	STLMatArray[1].UpperTriangular=false;
	STLMatArray[1].CheckForMatEl=OneChQSz_cddn_check; //This should work
	STLMatArray[1].CalcMatEl=OneChQSz_fNdn_MatEl;
	STLMatArray[1].SaveMatYN=false;

	STLMatArray.pop_back(); // Only TWO mats
	STLMatArray.pop_back(); // Only TWO mats
	// Set ChainH0: SingleSite is already set, just get Aeig and STLMatArray.
	OneChQSz_SetChainH0(pAeig,STLMatArray);
	NumNRGmats=STLMatArray.size();
	MatArray=&STLMatArray[0]; // Need to do this after
	SaveData=false;
	// BuildBasis params
	CommonQNs.push_back(2); // Q and Sz and commont QNs
	CommonQNs.push_back(0); // position of Q in old basis
	CommonQNs.push_back(1); // position of Sz in old basis
	CommonQNs.push_back(0); // position of Q in SingleSite
	CommonQNs.push_back(1); // position of Sz in SingleSite

	// No total S variables. Leave totSpos empty
	pHN->CalcHNMatEl=OneChQSz_HN_MatEl;

	if (BandNo==0){
	  chi_m1[0]=chiN(0,Lambda); // is actually chi0 // chiN is a codehandler func
	// Valid ONLY for SquareBand, z_twist=1! Check side dot calcs...
	}
	else{chi_m1[0]=Chains[0].GetChin(0);}

	NumNRGmats=STLMatArray.size(); // DO I STILL NEED THIS???
	MatArray=&STLMatArray[0]; // Need to do this after
	// changes in STLMatArray!

	// Need to change chain files for ALL Models!!
	strcpy(ThermoArray[0].ChainArqName,"SuscepChain1Ch_QSz");
	strcat(ThermoArray[0].ChainArqName,zstring);
	strcat(ThermoArray[0].ChainArqName,".dat");
	strcpy(ThermoArray[1].ChainArqName,"EntropyChain1Ch_QSz");
	strcat(ThermoArray[1].ChainArqName,zstring);
	strcat(ThermoArray[1].ChainArqName,".dat");
	break;
	// end OneChQSz chain only

      case 6: // OneChQSz DQD (Zeeman in both dots)

	// Initialize matrices (OneChQ routines work here!)
	// Jul 09: using STMatArray
	// fd_up
	STLMatArray[0].NeedOld=false;
	STLMatArray[0].CheckForMatEl=OneChQSz_cdup_check; //This should work
	STLMatArray[0].CalcMatEl=OneChQSz_fNup_MatEl;
	STLMatArray[0].SaveMatYN=false;

	// fd_dn
	STLMatArray[1].NeedOld=false;
	STLMatArray[1].CheckForMatEl=OneChQSz_cddn_check; //This should work
	STLMatArray[1].CalcMatEl=OneChQSz_fNdn_MatEl;
	STLMatArray[1].SaveMatYN=false;

	// Param for H0 (11 parameters)
	Params.push_back(Lambda);
	Params.push_back(HalfLambdaFactor);

	Params.push_back(dInitParams[0]); // U1
	Params.push_back(dInitParams[2]); // ed1
	Params.push_back(chi_m1[0]); // gamma1
	Params.push_back(dInitParams[7]); // Bmag1

	Params.push_back(dInitParams[3]); // U2
	Params.push_back(dInitParams[5]); // ed2
	chi2_m1=sqrt(2.0*dInitParams[4]/pi)/(sqrt(Lambda)*HalfLambdaFactor);
	Params.push_back(chi2_m1); // gamma2
	Params.push_back(dInitParams[8]); // Bmag2

	Params.push_back(dInitParams[6]); // lambda

	switch (calcdens){
	case 1: // Calculates spectral densities
	  // cd_up and cd_dn (OneChQSz_SetAnderson equals
	  // c1_up
	  STLMatArray[2].NeedOld=true;
	  STLMatArray[2].CheckForMatEl=OneChQSz_cdup_check;
	  STLMatArray[2].CalcMatEl=OneChQSz_cdup_MatEl;
	  STLMatArray[2].SaveMatYN=true;
	  strcpy(STLMatArray[2].MatName,"cdot1_up");
	  // c1_dn
	  STLMatArray[3].NeedOld=true;
	  STLMatArray[3].CheckForMatEl=OneChQSz_cddn_check;
	  STLMatArray[3].CalcMatEl=OneChQSz_cddn_MatEl;
	  STLMatArray[3].SaveMatYN=true;
	  strcpy(STLMatArray[3].MatName,"cdot1_dn");
	  // c2_up
	  auxNRGMat=STLMatArray[2];
	  STLMatArray.push_back(auxNRGMat);
	  strcpy(STLMatArray[4].MatName,"cdot2_up");
	  // c2_dn
	  auxNRGMat=STLMatArray[3];
	  STLMatArray.push_back(auxNRGMat);
	  strcpy(STLMatArray[5].MatName,"cdot2_dn");
	  // changes in STLMatArray
	  SaveData=true;
	  strcpy(SaveArraysFileName,"DQD_QSz");

	  break;
	case 2: // Thermodynamics for DQD+Zeeman
	  STLMatArray.pop_back(); // 2 Matrices only
	  STLMatArray.pop_back(); // 2 Matrices only
	  //strcpy(ThermoArray[0].ArqName,"SuscepImp1ChQSz_DQD.dat");
	  strcpy(ThermoArray[0].ArqName,"SuscepImp1ChQSz_DQD");
	  strcat(ThermoArray[0].ArqName,zstring);
	  strcat(ThermoArray[0].ArqName,".dat");
	  strcpy(ThermoArray[0].ChainArqName,"SuscepChain1Ch_QSz");
	  strcat(ThermoArray[0].ChainArqName,zstring);
	  strcat(ThermoArray[0].ChainArqName,".dat");
	  //strcpy(ThermoArray[1].ArqName,"EntropyImp1ChQSz_DQD.dat");
	  strcpy(ThermoArray[1].ArqName,"EntropyImp1ChQSz_DQD");
	  strcat(ThermoArray[1].ArqName,zstring);
	  strcat(ThermoArray[1].ArqName,".dat");
	  strcpy(ThermoArray[1].ChainArqName,"EntropyChain1Ch_QSz");
	  strcat(ThermoArray[1].ChainArqName,zstring);
	  strcat(ThermoArray[1].ChainArqName,".dat");

	  ThermoArray[0].Calc=CalcSuscep;
	  ThermoArray[0].dImpValue=1.0/4.0; // check
	  ThermoArray[1].Calc=CalcEntropy;
	  ThermoArray[1].dImpValue=4.0*log(2.0);
	  NumThermoMats=2;
	  SaveData=false;

	  break;

	default:
	  cout << " Please select calcdens=1,2 or 3 for 1chQSz_DQD " << endl;
	  exit(0);
	}
	// end switch calcdens

	OneChQSz_SetH0_DQD(Params,pAeig,pSingleSite,STLMatArray);

	// Wrap up
	NumChannels=1;
	Nsites0=1;
	NumNRGmats=STLMatArray.size();
	MatArray=&STLMatArray[0]; // Need to do this after
	if (BandNo==0)
	  chi_m1[0]=chiN(0,Lambda); // is actually chi0
	// Valid ONLY for SquareBand, z_twist=1! Check side dot calcs...
	else
	  chi_m1[0]=Chains[0].GetChin(0);
	///////////
	// BuildBasis params
	CommonQNs.push_back(2); // No of common QNs
	CommonQNs.push_back(0); // pos of QN 1 in old
	CommonQNs.push_back(1); // pos of QN 2 in old
	CommonQNs.push_back(0); // pos of QN 1 in SingleSite
	CommonQNs.push_back(1); // pos of QN 1 in SingleSite
	// No total S variables. Leave totSpos empty
	pHN->CalcHNMatEl=OneChQSz_HN_MatEl;

	break;
	// end DQD 1chQSz

      default:
	cout << " Model not implemented for this symmetry. Exiting... " << endl;
	exit(0);
      }
      // end switch ModelNo
      break;
    case 3: // OneChQ
      ///// Symmetry: OneChQ ////
      ////////////////////////////
      /////                  ////
      ///// Symmetry: OneChQ ////
      /////                  ////
      ////////////////////////////
      /// Models so far:       ///
      ///  0 - Anderson        ///
      ///  5 - SMM             ///
      ////////////////////////////

      switch(ModelNo){
      case 0: // Anderson

	// Initialize matrices (uncomment later)

	STLMatArray[0].NeedOld=false;
	STLMatArray[0].CheckForMatEl=OneChQ_cd_check;
	STLMatArray[0].CalcMatEl=OneChQ_fNup_MatEl;

	STLMatArray[1].NeedOld=false;
	STLMatArray[1].CheckForMatEl=OneChQ_cd_check;
	STLMatArray[1].CalcMatEl=OneChQ_fNdn_MatEl;

	// Sz and Sz2
	STLMatArray[2].NeedOld=true;
	STLMatArray[2].UpperTriangular=false;
	STLMatArray[2].CalcAvg=true;  // CalcAvg
	STLMatArray[2].CheckForMatEl=Diag_check;
	STLMatArray[2].CalcMatEl=ImpOnly_MatEl;
	strcpy(STLMatArray[2].MatName,"Sz");

	STLMatArray[3].NeedOld=true;
	STLMatArray[3].UpperTriangular=false;
	STLMatArray[3].CalcAvg=true;  // CalcAvg
	STLMatArray[3].CheckForMatEl=Diag_check;
	STLMatArray[3].CalcMatEl=ImpOnly_MatEl;
	strcpy(STLMatArray[3].MatName,"Sz2");

	if (calcdens==1){
	  // All operators have the same Checks/MatEls in |Q> basis
	  // what changes is the initial set-up
	  auxNRGMat.NeedOld=true;
	  auxNRGMat.UpperTriangular=false;
	  auxNRGMat.CheckForMatEl=OneChQ_cd_check;
	  auxNRGMat.CalcMatEl=OneChQ_cd_MatEl;
	  auxNRGMat.SaveMatYN=true;

	  STLMatArray.push_back(auxNRGMat); // 4 - cd1_up
	  STLMatArray.push_back(auxNRGMat); // 5 - cd1_dn
	  //NumNRGarrays=STLMatArray.size(); // do we need NumNRGarrays later?
	  NumNRGmats=STLMatArray.size();
	  MatArray=&STLMatArray[0]; //
	  // changes in STLMatArray!
	  SaveData=true;
	  strcpy(SaveArraysFileName,"1chQAnd");
	}
	else{
	  // Set Thermodynamics
	  // Entropy calculation only
	  strcpy(ThermoArray[0].ArqName,"EntropyImp1Ch_Anderson_Q.dat");
	  strcpy(ThermoArray[0].ChainArqName,"EntropyChain1Ch_Q.dat");
	  ThermoArray[0].Calc=CalcEntropy;
	  ThermoArray[0].dImpValue=2.0*log(2.0);
	  //ThermoArray[0].CalcChain=false;
	  NumThermoMats=1;
	}
	// calculate spec function or thermo?

	// Param for H0
	Params.push_back(dInitParams[0]); // U
	Params.push_back(dInitParams[2]); // ed
	Params.push_back(Lambda);
	Params.push_back(HalfLambdaFactor);
	OneChQ_SetAnderson_Hm1(Params,pAeig,STLMatArray);
	//OneChQ_SetAnderson_Hm1_old(Params,&Aeig,MatArray);

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

	// BuildBasis params
	CommonQNs.push_back(1); // No of common QNs
	CommonQNs.push_back(0); // pos of QN 1 in old
	CommonQNs.push_back(0); // pos of QN 1 in SingleSite
	// No total S variables. Leave totSpos empty
	pHN->CalcHNMatEl=OneChQ_HN_MatEl;

	NumChannels=1;
	Nsites0=0;

	break;
      case 5: // SMM model

	// Params for H0
	Params.push_back(Lambda);  // Lambda
	Params.push_back(HalfLambdaFactor); // Lambda factor

	if (dInitParams.size()>10){
	  Params.push_back(dInitParams[0]); // U1
	  Params.push_back(dInitParams[2]); // ed1

	  Params.push_back(dInitParams[3]); // U2
	  Params.push_back(dInitParams[5]); // ed2

	  Params.push_back(dInitParams[6]); // J12

	  Params.push_back(dInitParams[7]); // BmagPar
	  Params.push_back(dInitParams[8]); // BmagPerp

	  Params.push_back(dInitParams[9]); // Dz
	  Params.push_back(dInitParams[10]); // B2 anisot

	  double chi2_m1=sqrt(2.0*dInitParams[4]/pi)/(sqrt(Lambda)*HalfLambdaFactor);
	  Params.push_back(chi_m1[0]); // gamma1
	  Params.push_back(chi2_m1); // gamma2
	}
	else{
	  double U=0.5;
	  double ed=-0.5*U;
	  Params.push_back(U); // U1
	  Params.push_back(ed); // ed1

	  Params.push_back(0.0); // U2
	  Params.push_back(0.0); // ed2

	  Params.push_back(0.0); // J12

	  Params.push_back(0.0); // BmagPar
	  Params.push_back(0.0); // BmagPerp

	  Params.push_back(0.0); // Dz
	  Params.push_back(0.0); // B2 anisot

	  Params.push_back(chi_m1[0]); // gamma1
	  Params.push_back(0.0); // gamma2
	}

	// Set rules for MatArray
	// Jul 09: using STMatArray

	// fN_up
	STLMatArray[0].NeedOld=false;
	STLMatArray[0].CheckForMatEl=OneChQ_cd_check;
	STLMatArray[0].CalcMatEl=OneChQ_fNup_MatEl;

	// fN_dn
	STLMatArray[1].NeedOld=false;
	STLMatArray[1].CheckForMatEl=OneChQ_cd_check;
	STLMatArray[1].CalcMatEl=OneChQ_fNdn_MatEl;

	// Sz and Sz2

	STLMatArray[2].NeedOld=true;
	STLMatArray[2].UpperTriangular=false;
	STLMatArray[2].CalcAvg=true;  // CalcAvg
	STLMatArray[2].CheckForMatEl=Diag_check;
	STLMatArray[2].CalcMatEl=ImpOnly_MatEl;
	strcpy(STLMatArray[2].MatName,"Sz");

	STLMatArray[3].NeedOld=true;
	STLMatArray[3].UpperTriangular=false;
	STLMatArray[2].CalcAvg=true;
	STLMatArray[3].CheckForMatEl=Diag_check;
	STLMatArray[3].CalcMatEl=ImpOnly_MatEl;
	strcpy(STLMatArray[3].MatName,"Sz2");

	// cd1_up, cd1_dn, cd2_up, cd2_dn
	// MatArray is an STL vector!!!
	if (calcdens==1){
	  // All operators have the same Checks/MatEls in |Q> basis
	  // what changes is the initial set-up
	  auxNRGMat.NeedOld=true;
	  auxNRGMat.CheckForMatEl=OneChQ_cd_check;
	  auxNRGMat.CalcMatEl=OneChQ_cd_MatEl;
	  auxNRGMat.SaveMatYN=true;

	  STLMatArray.push_back(auxNRGMat); // 4 - cd1_up
	  STLMatArray.push_back(auxNRGMat); // 5 - cd1_dn
	  STLMatArray.push_back(auxNRGMat); // 6 - cd2_up
	  STLMatArray.push_back(auxNRGMat); // 7 - cd2_dn
	  NumNRGmats=STLMatArray.size();
	  MatArray=&STLMatArray[0]; // Need to do this after
	  // changes in STLMatArray!
	  SaveData=true;
	  strcpy(SaveArraysFileName,"SMM");

	}
	else{
	  // Set Thermodynamics

	  // Entropy calculation only

	  strcpy(ThermoArray[0].ArqName,"EntropyImp1Ch_Anderson_Q.dat");
	  strcpy(ThermoArray[0].ChainArqName,"EntropyChain1Ch_Q.dat");
	  ThermoArray[0].Calc=CalcEntropy;
	  ThermoArray[0].dImpValue=4.0*log(2.0); // Seems to be irrelevant...
	  ThermoArray[0].CalcChain=false;
	  NumThermoMats=1;
	}
	// calculate spec function or thermo?

	// Set H0: output pAeig, MatArray

	OneChQ_SetSMM_H0(Params,pAeig,pSingleSite,STLMatArray);

	// BuildBasis params
	CommonQNs.push_back(1); // No of common QNs
	CommonQNs.push_back(0); // pos of QN 1 in old
	CommonQNs.push_back(0); // pos of QN 1 in SingleSite
	// No total S variables. Leave totSpos empty
	pHN->CalcHNMatEl=OneChQ_HN_MatEl;
	Nsites0=1;
	NumChannels=1;

	chi_m1[0]=chiN(0,Lambda);

	break;
      default:
	cout << " Model not implemented for this symmetry. Exiting... " << endl;
	exit(0);
      }
      // end switch ModelNo
      break;

    case 4: // TwoChQSP
      switch(ModelNo){
      ////////////////////////////
      /////                     ///
      ///// Symmetry: TwoChQSP  ///
      /////                     ///
      ////////////////////////////
      /// Models so far:       ///
      ///  4 - CM Phonons (2ch)///
      ////////////////////////////
      case 4: // CM Phonons - 2channel
	Params.push_back(Lambda);  // Lambda
	Params.push_back(HalfLambdaFactor); // Lambda factor

	Params.push_back(dInitParams[0]); // U1
	Params.push_back(dInitParams[2]); // ed1

	Params.push_back(chi_m1[0]); // sqrt(2gamma1/Pi)/sqrt(L)*HalfLambdaFactor

	Params.push_back(dInitParams[3]); // w0
	Params.push_back(dInitParams[4]); // lambda_ph
	Params.push_back(dInitParams[5]); // alpha
	Params.push_back(dInitParams[6]); // Nph

	// Update rules for f_ch1 and fch2

	// Set rules for MatArray

	// fN_ch1 (reduced)
	MatArray[0].NeedOld=false;
	MatArray[0].CheckForMatEl=TwoChQS_cd_check;
	MatArray[0].CalcMatEl=TwoChQS_cd_ich1_Phonon_MatEl;

	// fN_ch2 (reduced)
	MatArray[1].NeedOld=false;
	MatArray[1].CheckForMatEl=TwoChQSP_fA_check; // mixes parities
	MatArray[1].CalcMatEl=TwoChQS_cd_ich2_Phonon_MatEl;

	//CNRGarray MyTest1;

	// Set H0, get pAeig and Update MatArray
	TwoChQSP_SetH0CMphonon(Params, pAeig,pSingleSite,MatArray);

	// No phonons needed from now on
	MatArray[0].CalcMatEl=TwoChQS_fNch1_MatEl;
	MatArray[1].CalcMatEl=TwoChQS_fNch2_MatEl;

	// BuildBasis params
	CommonQNs.push_back(3); // No of common QNs
	CommonQNs.push_back(0); // pos of QN 1 in old
	CommonQNs.push_back(1); // pos of QN 2 in old
	CommonQNs.push_back(2); // pos of QN 3 in old

	CommonQNs.push_back(0); // pos of QN 1 in SingleSite
	CommonQNs.push_back(1); // pos of QN 2 in SingleSite
	CommonQNs.push_back(3); // pos of QN 3 in SingleSite

	CommonQNs.push_back(2); // Pos of parity in old
	totSpos.push_back(1); // Pos of S in old

	// Hamiltonian/Code params
	pHN->CalcHNMatEl=TwoChQS_HN_MatEl;
	Nsites0=1;
	chi_m1[0]=chiN(0,Lambda);
	NumNRGmats=2;
	NumChannels=2;
	NumThermoMats=2;

	ThermoArray[0].dImpValue=0.0;
	ThermoArray[1].dImpValue=0.0; // Check this!!

	// Thermobasics

	strcpy(ThermoArray[0].ArqName,"SuscepImp2Ch_CMphonon_QSP.dat");
	strcpy(ThermoArray[0].ChainArqName,"SuscepChain2Ch.dat");
	ThermoArray[0].Calc=CalcSuscep;

	strcpy(ThermoArray[1].ArqName,"EntropyImp2Ch_CMphonon_QSP.dat");
	strcpy(ThermoArray[1].ChainArqName,"EntropyChain2Ch.dat");
	ThermoArray[1].Calc=CalcEntropy;

	cout << " Implementing this model NOW " << endl;

	cout << " fN_ch1 : " << endl;
	MatArray[0].PrintAllBlocks();
	cout << " fN_ch2 : " << endl;
	MatArray[1].PrintAllBlocks();

	break;
      default:
	cout << " Model not implemented for this symmetry. Exiting... " << endl;
	exit(0);
      }
      // end switch ModelNo
      break;

    case 6: // OneChNupPdn
      ////////////////////////////////
      /////                        ///
      ///// Symmetry: OneChNupPdn  ///
      /////                        ///
      ////////////////////////////////

      // BuildBasis params
      CommonQNs.push_back(2); // No of common QNs
      CommonQNs.push_back(0); // pos of QN 1 in old
      CommonQNs.push_back(1); // pos of QN 2 in old

      CommonQNs.push_back(0); // pos of QN 1 in SingleSite
      CommonQNs.push_back(1); // pos of QN 2 in SingleSite

      CommonQNs.push_back(1); // pos of Parity QN
      // No total S variables. Leave totSpos empty

      switch(ModelNo){
      ///////////////////////////////////
      /// Models so far:              ///
      ///  3 - Chain only             ///
      ///  7 - Anderson+Local Majorana///
      ///////////////////////////////////
      case 3: // Chain only

	ZeroParams();
	Nsites0=1; // IS THIS CORRECT? Yes. H_0 is set so we start NRG from H_1
	NumThermoMats=1; // Entropy only

	// Initialize matrices (real matrices)
	// fd_up
	STLMatArray[0].NeedOld=false;
 	STLMatArray[0].CheckForMatEl=OneChNupPdn_cdup_check;
  	STLMatArray[0].CalcMatEl=OneChNupPdn_fNup_MatEl;
 	//STLMatArray[0].CalcMatElCplx=OneChNupPdn_fNup_MatElCplx;
	STLMatArray[0].SaveMatYN=false;
	STLMatArray[0].IsComplex=false;

	// fd_dn
	STLMatArray[1].NeedOld=false;
 	STLMatArray[1].CheckForMatEl=OneChNupPdn_cddn_check;
  	STLMatArray[1].CalcMatEl=OneChNupPdn_fNdn_MatEl;
	STLMatArray[1].SaveMatYN=false;
	STLMatArray[1].IsComplex=false;

	STLMatArray.pop_back(); // Only TWO arrays
	STLMatArray.pop_back(); // Only TWO arrays

	OneChNupPdn_SetChainH0(pAeig,STLMatArray);

	// Set pHN
	pHN->CalcHNMatEl=OneChNupPdn_HN_MatEl; // REAL
	// Wrap up
	NumChannels=1;
	NumNRGmats=STLMatArray.size();
	MatArray=&STLMatArray[0]; // Need to do this after
	// changes in STLMatArray!

	if (BandNo==0){
	  chi_m1[0]=chiN(0,Lambda); // is actually chi0 // chiN is a codehandler func
	// Valid ONLY for SquareBand, z_twist=1! Check side dot calcs...
	}
	else{chi_m1[0]=Chains[0].GetChin(0);}
	// Need to change chain files for ALL Models!!
	strcpy(ThermoArray[0].ChainArqName,"EntropyChain1Ch_NupPdn");
	strcat(ThermoArray[0].ChainArqName,zstring);
	strcat(ThermoArray[0].ChainArqName,".dat");
	ThermoArray[0].Calc=CalcEntropy;
	ThermoArray[0].CalcChain=true; // Just to make sure

	break;

      case 7:
	cout << " Majorana + QD effective model " << endl;

	Params.push_back(Lambda);  // Lambda
	Params.push_back(HalfLambdaFactor); // Lambda factor

	Params.push_back(dInitParams[0]); // U1
	Params.push_back(dInitParams[2]); // ed1
	Params.push_back(chi_m1[0]); // sqrt(2gamma1/Pi)/sqrt(L)*HalfLambdaFactor

	Params.push_back(dInitParams[3]); // Mag Field

	Params.push_back(dInitParams[4]); // t1
	Params.push_back(dInitParams[5]); // t2
	Params.push_back(dInitParams[6]); // phi_mag
	Params.push_back(dInitParams[7]); // em

	// Initialize matrices
	// fd_up
	STLMatArray[0].NeedOld=false;
 	STLMatArray[0].CheckForMatEl=OneChNupPdn_cdup_check;
//  	STLMatArray[0].CalcMatEl=OneChNupPdn_fNup_MatEl;
 	STLMatArray[0].CalcMatElCplx=OneChNupPdn_fNup_MatElCplx;
	STLMatArray[0].SaveMatYN=false;
	STLMatArray[0].IsComplex=true;

	// fd_dn
	STLMatArray[1].NeedOld=false;
 	STLMatArray[1].CheckForMatEl=OneChNupPdn_cddn_check;
 	STLMatArray[1].CalcMatElCplx=OneChNupPdn_fNdn_MatElCplx;
	STLMatArray[1].SaveMatYN=false;
	STLMatArray[1].IsComplex=true;

	switch (calcdens){
	case 0: // OpAvg: n_d(T), S_z(T) -(2014)
	  auxNRGMat.NeedOld=true;
	  auxNRGMat.UpperTriangular=false; // update procedure only works for 'false'
	  auxNRGMat.CalcAvg=true;  // CalcAvg
	  auxNRGMat.CheckForMatEl=Diag_check;
	  auxNRGMat.CalcMatElCplx=ImpOnly_MatElCplx;
	  auxNRGMat.IsComplex=true;

	  STLMatArray[2]=auxNRGMat; // 2 - ndot (there already)
	  STLMatArray[3]=auxNRGMat; // 3 - Sz (there already)
	  strcpy(STLMatArray[2].MatName,"Ndot");
	  strcpy(STLMatArray[3].MatName,"Szdot");
	  STLMatArray.push_back(auxNRGMat);
	  strcpy(STLMatArray[4].MatName,"Nf"); // 4 -Nf
	  // changes in STLMatArray!
	  SaveData=false;
	  // No Thermo
	  NumThermoMats=0;
	  break;
 	case 1: //Spectral density
	  // cd_up and cd_dn (OneChQSz_SetAnderson equals
	  // MatArray[2,3] to MatArray[0,1]
	  STLMatArray[2].NeedOld=true;
	  STLMatArray[2].CheckForMatEl=OneChNupPdn_cdup_check;
	  STLMatArray[2].CalcMatElCplx=OneChNupPdn_cdup_MatElCplx;
	  STLMatArray[2].SaveMatYN=true;
	  STLMatArray[2].IsComplex=true;
	  strcpy(STLMatArray[2].MatName,"cd_up");

	  STLMatArray[3].NeedOld=true;
	  STLMatArray[3].CheckForMatEl=OneChNupPdn_cddn_check;
	  STLMatArray[3].CalcMatElCplx=OneChNupPdn_cddn_MatElCplx;
	  STLMatArray[3].SaveMatYN=true;
	  STLMatArray[3].IsComplex=true;
	  strcpy(STLMatArray[3].MatName,"cd_dn");

	  // f_Maj (this looks ok)
	  auxNRGMat=STLMatArray[3];
	  STLMatArray.push_back(auxNRGMat);
	  strcpy(STLMatArray[4].MatName,"f_Maj");

	  // TODO: Add other spectral functions??

	  cout << " N = -1: Nothing here" << endl;
// 	  cout << " fdN_up : " << endl;
// 	  STLMatArray[0].PrintAllBlocks();
// 	  cout << " fdN_dn : " << endl;
// 	  STLMatArray[1].PrintAllBlocks();
// 	  cout << " cdN_up : " << endl;
// 	  STLMatArray[2].PrintAllBlocks();
// 	  cout << " cdN_dn : " << endl;
// 	  STLMatArray[3].PrintAllBlocks();
// 	  cout << " f_Maj : " << endl;
// 	  STLMatArray[4].PrintAllBlocks();

	  SaveData=true;

 	  break;
	case 2:
	  cout << "Entropy calculation (implementing it...)" << endl;

	  STLMatArray.pop_back(); // Only TWO arrays
	  STLMatArray.pop_back(); // Only TWO arrays

	  // Need to change chain files for ALL Models!!
	  strcpy(ThermoArray[0].ArqName,"EntropyImp1ChNupPdn_Majorana");
	  strcat(ThermoArray[0].ArqName,zstring);
	  strcat(ThermoArray[0].ArqName,".dat");
	  strcpy(ThermoArray[0].ChainArqName,"EntropyChain1Ch_NupPdn");
	  strcat(ThermoArray[0].ChainArqName,zstring);
	  strcat(ThermoArray[0].ChainArqName,".dat");
	  ThermoArray[0].Calc=CalcEntropy;
	  ThermoArray[0].dImpValue=3.0*log(2.0); // log(8)
	  ThermoArray[0].CalcChain=false;
	  NumThermoMats=1;
	  SaveData=false;

	  break;
	default:
	  cout << " calcdens = " << calcdens << " not implemented. Exiting. " << endl;
	  exit(0);
	}
	// end switch calcdens

	// actually SetHm1 !
	OneChNupPdn_SetH0_AndersonMajorana(Params,pSingleSite,pAeig,STLMatArray);

// 	// BuildBasis params (already out of here)
	// Set pHN
	pHN->IsComplex=true;
	pHN->CalcHNMatElCplx=OneChNupPdn_HN_MatElCplx;

	// Wrap up
	NumChannels=1;
	Nsites0=0;
	NumNRGmats=STLMatArray.size();
	MatArray=&STLMatArray[0]; // Need to do this after
	// changes in STLMatArray!

	cout << " N=-1 ok. Going to N=0... " << endl;

	cout << " pAeig is complex ? " << pAeig->CheckComplex() << endl;

	//exit(0);
	break;
      default:
	cout << " Model not implemented for this symmetry. Exiting... " << endl;
	exit(0);
      }
      // end switch ModelNo for OneChNupPdn
      break;

    case 7: // OneChS
      /////////////////////////////
      /////                    ////
      ///// Symmetry: OneChS   ////
      /////                    ////
      /////////////////////////////
      // BuildBasis params
      CommonQNs.push_back(1); // No of common QNs
      CommonQNs.push_back(0); // pos of QN 1 in old
      CommonQNs.push_back(0); // pos of QN 1 in SingleSite

      totSpos.push_back(0);   // SU(2) symmetry in position 1

      switch(ModelNo){
      //////////////////////////////////////
      /// Models so far:                 ///
      ///  0 - Anderson (SC leads)       ///
      //////////////////////////////////////
      case 0: // Single-impurity Anderson model S symmetry with SC leads

	// Initialize matrices
	// Jul 09: using STMatArray

	// fN (reduced)
	STLMatArray[0].NeedOld=false;
	STLMatArray[0].UpperTriangular=false;
	STLMatArray[0].CheckForMatEl=OneChS_cd_check;
	STLMatArray[0].CalcMatEl=OneChS_fN_MatEl;
	STLMatArray[0].SaveMatYN=false;

	switch (calcdens){
	case 0: // OpAvg: n_d(T), n^2_d(T), S^2(T) - NEW (2012)
	  auxNRGMat.NeedOld=true;
	  auxNRGMat.UpperTriangular=false; // update procedure only works for 'false'
	  auxNRGMat.CalcAvg=true;  // CalcAvg
	  auxNRGMat.CheckForMatEl=Diag_check;
	  auxNRGMat.CalcMatEl=ImpOnly_MatEl;
	  STLMatArray[1]=auxNRGMat; // 1 - ndot (there already)
	  STLMatArray[2]=auxNRGMat; // 2 - ndot^2 (there already)
	  STLMatArray[3]=auxNRGMat; // 3 - Sdot^2 (there already)
	  strcpy(STLMatArray[1].MatName,"Ndot");
	  strcpy(STLMatArray[2].MatName,"NdSq");
	  strcpy(STLMatArray[3].MatName,"SdSq");
	  NumNRGmats=STLMatArray.size();
	  MatArray=&STLMatArray[0]; // Need to do this after
	  // changes in STLMatArray!
	  SaveData=false;
// 	  STLMatArray.pop_back(); // 1 Matrices only
// 	  STLMatArray.pop_back(); // 1 Matrices only
// 	  STLMatArray.pop_back(); // 1 Matrices only
	  // No Thermo
	  NumThermoMats=0;
	  break;
	case 1: //Spectral density
	  STLMatArray.pop_back(); // 2 Matrices
	  STLMatArray.pop_back(); // 2 Matrices
	  NumThermoMats=0;
	  // cd operator
	  STLMatArray[1].NeedOld=true;
	  STLMatArray[1].UpperTriangular=false;
	  STLMatArray[1].CheckForMatEl=OneChS_cd_check;
	  STLMatArray[1].CalcMatEl=OneChS_cd_MatEl;
	  STLMatArray[1].SaveMatYN=true;
	  strcpy(STLMatArray[1].MatName,"cdot1");
	  NumNRGmats=STLMatArray.size();
	  // changes in STLMatArray!
	  SaveData=true;
	  strcpy(SaveArraysFileName,"1chSAndersonSC");
	  break;
	case 4: // Dynamical spin susceptibility and <Sz> and <Sz^2> (2015)
	  // 6 Matrices
	  STLMatArray.push_back(auxNRGMat);
	  STLMatArray.push_back(auxNRGMat);
	  NumThermoMats=0;
	  // <Sz> and <Sz2> operator
	  auxNRGMat.NeedOld=true;
	  auxNRGMat.UpperTriangular=false;
	  // update procedure only works for 'false'
	  auxNRGMat.CalcAvg=true;  // CalcAvg
	  auxNRGMat.CheckForMatEl=Diag_check;
	  auxNRGMat.CalcMatEl=ImpOnly_MatEl;

	  STLMatArray[2]=auxNRGMat; // 2 - Sz dynamical
	  STLMatArray[2].SaveMatYN=true;
	  STLMatArray[2].CalcAvg=false;

	  STLMatArray[3]=auxNRGMat; // 3 - Nd dynamical
	  STLMatArray[3].SaveMatYN=true;
	  STLMatArray[3].CalcAvg=false;

	  STLMatArray[4]=auxNRGMat; // 4 - <Sz2>(T) static
	  STLMatArray[4].SaveMatYN=false;

	  STLMatArray[5]=auxNRGMat; // 5 - <Nd2>(T) static
	  STLMatArray[5].SaveMatYN=false;

	  strcpy(STLMatArray[2].MatName,"Szomega");
	  strcpy(STLMatArray[3].MatName,"Ndomega");
	  strcpy(STLMatArray[4].MatName,"Sz2dot");
	  strcpy(STLMatArray[5].MatName,"Nd2dot");

	  NumNRGmats=STLMatArray.size();
	  // changes in STLMatArray!
	  SaveData=true;
	  strcpy(SaveArraysFileName,"1chSAndersonSC");
	  break;

	default:
	  cout << " calcdens = " << calcdens << " not implemented. Exiting. " << endl;
	  exit(0);

	}
	// end switch calcdens

	// Param for H0
	Params.push_back(Lambda);
	Params.push_back(HalfLambdaFactor);
	Params.push_back(dInitParams[0]); // U
	Params.push_back(dInitParams[2]); // ed

	OneChS_SetAnderson_Hm1(Params,pAeig,STLMatArray);

	// HN params
	pHN->CalcHNMatEl=OneChS_HNsc_MatEl;

	// Wrap up
	NumChannels=1;
	Nsites0=0;
	NumNRGmats=STLMatArray.size();
	MatArray=&STLMatArray[0]; // Need to do this after
	// changes in STLMatArray!

	break;
	// end OneChS Anderson model set up

      default:
	cout << " Model not implemented for OneChS symmetry. Exiting... " << endl;
	exit(0);
      }
      // end switch ModelNo for OneChS
      break;

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

    case 8: // OneChSz
      /////////////////////////////
      /////                    ////
      ///// Symmetry: OneChSz   ////
      /////                    ////
      /////////////////////////////
      // BuildBasis params
      CommonQNs.push_back(1); // No of common QNs
      CommonQNs.push_back(0); // pos of QN 1 in old
      CommonQNs.push_back(0); // pos of QN 1 in SingleSite

      // No total S variables. Leave totSpos empty

      switch(ModelNo){
      //////////////////////////////////////
      /// Models so far:                 ///
      ///  0 - Anderson (SC leads)       ///
      //////////////////////////////////////
      case 0: // Single-impurity Anderson model S symmetry with SC leads

	// Initialize matrices
	// Jul 09: using STMatArray

	// fNup and fNdn
	STLMatArray[0].NeedOld=false;
	STLMatArray[0].UpperTriangular=false;
// 	STLMatArray[0].CheckForMatEl=OneChS_cd_check; // Should work
	STLMatArray[0].CheckForMatEl=OneChSz_cdup_check; // More efficient
	STLMatArray[0].CalcMatEl=OneChSz_fNup_MatEl;
	STLMatArray[0].SaveMatYN=false;

	STLMatArray[1].NeedOld=false;
	STLMatArray[1].UpperTriangular=false;
// 	STLMatArray[1].CheckForMatEl=OneChS_cd_check; // Should work
	STLMatArray[1].CheckForMatEl=OneChSz_cddn_check; // Should work
	STLMatArray[1].CalcMatEl=OneChSz_fNdn_MatEl;
	STLMatArray[1].SaveMatYN=false;

	switch (calcdens){
	case 0: // OpAvg: n_d(T), Sz(T), Sz(T) - NEW (2012)
	  auxNRGMat.NeedOld=true;
	  auxNRGMat.UpperTriangular=false; // update procedure only works for 'false'
	  auxNRGMat.CalcAvg=true;  // CalcAvg
	  auxNRGMat.CheckForMatEl=Diag_check;
	  auxNRGMat.CalcMatEl=ImpOnly_MatEl;
	  STLMatArray.push_back(auxNRGMat); // 5 matrices
	  STLMatArray[2]=auxNRGMat; // 1 - ndot (there already)
	  STLMatArray[3]=auxNRGMat; // 2 - ndot^2 (there already)
	  STLMatArray[4]=auxNRGMat; // 3 - Sdot^2 (there already)
	  strcpy(STLMatArray[2].MatName,"Ndot");
	  strcpy(STLMatArray[3].MatName,"NdSq");
	  strcpy(STLMatArray[4].MatName,"SdSq");
	  NumNRGmats=STLMatArray.size();
	  MatArray=&STLMatArray[0]; // Need to do this after
	  // changes in STLMatArray!
	  SaveData=false;
	  // No Thermo
	  NumThermoMats=0;
	  break;
	case 1: //Spectral density - 4 matrices (no pop-up)
	  NumThermoMats=0;
	  // cdup operator
	  STLMatArray[2].NeedOld=true;
	  STLMatArray[2].UpperTriangular=false;
// 	  STLMatArray[2].CheckForMatEl=OneChS_cd_check;
	  STLMatArray[2].CheckForMatEl=OneChSz_cdup_check;
	  STLMatArray[2].CalcMatEl=OneChSz_cdup_MatEl;
	  STLMatArray[2].SaveMatYN=true;
	  strcpy(STLMatArray[2].MatName,"cdotup");
	  // cddn operator
	  STLMatArray[3].NeedOld=true;
	  STLMatArray[3].UpperTriangular=false;
// 	  STLMatArray[3].CheckForMatEl=OneChS_cd_check;
	  STLMatArray[3].CheckForMatEl=OneChSz_cddn_check;
	  STLMatArray[3].CalcMatEl=OneChSz_cddn_MatEl;
	  STLMatArray[3].SaveMatYN=true;
	  strcpy(STLMatArray[3].MatName,"cdotdn");

	  NumNRGmats=STLMatArray.size();
	  // changes in STLMatArray!
	  SaveData=true;
	  strcpy(SaveArraysFileName,"1chSzAndersonSC");
	  break;
	case 4: // Dynamical spin susceptibility and <Sz> and <Sz^2> (2015)
	  /// Stopped here
	  // 6 Matrices
	  STLMatArray.push_back(auxNRGMat);
	  STLMatArray.push_back(auxNRGMat);
	  NumThermoMats=0;
	  // <Sz> and <Sz2> operator
	  auxNRGMat.NeedOld=true;
	  auxNRGMat.UpperTriangular=false;
	  // update procedure only works for 'false'
	  auxNRGMat.CalcAvg=true;  // CalcAvg
	  auxNRGMat.CheckForMatEl=Diag_check;
	  auxNRGMat.CalcMatEl=ImpOnly_MatEl;

	  STLMatArray[2]=auxNRGMat; // 2 - Sz dynamical
	  STLMatArray[2].SaveMatYN=true;
	  STLMatArray[2].CalcAvg=false;

	  STLMatArray[3]=auxNRGMat; // 3 - Nd dynamical
	  STLMatArray[3].SaveMatYN=true;
	  STLMatArray[3].CalcAvg=false;

	  STLMatArray[4]=auxNRGMat; // 4 - <Sz2>(T) static
	  STLMatArray[4].SaveMatYN=false;

	  STLMatArray[5]=auxNRGMat; // 5 - <Nd2>(T) static
	  STLMatArray[5].SaveMatYN=false;

	  strcpy(STLMatArray[2].MatName,"Szomega");
	  strcpy(STLMatArray[3].MatName,"Ndomega");
	  strcpy(STLMatArray[4].MatName,"Sz2dot");
	  strcpy(STLMatArray[5].MatName,"Nd2dot");

	  NumNRGmats=STLMatArray.size();
	  // changes in STLMatArray!
	  SaveData=true;
	  strcpy(SaveArraysFileName,"1chSzAndersonSC");
	  break;

	default:
	  cout << " calcdens = " << calcdens << " not implemented. Exiting. " << endl;
	  exit(0);

	}
	// end switch calcdens

	// Param for H0
	Params.push_back(Lambda);
	Params.push_back(HalfLambdaFactor);
	Params.push_back(dInitParams[0]); // U
	Params.push_back(dInitParams[2]); // ed
	Params.push_back(dInitParams[4]); // Mag Field.
       //Note that dInitParams[3] is DeltaSC

	OneChSz_SetAnderson_Hm1(Params,pAeig,STLMatArray);

	// HN params
	pHN->CalcHNMatEl=OneChSz_HNsc_MatEl;

	NumChannels=1;

	// Wrap up
	NumChannels=1;
	Nsites0=0;
	NumNRGmats=STLMatArray.size();
	MatArray=&STLMatArray[0]; // Need to do this after
	// changes in STLMatArray!

	//exit(0);
	break;
	// end OneChSz Anderson model set up

      default:
	cout << " Model not implemented for OneChSz symmetry. Exiting... " << endl;
	exit(0);
      }
      // end switch ModelNo for OneChSz
      break;
    case 9: // OneChPuPdn
      //////////////////////////////////
      /////                         ////
      ///// Symmetry: OneChPupPdn   ////
      /////                         ////
      //////////////////////////////////
      // BuildBasis params
      CommonQNs.push_back(2); // No of common QNs
      CommonQNs.push_back(0); // pos of QN 1 in old
      CommonQNs.push_back(1); // pos of QN 2 in old

      CommonQNs.push_back(0); // pos of QN 1 in SingleSite
      CommonQNs.push_back(1); // pos of QN 2 in SingleSite

      CommonQNs.push_back(0); // pos of Parity QN
      CommonQNs.push_back(1); // pos of Parity QN

      // No total S variables. Leave totSpos empty

      switch(ModelNo){
      ///////////////////////////////////////
      /// Models so far:                  ///
      ///  3 - Chain only                 ///
      ///  7 - Anderson+ 2 Local Majoranas///
      ///////////////////////////////////////
      case 3: // Chain only

	ZeroParams();
	Nsites0=1; // IS THIS CORRECT? Yes. H_0 is set so we start NRG from H_1
	NumThermoMats=1; // Entropy only

	// Initialize matrices (real matrices)
	// fd_up
	STLMatArray[0].NeedOld=false;
 	STLMatArray[0].CheckForMatEl=OneChPupPdn_cdup_check;
  	STLMatArray[0].CalcMatEl=OneChPupPdn_fNup_MatEl;
 	//STLMatArray[0].CalcMatElCplx=OneChNupPdn_fNup_MatElCplx;
	STLMatArray[0].SaveMatYN=false;
	STLMatArray[0].IsComplex=false;

	// fd_dn
	STLMatArray[1].NeedOld=false;
 	STLMatArray[1].CheckForMatEl=OneChPupPdn_cddn_check;
  	STLMatArray[1].CalcMatEl=OneChPupPdn_fNdn_MatEl;
	STLMatArray[1].SaveMatYN=false;
	STLMatArray[1].IsComplex=false;

	STLMatArray.pop_back(); // Only TWO arrays
	STLMatArray.pop_back(); // Only TWO arrays

	OneChPupPdn_SetChainH0(pAeig,STLMatArray);

	// Set pHN
	pHN->CalcHNMatEl=OneChPupPdn_HN_MatEl; // REAL
	// Wrap up
	NumChannels=1;
	NumNRGmats=STLMatArray.size();
	MatArray=&STLMatArray[0]; // Need to do this after
	// changes in STLMatArray!

	if (BandNo==0){
	  chi_m1[0]=chiN(0,Lambda); // is actually chi0 // chiN is a codehandler func
	// Valid ONLY for SquareBand, z_twist=1! Check side dot calcs...
	}
	else{chi_m1[0]=Chains[0].GetChin(0);}
	// Need to change chain files for ALL Models!!
	strcpy(ThermoArray[0].ChainArqName,"EntropyChain1Ch_PupPdn");
	strcat(ThermoArray[0].ChainArqName,zstring);
	strcat(ThermoArray[0].ChainArqName,".dat");
	ThermoArray[0].Calc=CalcEntropy;
	ThermoArray[0].CalcChain=true; // Just to make sure

	break;

// Working on this.
      case 7:
	cout << " 2 Majoranas + QD effective model " << endl;

	Params.push_back(Lambda);  // Lambda
	Params.push_back(HalfLambdaFactor); // Lambda factor

	Params.push_back(dInitParams[0]); // U1
	Params.push_back(dInitParams[2]); // ed1
	Params.push_back(chi_m1[0]); // sqrt(2gamma1/Pi)/sqrt(L)*HalfLambdaFactor

	Params.push_back(dInitParams[3]); // Mag Field

	Params.push_back(dInitParams[4]); // lambdaR (or tR)
	Params.push_back(dInitParams[5]); // lambdaL
	Params.push_back(dInitParams[6]); // phi/pi

	// Initialize matrices
	// fd_up
	STLMatArray[0].NeedOld=false;
 	STLMatArray[0].CheckForMatEl=OneChPupPdn_cdup_check;
 	STLMatArray[0].CalcMatElCplx=OneChPupPdn_fNup_MatElCplx;
	STLMatArray[0].SaveMatYN=false;
	STLMatArray[0].IsComplex=true;

	// fd_dn
	STLMatArray[1].NeedOld=false;
 	STLMatArray[1].CheckForMatEl=OneChPupPdn_cddn_check;
 	STLMatArray[1].CalcMatElCplx=OneChPupPdn_fNdn_MatElCplx;
	STLMatArray[1].SaveMatYN=false;
	STLMatArray[1].IsComplex=true;

	switch (calcdens){
	case 0: // OpAvg: n_d(T), S_z(T) -(2014)
	  auxNRGMat.NeedOld=true;
	  auxNRGMat.UpperTriangular=false; // update procedure only works for 'false'
	  auxNRGMat.CalcAvg=true;  // CalcAvg
	  auxNRGMat.CheckForMatEl=Diag_check;
	  auxNRGMat.CalcMatElCplx=ImpOnly_MatElCplx;
	  auxNRGMat.IsComplex=true;

	  STLMatArray[2]=auxNRGMat; // 2 - ndot (there already)
	  STLMatArray[3]=auxNRGMat; // 3 - Sz (there already)
	  strcpy(STLMatArray[2].MatName,"Ndot");
	  strcpy(STLMatArray[3].MatName,"Szdot");
	  STLMatArray.push_back(auxNRGMat);
	  strcpy(STLMatArray[4].MatName,"Nf"); // 4 -Nf
	  // changes in STLMatArray!
	  SaveData=false;
	  // No Thermo
	  NumThermoMats=0;
	  break;
 	case 1: //Spectral density
	  // cd_up and cd_dn (OneChQSz_SetAnderson equals
	  // MatArray[2,3] to MatArray[0,1]
	  STLMatArray[2].NeedOld=true;
	  STLMatArray[2].CheckForMatEl=OneChPupPdn_cdup_check;
	  STLMatArray[2].CalcMatElCplx=OneChPupPdn_cdup_MatElCplx;
	  STLMatArray[2].SaveMatYN=true;
	  STLMatArray[2].IsComplex=true;
	  strcpy(STLMatArray[2].MatName,"cd_up");

	  STLMatArray[3].NeedOld=true;
	  STLMatArray[3].CheckForMatEl=OneChPupPdn_cddn_check;
	  STLMatArray[3].CalcMatElCplx=OneChPupPdn_cddn_MatElCplx;
	  STLMatArray[3].SaveMatYN=true;
	  STLMatArray[3].IsComplex=true;
	  strcpy(STLMatArray[3].MatName,"cd_dn");

	  // f_Maj_up (this looks ok)
	  auxNRGMat=STLMatArray[2];
	  STLMatArray.push_back(auxNRGMat);
	  strcpy(STLMatArray[4].MatName,"f_MajUp");

	  // f_Maj_dn (this looks ok)
	  auxNRGMat=STLMatArray[3];
	  STLMatArray.push_back(auxNRGMat);
	  strcpy(STLMatArray[5].MatName,"f_MajDn");

	  // TODO: Add other spectral functions??

	  cout << " N = -1: Nothing here" << endl;
// 	  cout << " fdN_up : " << endl;
// 	  STLMatArray[0].PrintAllBlocks();
// 	  cout << " fdN_dn : " << endl;
// 	  STLMatArray[1].PrintAllBlocks();
// 	  cout << " cdN_up : " << endl;
// 	  STLMatArray[2].PrintAllBlocks();
// 	  cout << " cdN_dn : " << endl;
// 	  STLMatArray[3].PrintAllBlocks();
// 	  cout << " f_MajUp : " << endl;
// 	  STLMatArray[4].PrintAllBlocks();
// 	  cout << " f_MajDn : " << endl;
// 	  STLMatArray[5].PrintAllBlocks();

	  SaveData=true;

 	  break;
	case 2:
	  cout << "Entropy calculation (implementing it...)" << endl;

	  STLMatArray.pop_back(); // Only TWO arrays
	  STLMatArray.pop_back(); // Only TWO arrays

	  // Need to change chain files for ALL Models!!
	  strcpy(ThermoArray[0].ArqName,"EntropyImp1ChPupPdn_Majorana");
	  strcat(ThermoArray[0].ArqName,zstring);
	  strcat(ThermoArray[0].ArqName,".dat");
	  strcpy(ThermoArray[0].ChainArqName,"EntropyChain1Ch_PupPdn");
	  strcat(ThermoArray[0].ChainArqName,zstring);
	  strcat(ThermoArray[0].ChainArqName,".dat");
	  ThermoArray[0].Calc=CalcEntropy;
	  ThermoArray[0].dImpValue=4.0*log(2.0); // log(16) (check)
	  ThermoArray[0].CalcChain=false;
	  NumThermoMats=1;
	  SaveData=false;

	  break;
	default:
	  cout << " calcdens = " << calcdens << " not implemented. Exiting. " << endl;
	  exit(0);
	}
	// end switch calcdens

	// actually SetHm1 !
	OneChPupPdn_SetHm1_AndersonMajorana(Params,pSingleSite,pAeig,STLMatArray);

// 	// BuildBasis params (already out of here)
	// Set pHN
	pHN->IsComplex=true;
	pHN->CalcHNMatElCplx=OneChPupPdn_HN_MatElCplx;

	// Wrap up
	NumChannels=1;
	Nsites0=0;
	NumNRGmats=STLMatArray.size();
	MatArray=&STLMatArray[0]; // Need to do this after
	                          // changes in STLMatArray!

	cout << " N=-1 ok. Going to N=0... " << endl;

	cout << " pAeig is complex ? " << pAeig->CheckComplex() << endl;

	//exit(0);
	break;
      default:
	cout << " Model not implemented for this symmetry. Exiting... " << endl;
	exit(0);
      }
      // end switch ModelNo for OneChPupPdn
      break;
/////////////////////////////
    default:
      cout << " Symmetry not implemented. Exiting... " << endl;
      exit(0);
    }
    // end switch SymNo

}
// end ModelSwitch


#include "Time.hpp"
#include <boost/date_time/posix_time/posix_time.hpp>
#include "foundation/ArgumentException.hpp"
#include "foundation/OutOfRangeException.hpp"

using namespace boost::posix_time;

static const long ticks_per_day = 24 * 3600 * 1000 * (long)(time_duration::ticks_per_second() / 1000);

class axis::foundation::date_time::Time::TimeData
{
private:
	boost::posix_time::time_duration _time;
public:
	TimeData(void) : _time(time_duration::hour_type(0), time_duration::min_type(0), time_duration::sec_type(0))
	{
		// nothing to do here
	}
	boost::posix_time::time_duration GetTime(void) const
	{
		return _time;
	}
	void SetTime(const boost::posix_time::time_duration& time)
	{
		_time = time;
	}
	int GetHours(void) const
	{
		return _time.hours();
	}
	int GetMinutes(void) const
	{
		return _time.minutes();
	}
	int GetSeconds(void) const
	{
		return _time.seconds();
	}
	int GetMilliseconds(void) const
	{
		return (int)(_time.fractional_seconds()*1000 / boost::posix_time::time_duration::ticks_per_second());
	}
};

axis::foundation::date_time::Time::Time( void )
{
	_data = new TimeData();
}

axis::foundation::date_time::Time::Time( const Time& other )
{
	_data = new TimeData();
	_data->SetTime(other._data->GetTime());
}

axis::foundation::date_time::Time::Time( int hours, int minutes, int seconds )
{
	time_duration t(hours, minutes, seconds);
	if (t.total_milliseconds() < 0 || t.total_milliseconds() > ticks_per_day)
	{
		throw axis::foundation::ArgumentException(_T("Invalid time."));
	}
	_data = new TimeData();
	_data->SetTime(t);
}

axis::foundation::date_time::Time::Time( int hours, int minutes, int seconds, int milliseconds )
{
	int millis = (int)(milliseconds * (time_duration::ticks_per_second() / 1000));
	time_duration t(hours, minutes, seconds, millis);
	if (t.total_milliseconds() < 0 || t.total_milliseconds() > ticks_per_day)
	{
		throw axis::foundation::ArgumentException(_T("Invalid time."));
	}
	_data = new TimeData();
	_data->SetTime(t);
}

axis::foundation::date_time::Time::~Time( void )
{
	delete _data;
}

int axis::foundation::date_time::Time::GetHours( void ) const
{
	return _data->GetHours();
}

int axis::foundation::date_time::Time::GetMinutes( void ) const
{
	return _data->GetMinutes();
}

int axis::foundation::date_time::Time::GetSeconds( void ) const
{
	return _data->GetSeconds();
}

int axis::foundation::date_time::Time::GetMilliseconds( void ) const
{
	return _data->GetMilliseconds();
}

long axis::foundation::date_time::Time::GetTotalMinutes( void ) const
{
	return GetHours()*60 + GetMinutes();
}

long axis::foundation::date_time::Time::GetTotalSeconds( void ) const
{
	return GetTotalMinutes()*60 + GetSeconds();
}

long axis::foundation::date_time::Time::GetTotalMilliseconds( void ) const
{
	return GetTotalSeconds()*1000 + GetMilliseconds();
}

bool axis::foundation::date_time::Time::operator<( const Time& other ) const
{
	return _data->GetTime() < other._data->GetTime();
}

bool axis::foundation::date_time::Time::operator<=( const Time& other ) const
{
	return _data->GetTime() <= other._data->GetTime();
}

bool axis::foundation::date_time::Time::operator>( const Time& other ) const
{
	return _data->GetTime() > other._data->GetTime();
}

bool axis::foundation::date_time::Time::operator>=( const Time& other ) const
{
	return _data->GetTime() >= other._data->GetTime();
}

bool axis::foundation::date_time::Time::operator==( const Time& other ) const
{
	return _data->GetTime() == other._data->GetTime();
}

bool axis::foundation::date_time::Time::operator!=( const Time& other ) const
{
	return _data->GetTime() != other._data->GetTime();
}

axis::foundation::date_time::Time& axis::foundation::date_time::Time::operator=( const Time& other )
{
	if (this == &other) return *this;
	_data->SetTime(other._data->GetTime());
	return *this;
}

axis::foundation::date_time::Time axis::foundation::date_time::Time::Now( void )
{
	ptime p = microsec_clock::local_time();
	return Time(p.time_of_day().hours(), p.time_of_day().minutes(),
				p.time_of_day().seconds(), (int)(p.time_of_day().fractional_seconds() * 1000 / time_duration::ticks_per_second()));
}

axis::foundation::date_time::Time axis::foundation::date_time::operator+( const Time& time, const Timespan& timespan )
{
	if (timespan.HasWholeDays())
	{
		throw axis::foundation::ArgumentException(_T("Cannot operate on whole days."));
	}

	time_duration t1(time.GetHours(), time.GetMinutes(), time.GetSeconds(), time.GetMilliseconds() * time_duration::ticks_per_second() / 1000);
	time_duration t2(timespan.GetHours(), timespan.GetMinutes(), timespan.GetSeconds(), timespan.GetMilliseconds() * time_duration::ticks_per_second() / 1000);

	time_duration t = t1 + t2;
	if (t.total_milliseconds() < 0 || t.total_milliseconds() > ticks_per_day)
	{
		throw axis::foundation::OutOfRangeException(_T("Result is not a valid time."));
	}

	return Time(t.hours(), t.minutes(), t.seconds(), (int)(t.fractional_seconds() * 1000 / time_duration::ticks_per_second()));
}

axis::foundation::date_time::Time axis::foundation::date_time::operator-( const Time& time, const Timespan& timespan )
{
	if (timespan.HasWholeDays())
	{
		throw axis::foundation::ArgumentException(_T("Cannot operate on whole days."));
	}

	time_duration t1(time.GetHours(), time.GetMinutes(), time.GetSeconds(), time.GetMilliseconds() * time_duration::ticks_per_second() / 1000);
	time_duration t2(timespan.GetHours(), timespan.GetMinutes(), timespan.GetSeconds(), timespan.GetMilliseconds() * time_duration::ticks_per_second() / 1000);

	time_duration t = t1 - t2;
	if (t.total_milliseconds() < 0 || t.total_milliseconds() > ticks_per_day)
	{
		throw axis::foundation::OutOfRangeException(_T("Result is not a valid time."));
	}

	return Time(t.hours(), t.minutes(), t.seconds(), (int)(t.fractional_seconds() * 1000 / time_duration::ticks_per_second()));
}

axis::foundation::date_time::Timespan axis::foundation::date_time::operator-( const Time& t1, const Time& t2 )
{
	time_duration tm1(t1.GetHours(), t1.GetMinutes(), t1.GetSeconds(), t1.GetMilliseconds() * time_duration::ticks_per_second() / 1000);
	time_duration tm2(t2.GetHours(), t2.GetMinutes(), t2.GetSeconds(), t2.GetMilliseconds() * time_duration::ticks_per_second() / 1000);

	time_duration t = tm1 - tm2;

	return Timespan(t.hours(), t.minutes(), t.seconds(), (long)(t.fractional_seconds() * 1000 / time_duration::ticks_per_second()));
}


// Copyright (c) 2019, The Graft Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
//    conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
//    of conditions and the following disclaimer in the documentation and/or other
//    materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
//    used to endorse or promote products derived from this software without specific
//    prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//

#include <boost/endian/conversion.hpp>
#include "cryptmsg.h"
#include "crypto/chacha.h"

namespace {

static_assert(201103L <= __cplusplus, "C++11 compiler required");

#if 0
//can be used to check manually
#define native_to_little boost::endian::native_to_big
#define little_to_native boost::endian::big_to_native
#else
using namespace boost::endian;
#endif

inline size_t getEncryptChachaSize(size_t plainSize)
{
    return plainSize + sizeof(crypto::chacha_iv);
}

void encryptChacha(const uint8_t* plain, size_t plain_size, const crypto::secret_key &skey, uint8_t* cipher)
{
  crypto::chacha_key key;
  crypto::generate_chacha_key(&skey, sizeof(skey), key);
  crypto::chacha_iv& iv = *reinterpret_cast<crypto::chacha_iv*>(cipher);
  iv = crypto::rand<crypto::chacha_iv>();
  crypto::chacha8(plain, plain_size, key, iv, reinterpret_cast<char*>(cipher) + sizeof(iv));
}

void decryptChacha(const uint8_t* cipher, size_t cipher_size, const crypto::secret_key &skey, uint8_t* plain)
{
  const size_t prefix_size = sizeof(crypto::chacha_iv);
  crypto::chacha_key key;
  crypto::generate_chacha_key(&skey, sizeof(skey), key);
  const crypto::chacha_iv &iv = *reinterpret_cast<const crypto::chacha_iv*>(cipher);
  crypto::chacha8(reinterpret_cast<const char*>(cipher) + sizeof(iv), cipher_size - prefix_size, key, iv, reinterpret_cast<char*>(plain));
}

constexpr uint8_t cStart = 0xA5;
constexpr uint8_t cEnd = 0x5A;

#pragma pack(push, 1)

//Note, native order is little-endian

//SessionX in decrypted form contains session key x and constants to check that the decryption was correct
struct SessionX
{
    uint8_t cstart; //decrypted value cStart
    crypto::secret_key x;
    uint8_t cend; //decrypted value cEnd
};

struct XEntry
{
    uint32_t Bhash; //xor of B
    uint8_t cipherX[sizeof(SessionX) + sizeof(crypto::chacha_iv)]; //encrypted SessionX
};

struct CryptoMessageHead
{
    uint32_t plainSize; //size of decrypted data
    crypto::public_key R; //random key used to encrypt session key
    uint16_t count; //recipients count
    XEntry xentries[1];
};

#pragma pack(pop)

uint32_t getBhash(const crypto::public_key& B)
{
    uint32_t res = 0;
    static_assert(sizeof(B) % sizeof(res) == 0, "public key size must be a multiple of 4");
    const uint32_t* p = reinterpret_cast<const uint32_t*>(&B);
    for(int i = 0, cnt = sizeof(B) / sizeof(res); i < cnt; ++i, ++p)
    {
        res ^= *p;
    }
    return native_to_little(res);
}

/*!
 * \brief encryptMsg - encrypts data for recipients using their B public keys (assumed public view keys).
 *
 * The result has following structure [plainSize:32][R:32][count of XEntrys:16][XEntry]...[XEntry][x encrypted data:+8*8]
 * plainSize - size of original data, x encrypted data takes 8 more bytes
 * [XEntry] - [Bhash:32][rBX] pair for each recipient
 * where Bhash - xor of B (aka fingerprint of B, using which recipient can find his entry)
 * [rBX:+8*8] - encrypted X (aka SessionX)
 * [X] = [cstart:8][x:32*8][cend:8]
 *
 * \param inputSize - input buffer size.
 * \param input - input buffer to encrypt.
 * \param BkeysCount - count of B keys.
 * \param Bkeys - array of B keys for each recipients.
 * \param outputSize - output buffer size.
 * \param output - output buffer.
 * \returns output size required if output==nullptr or outputSize is not enough.
 * returns 0 on error
 */

size_t encryptMsg(size_t inputSize, const uint8_t* input, size_t BkeysCount, const crypto::public_key* Bkeys, size_t outputSize, uint8_t* output)
{
    if(!inputSize || !BkeysCount)
        return 0;

    //prepare
    size_t msgHeadSize = sizeof(CryptoMessageHead) + (BkeysCount - 1) * sizeof(XEntry);
    size_t msgSize = msgHeadSize + getEncryptChachaSize(inputSize);
    if(outputSize < msgSize)
        return msgSize;
    if(!input || !Bkeys || !output)
        return 0;

    //make decorated session key
    SessionX X; X.cstart = cStart; X.cend = cEnd;
    //generate session key X.x
    {
        crypto::public_key tmpX;
        crypto::generate_keys(tmpX,X.x);
    }
    //chacha encrypt input with x
    encryptChacha(input, inputSize, X.x, output + msgHeadSize);

    //fill head
    CryptoMessageHead& head = *reinterpret_cast<CryptoMessageHead*>(output);
    head.plainSize = native_to_little(uint32_t(inputSize));
    crypto::secret_key r;
    crypto::generate_keys(head.R, r);
    head.count = native_to_little(uint16_t(BkeysCount));
    //fill XEntry for each B
    const crypto::public_key* pB = Bkeys;
    XEntry* pxe = head.xentries;
    for(size_t i=0; i<BkeysCount; ++i, ++pxe, ++pB)
    {
        const crypto::public_key& B = *pB;
        XEntry& xe = *pxe;
        xe.Bhash = getBhash(B);
        //get rB key
        crypto::key_derivation rBv;
        crypto::generate_key_derivation(B, r, rBv);
        crypto::secret_key rB;
        crypto::derivation_to_scalar(rBv, 0, rB);
        //encrypt X with rB key
        encryptChacha(reinterpret_cast<const uint8_t*>(&X), sizeof(X), rB, xe.cipherX);
    }
    return msgSize;
}

/*!
 * \brief decryptMsg - (reverse of encryptMsg) decrypts data for one of the recipients using his secret key b.
 *
 * \param inputSize - input buffer size.
 * \param input - input buffer to decrypt.
 * \param bkey - secret key corresponding to one of Bs that were used to encrypt.
 * \param outputSize - output buffer size.
 * \param output - output buffer.
 * \returns output size required if output==nullptr or outputSize is not enough
 * returns 0 on error
 */

size_t decryptMsg(size_t inputSize, const uint8_t* input, const crypto::secret_key& bkey, size_t outputSize, uint8_t* output)
{
    if(!input || inputSize <= sizeof(CryptoMessageHead))
        return 0;
    //prepare
    const CryptoMessageHead& head = *reinterpret_cast<const CryptoMessageHead*>(input);
    size_t head_count = little_to_native(head.count);
    size_t head_plainSize = little_to_native(head.plainSize);
    size_t msgHeadSize = sizeof(CryptoMessageHead) + ((size_t)(head_count - 1)) * sizeof(XEntry);
    size_t msgSize = msgHeadSize + getEncryptChachaSize(head_plainSize);
    if(inputSize < msgSize)
        return 0;
    if(outputSize < head_plainSize)
        return head_plainSize;
    if(!output)
        return 0;

    //get Bhash from b
    const crypto::secret_key& b = bkey;
    uint32_t Bhash;
    {
        crypto::public_key B;
        bool res = crypto::secret_key_to_public_key(bkey, B);
        if(!res) return false; //corrupted key
        Bhash = getBhash(B);
    }
    //find XEntry for B
    const XEntry* pxe = head.xentries;
    for(size_t i=0; i<head_count; ++i, ++pxe)
    {
        const XEntry& xe = *pxe;
        if(xe.Bhash != Bhash) continue;
        //get bR key
        crypto::key_derivation bRv;
        crypto::generate_key_derivation(head.R, b, bRv);
        crypto::secret_key bR;
        crypto::derivation_to_scalar(bRv, 0, bR);
        //decrypt to X
        SessionX X;
        decryptChacha(xe.cipherX, sizeof(xe.cipherX), bR, reinterpret_cast<uint8_t*>(&X));
        if(X.cstart != cStart || X.cend != cEnd) continue;
        //decrypt with session key
        decryptChacha(input + msgHeadSize, getEncryptChachaSize(head_plainSize), X.x, output);
        return head_plainSize;
    }
    return 0;
}

} //namespace

namespace graft { namespace crypto_tools {

void encryptMessage(const std::string& input, const std::vector<crypto::public_key>& Bkeys, std::string& output)
{
    assert(!input.empty());
    //get output size
    size_t size = encryptMsg( input.size(), nullptr, Bkeys.size(), nullptr, 0, nullptr);
    assert(0<size);
    output.resize(size);
    //encrypt
    size_t res = encryptMsg( input.size(), reinterpret_cast<const uint8_t*>(input.data()),
                             Bkeys.size(), &Bkeys[0],
            output.size(), reinterpret_cast<uint8_t*>(&output[0]));
    assert(res == size);
}

void encryptMessage(const std::string& input, const crypto::public_key& Bkey, std::string& output)
{
    std::vector<crypto::public_key> v(1, Bkey);
    encryptMessage(input, v, output);
}

bool decryptMessage(const std::string& input, const crypto::secret_key& bkey, std::string& output)
{
    assert(!input.empty());
    //get output size
    size_t size = decryptMsg( input.size(), reinterpret_cast<const uint8_t*>(input.data()), bkey, 0, nullptr);
    if(!size)
    {
        output.clear();
        return false;
    }
    output.resize(size);
    //encrypt
    size_t res = decryptMsg( input.size(), reinterpret_cast<const uint8_t*>(input.data()),
                             bkey, output.size(), reinterpret_cast<uint8_t*>(&output[0]));
    if(!res)
    {
        output.clear();
        return false;
    }
    assert(res == size);
    return true;
}

}} //namespace graft::crypto_tools


// ====================================================================
// 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 Wed 4 Apr 2018 09:58:31

/**
 * @file MDM_two_scale_ewsb_solver.hpp
 *
 * @brief contains class for solving EWSB when two-scale algorithm is used
 *
 * This file was generated at Wed 4 Apr 2018 09:58:31 with FlexibleSUSY
 * 2.0.1 (git commit: unknown) and SARAH 4.12.2 .
 */

#ifndef MDM_TWO_SCALE_EWSB_SOLVER_H
#define MDM_TWO_SCALE_EWSB_SOLVER_H

#include "MDM_ewsb_solver.hpp"
#include "MDM_ewsb_solver_interface.hpp"
#include "error.hpp"

#include <Eigen/Core>

namespace flexiblesusy {

class EWSB_solver;
class Two_scale;

class MDM_mass_eigenstates;

template<>
class MDM_ewsb_solver<Two_scale> : public MDM_ewsb_solver_interface {
public:
   MDM_ewsb_solver() = default;
   MDM_ewsb_solver(const MDM_ewsb_solver&) = default;
   MDM_ewsb_solver(MDM_ewsb_solver&&) = default;
   virtual ~MDM_ewsb_solver() {}
   MDM_ewsb_solver& operator=(const MDM_ewsb_solver&) = default;
   MDM_ewsb_solver& operator=(MDM_ewsb_solver&&) = default;

   virtual void set_loop_order(int l) override { loop_order = l; }
   virtual void set_number_of_iterations(int n) override { number_of_iterations = n; }
   virtual void set_precision(double p) override { precision = p; }

   virtual int get_loop_order() const override { return loop_order; }
   virtual int get_number_of_iterations() const override { return number_of_iterations; }
   virtual double get_precision() const override { return precision; }

   virtual int solve(MDM_mass_eigenstates&) override;
private:
   static const int number_of_ewsb_equations = 1;
   using EWSB_vector_t = Eigen::Matrix<double,number_of_ewsb_equations,1>;

   class EEWSBStepFailed : public Error {
   public:
      virtual ~EEWSBStepFailed() {}
      virtual std::string what() const { return "Could not perform EWSB step."; }
   };

   int number_of_iterations{100}; ///< maximum number of iterations
   int loop_order{2};             ///< loop order to solve EWSB at
   double precision{1.e-5};       ///< precision goal

   void set_ewsb_solution(MDM_mass_eigenstates&, const EWSB_solver*);
   template <typename It> void set_best_ewsb_solution(MDM_mass_eigenstates&, It, It);

   int solve_tree_level(MDM_mass_eigenstates&);
   int solve_iteratively(MDM_mass_eigenstates&);
   int solve_iteratively_at(MDM_mass_eigenstates&, int);
   int solve_iteratively_with(MDM_mass_eigenstates&, EWSB_solver*, const EWSB_vector_t&);

   EWSB_vector_t initial_guess(const MDM_mass_eigenstates&) const;
   EWSB_vector_t tadpole_equations(const MDM_mass_eigenstates&) const;
   EWSB_vector_t ewsb_step(const MDM_mass_eigenstates&) const;
};

} // namespace flexiblesusy

#endif