ROOT logo
#ifndef FASTSIM_H
#define FASTSIM_H
#include <TSelector.h>
#include <TQObject.h>
#ifndef __CINT__
# include "AliGenerator.h"
# include "AliRunLoader.h"
# include "AliStack.h"
# include "AliHeader.h"
# include "AliGenEventHeader.h"
# include "AliRun.h"
# include "AliCollisionGeometry.h"
# include "AliGenPythiaEventHeader.h"
# include "AliGenDPMjetEventHeader.h"
# include "AliGenGeVSimEventHeader.h"
# include "AliGenHerwigEventHeader.h"
# include <TROOT.h>
# include <TString.h>
# include <TMath.h>
# include <TParticle.h>
# include <TH1.h>
# include <TTree.h>
# include <TClonesArray.h>
# include <TList.h>
# include <TProof.h>
# include <TParticlePDG.h>
# include <TStopwatch.h>
# include <TFile.h>
# include <TProofOutputFile.h>
# include <TCanvas.h>
# include <TTimer.h>
# include <fstream>
#else
class AliGenerator;
class AliRunLoader;
class AliStack;
class AliHeader;
class AliGenEventHeader;
class TH1;
class TTree;
class TClonesArray;
class TBrowser;
class TList;
class TFile;
class TProofOutputFile;
class TCanvas;
class TVirtualPad;
class TTimer;
#endif

//====================================================================
/** 
 * Monitor output objects
 */
struct FastMonitor : public TObject, public TQObject 
{
  /** 
   * Constructor 
   * 
   * 
   * @return 
   */
  FastMonitor(TSelector* s=0)
    : fName("FastMonitor"),
      fCanvas(0),
      fSelector(s)
  {
    if (gROOT->IsBatch()) {
      Warning("FastMonitor", "Batch processing, no monitoring");
      return;
    }

    if (gProof) {
      fName = gProof->GetSessionTag();
      gDirectory->Add(this);
      Bool_t ret = gProof->Connect("Feedback(TList *objs)", "FastMonitor", this, 
				   "Feedback(TList *objs)");
      if (!ret) {
	Warning("FastMonitor", "Failed to connect to Proof");
	return;
      }
    }
    else if (!s) return;
    
    fCanvas = new TCanvas(fName, Form("Monitor %s", fName.Data()), 1000, 800);
    fCanvas->SetFillColor(0);
    fCanvas->SetFillStyle(0);
    fCanvas->SetTopMargin(0.01);
    fCanvas->SetRightMargin(0.01);

    fCanvas->Divide(2,2);
    RegisterDraw(1, "type",   "", 0);
    RegisterDraw(2, "b",      "", 0);
    RegisterDraw(3, "cent",   "", 0);
    RegisterDraw(4, "dNdeta", "", 0x8);
  }
  /** 
   * Register a draw of a an object 
   * 
   * @param i      Pad number 
   * @param name   Name of object 
   * @param option Drawing option
   * @param flags  Flags 
   *
   *  - 0x1   Log(x)
   *  - 0x2   Log(y)
   *  - 0x4   Log(z)
   *  - 0x8   Scale to events and bin width 
   */
  void RegisterDraw(Int_t i,
		    const char* name,
		    const char* option,
		    UShort_t    flags=0)
  {
    TVirtualPad* p = fCanvas->GetPad(i);
    if (!p) {
      Warning("RegisterDraw", "Not enough sub-pads (%d)", i);
      return;
    }
    p->SetFillColor(0);
    p->SetFillStyle(0);
    p->SetTopMargin(0.01);
    p->SetRightMargin(0.01);
    p->SetName(Form("p_%s", name));
    p->SetTitle(option);
    if (flags & 0x1) p->SetLogx();
    if (flags & 0x2) p->SetLogy();
    if (flags & 0x4) p->SetLogz();
    if (flags & 0x8) p->SetBit(BIT(15));
  }
  /** 
   * Desctructor 
   */
  virtual ~FastMonitor() 
  {
    if (!gProof) return;
    gProof->Disconnect("Feedback(TList *objs)",this, 
		       "Feedback(TList* objs)");
  }
  /** 
   * Set name of this object 
   * 
   * @param name Name 
   */
  void SetName(const char* name) { fName = name; }
  /** 
   * Get the name of this object 
   * 
   * @return Name 
   */
  const char* GetName() const { return fName.Data(); }
  /** 
   * Find pad corresponding to an object
   * 
   * @param name Name of object 
   * 
   * @return Pointer to pad or null
   */
  TVirtualPad* FindPad(const TString& name)
  {
    TVirtualPad* p = 0;
    Int_t        i = 1;
    TString      t = Form("p_%s", name.Data());
    while ((p = fCanvas->GetPad(i))) {
      if (t.EqualTo(p->GetName())) return p;
      i++;
    }
    return 0;
  }
  /** 
   * Called when we get notified of 
   * 
   * @param objs List of monitored objects
   */
  void Feedback(TList* objs)
  {
    // Info("FeedBack", "List is %p", objs);
    // if (objs) objs->ls();
    if (!fCanvas) return;

    TList* l = static_cast<TList*>(objs->FindObject("histograms"));
    if (!l) {
      Warning("Feedback", "No histograms");
      return;
    }
    Int_t nEvents = 1;
    TObject* oIpz = l->FindObject("ipZ");
    if (oIpz && oIpz->IsA()->InheritsFrom(TH1::Class())) 
      nEvents = static_cast<TH1*>(oIpz)->GetEntries();
    else 
      Warning("Feedback", "Histogram ipZ not found");
    
    TIter next(l);
    TObject* o = 0;
    while ((o = next())) {
      TVirtualPad* p = FindPad(o->GetName());
      if (!p) 
	// Info("FeedBack", "no pad for %s", o->GetName());
	continue;

      p->cd();
      if (o->IsA()->InheritsFrom(TH1::Class())) {
	TH1* h = static_cast<TH1*>(o);
	TH1* c = h->DrawCopy(p->GetTitle());
	c->SetDirectory(0);
	c->SetBit(TObject::kCanDelete);
	if (p->TestBit(BIT(15))) {
	  Info("Feedback", "Scaling %s by 1./%d and width",
	       c->GetName(), nEvents);
	  c->Scale(1./nEvents, "width");
	}
      }
      else {
	TObject* c = o->DrawClone(p->GetTitle());
	c->SetBit(TObject::kCanDelete);
      }
      p->Modified();
    }
    fCanvas->Modified();
    fCanvas->Update();
    fCanvas->cd();
  }
  /** 
   * Function to handle connect signals 
   * 
   */
  void Handle()
  {
    HandleTimer(0);
  }
  /**
   * Function to handle timer events 
   */
  Bool_t HandleTimer(TTimer*)
  {
    Info("HandleTimer", "Selector=%p", fSelector);
    if (!fSelector) return false;
    Feedback(fSelector->GetOutputList());
    return true;
  }
  /** Our name */
  TString fName;
  /** Our canvas */
  TCanvas* fCanvas;
  /** Possibly link to selector */
  TSelector* fSelector;
  ClassDef(FastMonitor,1);
};


//====================================================================
/** 
 * Run a event generator simulation 
 */
