using namespace std;
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdlib>
#include <cmath>
#include "PDefines.h"
const char *Message[14] = {
    "PChannel: operation successful",
    "PChannel: invalid pids",
    "PChannel: reconstruct failed, increase beam energy",
    "PChannel: decay failed, system invariant mass is zero",
    "PChannel: decay failed, insufficient energy",
    "PChannel: genbod failed, fewer than two decay products",
    "PChannel: genbod failed",
    "PChannel: baryon_cos algorithm failed",
    "PChannel: mflag must be 0 or 1",
    "PChannel: aflag must be 0 or 1",
    "PChannel: bflag must be 0 or 1",
    "PChannel: reconstruct failed for deuteron Fermi sampling",
    "PChannel: invalid decay-mode index in the constructor",
    "PChannel: inconsistent particle array with decay-mode index"};
#include "PChannel.h"
#include "PDynamicData.h"
#include "PUtils.h"
#include "PFileInput.h"
#include "PFireball.h"
#include "PDiLepton.h"
#include "PKinematics.h"
double PChannel::globalWeight = 1.;              
PChannel::PChannel(PParticle **particles, int nt, int, int, int) {
    
    
    
    
    makeStdData()->fillDataBase();
    init_done = kFALSE;
    e_cm = 0;
    int i, k = -1;
    status = (!particles);         
    n = nt;                        
  
    if (!status) {
	ipid.Set(n+1);                 
	for (i=0; i<=n; ++i) {
	    k = particles[i]->ID();      
	    if (!k) { 
		status = 1;
		ipid.~TArrayI();
		break;
	    }
	    ipid[i] = k;
	}
    }
    if (status) {                  
	printf("%s:", Message[status]);
	for(i=0; i<=n; i++) printf(" %d", particles[i]->ID());
	printf("\n");
	return;
    }
    ptcls = particles;
    ptcls_extern = -1;
    IsReaction();     
    particles[0]->SetDecayModeIndex(makeStaticData()->GetDecayIdxByKey(decay_key));
    w = particles[0]->W();           
    ecm = particles[0]->M();         
    DMIndex=makeStaticData()->GetDecayIdx(ipid.GetArray(),n); 
}
PChannel::PChannel(int idx, PParticle **particles, int, int, int) {
    
    
    makeStdData()->fillDataBase();
    init_done = kFALSE;
    e_cm = 0;
    
    Int_t key = makeDataBase()->GetEntryInt("didx", idx);
    Int_t ia[11];
    ia[0] = 10;
    if (key < 0) {
	printf("%s\n", Message[12]);    
	return;
    } else 
	makeStaticData()->GetDecayMode(idx,ia); 
    DMIndex = idx;                   
    n = ia[0];                       
    ipid.Set(n+1);                   
    
    if (!particles) {                
	ptcls = new PParticle*[n+1];   
	for(int i=0; i<=n; i++) ptcls[i] = 0;
	ptcls_extern = 0;
    } else {
	ptcls = particles;          
	ptcls_extern = -1;
    }
    
    int i;
    for (i=0; i<=n; ++i) {             
	if (particles) 
	    ipid[i] = particles[i]->ID();
	else {                         
	    if (!i)                      
		ipid[0] = makeStaticData()->GetDecayParent(idx);
	    else 
		ipid[i] = ia[i+1];        
	    ptcls[i] = new PParticle(ipid[i]);
	}
    }      
    
    if (particles) {                 
	ia[0] = ipid[0];               
	PUtils::isort(ia, n+1);        
	TArrayI test(ipid);            
	PUtils::isort(test.GetArray(), n+1);
	int hit = 0;
	for (i=0; i<=n; ++i)             
	    hit += (test[i]==ia[i]);
	if (hit != n) {                  
	    printf("%s\n", Message[13]);
	    ipid.~TArrayI();
	    return;
	}
    }
    
    IsReaction();                  
    ptcls[0]->SetDecayModeIndex(makeStaticData()->GetDecayIdxByKey(decay_key));
    w = ptcls[0]->W();               
    ecm = ptcls[0]->M();             
}     
PChannel::PChannel(int idx, PParticle &parent, int, int, int) {
    
    
    makeStdData()->fillDataBase();
    init_done = kFALSE;
    e_cm = 0;
    
    Int_t key = makeDataBase()->GetEntryInt("didx", idx);
    Int_t ia[11];
    ia[0] = 10;
    if (key < 0) {
	printf("%s\n", Message[12]);    
	return;
    } else 
	makeStaticData()->GetDecayMode(idx,ia); 
    DMIndex = idx;                   
    n = ia[0];                       
    ipid.Set(n+1);                   
    
    ptcls = new PParticle*[n+1];     
    ptcls[0] = &parent;
    ptcls_extern = 1;
    ipid[0] = parent.ID();           
    if (ipid[0] != makeStaticData()->GetDecayParent(idx)) {
	printf("%s\n", Message[13]);
	ipid.~TArrayI();
	delete [] ptcls;
	return;
    }
    int i;
    for (i=1; i<=n; ++i) {             
	ipid[i] = ia[i];               
	ptcls[i] = new PParticle(ipid[i]);
    }
    IsReaction();                    
    ptcls[0]->SetDecayModeIndex(makeStaticData()->GetDecayIdxByKey(decay_key));
    w = ptcls[0]->W();                 
    ecm = ptcls[0]->M();               
}     
void PChannel::IsReaction() {
    
 
    event_impact_param = (makeStaticData()->GetBatchValue("_event_impact_param"));
    event_plane        = (makeStaticData()->GetBatchValue("_event_plane"));
    weight_version     = makeStaticData()->GetBatchValue("_system_weight_version");
    makeDynamicData()->PushPChannel((TObject *) this);
    parent      = ptcls[0];
    orig_parent = ptcls[0];
    int k = GetParentSize();                   
    thSrc = 0;
    tcSrc = 0;
    dlSrc = 0;
    sourceid = 0;
    fEnablePattern = 0;
    thermal_disable_didx = 0;
    num_not_finalized = 0;
    distribution_position = 0;
    print_tentative = kTRUE;
    quasi_pchannel = NULL;
    weight_sum = 0;
    tid = bid = 0;
    bulkdecay_pos = 0;
    pro_bulkdecay_pos = 0;
    current_projector = NULL;
    events = makeStaticData()->GetBatchValue("_system_total_event_number");
    k = GetParentSize();
    parent->SetSourceId(parent->ID());
    parent->SetParentId(0);
    for (int i=1; i<=n; i++) {
	ptcls[i]->SetValue(CHANNEL_POS, (double)i);
    }
    emin = 0.;                       
    if (k == 1) {                              
	if (parent->IsFireball()) 
	    thSrc = 1;
	else if (parent->IsFileInput()) 
	    tcSrc = 1;
	else if (parent->IsDilepton()) 
	    dlSrc = 1;
	else {
	    IdChannel();
	    
	    decay_key = makeStaticData()->GetDecayKey(ipid.GetArray(), n);
	    CheckDecayKey();
	    return;	 
	}
    }
    
    parent->SetParentIndex(-2);              
    parent->SetIndex(-1);
    tid = ipid[0]/1000;                    
    bid = ipid[0]%1000;                    
    IdChannel();                             
 
    
    decay_key = makeStaticData()->GetDecayKey(ipid.GetArray(), n);
    if ((decay_key < 0) && (ipid[0]>999)) {
	
	decay_key = makeStaticData()->AddDecay(ipid.GetArray(), n);
    }
    if (!thSrc && !tcSrc) CheckDecayKey();
    
    for (int i=1; i<=n; ++i) { 
	emin += PData::LMass(ptcls[i]->ID());
    }
    
}
void PChannel::CheckDecayKey(void) const {
    if (decay_key < 0) {
	Error("Initialization", "No database entry for:");
	cout << "               ";
	PrintReaction(0);
	Error("Initialization", "Bye...");
	exit(1);
    }
}
void PChannel::IdChannel() {
    
    int i, nucleon_pid;
    spectator       = NULL;
    participant     = NULL;
    quasi_composite = NULL;
    quasi_pchannel  = NULL;
    
    
    
    
    
    
    
    int has_spec = 0;
    for (i=1; i<=n; ++i) {
	if (ptcls[n]->IsSpectator() != -1)
	    has_spec=1;
    }
    
    if (!has_spec) return;
    has_spec = ptcls[n]->IsSpectator();
    
    if (has_spec == 0) {
	if ((bid != makeStaticData()->GetParticleID("d")) &&
	    (tid  != makeStaticData()->GetParticleID("d")))
	    return;
	
	if (!ptcls[n]->IsNucleon()) return;
    }
        
    for (i=1; i<=n; ++i) {
	if (!has_spec) {
	    if (ptcls[i]->ID() == makeStaticData()->GetParticleID("d")) 
		return; 
	}
	if (ptcls[i]->ID()>1000) return; 
    }
    
    if (bid == makeStaticData()->GetParticleID("d")) 
	nucleon_pid = tid;
    else 
	nucleon_pid = bid;
    
    spectator = ptcls[n];
    
    if (spectator->ID() == makeStaticData()->GetParticleID("p"))
	participant = new PParticle("n");
    else if (spectator->ID() == makeStaticData()->GetParticleID("n"))
	participant = new PParticle("p");
    else {
	
	for (i=1; i<=(n-1); ++i) {
	    if (ptcls[i]->IsSpectator() == 1)
		participant = new PParticle(ptcls[i]);
	}
	
	
	Int_t a_pos = -1;
	for (i=1; i<=(n-1); ++i) {
	    if (ptcls[i]->IsSpectator() == 3)
		participant = new PParticle(ptcls[i]);
	    if (ptcls[i]->IsSpectator() == 2)
		a_pos = i;
	}
	if (a_pos != 1)  {
	    Error("IdChannel","Quasi-reaction must come at first");
	} else if ((a_pos == 1) && (participant)) {
	    for (i=1; i<=(n-3); ++i) {
		ptcls[i] = ptcls[i+2];
		ipid[i]  = ipid[i+2];
	    }
	    n -= 2;
	}
	if (!participant) {
	    Warning("IdChannel", "No valid spectator found");
	    return;
	}
    }
    PParticle dummy(nucleon_pid);
    if (nucleon_pid == tid) { 
	quasi_composite = new PParticle(*participant + dummy);
    } else {
	quasi_composite = new PParticle(dummy + *participant);  
    }
    PParticle **cc1 = new PParticle*[3];   
    cc1[0] = parent;
    cc1[1] = quasi_composite;
    cc1[2] = spectator;
    quasi_pchannel = new PChannel(cc1,2);
    
    
    
    
    Info("IdChannel", "(%s) Quasi-free production", PRINT_AUTO_ALLOC);
    n--;
  
    parent      = quasi_composite;
    orig_parent = quasi_composite;
    ptcls[0]    = quasi_composite;
    ipid[0]     = quasi_composite->ID();
}
void PChannel::ThermalSampling() {
    
    double px, py, pz, Energy;
    float b = 0., phi = 0.;
    int i, nTherm = n;
    PFireball *pFire =          
	(ptcls[0]->IsFireball()) ? (PFireball *)ptcls[0] : NULL;
    if (pFire->IsRandomB()) {             
	if (*event_impact_param == 0.) {
	    b = pFire->sampleB() + 0.001;     
	    phi = 2.*TMath::Pi()*PUtils::sampleFlat(); 
	} else {
	    b   = *event_impact_param;    
	    phi = *event_plane;
	}
	nTherm = pFire->sampleNProd(b);
	if (nTherm > n) nTherm = n;
    } else if (pFire->IsRandomN()) {        
	nTherm = pFire->sampleNProd();
	if (*event_plane == 0.) 
	    phi = 2.*TMath::Pi()*PUtils::sampleFlat(); 
	else 
	    phi = *event_plane;
	if (nTherm > n) 
	    nTherm = n;
    }
    *event_impact_param = b;
    *event_plane        = phi;
    for(i=1; i<=nTherm; i++) {
	int didx = ptcls[i]->GetDecayModeIndex(1);
    redo_thermal:
	if (nTherm == 1) {
	    pFire->samplePartCM(px, py, pz, Energy,didx);
	} else {
	    
	    
	    
	    if (thermal_disable_didx)
		pFire->samplePartCM(px, py, pz, Energy, -1);
	    else if ((i>1) && didx != ptcls[i-1]->GetDecayModeIndex(1)) {
		
		thermal_disable_didx = 1;
		Warning("ThermalSampling","Different decays, disable partial decay sampling");
	    } else {
		pFire->samplePartCM(px, py, pz, Energy, didx);
	    }
	}
	ptcls[i]->SetPxPyPzE(px, py, pz, Energy);
	if (ptcls[i]->M() < PData::LMass(ptcls[i]->ID()) || ptcls[i]->M() > PData::UMass(ptcls[i]->ID())) goto redo_thermal; 
	ptcls[i]->SetW(w*(pFire->mtScale(ptcls[i]->M())));  
	if (sourceid) 
	    ptcls[i]->SetSourceId(sourceid);
	else 
	    ptcls[i]->SetSourceId(pFire->ID());
	
	ptcls[i]->SetParentId(pFire->ID());
	ptcls[i]->SetActive();
    }
    for(i=nTherm+1; i<=n; i++) {
	ptcls[i]->SetInActive();  
    }
    TVector3 Beta = pFire->BoostVector();
    if (Beta.Mag() > 0.) {
	for(i=1; i<=nTherm; ++i) {
	    ptcls[i]->Boost(Beta);
	}
    }
}
void PChannel::MakeDilepton() {
    
    double px, py, pz, Energy;
    PDiLepton * pDL =          
	(ptcls[0]->IsDilepton()) ? (PDiLepton *)ptcls[0] : NULL;
    pDL->samplePartCM(px, py, pz, Energy);
    ptcls[1]->SetPxPyPzE(px, py, pz, Energy);
    ptcls[1]->SetW(w);
    ptcls[1]->SetSourceId(pDL->ID());
    ptcls[1]->SetParentId(pDL->ID());
    ptcls[1]->SetActive();
    TVector3 Beta=pDL->BoostVector();
    if (Beta.Mag()>0.) ptcls[1]->Boost(Beta);
}
Int_t PChannel:: ReadFileInput() {  
    Double_t px, py, pz, Energy, vx, vy, vz, vt;
    Float_t b = 0., phi = 0.;
    Int_t id, idSrc, idPar, indPar = -2, ret;
    Int_t i, nFile = 0, nMax;
    PFileInput *pFile =             
	(ptcls[0]->IsFileInput()) ? (PFileInput*)ptcls[0] : NULL;
    nMax = nFile = pFile->readEventHeader(b);  
    if (nFile == -1) {
	printf("**** EOF reached on file input ****\n");
	for(i=1; i<=n; i++) 
	    ptcls[i]->SetInActive();  
	return 8;
    }
    if (nMax > n) nMax = n;
  
    if (*event_impact_param == 0.) {
	phi = 2.*TMath::Pi()*PUtils::sampleFlat(); 
    }
    else {
	b =   *event_impact_param;           
	phi = *event_plane;
    }
    *event_impact_param = b;
    *event_plane = phi;
    
    for(i=1; i<=nFile; i++) {
	ret = pFile->readParticle(px,py,pz,Energy,vx,vy,vz,vt,id,idSrc,idPar,indPar,w);
	if (ret == -1) break;  
	if (i > n) continue;
	ptcls[i]->SetID(id);           
	ptcls[i]->SetPxPyPzE(px, py, pz, Energy);
	ptcls[i]->ResetE();            
	ptcls[i]->SetW(w);
	ptcls[i]->SetSourceId(idSrc);
	ptcls[i]->SetParentId(idPar);
	ptcls[i]->SetParentIndex(indPar);
	ptcls[i]->SetActive();
	ptcls[i]->SetVertex(vx,vy,vz,vt);
    }
    if (ret == -1) return 8;
    for(i=nMax+1; i<=n; i++) 
	ptcls[i]->SetInActive();  
    TVector3 Beta = pFile->BoostVector();
    if (Beta.Mag() > 0.) 
	for(i=1; i<=nMax; ++i) 
	    ptcls[i]->Boost(Beta);
    return 0;
}
int PChannel::CheckSiblings(PParticle *p, PDistribution *dist, int flag) {
    if (p == NULL) {
	return -1;
    }
    if (p->GetParent()) 
	if ((p->GetParent()->ID() > 500) && (p->GetParent()->ID() <1000)) return -1;
    if (p->ID() >= 1000) return -1; 
   
    
    PParticle *currentSibling = p->GetSibling();   
    Int_t counter = 0; 
    Int_t ret     = -1;
    while (currentSibling != p) {
	if (currentSibling == NULL) return ret;
	if (ret == 0) { 
	    ret = -1;
	}
  	
 	
	if (dist->SetParticle(currentSibling, currentSibling->ID(), flag | PARTICLE_LIST_SIBLING) == 0) { 
	    
	    ret = 0;
	    
	}  
	
	currentSibling = currentSibling->GetSibling();
	counter++;
	if (counter == 8) { 
	    Warning("CheckSiblings", "Sibling chain seems to be corrupted");
	    Print();
	    return -1;
	}
    }
    if (ret == -1) dist->ResetRelatives(flag | PARTICLE_LIST_SIBLING); 
    return ret;
}
Bool_t PChannel::Reset() {
    for (int j=0; j<distribution_position; j++) {
	dist[j]->ResetRelatives();
	dist[j]->SetEnable(1); 
    }
    grandparent      = NULL;
    grandgrandparent = NULL;
    init_done        = kFALSE;
    return kTRUE;
}
Bool_t PChannel::SetDaughters() {
    if (thSrc || tcSrc)  return kTRUE; 
    parent->ResetDaughters();
    
    if (parent->IsActive() == kTRUE) {
	for (Int_t k=1; k<=n; k++) {
	    parent->SetDaughter(ptcls[k]);
	}
    }
    return kTRUE;
}
Bool_t PChannel::Init() {
    
    
    
    int has_printed = 0;
    
    if (orig_parent != ptcls[0]) {
	
	parent = ptcls[0];
	*orig_parent = *(ptcls[0]);
    }
    grandparent = parent->GetParent();
    grandgrandparent = NULL;
    if (grandparent) 
	grandgrandparent = grandparent->GetParent();
    Double_t set_mode_index = 0;
    for (int j=0; j<distribution_position; j++) { 
	if (dist[j]->GetVersionFlag() & VERSION_INVERT_WEIGHTING) {
	    set_mode_index++;
	} else if ((dist[j]->GetVersionFlag() & VERSION_WEIGHTING) 
		   && (dist[j]->GetVersionFlag() & VERSION_GENERATOR_MC) 
		   ) {
	    set_mode_index++;
	} else if (dist[j]->GetVersionFlag() & VERSION_FORCE_NO_PARTIAL_WIDTH) 
	    set_mode_index =  set_mode_index + 2;
    }
    if (set_mode_index < 2)  
	ptcls[0]->SetDecayModeIndex(makeStaticData()->GetDecayIdxByKey(decay_key));
    else {
	ptcls[0]->SetDecayModeIndex(-1);
    }
    if (parent->IsActive() == kTRUE) {
	parent->SetDaughterIndex(ptcls[1]->GetIndex());
	Int_t parId, parInd;
	for (Int_t k=1; k<=n; k++) {
	    ptcls[k]->SetActive();          
	    if (sourceid) 
		ptcls[k]->SetSourceId(sourceid);
	    else 
		ptcls[k]->SetSourceId(parent->GetSourceId());
	    parId  = parent->ID();
	    parInd = parent->GetIndex();
	    if ((parId==50 || parId==51) && grandparent!=NULL) {
		parId += 1000*(grandparent->ID()); 
		
		parInd = grandparent->GetIndex();
	    }
	    ptcls[k]->SetParentId(parId);
	    ptcls[k]->SetParentIndex(parInd);
	    ptcls[k]->SetDaughterIndex(-1);
	    if (k != n) {
		ptcls[k]->SetSiblingIndex(ptcls[k+1]->GetIndex());
		ptcls[k]->SetSibling(ptcls[k+1]);
	    } else {
		ptcls[k]->SetSiblingIndex(ptcls[1]->GetIndex());
		ptcls[k]->SetSibling(ptcls[1]);
	    }	  
	}
    } 
    
    for (int j=0; j<distribution_position; j++) {
	if ((dist[j]->GetStatus() == -1) && dist[j]->GetEnable()){ 
	    if (has_printed == 0 && print_tentative) {
		has_printed = 1;
		cout << "checking tentative distributions for ";
		if (grandparent) {
		    if (grandparent->ID() < 1000)
			printf("%s --> ",makeStaticData()->GetParticleName(grandparent->ID()));
		}
		PrintReaction();
		cout << endl;
	    }
	    if (print_tentative) 
		printf("                             [checking] %s\n",
		       dist[j]->GetDescription());
	    
	    if (grandparent) {
		if (grandparent->ID() < 1000)
		    dist[j]->SetParticle(grandparent, grandparent->ID(), PARTICLE_LIST_GRANDPARENT);
		else
		    dist[j]->SetParticle(grandparent, DISTRIBUTION_QUASI_PID, PARTICLE_LIST_GRANDPARENT);
	    }
	    if (grandgrandparent) {
		if (grandgrandparent->ID() < 1000)
		    dist[j]->SetParticle(grandgrandparent, grandgrandparent->ID(), PARTICLE_LIST_GRANDGRANDPARENT);
		else
		    dist[j]->SetParticle(grandgrandparent, DISTRIBUTION_QUASI_PID, PARTICLE_LIST_GRANDGRANDPARENT);
	    }
#if 1
	    
	    if (parent->ID() >= 1000) {
		if (parent->GetScattering(0)) {
		    dist[j]->SetParticle(parent->GetScattering(0), parent->GetScattering(0)->ID(), PARTICLE_LIST_GRANDPARENT);
		    dist[j]->SetParticle(parent->GetScattering(1), parent->GetScattering(1)->ID(), PARTICLE_LIST_GRANDPARENT);
		}
	    } else if (grandparent) {
		if (grandparent->ID() >= 1000) {
		    if (grandparent->GetScattering(0)) {
			dist[j]->SetParticle(grandparent->GetScattering(0), grandparent->GetScattering(0)->ID(), PARTICLE_LIST_GRANDGRANDPARENT);
			dist[j]->SetParticle(grandparent->GetScattering(1), grandparent->GetScattering(1)->ID(), PARTICLE_LIST_GRANDGRANDPARENT);
		    }
		}
	    }
#endif
	    
	    CheckSiblings(parent, dist[j], PARTICLE_LIST_PARENT);
	    if (grandparent) {
		CheckSiblings(grandparent, dist[j], PARTICLE_LIST_GRANDPARENT);
		if (grandgrandparent)
		    CheckSiblings(grandgrandparent, dist[j], PARTICLE_LIST_GRANDGRANDPARENT);
	    }
	    
	    for (Int_t k=1; k<=n; k++) {
		
		int kk = 0;
		
		while (ptcls[k]->GetDaughter(kk) != NULL) {  
		    
		    dist[j]->SetParticle(ptcls[k]->GetDaughter(kk), 
					 ptcls[k]->GetDaughter(kk)->ID(), 
					 PARTICLE_LIST_GRANDDAUGHTER);
		    kk++;
		}
	    }
	    
	    
	    if (dist[j]->GetStatus() == -1) { 
		if (print_tentative) printf("                             [removed] %s\n",dist[j]->GetDescription());
		dist[j]->SetEnable(0); 
	    }
	    else {
		if (dist[j]->Init() == kTRUE) {
		    if (print_tentative) printf("                             [enabled] %s\n",dist[j]->GetDescription()); }
		else {
		    printf("                             [error] %s:%s\n",dist[j]->GetDescription(),dist[j]->GetIdentifier());
		    dist[j]->SetEnable(0);
		  
		}
	    }
	}      
    }
    
    
    Int_t num_mass_sampling = 0;
    if (parent->ID() > PLUTO_COMPOSITE) {
	for (int j=0; j<distribution_position; j++) { 
	    if (dist[j]->GetVersionFlag() & VERSION_MASS_SAMPLING) {
		if (num_mass_sampling)
		    dist[j]->SetEnable(0);
		num_mass_sampling++;
	    }
	}
    }
    
    init_done       = kTRUE;
    print_tentative = kFALSE;
    for (int j=0; j<distribution_position; j++) { 
	if ((dist[j]->GetStatus() == 0) && dist[j]->GetEnable()){ 
	    dist[j]->GetDepth();  
	}
    }
    return kTRUE;
}
int PChannel::Decay() {
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    TVector3 vCreation, vDecay;
    Double_t tCreation, tDecay;
    if (!init_done) Init();
    if (parent->IsActive() == kFALSE) {
	for (Int_t k=1; k<=n; k++) 
	    ptcls[k]->SetInActive();  
	return STATUS_OK;
    }
    else {
	for (Int_t k=1; k<=n; k++) {
	    if (ptcls[k]->ID() > 0)
		ptcls[k]->SetM(makeStaticData()->GetParticleMass(ptcls[k]->ID()));
	    ptcls[k]->SetActive();   
	}
	
      
	
	vCreation = parent->GetVertex();  
	tCreation = parent->T();          
	tDecay = tCreation + parent->Gamma() * parent->GetProperTime();
	vDecay = vCreation + parent->Gamma() * parent->GetProperTime()
	    * parent->BoostVector();  
	for (Int_t k=1; k<=n; k++) {
	    ptcls[k]->SetProperTime();           
	    ptcls[k]->SetVertex(vDecay,tDecay);  
	    
	}
    }
    int decay_done[n+1];
    int real_size = n+1;
    for (int i=0; i<=n; i++) {
	
	decay_done[i] = 0;
    }
    for (int bu=0; bu<pro_bulkdecay_pos; bu++) {
	pro_bulk[bu]->Modify(ptcls, decay_done, &real_size, n+1);
    }	
    w = parent->W();
    int i, nProd = n;
    status = 0;
    num_not_finalized = 0;
    if (thSrc) {                   
	ThermalSampling();
	decay_done[0] = 1;
	for (int bu=0; bu<bulkdecay_pos; bu++)
	    bulk[bu]->Modify(ptcls, decay_done, &real_size, n+1);
	return status;
    }    
    if (tcSrc) {                   
	status = ReadFileInput();
	return status;
    }    
    if (dlSrc) {                   
	MakeDilepton();
	return status;
    }
    ecm = parent->M();
    for (int j=0; j<distribution_position; j++) { 
	if ((dist[j]->GetStatus() == 0) && dist[j]->GetEnable()){ 
	    dist[j]->Prepare();
	}
    } 
    if (ecm < emin) {
	
 	
 	
	return status = 4; 
    }
    
    Int_t do_flag = 1, 
	distr_status = 0;
    while (do_flag) {  
	status = Genbod(nProd);  
	distr_status = 0;
	for (int j=0; j<distribution_position; j++) { 
	    if ((dist[j]->GetStatus() == 0) && dist[j]->GetEnable()) { 
		if (dist[j]->IsNotRejected() == kFALSE) { 
		    distr_status++;
		    if (dist[j]->CheckAbort()) {
			do_flag = 1000000;
		    }
		}
	    } 
	} 
	if (distr_status) {
	    do_flag++;
	    if (do_flag > 10) { 
		status  = 10;
		do_flag = 0;
	    }
	} else {
	    do_flag = 0;
	} 
    } 
    for (int j=0; j<distribution_position; j++) { 
	if ((dist[j]->GetStatus() == 0) && dist[j]->GetEnable()){ 
	    if (!dist[j]->Finalize()) {
		
		if (num_not_finalized < MAX_NUM_NOT_FINALIZED) {
		    distribution_not_finalized[num_not_finalized] = dist[j];
		    num_not_finalized++;
		} else {
		    Error("Decay","MAX_NUM_NOT_FINALIZED reached");
		}
	    }
	}
    }
    if (status) return status;                  
    
    
    
 
    Double_t new_weight              = ptcls[0]->W();
    Double_t hidden_generator_weight = ptcls[0]->GenW();
    Double_t inv_generator_weight    = ptcls[0]->InvGenW();
    Double_t dynamic_range = 1.;
    if (*weight_version) {	
#if 1
	Double_t generator_weight = hidden_generator_weight; 
	for (int j=0; j<distribution_position; j++) { 
	    if ((dist[j]->GetStatus() == 0) && dist[j]->GetEnable()){ 
		dist_weight[j] = dist[j]->GetWeight();
		if ((dist[j]->GetVersionFlag()) & VERSION_INVERT_WEIGHTING) { 
		    generator_weight *= dist_weight[j];
		    dynamic_range    *= dist[j]->GetDynamicRange();
		}
	    }
	}
	
	for (int j=0; j<distribution_position; j++) { 
	    if ((dist[j]->GetStatus() == 0) && dist[j]->GetEnable()){ 
		if (((dist[j]->GetVersionFlag()) & VERSION_INVERT_WEIGHTING) ||
		    ((dist[j]->GetVersionFlag()) & VERSION_WEIGHTING)) {
		    
		    Double_t local_weight = dist_weight[j];
		    
		
		    if ((dist[j]->GetVersionFlag()) & VERSION_GENERATOR_MC) { 
			
			
			
			
			
			local_weight *= dynamic_range;
			dynamic_range = 1.;
		    }	
		    if (dist[j]->GetExpectedWeightMean() > 0) { 
			if ((dist[j]->GetVersionFlag()) & VERSION_INVERT_WEIGHTING) {
			    local_weight = (1./local_weight);
			    dist[j]->IncrementWeightSum(local_weight);
			} else {
			    
			    dist[j]->IncrementWeightSum(local_weight, 1./generator_weight);
			}
			local_weight *= (dist[j]->GetExpectedWeightMean()/dist[j]->CalcWeightMean());
		    } 
		    new_weight *= local_weight;  
		    
		    dist_weight_sum[j] += local_weight;
		    dist_counter[j]++;
		} else { 
		    if (dist[j]->GetExpectedWeightMean() > 0) {
			new_weight *= dist[j]->GetExpectedWeightMean();
			dist_weight_sum[j] += dist[j]->GetExpectedWeightMean();
			dist_counter[j]++;
			
			
			
			
			
			
			
			
			
			Double_t local_weight = dist_weight[j];
			
			
			Double_t inv_local_weight = 0.;
			if (local_weight > 0.) 
			    inv_local_weight = (1./local_weight);
			dist[j]->IncrementWeightSum(inv_local_weight);
			
		    } 
		    
		    
		    
		}
	    }
	}
	if (new_weight <= 0.) 
	    return status = 2; 
	weight_sum += new_weight;	
#endif
    }
    TVector3 beta = parent->BoostVector();        
    if (beta.Mag() > 0.) 
	for (i=1; i<=nProd; ++i) 
	    ptcls[i]->Boost(beta);
    for (i=1; i<=nProd; ++i) {
	ptcls[i]->SetParent(parent); 
	
	ptcls[i]->SetW(new_weight);
	ptcls[i]->SetGenW(hidden_generator_weight);
	ptcls[i]->SetInvGenW(inv_generator_weight);
    }
    
#if 0
    for (int j=0; j<distribution_position; j++) { 
	if ((dist[j]->GetStatus() == 0) && dist[j]->GetEnable()){ 
	    
	}
    }
#endif
    decay_done[0] = 1;
    for (int bu=0; bu<bulkdecay_pos; bu++)
	bulk[bu]->Modify(ptcls, decay_done, &real_size, n+1);
    return status;
}
int PChannel::Genbod(int nProd) {
    
    
    int i; 
    double conserve_e = 0., 
	em[nProd];
    Bool_t PDistribution_sampleMass  = kFALSE;
    int PDistribution_sampleMomentum = 0, 
	PDistribution_sampleModels   = 0;
    
    for (int j=0; j<distribution_position; j++) { 
	if ((dist[j]->GetStatus() == 0) && dist[j]->GetEnable()) { 
	    if ((dist[j]->GetVersionFlag()) & VERSION_SAMPLING)
		if (dist[j]->SampleMass()) {
		    PDistribution_sampleMass = kTRUE;
		    for (i=0; i<nProd; ++i) {
			em[i] = ptcls[i+1]->M(); 
		    }
		}
	}
    }
    conserve_e = ptcls[0]->M(); 
    if (PDistribution_sampleMass == kFALSE) { 
	Warning("Genbod","No mass sampling model(s) found in %s",
		makeStaticData()->GetDecayNameByKey(decay_key));
	return status = 6;
    } else { 
	for (i=0; i<nProd; ++i) 
	    conserve_e -= em[i];
    }
  
    if ((conserve_e < -1.e-8) && (nProd>1) ) {
      
      
      
	return status = 5;
    }
    conserve_e = ptcls[0]->M();
    Int_t last_sampling_model = 0;
    
    for (int j=0; j<distribution_position; j++) { 
	if ((dist[j]->GetStatus() == 0) && dist[j]->GetEnable()){ 
	    if ((dist[j]->GetVersionFlag()) & VERSION_SAMPLING) {
		PDistribution_sampleModels++;
		if (dist[j]->SampleMomentum()) {
		    if (!PDistribution_sampleMomentum) last_sampling_model = j;
		    PDistribution_sampleMomentum++;
		} 
	    }
	    if (PDistribution_sampleMomentum > 1) {
		Warning("Genbod", "More than one momentum sampling model found in %s",
			makeStaticData()->GetDecayNameByKey(decay_key));
		Warning("Genbod","Model 1: [%s]", dist[last_sampling_model]->GetIdentifier());
		Warning("Genbod","Model 2: [%s]", dist[j]->GetIdentifier());
		
		return status=6;
	    }
	}
    }
    if (PDistribution_sampleModels == 0) {
	Warning("Genbod","No momentum sampling model found in %s",
		makeStaticData()->GetDecayNameByKey(decay_key));
	return status = 6;
    }
    
    for (int j=0; j<distribution_position; j++) { 
	if ((dist[j]->GetStatus() == 0) && dist[j]->GetEnable()){ 
	    if ((dist[j]->GetVersionFlag()) & VERSION_SAMPLING)
		dist[j]->SampleAngle();
	}
    }
    
    for (i=0; i<nProd; ++i) 
	conserve_e -= ptcls[i+1]->E();
    if (fabs(conserve_e) > 1.e-8) {
 	 
 	 
 	 
 	 
	return status = 7; 
    }
    else return status=STATUS_OK;
}
int PChannel::SetDistribution(PDistribution *distribution) {
    
    if (distribution->GetEnable() == 0) return -1;
    if (distribution_position == MAX_DISTRIBUTIONS) {
	Warning("SetDistribution","MAX_DISTRIBUTIONS reached");
	return -1;
    }
    Int_t check = 0;
    PParticle *p0 = ptcls[0];
    
    
    
    
    distribution->Reset();
    check = distribution->SetParticle(p0, p0->ID(), PARTICLE_LIST_PARENT); 
    if (check<0 && (p0->ID() > 1000))
	check = distribution->SetParticle(p0, DISTRIBUTION_QUASI_PID, PARTICLE_LIST_PARENT); 
    for (int i=0;i<n;i++) {
	check += distribution->SetParticle(ptcls[i+1], ptcls[i+1]->ID(), PARTICLE_LIST_DAUGHTER); 
    }
   
    check += distribution->CheckDaughters(); 
    if (!strcmp(distribution->GetName(), "_testmodel"))
	cout << "Check number in PChannel::SetDistribution: " << check << endl;
 
    
    if (check < 0) {
	
	if (distribution->debug_flag)
	     distribution->Print();
	distribution->Reset();
	return -1;
    }
    
    
    distribution = (PDistribution*) distribution->Clone();
    
    if (distribution->GetStatus() == 0) { 
	
	if (distribution->Init() == kFALSE) {
	    printf("                             [error] %s:%s\n",distribution->GetDescription(),distribution->GetIdentifier());
	    distribution->SetEnable(0);
	} else {
	    distribution->GetDepth();  
	    dist[distribution_position] = distribution;
	    distribution_position++;
	}
    } else { 
	if (init_done) { 
	    Reset();
	    Init();
	    if (distribution->GetStatus() == 0) { 
		if (distribution->Init() == kFALSE) {
		    printf("                             [error] %s:%s\n",distribution->GetDescription(),distribution->GetIdentifier());
		    distribution->SetEnable(0);
		} else {
		    distribution->GetDepth();  
		    dist[distribution_position] = distribution;
		    distribution_position++;
		}		
		
	    }
	} else { 
	    dist[distribution_position] = distribution;
	    distribution_position++;
	}
    } 
    return 0;
}
void PChannel::GetMessage() { printf(" %s\n",Message[status]); }
char const *PChannel::GetName(void) const {
    
    if (!thSrc && !tcSrc && !dlSrc) {
	
	if (decay_key < 0 ) {
	    Warning("GetName", "No decay key found");
	    exit(1);
	}
	return makeStaticData()->GetDecayNameByKey(decay_key);
    }
    if (thSrc) 
	return " Fireball";
    else if (tcSrc) 
	return " File Input";
    else if (dlSrc) 
	return " Dilepton source";
    return "<unknown>";
}
void PChannel::PrintReport() const {
    PrintReaction();
    cout << "Weighting report" << endl;
    for (int j=0;j<distribution_position;j++) { 
	printf("        [%s] %s\n",dist[j]->GetIdentifier(),dist[j]->GetDescription());
	    if (dist[j]->GetExpectedWeightMean() > 0) { 
		cout << "Expected mean: " << dist[j]->GetExpectedWeightMean() << " Reached: " 
		     << dist_weight_sum[j]/dist_counter[j] << endl;
		cout << "Calc mean: " << dist[j]->CalcWeightMean() << endl;
	    } else {
		cout << "Mean: " << dist_weight_sum[j]/dist_counter[j] << endl;
	    }	    
    }
}
void PChannel::PrintReaction(Int_t check_key) const {
    if (check_key) {
	if (!thSrc && !tcSrc && !dlSrc) {
	    if (decay_key < 0 ) {
		Warning("PrintReaction", "No decay key found");
		exit(1);
	    }
	    cout << makeStaticData()->GetDecayNameByKey(decay_key) << endl;
	    return;
	}
    }
    int j;
    if (thSrc) 
	printf("Fireball");
    else if (tcSrc) 
	printf("File Input");
    else if (dlSrc) 
	printf("Dilepton source");
    else 
	printf("%s", makeStaticData()->GetParticleName(ipid[0]));
    printf(" --> %s", makeStaticData()->GetParticleName(ipid[1]));
    for (j=2; j<=n; ++j) 
	printf(" + %s",makeStaticData()->GetParticleName(ipid[j]));
    cout << endl;
}
void PChannel::PrintNew() {
    
    Long_t nPattern = 0;
    for (int j=0; j<distribution_position; j++)
	if (dist[j]->GetEnable() != 0) {
	    nPattern += ((j<64) ?  1<<(j+1) : 0);
	}
    if (nPattern & (~fEnablePattern)) { 
	fEnablePattern |= nPattern;
	Print();
    }
}
void PChannel::Print(const Option_t *delme) const {
    int j;
    if (quasi_pchannel)
 	quasi_pchannel->Print();
    PrintReaction();
    printf("        Interaction model(s):\n");
    
    for (j=0;j<distribution_position;j++) {
	
	if (dist[j]->GetEnable() != 0) {
	    if (dist[j]->GetStatus() == 0) { 
		dist[j]->GetDepth();  
		if (dist[j]->Path())
		    printf("        [%s] %s {/%s}",dist[j]->GetIdentifier(),dist[j]->GetDescription(),dist[j]->Path());
		else
		    printf("        [%s] %s",dist[j]->GetIdentifier(),dist[j]->GetDescription());
		dist[j]->SubPrint(0);
		cout << endl;
	    } else
		printf("        [%s,tentative] %s\n",dist[j]->GetIdentifier(),dist[j]->GetDescription());
	    if (delme != NULL && strcmp(delme,"debug") == 0) {
		dist[j]->Print();
	    }	    
	}
    }
    if (thSrc) ((PFireball * )parent)->Print("      ");
    if (bulkdecay_pos || pro_bulkdecay_pos) {
	printf("        Bulk Classes:\n");
	if (pro_bulkdecay_pos) {
	    printf("          Prologue: ");
	    for (j=0;j<pro_bulkdecay_pos;j++) 
		cout << "<" << pro_bulk[j]->ClassName() << "> ";
	    cout << endl;
	}
	if (bulkdecay_pos) {
	    printf("          Epilogue: ");
	    for (j=0;j<bulkdecay_pos;j++) 
		cout << "<" << bulk[j]->ClassName() << "> ";
	    cout << endl;
	}
    }
}
Bool_t PChannel::AddBulk(PBulkInterface *mybulk) {
    
    
    
  
    if (bulkdecay_pos == MAX_BULKDECAY ) {
	Error("AddBulk","MAX_BULKDECAY reached");
	return kFALSE;
    }
    if (bulkdecay_pos && (bulk[bulkdecay_pos-1]->GetPriority() > mybulk->GetPriority())) {
	bulk[bulkdecay_pos]   = bulk[bulkdecay_pos-1];
	bulk[bulkdecay_pos-1] = mybulk;
	bulkdecay_pos++;
    } else
	bulk[bulkdecay_pos++]=mybulk;
    return kTRUE;
}
Bool_t PChannel::AddPrologueBulk(PBulkInterface * mybulk) {
    
    
    
    if (pro_bulkdecay_pos == MAX_BULKDECAY ) {
	Error("AddPrologueBulk","MAX_BULKDECAY reached");
	return kFALSE;
    }
    
    if (pro_bulkdecay_pos && (pro_bulk[pro_bulkdecay_pos-1]->GetPriority() > mybulk->GetPriority())) {
	pro_bulk[pro_bulkdecay_pos]   = pro_bulk[pro_bulkdecay_pos-1];
	pro_bulk[pro_bulkdecay_pos-1] = mybulk;
	pro_bulkdecay_pos++;
    } else
	pro_bulk[pro_bulkdecay_pos++]=mybulk;
    return kTRUE;
}
ClassImp(PChannel)