// main322.cc is a part of the PYTHIA event generator.
// Copyright (C) 2026 Torbjorn Sjostrand.
// PYTHIA is licenced under the GNU GPL v2 or later, see COPYING for details.
// Please respect the MCnet Guidelines, see GUIDELINES for details.

// Keywords: basic usage; biasing; pT bias

// This is a simple test program.
// It illustrates methods to emphasize generation at high pT.

#include "Pythia8/Pythia.h"

using namespace Pythia8;

//==========================================================================

int main() {

  // Different modes are illustrated for setting the pT ranges.
  // 1 - 3 are for hard jets only, 4 - 5 also for soft physics.
  // 1 : Hardcoded in the main program.
  // 2 : Using the Main:subrun keyword in a separate command file.
  // A third method instead biases selection continuously.
  // 3 : Bias high-pT selection by a pT^4 factor.
  // Matching also to low-pT processes is more complicated.
  // 4 : Matching between low- and high-pT. (No diffraction.)
  // 5: As 4, but bias high-pT selection by a pT^4 factor.
  int mode = 5;

  // Number of events to generate per bin.
  int nEvent = 10000;

  // One does not need complete events to study pThard spectrum only.
  bool completeEvents = false;

  // Optionally minimize output (almost) to final results.
  bool smallOutput = true;

  // Book histograms.
  int nRange = 100;
  double pTrange = (mode < 4) ? 1000. : 100.;
  Hist pTraw("pTHat distribution, unweighted", nRange, 0., pTrange);
  Hist pTnorm("pTHat distribution, weighted", nRange, 0., pTrange);
  Hist pTpow3("pTHat distribution, pT3*weighted", nRange, 0., pTrange);
  Hist pTpow5("pTHat distribution, pT5*weighted", nRange, 0., pTrange);
  Hist pTnormPart("pTHat distribution, weighted", nRange, 0., pTrange);
  Hist pTpow3Part("pTHat distribution, pT3*weighted", nRange, 0., pTrange);
  Hist pTpow5Part("pTHat distribution, pT5*weighted", nRange, 0., pTrange);

  // Generator.
  Pythia pythia;

  // Shorthand for some public members of pythia (also static ones).
  Settings& settings = pythia.settings;
  const Info& info = pythia.info;

  // Optionally limit output to minimal one.
  if (smallOutput) {
    pythia.readString("Init:showProcesses = off");
    pythia.readString("Init:showMultipartonInteractions = off");
    pythia.readString("Init:showChangedSettings = off");
    pythia.readString("Init:showChangedParticleData = off");
    pythia.readString("Next:numberCount = 1000000000");
    pythia.readString("Next:numberShowInfo = 0");
    pythia.readString("Next:numberShowProcess = 0");
    pythia.readString("Next:numberShowEvent = 0");
  }

  // Number of bins to use. In mode 2 read from main322.cmnd file.
  int nBin = 5;
  if (mode == 2) {
    pythia.readFile("main322.cmnd");
    nBin = pythia.mode("Main:numberOfSubruns");
  }
  else if (mode == 3) nBin = 1;
  else if (mode == 4) nBin = 4;
  else if (mode == 5) nBin = 2;

  // Mode 1: set up five pT bins - last one open-ended.
  double pTlimit[6] = {100., 150., 250., 400., 600., 0.};

  // Modes 4 & 5: set up pT bins for range [0, 100]. The lowest bin
  // is generated with soft processes, to regularize pT -> 0 blowup.
  // Warning: if pTlimitLow[1] is picked too low there will be a
  // visible discontinuity, since soft processes are generated with
  // dampening and "Sudakov" for pT -> 0, while hard processes are not.
  double pTlimitLow[6] = {0., 20., 40., 70., 100.};
  double pTlimitTwo[3] = {0., 20., 100.};

  // Loop over number of bins, i.e. number of subruns.
  for (int iBin = 0; iBin < nBin; ++iBin) {

    // Normally HardQCD, but in two cases nonDiffractive.
    // Need MPI on in nonDiffractive to get first interaction, but not else.
    if (mode > 3 && iBin == 0) {
      pythia.readString("HardQCD:all = off");
      pythia.readString("SoftQCD:nonDiffractive = on");
      if (!completeEvents) {
      pythia.readString("PartonLevel:all = on");
        pythia.readString("PartonLevel:ISR = off");
        pythia.readString("PartonLevel:FSR = off");
        pythia.readString("HadronLevel:all = off");
      }
    } else {
      pythia.readString("HardQCD:all = on");
      pythia.readString("SoftQCD:nonDiffractive = off");
      if (!completeEvents) pythia.readString("PartonLevel:all = off");
    }

    // Mode 1: hardcoded here. Use settings.parm for non-string input.
    if (mode == 1) {
      settings.parm("PhaseSpace:pTHatMin", pTlimit[iBin]);
      settings.parm("PhaseSpace:pTHatMax", pTlimit[iBin + 1]);
    }

    // Mode 2: subruns stored in the main322.cmnd file.
    else if (mode == 2) pythia.readFile("main322.cmnd", iBin);

    // Mode 3: The whole range in one step, but pT-weighted.
    else if (mode == 3) {
      settings.parm("PhaseSpace:pTHatMin", pTlimit[0]);
      settings.parm("PhaseSpace:pTHatMax", 0.);
      pythia.readString("PhaseSpace:bias2Selection = on");
      pythia.readString("PhaseSpace:bias2SelectionPow = 4.");
      pythia.readString("PhaseSpace:bias2SelectionRef = 100.");
    }

    // Mode 4: hardcoded here. Use settings.parm for non-string input.
    else if (mode == 4) {
      settings.parm("PhaseSpace:pTHatMin", pTlimitLow[iBin]);
      settings.parm("PhaseSpace:pTHatMax", pTlimitLow[iBin + 1]);
    }

    // Mode 5: hardcoded here. Use settings.parm for non-string input.
    // Hard processes in one step, but pT-weighted.
    else if (mode == 5) {
      settings.parm("PhaseSpace:pTHatMin", pTlimitTwo[iBin]);
      settings.parm("PhaseSpace:pTHatMax", pTlimitTwo[iBin + 1]);
      if (iBin == 1) {
        pythia.readString("PhaseSpace:bias2Selection = on");
        pythia.readString("PhaseSpace:bias2SelectionPow = 4.");
        pythia.readString("PhaseSpace:bias2SelectionRef = 20.");
      }
    }

    // Initialize for LHC at 14 TeV.
    pythia.readString("Beams:eCM = 14000.");

    // If Pythia fails to initialize, exit with error.
    if (!pythia.init()) return 1;

    // Reset local histograms (that need to be rescaled before added).
    pTnormPart.null();
    pTpow3Part.null();
    pTpow5Part.null();

    // Begin event loop.
    for (int iEvent = 0; iEvent < nEvent; ++iEvent) {

      // Generate events. Skip if failure.
      if (!pythia.next()) continue;

      // Soft events have no upper pT limit. They therefore overlap
      // with hard events, and the overlap must be removed by hand.
      // No overlap for elastic/diffraction, which is only part of soft.
      double pTHat  = info.pTHat();
      if (mode > 3 && iBin == 0 && info.isNonDiffractive()
        && pTHat > pTlimitLow[1]) continue;

      // Fill hard scale of event.
      double weight = info.weight();
      pTraw.fill( pTHat );
      pTnormPart.fill( pTHat, weight);
      pTpow3Part.fill( pTHat, weight * pow3(pTHat) );
      pTpow5Part.fill( pTHat, weight * pow5(pTHat) );

    // End of event loop. Statistics.
    }
    if (!smallOutput) pythia.stat();

    // Normalize to cross section for each case, and add to sum.
    double sigmaNorm = (info.sigmaGen() / info.weightSum())
                     * (nRange / pTrange);
    pTnorm += sigmaNorm * pTnormPart;
    pTpow3 += sigmaNorm * pTpow3Part;
    pTpow5 += sigmaNorm * pTpow5Part;

  // End of pT-bin loop.
  }

  // Output histograms.
  cout << pTraw << pTnorm << pTpow3 << pTpow5;

  // Done.
  return 0;
}