struct FastSim : public TSelector
{
  /** 
   * Constructor 
   * 
   * @param eg     Event generator 
   * @param runNo  Run number to simulate 
   * @param bMin   Lease impact parameter 
   * @param bMax   Largest impact parameter 
   */
  FastSim(const char* eg="",
	  ULong_t runNo=0,
	  Double_t bMin=0,
	  Double_t bMax=20,
	  Long64_t nEvents=0)
    : TSelector(),
      fEGName(eg),
      fRunNo(runNo),
      fBMin(bMin),
      fBMax(bMax),
      fGRP(0),
      fNEvents(nEvents),
      fGenerator(0),
      fRunLoader(0),
      fStack(0),
      fHeader(0),
      fTree(0),
      fParticles(0),
      fList(0),
      fHEta(0),
      fHIpz(0),
      fHType(0),
      fHCent(0),
      fHB(0),
      fHPhiR(0),
      fHTime(0),
      fProofFile(0),
      fFile(0),
      fFileName("")
  {}
  const char* FileName() const
  {
    static TString fn;
    if (fn.IsNull()) {
      if (!fFileName.IsNull())  fn = fFileName;
      else {
	const char* egName = (fGenerator ?
			      fGenerator->GetName() :
			      fEGName.Data());
	fn = Form("%s_%09d", egName, fRunNo);
	if (fNEvents > 0) {
	  if (fNEvents >= 1000000)
	    fn.Append(Form("_%lldM", fNEvents/1000000));
	  else if (fNEvents >= 1000)
	    fn.Append(Form("_%lldk", fNEvents/1000));
	  else
	    fn.Append(Form("_%lld", fNEvents));
	}
	fn.Append(".root");
	fFileName = fn;
      }
    }
    return fn.Data();
    /*
      if (fFileName.IsNull())
      fFileName = Form("%s_%09d.root", fEGName.Data(), fRunNo);
      return fFileName.Data();*/
  }
  const char* GetName() const { return "FastSim"; }
  const char* GetTitle() const { return "ALICE Event Generator simulation"; }
  /** 
   * Create our outputs 
   * 
   * 
   * @return true on success 
   */
  Bool_t SetupOutput()
  {
    Info("SetupOutput", "First the file");
    Bool_t isProof = false;
    if (fInput && fInput->FindObject("PROOF_Ordinal"))
      isProof = true;
    if (isProof) {
      Info("SetupOutput", "Making Proof File");
      fProofFile = new TProofOutputFile(FileName(), "M");
      // TProofOutputFile::kMerge,
      // TProofOutputFile::kRemote);
      fFile = fProofFile->OpenFile("RECREATE");
    }
    else
      fFile = TFile::Open(FileName(), "RECREATE");

    Info("SetupOutput", "Making our tree");
    fTree      = new TTree("T", "T");
    fParticles = new TClonesArray("TParticle");
    fTree->Branch("header", &fShortHead,
		  "run/i:event:npart:nbin:type:ipx/D:ipy:ipz:b:c:phir");
    fTree->Branch("particles", &fParticles);
    fTree->AutoSave();
    fTree->SetDirectory(fFile);
    fTree->SetAlias("primary", "(particles.fBits&(1<<14))");
    fTree->SetAlias("weak",    "(particles.fBits&(1<<15))");
    fTree->SetAlias("charged", "(particles.fBits&(1<<16))");
    fTree->SetAlias("pt",      "(sqrt(pow(particles.fPx,2)+"
		    /*       */"pow(particles.fPy,2)))");
    fTree->SetAlias("eta",     "(pt<1e-10?1024:"
		    "-log(tan(atan2(particles.Pt(),particles.fPz)/2)))");
    fTree->SetAlias("good",    "(primary&&charged&&abs(eta)<1000)");
    fTree->SetAlias("sd",      "(header.fType & 0x1)");
    fTree->SetAlias("dd",      "(header.fType & 0x2)");
    fTree->SetAlias("pion",    "(abs(particles.fPdgCode)==211)");
    fTree->SetAlias("kaon",    "(abs(particles.fPdgCode)==321)");
    fTree->SetAlias("proton",  "(abs(particles.fPdgCode)==2212)");
    fTree->SetAlias("electron","(abs(particles.fPdgCode)==11)");
    fTree->SetAlias("other",   "(!pion&&!kaon&&!proton&&!electron)");
    fTree->SetAlias("beta",    "(particles.P()/particle.Energy())");
    fTree->SetAlias("gamma",   "(1./sqrt(1-beta*beta))");

    Info("SetupOutput", "Making histograms");
    Double_t maxEta = 10;
    Double_t dEta   = 10./200;
    fHEta = new TH1D("dNdeta", "Charged particle pseudo-rapidity density",
		     Int_t(2*maxEta/dEta+.5), -maxEta, +maxEta);
    fHEta->Sumw2();
    fHEta->SetXTitle("#it{#eta}");
    fHEta->SetYTitle("1/N d#it{N}_{ch}/d#it{#eta}");
    fHEta->SetMarkerColor(kRed+2);
    fHEta->SetMarkerStyle(20);
    fHEta->SetDirectory(0);
    
    fHIpz = new TH1D("ipZ", "Z-coordinate of interaction point",
		     10, -10, 10);
    fHIpz->SetMarkerColor(kGreen+2);
    fHIpz->SetFillColor(kGreen+2);
    fHIpz->SetFillStyle(3001);
    fHIpz->SetXTitle("IP_{#it{z}} [cm]");
    fHIpz->SetDirectory(0);

    fHType = new TH1D("type", "Diffractive", 3, .5, 3.5);
    fHType->SetMarkerColor(kOrange+2);
    fHType->SetFillColor(kOrange+2);
    fHType->SetFillStyle(3001);
    fHType->SetDirectory(0);
    fHType->GetXaxis()->SetBinLabel(1, "Non");
    fHType->GetXaxis()->SetBinLabel(2, "Single");
    fHType->GetXaxis()->SetBinLabel(3, "Double");

    fHCent = new TH1D("cent", "Centrality", 20, 0, 100);
    fHCent->SetMarkerColor(kPink+2);
    fHCent->SetFillColor(kPink+2);
    fHCent->SetFillStyle(3001);
    fHCent->SetDirectory(0);
    fHCent->SetXTitle("Centrality [%]");
    
    fHB = new TH1D("b", "Impact parameter", 20, 0, 20);
    fHB->SetMarkerColor(kYellow+2);
    fHB->SetFillColor(kYellow+2);
    fHB->SetFillStyle(3001);
    fHB->SetXTitle("#it{b} [fm]");
    fHB->SetDirectory(0);

    fHPhiR = new TH1D("phiR", "Event plane angle", 360, 0, 360);
    fHPhiR->SetMarkerColor(kMagenta+2);
    fHPhiR->SetFillColor(kMagenta+2);
    fHPhiR->SetFillStyle(3001);
    fHPhiR->SetXTitle("#it{#Phi}_{R} [degrees]");
    fHPhiR->SetDirectory(0);

    fHTime = new TH1D("timing", "Timing of processing", 5,0.5,5.5);
    fHTime->SetMarkerColor(kBlue+2);
    fHTime->SetFillColor(kBlue+2);
    fHTime->SetFillStyle(3001);
    fHTime->GetXaxis()->SetBinLabel(1,"Reset");
    fHTime->GetXaxis()->SetBinLabel(2,"Generate");
    fHTime->GetXaxis()->SetBinLabel(3,"Header");
    fHTime->GetXaxis()->SetBinLabel(4,"Particles");
    fHTime->GetXaxis()->SetBinLabel(5,"Filling");
    fHTime->SetDirectory(0);
				    
    fList = new TList;
    fList->SetName("histograms");
    fList->SetOwner(true);
    fList->Add(fHEta);
    fList->Add(fHIpz);
    fList->Add(fHType);
    fList->Add(fHCent);
    fList->Add(fHB);
    fList->Add(fHPhiR);
    fList->Add(fHTime);

    Info("SetupOutput", "Adding list ot outputs");
    fOutput->Add(fList);

    return true;
  }
  Bool_t SetupGen()
  {
    Printf(" === Setup ==============================");
    Printf("  Run #:                          %6d", fRunNo);
    Printf("  EG:     %30s", fEGName.Data());
    Printf("  B range:             %5.1ffm - %5.1ffm", fBMin, fBMax);
    Printf(" ========================================");
    Printf("Macro path: %s", gROOT->GetMacroPath());

    // --- Check if we shoud get the GRP line ------------------------
    if (!fGRP && fInput) {
      fGRP = fInput->FindObject("GRP");
      std::ofstream* pout = new std::ofstream("grp.dat");
      if (pout) {
	Info("SetupGen", "Writing GRP line '%s' to \"grp.dat\"",
	     fGRP->GetTitle());
	std::ostream& out = *pout;
	out << fGRP->GetTitle() << std::endl;
	pout->close();
      }
    }

    // --- Load our settings -----------------------------------------
    Info("SetupGen", "Loading scripts");
    gROOT->Macro(Form("GRP.C(%d)", fRunNo));
    gROOT->Macro("BaseConfig.C");
    gROOT->Macro("EGConfig.C");

    gROOT->ProcessLine(Form("VirtualEGCfg::LoadGen(\"%s\")",fEGName.Data()));

    // --- Make our generator ----------------------------------------
    Info("SetupGen", "Creating generator");
    TString egMk = Form("egCfg->MakeGenerator(\"%s\",%f,%f)",
			fEGName.Data(), fBMin, fBMax);
    Long64_t egPtr = gROOT->ProcessLine(egMk);
    if (egPtr == 0) {
      Error("Setup", "Failed to make generator");
      return false;
    }
    fGenerator = reinterpret_cast<AliGenerator*>(egPtr);


    if (fFileName.IsNull()) FileName();
    Info("SetupRun", "File name is '%s'", fFileName.Data());

    return true;
  }    
  /** 
   * Setup the generator etc. of the job 
   * 
   * @param nev Maximum number of events per file 
   * 
   * @return true on success 
   */
  Bool_t SetupRun()
  {
    // --- gAlice (bare ROOT) ----------------------------------------
    if (!gAlice)
      new AliRun("gAlice", "The ALICE Off-line framework");

    Long64_t nev = (fNEvents <= 0 ? 0xFFFFFFFF : fNEvents);
    // --- Run-loader, stack, etc  -----------------------------------
    Info("SetupRun", "Set-up run Loader");    
    fRunLoader = AliRunLoader::Open("galice.root", "FASTRUN", "RECREATE");
    fRunLoader->SetCompressionLevel(2);
    fRunLoader->SetNumberOfEventsPerFile(nev);
    fRunLoader->LoadKinematics("RECREATE");
    fRunLoader->MakeTree("E");
    gAlice->SetRunLoader(fRunLoader);
    fRunLoader->MakeStack();
    fStack  = fRunLoader->Stack();
    fHeader = fRunLoader->GetHeader();

    // --- Initialize generator --------------------------------------
    Info("SetupRun", "Initializing generator");
    fGenerator->Init();
    fGenerator->SetStack(fStack);

    return true;
  }
  /** 
   * Read the previously created grp.dat file 
   * 
   */
  Bool_t ReadGRPLine()
  {
    std::ifstream* pin = new std::ifstream("grp.dat");
    if (!pin) {
      Warning("ReadGRPLine", "Failed to open \"grp.dat\"");
      return false;
    }
    std::istream&  in  = *pin;
    TString line;
    TString env;
    do {
      line.ReadLine(in);
      if (line.IsNull()) continue;
      if (line.BeginsWith("#")) continue;
      env = line;
      break;
    } while (!in.eof());
    pin->close();

    if (env.IsNull()) {
      Warning("ReadGRPLine", "Got no line from \"grp.dat\"");
      return false;
    }
    
    fGRP = new TNamed("GRP",env.Data());
    return true;
  }
    
  /** 
   * Set up job 
   * 
   */
  void Init(TTree*)
  {
  }
  /** 
   * Set up job 
   * 
   */
  void Begin(TTree*)
  {
    // Make a monitor
    Info("Begin", "gProof=%p Nomonitor=%p",
	 gProof, (gProof ? gProof->GetParameter("NOMONITOR") : 0));

    if (gProof && !gProof->GetParameter("NOMONITOR")) { 
      new FastMonitor;
      gProof->AddFeedback("histograms");
      Info("Begin", "Adding monitoring");
    }
    gROOT->Macro(Form("GRP.C(%d)", fRunNo));
    if (ReadGRPLine()) {
      if(gProof) {
	gProof->AddInput(fGRP);
      }
    }
  }
  /** 
   * Set-up this sub-job 
   * 
   */
  void SlaveBegin(TTree*)
  {
    SetupGen();
    SetupOutput();
    SetupRun();
  }
  /* Reset internal caches etc. 
   * 
   * @param iEv Event number 
   * 
   * @return true on success
   */
  Bool_t PreEvent(Long64_t iEv)
  {
    // --- Reset header ----------------------------------------------
    fShortHead.fRunNo   = fRunNo;
    fShortHead.fEventId = iEv;
    fShortHead.fIpX     = 1024;
    fShortHead.fIpY     = 1024;
    fShortHead.fIpZ     = 1024;
    fShortHead.fNpart   = -1;
    fShortHead.fNbin    = -1;
    fShortHead.fPhiR    = -1;
    fShortHead.fB       = -1;
    fShortHead.fC       = -1; 
    fParticles->Clear();
    // --- Reset header, etc.  ---------------------------------------
    fHeader->Reset(fRunNo, iEv);
    fRunLoader->SetEventNumber(iEv);
    fStack->Reset();
    fRunLoader->MakeTree("K");

    return true;
  }
  /** 
   * Process the event header 
   * 
   * @return true if the event should be diagnosed
   */
  Bool_t ProcessHeader()
  {
    // --- Copy to short header --------------------------------------
    fShortHead.fRunNo   = fHeader->GetRun();
    fShortHead.fEventId = fHeader->GetEvent();
    TArrayF ip;
    fHeader->GenEventHeader()->PrimaryVertex(ip);
    fShortHead.fIpX     = ip[0];
    fShortHead.fIpY     = ip[1];
    fShortHead.fIpZ     = ip[2];

    // --- Check header type -----------------------------------------
    AliGenEventHeader* genHeader = fHeader->GenEventHeader();
    AliCollisionGeometry* geometry = 
      dynamic_cast<AliCollisionGeometry*>(genHeader);
    AliGenPythiaEventHeader* pythia    = 
      dynamic_cast<AliGenPythiaEventHeader*>(genHeader);
    AliGenDPMjetEventHeader* dpm       = 
      dynamic_cast<AliGenDPMjetEventHeader*>(genHeader);
    AliGenGeVSimEventHeader* gev       = 
      dynamic_cast<AliGenGeVSimEventHeader*>(genHeader);
    AliGenHerwigEventHeader* herwig    = 
      dynamic_cast<AliGenHerwigEventHeader*>(genHeader);
    if (geometry) {
      fShortHead.fB     = geometry->ImpactParameter();
      fShortHead.fNpart = (geometry->ProjectileParticipants() + 
			   geometry->TargetParticipants());
      fShortHead.fNbin  = geometry->NN();
      fShortHead.fPhiR  = geometry->ReactionPlaneAngle();
    }
    // --- Determine diffraction flags -------------------------------
    Bool_t sd = false;
    Bool_t dd = false;
    if (pythia) {
      Int_t type = pythia->ProcessType();
      if (type < 100) { // pythia6
	switch (type) {
	case 92: case 93: sd = true; break;
	case 94:          dd = true; break;
	}
      }
      else {
	switch (type) { // Pythia8
	case 103: case 104: sd = true; break;
	case 105:           dd = true; break;
	}
      }
      fShortHead.fB     = pythia->GetImpactParameter();
      fShortHead.fNpart = 2;
      fShortHead.fNbin  = 1;
    }
    if (dpm) {
      Int_t type = dpm->ProcessType();
      switch (type) {
      case 5: case 6: sd = true;

      case 7:         dd = true;
      }
    }
    if (gev) fShortHead.fPhiR = gev->GetEventPlane();
    if (herwig) {
      Int_t type = herwig->ProcessType();
      switch (type) {
      case 5: case 6: sd = true; break;
      }
      fShortHead.fNpart = 2;
      fShortHead.fNbin  = 1;
    }
    fShortHead.fType = (sd ? 0x1 : 0) | (dd ? 0x2 : 0);

    // --- Check centrality -----------------------------------------
    // PbPb only 
    // Updated 4th of November 2014 from 
    // cern.ch/twiki/bin/view/ALICE/CentStudies#Tables_with_centrality_bins_AN1
    Float_t  np = 0;
    UInt_t   nc = 0;
    Double_t c  = -1;
    Double_t b  = fShortHead.fB;
    if (b >= 0) {
      if      (0.00 >= b  && b < 1.57)  { c=0.5;  np=403.8; nc=1861; } 
      else if (1.57 >= b  && b < 2.22)  { c=1.5;  np=393.6; nc=1766; } 
      else if (2.22 >= b  && b < 2.71)  { c=2.5;  np=382.9; nc=1678; } 
      else if (2.71 >= b  && b < 3.13)  { c=3.5;  np=372;   nc=1597; }  
      else if (3.13 >= b  && b < 3.50)  { c=4.5;  np=361.1; nc=1520; } 
      else if (3.50 >= b  && b < 4.94)  { c=7.5;  np=329.4; nc=1316; } 
      else if (4.94 >= b  && b < 6.05)  { c=12.5; np=281.2; nc=1032; } 
      else if (6.05 >= b  && b < 6.98)  { c=17.5; np=239;   nc=809.8; }
      else if (6.98 >= b  && b < 7.81)  { c=22.5; np=202.1; nc=629.6; }
      else if (7.81 >= b  && b < 8.55)  { c=27.5; np=169.5; nc=483.7; }
      else if (8.55 >= b  && b < 9.23)  { c=32.5; np=141;   nc=366.7; }
      else if (9.23 >= b  && b < 9.88)  { c=37.5; np=116;   nc=273.4; }
      else if (9.88 >= b  && b < 10.47) { c=42.5; np=94.11; nc=199.4; } 
      else if (10.47 >= b && b < 11.04) { c=47.5; np=75.3;  nc=143.1; } 
      else if (11.04 >= b && b < 11.58) { c=52.5; np=59.24; nc=100.1; }
      else if (11.58 >= b && b < 12.09) { c=57.5; np=45.58; nc=68.46; }
      else if (12.09 >= b && b < 12.58) { c=62.5; np=34.33; nc=45.79; }
      else if (12.58 >= b && b < 13.05) { c=67.5; np=25.21; nc=29.92; }
      else if (13.05 >= b && b < 13.52) { c=72.5; np=17.96; nc=19.08; }
      else if (13.52 >= b && b < 13.97) { c=77.5; np=12.58; nc=12.07; }
      else if (13.97 >= b && b < 14.43) { c=82.5; np=8.812; nc=7.682; }
      else if (14.43 >= b && b < 14.96) { c=87.5; np=6.158; nc=4.904; }
      else if (14.96 >= b && b < 15.67) { c=92.5; np=4.376; nc=3.181; }
      else if (15.67 >= b && b < 20.00) { c=97.5; np=3.064; nc=1.994; }
      fShortHead.fC = c;
      // Be careful to round off
      if (fShortHead.fNpart <= 0) fShortHead.fNpart = Int_t(np+.5);
      if (fShortHead.fNbin  <= 0) fShortHead.fNbin  = Int_t(nc+.5)/2;
    }
    
    // --- Check if within vertex cut -------------------------------
    Bool_t selected = (fShortHead.fIpZ <= fHIpz->GetXaxis()->GetXmax() &&
		       fShortHead.fIpZ >= fHIpz->GetXaxis()->GetXmin());

    // --- Only update histograms if within IPz cut ------------------
    if (selected) {
      fHPhiR->Fill(fShortHead.fPhiR*TMath::RadToDeg());
      fHB->Fill(fShortHead.fB);
      fHIpz->Fill(fShortHead.fIpZ);
      fHType->Fill(dd ? 3 : sd ? 2 : 1);
      fHCent->Fill(c);
    }
    return selected;
  }
  /** 
   * Process all particles 
   * 
   * @param selected True if particle information should be diagnosed
   * 
   * @return true on success
   */
  Bool_t ProcessParticles(Bool_t selected)
  {
    Int_t nPart = fStack->GetNprimary();
    for (Int_t iPart = 0; iPart < nPart; iPart++) {
      TParticle*    particle  = fStack->Particle(iPart);
      TParticlePDG* pdg       = particle->GetPDG();
      Bool_t        primary   = fStack->IsPhysicalPrimary(iPart);
      Bool_t        weakDecay = fStack->IsSecondaryFromWeakDecay(iPart);
      Bool_t        charged   = (pdg &&  TMath::Abs(pdg->Charge()) > 0);
      if (primary)   particle->SetBit(BIT(14));
      if (weakDecay) particle->SetBit(BIT(15));
      if (charged)   particle->SetBit(BIT(16));

      new ((*fParticles)[iPart]) TParticle(*particle);

      if (!selected || !charged || !primary) continue;
      Double_t pT    = particle->Pt();
      if (pT < 1e-10) continue; /// Along beam axis 
      Double_t pZ    = particle->Pz();
      Double_t theta = TMath::ATan2(pT, pZ);
      Double_t eta   = -TMath::Log(TMath::Tan(theta/2));
      fHEta->Fill(eta);
    }
    return true;
  }
  /** 
   * Do final event processing (fill output)
   * 
   */
  void PostEvent()
  {
    fHeader->SetNprimary(fStack->GetNprimary());
    fHeader->SetNtrack(fStack->GetNtrack());

    fTree->Fill();
    
    fStack->FinishEvent();
    fHeader->SetStack(fStack);
    
    fRunLoader->TreeE()->Fill();
    fRunLoader->WriteKinematics("OVERWRITE");
  }    
  /** 
   * Process one event 
   * 
   * @param iEv Event number 
   * 
   * @return true on success, false otherwize 
   */
  Bool_t Process(Long64_t iEv)
  {
    // --- The stopwatch ---------------------------------------------
    TStopwatch timer;
    timer.Start();
    PreEvent(iEv);
    fHTime->Fill(1, timer.RealTime());
    
    // --- Generate event --------------------------------------------
    timer.Start();
    fGenerator->Generate();
    fHTime->Fill(2, timer.RealTime());

    // --- Process the header ----------------------------------------
    timer.Start();
    Bool_t selected = ProcessHeader();
    fHTime->Fill(3, timer.RealTime());
    
    // --- Loop over particles ---------------------------------------
    timer.Start();
    ProcessParticles(selected);
    fHTime->Fill(4, timer.RealTime());

    // --- Do final stuff --------------------------------------------    
    timer.Start();
    PostEvent();
    fHTime->Fill(5, timer.RealTime());

    return true;
  }
  /** 
   * Finalize this sub-job  
   * 
   */
  void SlaveTerminate()
  {
    fGenerator->FinishRun();
    fRunLoader->WriteHeader("OVERWRITE");
    fGenerator->Write();
    fRunLoader->Write();

    if (fFile) {
      if (fProofFile) {
	fOutput->Add(fProofFile);
	fOutput->Add(new TH1F("filename", fFileName.Data(),1,0,1));
      }
      // Flush out tree 
      fFile->cd();
      fTree->Write(0, TObject::kOverwrite);
      fFile->Close();
      fFile->Delete();
      fFile = 0;
    }
  }
  /** 
   * Final processing of the data 
   * 
   */
  void Terminate()
  {
    if (gProof) gProof->ClearFeedback();
    
    if (!fList)
      fList = static_cast<TList*>(fOutput->FindObject("histograms"));
    if (!fList) {
      Error("Terminate", "No output list");
      return;
    }
    
    if (!fProofFile) {
      TObject* fn = fOutput->FindObject("filename");
      if (fn) fFileName  = fn->GetTitle();
      fProofFile =
	static_cast<TProofOutputFile*>(fOutput->FindObject(FileName()));
    }
    if (fProofFile) 
      fFile = fProofFile->OpenFile("UPDATE");
    if (!fFile)
      fFile = TFile::Open(FileName(),"UPDATE");
    
	
    fHEta  = static_cast<TH1*>(fList->FindObject("dNdeta"));
    fHIpz  = static_cast<TH1*>(fList->FindObject("ipZ"));
    fHType = static_cast<TH1*>(fList->FindObject("type"));
    fHCent = static_cast<TH1*>(fList->FindObject("cent"));
    fHB    = static_cast<TH1*>(fList->FindObject("b"));
    fHPhiR = static_cast<TH1*>(fList->FindObject("phiR"));
    fHTime = static_cast<TH1*>(fList->FindObject("timing"));

    if (!(fHEta && fHIpz && fHType && fHB && fHPhiR && fHTime)) {
      Warning("Terminate", "Missing histograms (%p,%p,%p,%p,%p,%p)",
	      fHEta, fHIpz, fHType, fHB, fHPhiR, fHTime);
      return;
    }

    Int_t nTotal = fHIpz->GetEntries();
    fHEta ->Scale(1./nTotal, "width");
    fHB   ->Scale(1./nTotal, "width");
    fHPhiR->Scale(1./nTotal, "width");
    fHTime->Scale(1./nTotal, "width");

    if (!fFile){
      Warning("Terminate", "No file to write to");
      return;
    }

    fHEta ->Write();
    fHIpz ->Write();
    fHType->Write();
    fHCent->Write();
    fHB   ->Write();
    fHPhiR->Write();
    fHTime->Write();

    fTree = static_cast<TTree*>(fFile->Get("T"));
    if (!fTree)  Warning("Terminate", "No tree");
    
    fFile->Close();
  }
  /** 
   * Interface version used 
   * 
   * @return 1
   */
  Int_t Version() const { return 1; }

  /**
   * @{ 
   * @name Parameters 
   */
  TString  fEGName;               // Name of event generator
  Int_t    fRunNo;                // Run to simulate 
  Double_t fBMin;                 // Least impact parameter 
  Double_t fBMax;                 // Largest impact parameter
  TObject* fGRP;                  //! GRP in one line
  Long64_t fNEvents;              //  Number of requested events
  /* @} */
  /** 
   * @{ 
   * @name ALICE EG interface 
   */
  AliGenerator* fGenerator;       //! Event generator
  AliRunLoader* fRunLoader;       //! Loader of trees
  AliStack*     fStack;           //! Stack of particles
  AliHeader*    fHeader;          //! Header handler
  /* @} */
  /** 
   * @{ 
   * @name Custom output 
   */
  TTree*        fTree;            //! Custom tree 
  TClonesArray* fParticles;       //! List of particles
  /**
   * @{ 
   * @name Diagnostics 
   */
  TList* fList;                   //! List of outputs
  TH1*   fHEta;                   //! dN/deta
  TH1*   fHIpz;                   //! IPz histogram
  TH1*   fHType;                  //! Event type histogram
  TH1*   fHCent;                  //! Event type histogram
  TH1*   fHB;                     //! B histogram
  TH1*   fHPhiR;                  //! Reaction plane
  TH1*   fHTime;                  //! Timing 
  /* @} */
  /**
   * @{ 
   * @name Output files 
   */
  TProofOutputFile* fProofFile;   //! Proof output file 
  TFile*            fFile;        //! Output file
  mutable TString   fFileName;    //! Output file name 
  /* @} */

  // Hide from CINT 
#ifndef __CINT__
  struct ShortHeader {
    UInt_t   fRunNo;
    UInt_t   fEventId;
    UInt_t   fNpart;
    UInt_t   fNbin;
    UInt_t   fType;
    Double_t fIpX;
    Double_t fIpY;
    Double_t fIpZ;
    Double_t fB;
    Double_t fC;
    Double_t fPhiR;
  } fShortHead;
#endif
  /** 
   * Run this selector as a normal process
   * 
   * @param nev        Number of events
   * @param run        Run number to anchor in
   * @param gen        Generator 
   * @param bMin       Least impact parameter [fm]
   * @param bMax       Largest impact parameter [fm]
   * @param monitor    Monitor frequency [s]
   * 
   * @return true on succes
   */
  static Bool_t  Run(Long64_t    nev,
		     UInt_t      run,
		     const char* gen,
		     Double_t    bMin,
		     Double_t    bMax,
		     Int_t       monitor)
  {
    TStopwatch stopwatch;
    stopwatch.Start();
    
    FastSim* sim = new FastSim(gen,run,bMin,bMax,nev);
    sim->Begin(0);
    sim->SlaveBegin(0);

    TTimer* timer = 0;
    if (monitor > 0) {
      // timer = new TTimer(new FastMonitor(sim), monitor*1000,true);
      timer = new TTimer(1000);
      timer->Connect("Timeout()","FastMonitor",
		     new FastMonitor(sim), "Handle()");
      ::Info("Run", "Turning on monitoring");
      timer->Start(-1,false);
    }
      
    for (Long64_t i=0; i <nev; i++) {
      Printf("=== Event # %6lld/%6lld ==========================",
	     i+1, nev);
      sim->Process(i);
      if (timer && (i > 0) && (i % 500 == 0)) {
	if (timer->CheckTimer(gSystem->Now()))
	  Printf("Fired timer");
      }
    }
    if (timer) timer->TurnOff();
    sim->SlaveTerminate();
    sim->Terminate();

    stopwatch.Print();
    return true;
  }
  static void ProofLoadLibs()
  {
    if (!gProof) return;

    // Remember to copy changes to RunFast.C
    TList clsLib;
    clsLib.Add(new TNamed("TVirtualMC",              "libVMC"));
    clsLib.Add(new TNamed("TLorentzVector",          "libPhysics"));
    clsLib.Add(new TNamed("TLinearFitter",           "libMinuit"));
    clsLib.Add(new TNamed("TTree",                   "libTree"));
    clsLib.Add(new TNamed("TProof",                  "libProof"));
    clsLib.Add(new TNamed("TGFrame",                 "libGui"));
    clsLib.Add(new TNamed("TSAXParser",              "libXMLParser"));
    clsLib.Add(new TNamed("AliVEvent",               "libSTEERBase"));
    clsLib.Add(new TNamed("AliESDEvent",             "libESD"));
    clsLib.Add(new TNamed("AliAODEvent",             "libAOD"));
    clsLib.Add(new TNamed("AliAnalysisManager",      "libANALYSIS"));
    clsLib.Add(new TNamed("AliCDBManager",           "libCDB"));
    clsLib.Add(new TNamed("AliRawVEvent",            "libRAWDatabase"));
    clsLib.Add(new TNamed("AliHit",                  "libSTEER"));
    clsLib.Add(new TNamed("AliGenMC",                "libEVGEN"));
    clsLib.Add(new TNamed("AliFastEvent",            "libFASTSIM"));

    TIter next(&clsLib);
    TObject* obj = 0;
    while ((obj = next())) {
      gProof->Exec(Form("gROOT->LoadClass(\"%s\",\"%s\");",
			obj->GetName(), obj->GetTitle()));
    }
  }
  /** 
   * Run this selector in PROOF(Lite)
   * 
   * @param url        Proof URL
   * @param nev        Number of events
   * @param run        Run number to anchor in
   * @param gen        Generator 
   * @param bMin       Least impact parameter [fm]
   * @param bMax       Largest impact parameter [fm]
   * @param monitor    Monitor frequency [s]
   * @param opt        Compilation options
   * 
   * @return true on succes
   */
  static Bool_t Proof(const char*  url,
		      Long64_t     nev,
		      UInt_t       run,
		      const char*  gen,
		      Double_t     bMin,
		      Double_t     bMax,
		      Int_t        monitor=-1,
		      const char*  opt="")
  {
    Printf("# events:  %lld", nev);
    Printf("Run #:     %u",   run);
    Printf("Generator: %s",   gen);
    Printf("b range:   %5.1f-%5.1f", bMin, bMax);
    Printf("monitor:   %ds", monitor);
	   
    TStopwatch timer;
    timer.Start();
    
    TProof::Reset(url);
    TProof::Open(url);
    gProof->ClearCache();

    TString ali = gSystem->ExpandPathName("$(ALICE_ROOT)");
    // TString fwd = gSystem->ExpandPathName("$ANA_SRC");
    TString fwd = ali + "/PWGLF/FORWARD/analysis2";

    gProof->AddIncludePath(Form("%s/include", ali.Data()));
    ProofLoadLibs();
    gProof->Load(Form("%s/sim/GRP.C",fwd.Data()), true);
    gProof->Load(Form("%s/sim/BaseConfig.C",fwd.Data()), true);
    gProof->Load(Form("%s/sim/EGConfig.C",fwd.Data()), true);

    // gROOT->ProcessLine("gProof->SetLogLevel(5);");
    gProof->Load(Form("%s/sim/FastSim.C+%s", fwd.Data(), opt),true);

    if (monitor <= 0) gProof->SetParameter("NOMONITOR", true/*ignored*/);
    else              gProof->SetParameter("PROOF_FeedbackPeriod",
					   monitor*1000/*ms*/);

    FastSim* sim = new FastSim(gen,run,bMin,bMax,nev);
    gProof->Process(sim, nev, "");

    timer.Print();
    return true; // status >= 0;
  }
  ClassDef(FastSim,1); 
};

#endif
//
// EOF
// 
 FastSim.C:1
 FastSim.C:2
 FastSim.C:3
 FastSim.C:4
 FastSim.C:5
 FastSim.C:6
 FastSim.C:7
 FastSim.C:8
 FastSim.C:9
 FastSim.C:10
 FastSim.C:11
 FastSim.C:12
 FastSim.C:13
 FastSim.C:14
 FastSim.C:15
 FastSim.C:16
 FastSim.C:17
 FastSim.C:18
 FastSim.C:19
 FastSim.C:20
 FastSim.C:21
 FastSim.C:22
 FastSim.C:23
 FastSim.C:24
 FastSim.C:25
 FastSim.C:26
 FastSim.C:27
 FastSim.C:28
 FastSim.C:29
 FastSim.C:30
 FastSim.C:31
 FastSim.C:32
 FastSim.C:33
 FastSim.C:34
 FastSim.C:35
 FastSim.C:36
 FastSim.C:37
 FastSim.C:38
 FastSim.C:39
 FastSim.C:40
 FastSim.C:41
 FastSim.C:42
 FastSim.C:43
 FastSim.C:44
 FastSim.C:45
 FastSim.C:46
 FastSim.C:47
 FastSim.C:48
 FastSim.C:49
 FastSim.C:50
 FastSim.C:51
 FastSim.C:52
 FastSim.C:53
 FastSim.C:54
 FastSim.C:55
 FastSim.C:56
 FastSim.C:57
 FastSim.C:58
 FastSim.C:59
 FastSim.C:60
 FastSim.C:61
 FastSim.C:62
 FastSim.C:63
 FastSim.C:64
 FastSim.C:65
 FastSim.C:66
 FastSim.C:67
 FastSim.C:68
 FastSim.C:69
 FastSim.C:70
 FastSim.C:71
 FastSim.C:72
 FastSim.C:73
 FastSim.C:74
 FastSim.C:75
 FastSim.C:76
 FastSim.C:77
 FastSim.C:78
 FastSim.C:79
 FastSim.C:80
 FastSim.C:81
 FastSim.C:82
 FastSim.C:83
 FastSim.C:84
 FastSim.C:85
 FastSim.C:86
 FastSim.C:87
 FastSim.C:88
 FastSim.C:89
 FastSim.C:90
 FastSim.C:91
 FastSim.C:92
 FastSim.C:93
 FastSim.C:94
 FastSim.C:95
 FastSim.C:96
 FastSim.C:97
 FastSim.C:98
 FastSim.C:99
 FastSim.C:100
 FastSim.C:101
 FastSim.C:102
 FastSim.C:103
 FastSim.C:104
 FastSim.C:105
 FastSim.C:106
 FastSim.C:107
 FastSim.C:108
 FastSim.C:109
 FastSim.C:110
 FastSim.C:111
 FastSim.C:112
 FastSim.C:113
 FastSim.C:114
 FastSim.C:115
 FastSim.C:116
 FastSim.C:117
 FastSim.C:118
 FastSim.C:119
 FastSim.C:120
 FastSim.C:121
 FastSim.C:122
 FastSim.C:123
 FastSim.C:124
 FastSim.C:125
 FastSim.C:126
 FastSim.C:127
 FastSim.C:128
 FastSim.C:129
 FastSim.C:130
 FastSim.C:131
 FastSim.C:132
 FastSim.C:133
 FastSim.C:134
 FastSim.C:135
 FastSim.C:136
 FastSim.C:137
 FastSim.C:138
 FastSim.C:139
 FastSim.C:140
 FastSim.C:141
 FastSim.C:142
 FastSim.C:143
 FastSim.C:144
 FastSim.C:145
 FastSim.C:146
 FastSim.C:147
 FastSim.C:148
 FastSim.C:149
 FastSim.C:150
 FastSim.C:151
 FastSim.C:152
 FastSim.C:153
 FastSim.C:154
 FastSim.C:155
 FastSim.C:156
 FastSim.C:157
 FastSim.C:158
 FastSim.C:159
 FastSim.C:160
 FastSim.C:161
 FastSim.C:162
 FastSim.C:163
 FastSim.C:164
 FastSim.C:165
 FastSim.C:166
 FastSim.C:167
 FastSim.C:168
 FastSim.C:169
 FastSim.C:170
 FastSim.C:171
 FastSim.C:172
 FastSim.C:173
 FastSim.C:174
 FastSim.C:175
 FastSim.C:176
 FastSim.C:177
 FastSim.C:178
 FastSim.C:179
 FastSim.C:180
 FastSim.C:181
 FastSim.C:182
 FastSim.C:183
 FastSim.C:184
 FastSim.C:185
 FastSim.C:186
 FastSim.C:187
 FastSim.C:188
 FastSim.C:189
 FastSim.C:190
 FastSim.C:191
 FastSim.C:192
 FastSim.C:193
 FastSim.C:194
 FastSim.C:195
 FastSim.C:196
 FastSim.C:197
 FastSim.C:198
 FastSim.C:199
 FastSim.C:200
 FastSim.C:201
 FastSim.C:202
 FastSim.C:203
 FastSim.C:204
 FastSim.C:205
 FastSim.C:206
 FastSim.C:207
 FastSim.C:208
 FastSim.C:209
 FastSim.C:210
 FastSim.C:211
 FastSim.C:212
 FastSim.C:213
 FastSim.C:214
 FastSim.C:215
 FastSim.C:216
 FastSim.C:217
 FastSim.C:218
 FastSim.C:219
 FastSim.C:220
 FastSim.C:221
 FastSim.C:222
 FastSim.C:223
 FastSim.C:224
 FastSim.C:225
 FastSim.C:226
 FastSim.C:227
 FastSim.C:228
 FastSim.C:229
 FastSim.C:230
 FastSim.C:231
 FastSim.C:232
 FastSim.C:233
 FastSim.C:234
 FastSim.C:235
 FastSim.C:236
 FastSim.C:237
 FastSim.C:238
 FastSim.C:239
 FastSim.C:240
 FastSim.C:241
 FastSim.C:242
 FastSim.C:243
 FastSim.C:244
 FastSim.C:245
 FastSim.C:246
 FastSim.C:247
 FastSim.C:248
 FastSim.C:249
 FastSim.C:250
 FastSim.C:251
 FastSim.C:252
 FastSim.C:253
 FastSim.C:254
 FastSim.C:255
 FastSim.C:256
 FastSim.C:257
 FastSim.C:258
 FastSim.C:259
 FastSim.C:260
 FastSim.C:261
 FastSim.C:262
 FastSim.C:263
 FastSim.C:264
 FastSim.C:265
 FastSim.C:266
 FastSim.C:267
 FastSim.C:268
 FastSim.C:269
 FastSim.C:270
 FastSim.C:271
 FastSim.C:272
 FastSim.C:273
 FastSim.C:274
 FastSim.C:275
 FastSim.C:276
 FastSim.C:277
 FastSim.C:278
 FastSim.C:279
 FastSim.C:280
 FastSim.C:281
 FastSim.C:282
 FastSim.C:283
 FastSim.C:284
 FastSim.C:285
 FastSim.C:286
 FastSim.C:287
 FastSim.C:288
 FastSim.C:289
 FastSim.C:290
 FastSim.C:291
 FastSim.C:292
 FastSim.C:293
 FastSim.C:294
 FastSim.C:295
 FastSim.C:296
 FastSim.C:297
 FastSim.C:298
 FastSim.C:299
 FastSim.C:300
 FastSim.C:301
 FastSim.C:302
 FastSim.C:303
 FastSim.C:304
 FastSim.C:305
 FastSim.C:306
 FastSim.C:307
 FastSim.C:308
 FastSim.C:309
 FastSim.C:310
 FastSim.C:311
 FastSim.C:312
 FastSim.C:313
 FastSim.C:314
 FastSim.C:315
 FastSim.C:316
 FastSim.C:317
 FastSim.C:318
 FastSim.C:319
 FastSim.C:320
 FastSim.C:321
 FastSim.C:322
 FastSim.C:323
 FastSim.C:324
 FastSim.C:325
 FastSim.C:326
 FastSim.C:327
 FastSim.C:328
 FastSim.C:329
 FastSim.C:330
 FastSim.C:331
 FastSim.C:332
 FastSim.C:333
 FastSim.C:334
 FastSim.C:335
 FastSim.C:336
 FastSim.C:337
 FastSim.C:338
 FastSim.C:339
 FastSim.C:340
 FastSim.C:341
 FastSim.C:342
 FastSim.C:343
 FastSim.C:344
 FastSim.C:345
 FastSim.C:346
 FastSim.C:347
 FastSim.C:348
 FastSim.C:349
 FastSim.C:350
 FastSim.C:351
 FastSim.C:352
 FastSim.C:353
 FastSim.C:354
 FastSim.C:355
 FastSim.C:356
 FastSim.C:357
 FastSim.C:358
 FastSim.C:359
 FastSim.C:360
 FastSim.C:361
 FastSim.C:362
 FastSim.C:363
 FastSim.C:364
 FastSim.C:365
 FastSim.C:366
 FastSim.C:367
 FastSim.C:368
 FastSim.C:369
 FastSim.C:370
 FastSim.C:371
 FastSim.C:372
 FastSim.C:373
 FastSim.C:374
 FastSim.C:375
 FastSim.C:376
 FastSim.C:377
 FastSim.C:378
 FastSim.C:379
 FastSim.C:380
 FastSim.C:381
 FastSim.C:382
 FastSim.C:383
 FastSim.C:384
 FastSim.C:385
 FastSim.C:386
 FastSim.C:387
 FastSim.C:388
 FastSim.C:389
 FastSim.C:390
 FastSim.C:391
 FastSim.C:392
 FastSim.C:393
 FastSim.C:394
 FastSim.C:395
 FastSim.C:396
 FastSim.C:397
 FastSim.C:398
 FastSim.C:399
 FastSim.C:400
 FastSim.C:401
 FastSim.C:402
 FastSim.C:403
 FastSim.C:404
 FastSim.C:405
 FastSim.C:406
 FastSim.C:407
 FastSim.C:408
 FastSim.C:409
 FastSim.C:410
 FastSim.C:411
 FastSim.C:412
 FastSim.C:413
 FastSim.C:414
 FastSim.C:415
 FastSim.C:416
 FastSim.C:417
 FastSim.C:418
 FastSim.C:419
 FastSim.C:420
 FastSim.C:421
 FastSim.C:422
 FastSim.C:423
 FastSim.C:424
 FastSim.C:425
 FastSim.C:426
 FastSim.C:427
 FastSim.C:428
 FastSim.C:429
 FastSim.C:430
 FastSim.C:431
 FastSim.C:432
 FastSim.C:433
 FastSim.C:434
 FastSim.C:435
 FastSim.C:436
 FastSim.C:437
 FastSim.C:438
 FastSim.C:439
 FastSim.C:440
 FastSim.C:441
 FastSim.C:442
 FastSim.C:443
 FastSim.C:444
 FastSim.C:445
 FastSim.C:446
 FastSim.C:447
 FastSim.C:448
 FastSim.C:449
 FastSim.C:450
 FastSim.C:451
 FastSim.C:452
 FastSim.C:453
 FastSim.C:454
 FastSim.C:455
 FastSim.C:456
 FastSim.C:457
 FastSim.C:458
 FastSim.C:459
 FastSim.C:460
 FastSim.C:461
 FastSim.C:462
 FastSim.C:463
 FastSim.C:464
 FastSim.C:465
 FastSim.C:466
 FastSim.C:467
 FastSim.C:468
 FastSim.C:469
 FastSim.C:470
 FastSim.C:471
 FastSim.C:472
 FastSim.C:473
 FastSim.C:474
 FastSim.C:475
 FastSim.C:476
 FastSim.C:477
 FastSim.C:478
 FastSim.C:479
 FastSim.C:480
 FastSim.C:481
 FastSim.C:482
 FastSim.C:483
 FastSim.C:484
 FastSim.C:485
 FastSim.C:486
 FastSim.C:487
 FastSim.C:488
 FastSim.C:489
 FastSim.C:490
 FastSim.C:491
 FastSim.C:492
 FastSim.C:493
 FastSim.C:494
 FastSim.C:495
 FastSim.C:496
 FastSim.C:497
 FastSim.C:498
 FastSim.C:499
 FastSim.C:500
 FastSim.C:501
 FastSim.C:502
 FastSim.C:503
 FastSim.C:504
 FastSim.C:505
 FastSim.C:506
 FastSim.C:507
 FastSim.C:508
 FastSim.C:509
 FastSim.C:510
 FastSim.C:511
 FastSim.C:512
 FastSim.C:513
 FastSim.C:514
 FastSim.C:515
 FastSim.C:516
 FastSim.C:517
 FastSim.C:518
 FastSim.C:519
 FastSim.C:520
 FastSim.C:521
 FastSim.C:522
 FastSim.C:523
 FastSim.C:524
 FastSim.C:525
 FastSim.C:526
 FastSim.C:527
 FastSim.C:528
 FastSim.C:529
 FastSim.C:530
 FastSim.C:531
 FastSim.C:532
 FastSim.C:533
 FastSim.C:534
 FastSim.C:535
 FastSim.C:536
 FastSim.C:537
 FastSim.C:538
 FastSim.C:539
 FastSim.C:540
 FastSim.C:541
 FastSim.C:542
 FastSim.C:543
 FastSim.C:544
 FastSim.C:545
 FastSim.C:546
 FastSim.C:547
 FastSim.C:548
 FastSim.C:549
 FastSim.C:550
 FastSim.C:551
 FastSim.C:552
 FastSim.C:553
 FastSim.C:554
 FastSim.C:555
 FastSim.C:556
 FastSim.C:557
 FastSim.C:558
 FastSim.C:559
 FastSim.C:560
 FastSim.C:561
 FastSim.C:562
 FastSim.C:563
 FastSim.C:564
 FastSim.C:565
 FastSim.C:566
 FastSim.C:567
 FastSim.C:568
 FastSim.C:569
 FastSim.C:570
 FastSim.C:571
 FastSim.C:572
 FastSim.C:573
 FastSim.C:574
 FastSim.C:575
 FastSim.C:576
 FastSim.C:577
 FastSim.C:578
 FastSim.C:579
 FastSim.C:580
 FastSim.C:581
 FastSim.C:582
 FastSim.C:583
 FastSim.C:584
 FastSim.C:585
 FastSim.C:586
 FastSim.C:587
 FastSim.C:588
 FastSim.C:589
 FastSim.C:590
 FastSim.C:591
 FastSim.C:592
 FastSim.C:593
 FastSim.C:594
 FastSim.C:595
 FastSim.C:596
 FastSim.C:597
 FastSim.C:598
 FastSim.C:599
 FastSim.C:600
 FastSim.C:601
 FastSim.C:602
 FastSim.C:603
 FastSim.C:604
 FastSim.C:605
 FastSim.C:606
 FastSim.C:607
 FastSim.C:608
 FastSim.C:609
 FastSim.C:610
 FastSim.C:611
 FastSim.C:612
 FastSim.C:613
 FastSim.C:614
 FastSim.C:615
 FastSim.C:616
 FastSim.C:617
 FastSim.C:618
 FastSim.C:619
 FastSim.C:620
 FastSim.C:621
 FastSim.C:622
 FastSim.C:623
 FastSim.C:624
 FastSim.C:625
 FastSim.C:626
 FastSim.C:627
 FastSim.C:628
 FastSim.C:629
 FastSim.C:630
 FastSim.C:631
 FastSim.C:632
 FastSim.C:633
 FastSim.C:634
 FastSim.C:635
 FastSim.C:636
 FastSim.C:637
 FastSim.C:638
 FastSim.C:639
 FastSim.C:640
 FastSim.C:641
 FastSim.C:642
 FastSim.C:643
 FastSim.C:644
 FastSim.C:645
 FastSim.C:646
 FastSim.C:647
 FastSim.C:648
 FastSim.C:649
 FastSim.C:650
 FastSim.C:651
 FastSim.C:652
 FastSim.C:653
 FastSim.C:654
 FastSim.C:655
 FastSim.C:656
 FastSim.C:657
 FastSim.C:658
 FastSim.C:659
 FastSim.C:660
 FastSim.C:661
 FastSim.C:662
 FastSim.C:663
 FastSim.C:664
 FastSim.C:665
 FastSim.C:666
 FastSim.C:667
 FastSim.C:668
 FastSim.C:669
 FastSim.C:670
 FastSim.C:671
 FastSim.C:672
 FastSim.C:673
 FastSim.C:674
 FastSim.C:675
 FastSim.C:676
 FastSim.C:677
 FastSim.C:678
 FastSim.C:679
 FastSim.C:680
 FastSim.C:681
 FastSim.C:682
 FastSim.C:683
 FastSim.C:684
 FastSim.C:685
 FastSim.C:686
 FastSim.C:687
 FastSim.C:688
 FastSim.C:689
 FastSim.C:690
 FastSim.C:691
 FastSim.C:692
 FastSim.C:693
 FastSim.C:694
 FastSim.C:695
 FastSim.C:696
 FastSim.C:697
 FastSim.C:698
 FastSim.C:699
 FastSim.C:700
 FastSim.C:701
 FastSim.C:702
 FastSim.C:703
 FastSim.C:704
 FastSim.C:705
 FastSim.C:706
 FastSim.C:707
 FastSim.C:708
 FastSim.C:709
 FastSim.C:710
 FastSim.C:711
 FastSim.C:712
 FastSim.C:713
 FastSim.C:714
 FastSim.C:715
 FastSim.C:716
 FastSim.C:717
 FastSim.C:718
 FastSim.C:719
 FastSim.C:720
 FastSim.C:721
 FastSim.C:722
 FastSim.C:723
 FastSim.C:724
 FastSim.C:725
 FastSim.C:726
 FastSim.C:727
 FastSim.C:728
 FastSim.C:729
 FastSim.C:730
 FastSim.C:731
 FastSim.C:732
 FastSim.C:733
 FastSim.C:734
 FastSim.C:735
 FastSim.C:736
 FastSim.C:737
 FastSim.C:738
 FastSim.C:739
 FastSim.C:740
 FastSim.C:741
 FastSim.C:742
 FastSim.C:743
 FastSim.C:744
 FastSim.C:745
 FastSim.C:746
 FastSim.C:747
 FastSim.C:748
 FastSim.C:749
 FastSim.C:750
 FastSim.C:751
 FastSim.C:752
 FastSim.C:753
 FastSim.C:754
 FastSim.C:755
 FastSim.C:756
 FastSim.C:757
 FastSim.C:758
 FastSim.C:759
 FastSim.C:760
 FastSim.C:761
 FastSim.C:762
 FastSim.C:763
 FastSim.C:764
 FastSim.C:765
 FastSim.C:766
 FastSim.C:767
 FastSim.C:768
 FastSim.C:769
 FastSim.C:770
 FastSim.C:771
 FastSim.C:772
 FastSim.C:773
 FastSim.C:774
 FastSim.C:775
 FastSim.C:776
 FastSim.C:777
 FastSim.C:778
 FastSim.C:779
 FastSim.C:780
 FastSim.C:781
 FastSim.C:782
 FastSim.C:783
 FastSim.C:784
 FastSim.C:785
 FastSim.C:786
 FastSim.C:787
 FastSim.C:788
 FastSim.C:789
 FastSim.C:790
 FastSim.C:791
 FastSim.C:792
 FastSim.C:793
 FastSim.C:794
 FastSim.C:795
 FastSim.C:796
 FastSim.C:797
 FastSim.C:798
 FastSim.C:799
 FastSim.C:800
 FastSim.C:801
 FastSim.C:802
 FastSim.C:803
 FastSim.C:804
 FastSim.C:805
 FastSim.C:806
 FastSim.C:807
 FastSim.C:808
 FastSim.C:809
 FastSim.C:810
 FastSim.C:811
 FastSim.C:812
 FastSim.C:813
 FastSim.C:814
 FastSim.C:815
 FastSim.C:816
 FastSim.C:817
 FastSim.C:818
 FastSim.C:819
 FastSim.C:820
 FastSim.C:821
 FastSim.C:822
 FastSim.C:823
 FastSim.C:824
 FastSim.C:825
 FastSim.C:826
 FastSim.C:827
 FastSim.C:828
 FastSim.C:829
 FastSim.C:830
 FastSim.C:831
 FastSim.C:832
 FastSim.C:833
 FastSim.C:834
 FastSim.C:835
 FastSim.C:836
 FastSim.C:837
 FastSim.C:838
 FastSim.C:839
 FastSim.C:840
 FastSim.C:841
 FastSim.C:842
 FastSim.C:843
 FastSim.C:844
 FastSim.C:845
 FastSim.C:846
 FastSim.C:847
 FastSim.C:848
 FastSim.C:849
 FastSim.C:850
 FastSim.C:851
 FastSim.C:852
 FastSim.C:853
 FastSim.C:854
 FastSim.C:855
 FastSim.C:856
 FastSim.C:857
 FastSim.C:858
 FastSim.C:859
 FastSim.C:860
 FastSim.C:861
 FastSim.C:862
 FastSim.C:863
 FastSim.C:864
 FastSim.C:865
 FastSim.C:866
 FastSim.C:867
 FastSim.C:868
 FastSim.C:869
 FastSim.C:870
 FastSim.C:871
 FastSim.C:872
 FastSim.C:873
 FastSim.C:874
 FastSim.C:875
 FastSim.C:876
 FastSim.C:877
 FastSim.C:878
 FastSim.C:879
 FastSim.C:880
 FastSim.C:881
 FastSim.C:882
 FastSim.C:883
 FastSim.C:884
 FastSim.C:885
 FastSim.C:886
 FastSim.C:887
 FastSim.C:888
 FastSim.C:889
 FastSim.C:890
 FastSim.C:891
 FastSim.C:892
 FastSim.C:893
 FastSim.C:894
 FastSim.C:895
 FastSim.C:896
 FastSim.C:897
 FastSim.C:898
 FastSim.C:899
 FastSim.C:900
 FastSim.C:901
 FastSim.C:902
 FastSim.C:903
 FastSim.C:904
 FastSim.C:905
 FastSim.C:906
 FastSim.C:907
 FastSim.C:908
 FastSim.C:909
 FastSim.C:910
 FastSim.C:911
 FastSim.C:912
 FastSim.C:913
 FastSim.C:914
 FastSim.C:915
 FastSim.C:916
 FastSim.C:917
 FastSim.C:918
 FastSim.C:919
 FastSim.C:920
 FastSim.C:921
 FastSim.C:922
 FastSim.C:923
 FastSim.C:924
 FastSim.C:925
 FastSim.C:926
 FastSim.C:927
 FastSim.C:928
 FastSim.C:929
 FastSim.C:930
 FastSim.C:931
 FastSim.C:932
 FastSim.C:933
 FastSim.C:934
 FastSim.C:935
 FastSim.C:936
 FastSim.C:937
 FastSim.C:938
 FastSim.C:939
 FastSim.C:940
 FastSim.C:941
 FastSim.C:942
 FastSim.C:943
 FastSim.C:944
 FastSim.C:945
 FastSim.C:946
 FastSim.C:947
 FastSim.C:948
 FastSim.C:949
 FastSim.C:950
 FastSim.C:951
 FastSim.C:952
 FastSim.C:953
 FastSim.C:954
 FastSim.C:955
 FastSim.C:956
 FastSim.C:957
 FastSim.C:958
 FastSim.C:959
 FastSim.C:960
 FastSim.C:961
 FastSim.C:962
 FastSim.C:963
 FastSim.C:964
 FastSim.C:965
 FastSim.C:966
 FastSim.C:967
 FastSim.C:968
 FastSim.C:969
 FastSim.C:970
 FastSim.C:971
 FastSim.C:972
 FastSim.C:973
 FastSim.C:974
 FastSim.C:975
 FastSim.C:976
 FastSim.C:977
 FastSim.C:978
 FastSim.C:979
 FastSim.C:980
 FastSim.C:981
 FastSim.C:982
 FastSim.C:983
 FastSim.C:984
 FastSim.C:985
 FastSim.C:986
 FastSim.C:987
 FastSim.C:988
 FastSim.C:989
 FastSim.C:990
 FastSim.C:991
 FastSim.C:992
 FastSim.C:993
 FastSim.C:994
 FastSim.C:995
 FastSim.C:996
 FastSim.C:997
 FastSim.C:998
 FastSim.C:999
 FastSim.C:1000
 FastSim.C:1001
 FastSim.C:1002
 FastSim.C:1003
 FastSim.C:1004
 FastSim.C:1005
 FastSim.C:1006
 FastSim.C:1007
 FastSim.C:1008
 FastSim.C:1009
 FastSim.C:1010
 FastSim.C:1011
 FastSim.C:1012
 FastSim.C:1013
 FastSim.C:1014
 FastSim.C:1015
 FastSim.C:1016
 FastSim.C:1017
 FastSim.C:1018
 FastSim.C:1019
 FastSim.C:1020
 FastSim.C:1021
 FastSim.C:1022
 FastSim.C:1023
 FastSim.C:1024
 FastSim.C:1025
 FastSim.C:1026
 FastSim.C:1027
 FastSim.C:1028
 FastSim.C:1029
 FastSim.C:1030
 FastSim.C:1031
 FastSim.C:1032
 FastSim.C:1033
 FastSim.C:1034
 FastSim.C:1035
 FastSim.C:1036
 FastSim.C:1037
 FastSim.C:1038
 FastSim.C:1039
 FastSim.C:1040
 FastSim.C:1041
 FastSim.C:1042
 FastSim.C:1043
 FastSim.C:1044
 FastSim.C:1045
 FastSim.C:1046
 FastSim.C:1047
 FastSim.C:1048
 FastSim.C:1049
 FastSim.C:1050
 FastSim.C:1051
 FastSim.C:1052
 FastSim.C:1053
 FastSim.C:1054
 FastSim.C:1055
 FastSim.C:1056
 FastSim.C:1057
 FastSim.C:1058
 FastSim.C:1059
 FastSim.C:1060
 FastSim.C:1061
 FastSim.C:1062
 FastSim.C:1063
 FastSim.C:1064
 FastSim.C:1065
 FastSim.C:1066
 FastSim.C:1067
 FastSim.C:1068
 FastSim.C:1069
 FastSim.C:1070
 FastSim.C:1071
 FastSim.C:1072
 FastSim.C:1073
 FastSim.C:1074
 FastSim.C:1075
 FastSim.C:1076
 FastSim.C:1077
 FastSim.C:1078
 FastSim.C:1079
 FastSim.C:1080
 FastSim.C:1081
 FastSim.C:1082
 FastSim.C:1083
 FastSim.C:1084
 FastSim.C:1085
 FastSim.C:1086
 FastSim.C:1087
 FastSim.C:1088
 FastSim.C:1089
 FastSim.C:1090
 FastSim.C:1091
 FastSim.C:1092
 FastSim.C:1093
 FastSim.C:1094
 FastSim.C:1095
 FastSim.C:1096
 FastSim.C:1097
 FastSim.C:1098
 FastSim.C:1099
 FastSim.C:1100
 FastSim.C:1101
 FastSim.C:1102
 FastSim.C:1103
 FastSim.C:1104
 FastSim.C:1105
 FastSim.C:1106
 FastSim.C:1107
 FastSim.C:1108
 FastSim.C:1109
 FastSim.C:1110
 FastSim.C:1111
 FastSim.C:1112
 FastSim.C:1113
 FastSim.C:1114
 FastSim.C:1115
 FastSim.C:1116
 FastSim.C:1117
 FastSim.C:1118
 FastSim.C:1119
 FastSim.C:1120
 FastSim.C:1121
 FastSim.C:1122
 FastSim.C:1123
 FastSim.C:1124
 FastSim.C:1125
 FastSim.C:1126
 FastSim.C:1127
 FastSim.C:1128
 FastSim.C:1129
 FastSim.C:1130
 FastSim.C:1131
 FastSim.C:1132
 FastSim.C:1133
 FastSim.C:1134
 FastSim.C:1135
 FastSim.C:1136
 FastSim.C:1137
 FastSim.C:1138
 FastSim.C:1139
 FastSim.C:1140
 FastSim.C:1141
 FastSim.C:1142
 FastSim.C:1143
 FastSim.C:1144
 FastSim.C:1145
 FastSim.C:1146
 FastSim.C:1147
 FastSim.C:1148
 FastSim.C:1149
 FastSim.C:1150
 FastSim.C:1151