// SigmaQCD.cc is a part of the PYTHIA event generator.
// Copyright (C) 2025 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.

// Function definitions (not found in the header) for the
// QCD simulation classes.

#include "Pythia8/SigmaQCD.h"

namespace Pythia8 {

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

// Sigma0AB2AB class.
// Cross section for elastic scattering A B -> A B.

//--------------------------------------------------------------------------

// Select identity, colour and anticolour.

void Sigma0AB2AB::setIdColAcol() {

  // Flavours and colours are trivial.
  setId( idA, idB, idA, idB);
  setColAcol( 0, 0, 0, 0, 0, 0, 0, 0);
}

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

// Sigma0AB2XB class.
// Cross section for single diffractive scattering A B -> X B.

//--------------------------------------------------------------------------

// Select identity, colour and anticolour.

void Sigma0AB2XB::setIdColAcol() {

  // Flavours and colours are trivial.
  int idX          = 10* (abs(idA) / 10) + 9900000;
  if (idA < 0) idX = -idX;
  setId( idA, idB, idX, idB);
  setColAcol( 0, 0, 0, 0, 0, 0, 0, 0);

}

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

// Sigma0AB2AX class.
// Cross section for single diffractive scattering A B -> A X.

//--------------------------------------------------------------------------

// Select identity, colour and anticolour.

void Sigma0AB2AX::setIdColAcol() {

  // Flavours and colours are trivial.
  int idX          = 10* (abs(idB) / 10) + 9900000;
  if (idB < 0) idX = -idX;
  setId( idA, idB, idA, idX);
  setColAcol( 0, 0, 0, 0, 0, 0, 0, 0);

}

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

// Sigma0AB2XX class.
// Cross section for double diffractive scattering A B -> X X.

//--------------------------------------------------------------------------

// Select identity, colour and anticolour.

void Sigma0AB2XX::setIdColAcol() {

  // Flavours and colours are trivial.
  int          idX1 = 10* (abs(idA) / 10) + 9900000;
  if (idA < 0) idX1 = -idX1;
  int          idX2 = 10* (abs(idB) / 10) + 9900000;
  if (idB < 0) idX2 = -idX2;
  setId( idA, idB, idX1, idX2);
  setColAcol( 0, 0, 0, 0, 0, 0, 0, 0);

}

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

// Sigma0AB2AXB class.
// Cross section for central scattering A B -> A X B.

//--------------------------------------------------------------------------

// Select identity, colour and anticolour.

void Sigma0AB2AXB::setIdColAcol() {

  // Central diffractive state represented by rho_diffr0. Colours trivial.
  int idX = 9900110;
  setId( idA, idB, idA, idB,idX);
  setColAcol( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);

}

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

// Sigma2gg2gg class.
// Cross section for g g -> g g.

//--------------------------------------------------------------------------

// Evaluate d(sigmaHat)/d(tHat) - no incoming flavour dependence.

void Sigma2gg2gg::sigmaKin() {

  // Calculate kinematics dependence.
  sigTS  = (9./4.) * (tH2 / sH2 + 2. * tH / sH + 3. + 2. * sH / tH
           + sH2 / tH2);
  sigUS  = (9./4.) * (uH2 / sH2 + 2. * uH / sH + 3. + 2. * sH / uH
           + sH2 / uH2);
  sigTU  = (9./4.) * (tH2 / uH2 + 2. * tH / uH + 3. + 2. * uH / tH
           + uH2 / tH2);
  sigSum = sigTS + sigUS + sigTU;

  // Answer contains factor 1/2 from identical gluons.
  sigma  = (M_PI / sH2) * pow2(alpS) * 0.5 * sigSum;

}

//--------------------------------------------------------------------------

// Select identity, colour and anticolour.

void Sigma2gg2gg::setIdColAcol() {

  // Flavours are trivial.
  setId( id1, id2, 21, 21);

  // Three colour flow topologies, each with two orientations.
  double sigRand = sigSum * rndmPtr->flat();
  if (sigRand < sigTS) setColAcol( 1, 2, 2, 3, 1, 4, 4, 3);
  else if (sigRand < sigTS + sigUS)
                       setColAcol( 1, 2, 3, 1, 3, 4, 4, 2);
  else                 setColAcol( 1, 2, 3, 4, 1, 4, 3, 2);
  if (rndmPtr->flat() > 0.5) swapColAcol();

}

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

// Sigma2gg2qqbar class.
// Cross section for g g -> q qbar (q = u, d, s, i.e. almost massless).

//--------------------------------------------------------------------------

// Initialize process.

void Sigma2gg2qqbar::initProc() {

  // Read number of quarks to be considered in massless approximation.
  nQuarkNew       = mode("HardQCD:nQuarkNew");

}

//--------------------------------------------------------------------------

// Evaluate d(sigmaHat)/d(tHat) - no incoming flavour dependence.

void Sigma2gg2qqbar::sigmaKin() {

  // Pick new flavour.
  idNew = 1 + int( nQuarkNew * rndmPtr->flat() );
  mNew  = particleDataPtr->m0(idNew);
  m2New = mNew*mNew;

  // Calculate kinematics dependence.
  sigTS = 0.;
  sigUS = 0.;
  if (sH > 4. * m2New) {
    sigTS = (1./6.) * uH / tH - (3./8.) * uH2 / sH2;
    sigUS = (1./6.) * tH / uH - (3./8.) * tH2 / sH2;
  }
  sigSum = sigTS + sigUS;

  // Answer is proportional to number of outgoing flavours.
  sigma  = (M_PI / sH2) * pow2(alpS) * nQuarkNew * sigSum;

}

//--------------------------------------------------------------------------

// Select identity, colour and anticolour.

void Sigma2gg2qqbar::setIdColAcol() {

  // Flavours are trivial.
  setId( id1, id2, idNew, -idNew);

  // Two colour flow topologies.
  double sigRand = sigSum * rndmPtr->flat();
  if (sigRand < sigTS) setColAcol( 1, 2, 2, 3, 1, 0, 0, 3);
  else                 setColAcol( 1, 2, 3, 1, 3, 0, 0, 2);

}

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

// Sigma2qg2qg class.
// Cross section for q g -> q g.

//--------------------------------------------------------------------------

// Evaluate d(sigmaHat)/d(tHat) - no incoming flavour dependence.

void Sigma2qg2qg::sigmaKin() {

  // Calculate kinematics dependence.
  sigTS  = uH2 / tH2 - (4./9.) * uH / sH;
  sigTU  = sH2 / tH2 - (4./9.) * sH / uH;
  sigSum = sigTS + sigTU;

  // Answer.
  sigma  = (M_PI / sH2) * pow2(alpS) * sigSum;

}

//--------------------------------------------------------------------------

// Select identity, colour and anticolour.

void Sigma2qg2qg::setIdColAcol() {

  // Outgoing = incoming flavours.
  setId( id1, id2, id1, id2);

  // Two colour flow topologies. Swap if first is gluon, or when antiquark.
  double sigRand = sigSum * rndmPtr->flat();
  if (sigRand < sigTS) setColAcol( 1, 0, 2, 1, 3, 0, 2, 3);
  else                 setColAcol( 1, 0, 2, 3, 2, 0, 1, 3);
  if (id1 == 21) swapCol1234();
  if (id1 < 0 || id2 < 0) swapColAcol();

}

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

// Sigma2qq2qq class.
// Cross section for q qbar' -> q qbar' or q q' -> q q'
// (qbar qbar' -> qbar qbar'), q' may be same as q.
// s-channel part of q qbar -> q qbar / q' qbar' handled separately below.

//--------------------------------------------------------------------------

// Evaluate d(sigmaHat)/d(tHat), part independent of incoming flavour.

void Sigma2qq2qq::sigmaKin() {

  // Calculate kinematics dependence for different terms.
  sigT   = (4./9.) * (sH2 + uH2) / tH2;
  sigU   = (4./9.) * (sH2 + tH2) / uH2;
  sigTU  = - (8./27.) * sH2 / (tH * uH);
  sigST  = - (8./27.) * uH2 / (sH * tH);

}

//--------------------------------------------------------------------------


// Evaluate d(sigmaHat)/d(tHat), including incoming flavour dependence.

double Sigma2qq2qq::sigmaHat() {

  // Combine cross section terms; factor 1/2 when identical quarks.
  if      (id2 ==  id1) sigSum = 0.5 * (sigT + sigU + sigTU);
  else if (id2 == -id1) sigSum = sigT + sigST;
  else                  sigSum = sigT;

  // Uncomment line below to obtain only q q' -> q q' scattering.
  // (Useful for studies of well-defined colour flow.)
  // if (id1 < 0 || id2 < 0 || id2 == id1) sigSum = 0.;

  // Answer.
  return (M_PI/sH2) * pow2(alpS) * sigSum;

}

//--------------------------------------------------------------------------

// Select identity, colour and anticolour.

void Sigma2qq2qq::setIdColAcol() {

  // Outgoing = incoming flavours.
  setId( id1, id2, id1, id2);

  // Colour flow topologies. Swap when antiquarks.
  if (id1 * id2 > 0)  setColAcol( 1, 0, 2, 0, 2, 0, 1, 0);
  else                setColAcol( 1, 0, 0, 1, 2, 0, 0, 2);
  if (id2 == id1 && (sigT + sigU) * rndmPtr->flat() > sigT)
                      setColAcol( 1, 0, 2, 0, 1, 0, 2, 0);
  if (id1 < 0) swapColAcol();

}

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

// Sigma2qqbar2gg class.
// Cross section for q qbar -> g g.

//--------------------------------------------------------------------------

// Evaluate d(sigmaHat)/d(tHat) - no incoming flavour dependence.

void Sigma2qqbar2gg::sigmaKin() {

  // Calculate kinematics dependence.
  sigTS  = (32./27.) * uH / tH - (8./3.) * uH2 / sH2;
  sigUS  = (32./27.) * tH / uH - (8./3.) * tH2 / sH2;
  sigSum = sigTS + sigUS;

  // Answer contains factor 1/2 from identical gluons.
  sigma  = (M_PI / sH2) * pow2(alpS) * 0.5 * sigSum;

}

//--------------------------------------------------------------------------

// Select identity, colour and anticolour.

void Sigma2qqbar2gg::setIdColAcol() {

  // Outgoing flavours trivial.
  setId( id1, id2, 21, 21);

  // Two colour flow topologies. Swap if first is antiquark.
  double sigRand = sigSum * rndmPtr->flat();
  if (sigRand < sigTS) setColAcol( 1, 0, 0, 2, 1, 3, 3, 2);
  else                 setColAcol( 1, 0, 0, 2, 3, 2, 1, 3);
  if (id1 < 0) swapColAcol();

}

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

// Sigma2qqbar2qqbarNew class.
// Cross section q qbar -> q' qbar'.

//--------------------------------------------------------------------------

// Initialize process.

void Sigma2qqbar2qqbarNew::initProc() {

  // Read number of quarks to be considered in massless approximation.
  nQuarkNew       = mode("HardQCD:nQuarkNew");

}

//--------------------------------------------------------------------------

// Evaluate d(sigmaHat)/d(tHat) - no incoming flavour dependence.

void Sigma2qqbar2qqbarNew::sigmaKin() {

  // Pick new flavour.
  idNew = 1 + int( nQuarkNew * rndmPtr->flat() );
  mNew  = particleDataPtr->m0(idNew);
  m2New = mNew*mNew;

  // Calculate kinematics dependence.
  sigS                      = 0.;
  if (sH > 4. * m2New) sigS = (4./9.) * (tH2 + uH2) / sH2;

  // Answer is proportional to number of outgoing flavours.
  sigma = (M_PI / sH2) * pow2(alpS) * nQuarkNew * sigS;

}

//--------------------------------------------------------------------------

// Select identity, colour and anticolour.

void Sigma2qqbar2qqbarNew::setIdColAcol() {

  // Set outgoing flavours ones.
  id3 = (id1 > 0) ? idNew : -idNew;
  setId( id1, id2, id3, -id3);

  // Colour flow topologies. Swap when antiquarks.
  setColAcol( 1, 0, 0, 2, 1, 0, 0, 2);
  if (id1 < 0) swapColAcol();

}

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

// TopThreshold.
// Auxiliary class to Sigma2gg2QQbar and Sigma2qqbar2QQbar, which allows
// simulation of top threshold enhancement factors according to
// V. Fadin,  V. Khoze and T. Sjostrand, Z. Phys. C48 (1990) 613.

//--------------------------------------------------------------------------

// Initialization setup - read in necessary settings.

void TopThreshold::setup( int topModelIn, double mtIn, double gammatIn,
  double thrWidthIn, double singletFracIn, int alphasOrder,
  double alphasValue) {

  topModel    = topModelIn;
  mt          = mtIn;
  gammat      = gammatIn;
  thrWidth    = thrWidthIn;
  singletFrac = singletFracIn;
  alphas.init( alphasValue, alphasOrder);

}

//--------------------------------------------------------------------------

// Cross section enhancement factor.

double TopThreshold::multiplySigmaBy( bool inInit, double mHat, double m3,
  double m4, double eThr) {

  // No rescaling where not defined.
  if (topModel < 0 || topModel > 4) return 1.;

  // Calculate key kinematics variables.
  double mtAvg  = sqrt(0.5 * (m3*m3+m4*m4) - 0.25 * pow2((m3*m3-m4*m4)/mHat) );
  double betaThr = sqrtpos( 1. - pow2(2. * mtAvg/mHat));

  // alpha_strong value.
  double q2Thr = pow2(eThr) + pow2(gammat);
  double q2alp = mtAvg * sqrt(q2Thr);
  alps         = alphas.alphaS(q2alp);

  // Initial values.
  double fAttr = 0., fRepu = 0.;

  // Coulomb threshold factors for attractive (singlet) and repulsive (octet).
  if (topModel != 3) {
    double xAttr = (4. / 3.) * M_PI * alps / betaThr;
    fAttr        =  betaThr * xAttr / (1. - exp(-xAttr));
    double xRepu = (1. / 6.) * M_PI * alps / betaThr;
    fRepu        = betaThr * xRepu / (exp(xRepu) - 1);
  }

  // Green's function factors for attractive (singlet) and repulsive (octet).
  // Above threshold region transition to Coulomb.
  if ((topModel == 2 || topModel == 4) && eThr > 0. && eThr < 2. * thrWidth) {
    double damp = (eThr < thrWidth) ? 1. : 2. - eThr / thrWidth;
    fAttr = (1. - damp) * fAttr + damp * imGreenSin( eThr, mtAvg);
    fRepu = (1. - damp) * fRepu + damp * imGreenOct( eThr, mtAvg);
  }

  // Mirror Green's function contribution E < 0 to E > 0.
  // Below threshold region transition to 0.
  if (topModel == 3 && eThr > 0.  && eThr < 2. * thrWidth) {
    double damp = (eThr < thrWidth) ? 1. : 2. - eThr / thrWidth;
    fAttr = damp * imGreenSin( -eThr, mtAvg);
    fRepu = damp * imGreenOct( -eThr, mtAvg);
  }
  // During initialization need to ensure that cross section does not vanish.
  if (topModel == 3 && inInit) {
    fAttr = max( fAttr, 0.2);
    fRepu = max( fAttr, 0.2);
  }

  // Proper below-threshold contribution. Again transition to zero.
  if (topModel == 4 && eThr < 0. && eThr > -2. * thrWidth) {
    double damp = (eThr > -thrWidth) ? 1. : 2. + eThr / thrWidth;
    fAttr = damp * imGreenSin( eThr, mtAvg);
    fRepu = damp * imGreenOct( eThr, mtAvg);
  }

  // Mix of attractive and repulsive channels.
  double facAR = singletFrac * fAttr + (1. - singletFrac) * fRepu;

  // Since the threshold cross section contains a beta factor
  // this must be removed in the final answer.
  return facAR / betaThr;
}

//--------------------------------------------------------------------------

// Imaginary part of Green's function for singlet state.

double TopThreshold::imGreenSin(double eNow, double mtNow) {

  // Basic expressions.
  double ps   = (2. / 3.) * mtNow * alps;
  double egrt = sqrt(eNow * eNow + gammat * gammat);
  double p1   = sqrt( 0.5 * mtNow * (egrt - eNow));
  double p2   = sqrt( 0.5 * mtNow * (egrt + eNow));

  // Sum over resonance contributions.
  double ressum = 0.;
  for (int n = 1; n < 21; ++n)
    ressum += (gammat * ps * n + p2 * (n*n * egrt + ps * ps / mtNow))
    / ( pow4(n) * (pow2(eNow + ps * ps / (mtNow * n*n)) + gammat * gammat) );

  // Combine with non-resonant terms and done.
  return p2 / mtNow + (2. * ps / mtNow) * atan(p2 / p1)
    + 2. * pow2(ps / mtNow) * ressum;

}

//--------------------------------------------------------------------------

// Imaginary part of Green's function for octet state.

double TopThreshold::imGreenOct(double eNow, double mtNow) {

 // Basic expressions.
  double p8   = - (1. / 12.) * mtNow * alps;
  double egrt = sqrt(eNow * eNow + gammat * gammat);
  double p1   = sqrt( 0.5 * mtNow * (egrt - eNow));
  double p2   = sqrt( 0.5 * mtNow * (egrt + eNow));

  // Sum over resonance contributions.
  double ressum = 0.;
  for (int n = 1; n < 21; ++n)
    ressum += mtNow * p2 / (pow2(n * p1 - p8) + pow2(n * p2));

  // Combine with non-resonant terms and done.
  return p2 / mtNow + (2. * p8 / mtNow) * atan(p2 / p1)
    + 2. * pow2(p8 / mtNow) * ressum;

}

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

// Sigma2gg2QQbar class.
// Cross section g g -> Q Qbar (Q = c, b or t).
// Only provided for fixed m3 = m4 so do some gymnastics:
// i) s34Avg picked so that beta34 same when s3, s4 -> s34Avg.
// ii) tHQ = tH - mQ^2 = -0.5 sH (1 - beta34 cos(thetaH)) for m3 = m4 = mQ,
//     but tH - uH = sH beta34 cos(thetaH) also for m3 != m4, so use
//     tH, uH selected for m3 != m4 to derive tHQ, uHQ valid for m3 = m4.

//--------------------------------------------------------------------------

// Initialize process.

void Sigma2gg2QQbar::initProc() {

  // Process name.
  nameSave                 = "g g -> Q Qbar";
  if (idNew == 4) nameSave = "g g -> c cbar";
  if (idNew == 5) nameSave = "g g -> b bbar";
  if (idNew == 6) nameSave = "g g -> t tbar";
  if (idNew == 7) nameSave = "g g -> b' b'bar";
  if (idNew == 8) nameSave = "g g -> t' t'bar";

  // Secondary open width fraction.
  openFracPair = particleDataPtr->resOpenFrac(idNew, -idNew);

  // Special top threshold enhancement modelling.
  topModel             = (idNew == 6) ? mode("TopThreshold:model") : 0;
  if (topModel > 0) {
    double mt          = particleDataPtr->m0(6);
    double gammat      = particleDataPtr->mWidth(6);
    double thrWidth    = parm("TopThreshold:width");
    ggSingletFrac      = parm("TopThreshold:ggSingletFrac");
    int    alphasOrder = mode("TopThreshold:alphasOrder");
    double alphasValue = parm("TopThreshold:alphasValue");
    topThreshold.setup( topModel, mt, gammat, thrWidth, ggSingletFrac,
      alphasOrder, alphasValue);
  }

}

//--------------------------------------------------------------------------

// Evaluate d(sigmaHat)/d(tHat) - no incoming flavour dependence.

void Sigma2gg2QQbar::sigmaKin() {

  // Modified Mandelstam variables for massive kinematics with m3 = m4.
  double s34Avg = 0.5 * (s3 + s4) - 0.25 * pow2(s3 - s4) / sH;
  double tHQ    = -0.5 * (sH - tH + uH);
  double uHQ    = -0.5 * (sH + tH - uH);
  double tHQ2   = tHQ * tHQ;
  double uHQ2   = uHQ * uHQ;

  // Calculate kinematics dependence.
  double tumHQ = tHQ * uHQ - s34Avg * sH;
  sigTS = ( uHQ / tHQ - 2.25 * uHQ2 / sH2 + 4.5 * s34Avg * tumHQ
    / ( sH * tHQ2) + 0.5 * s34Avg * (tHQ + s34Avg) / tHQ2
    - s34Avg*s34Avg / (sH * tHQ) ) / 6.;
  sigUS = ( tHQ / uHQ - 2.25 * tHQ2 / sH2 + 4.5 * s34Avg * tumHQ
    / ( sH * uHQ2) + 0.5 * s34Avg * (uHQ + s34Avg) / uHQ2
    - s34Avg*s34Avg / (sH * uHQ) ) / 6.;
  sigSum = sigTS + sigUS;

  // Answer.
  sigma = (M_PI / sH2) * pow2(alpS) * sigSum * openFracPair;

  // Special top threshold enhancement.
  if (topModel > 0) sigma *= topThreshold.multiplySigmaBy(
    infoPtr->getInInit(), sqrt(sH), m3, m4, infoPtr->toponiumE);

}

//--------------------------------------------------------------------------

// Select identity, colour and anticolour.

void Sigma2gg2QQbar::setIdColAcol() {

  // Flavours are trivial.
  setId( id1, id2, idNew, -idNew);

  // Two colour flow topologies.
  double sigRand = sigSum * rndmPtr->flat();
  if (sigRand < sigTS) setColAcol( 1, 2, 2, 3, 1, 0, 0, 3);
  else                 setColAcol( 1, 2, 3, 1, 3, 0, 0, 2);

  // Special ttbar singlet setup.
  if (topModel > 0 && rndmPtr->flat() < ggSingletFrac)
    setColAcol( 1, 2, 2, 1, 3, 0, 0, 3);

}

//--------------------------------------------------------------------------

// Evaluate weight for decay angles of W in top decay.

double Sigma2gg2QQbar::weightDecay( Event& process, int iResBeg,
  int iResEnd) {

  // For top decay hand over to standard routine, else done.
  if (idNew == 6 && process[process[iResBeg].mother1()].idAbs() == 6)
       return weightTopDecay( process, iResBeg, iResEnd);
  else return 1.;

}

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

// Sigma2qqbar2QQbar class.
// Cross section q qbar -> Q Qbar (Q = c, b or t).
// Only provided for fixed m3 = m4 so do some gymnastics:
// i) s34Avg picked so that beta34 same when s3, s4 -> s34Avg.
// ii) tHQ = tH - mQ^2 = -0.5 sH (1 - beta34 cos(thetaH)) for m3 = m4 = mQ,
//     but tH - uH = sH beta34 cos(thetaH) also for m3 != m4, so use
//     tH, uH selected for m3 != m4 to derive tHQ, uHQ valid for m3 = m4.

//--------------------------------------------------------------------------

// Initialize process, especially parton-flux object.

void Sigma2qqbar2QQbar::initProc() {

  // Process name.
  nameSave                 = "q qbar -> Q Qbar";
  if (idNew == 4) nameSave = "q qbar -> c cbar";
  if (idNew == 5) nameSave = "q qbar -> b bbar";
  if (idNew == 6) nameSave = "q qbar -> t tbar";
  if (idNew == 7) nameSave = "q qbar -> b' b'bar";
  if (idNew == 8) nameSave = "q qbar -> t' t'bar";

  // Secondary open width fraction.
  openFracPair = particleDataPtr->resOpenFrac(idNew, -idNew);

  // Special top threshold enhancement modelling.
  topModel             = (idNew == 6) ? mode("TopThreshold:model") : 0;
  if (topModel > 0) {
    double mt          = particleDataPtr->m0(6);
    double gammat      = particleDataPtr->mWidth(6);
    double thrWidth    = parm("TopThreshold:width");
    qqSingletFrac      = parm("TopThreshold:qqSingletFrac");
    int    alphasOrder = mode("TopThreshold:alphasOrder");
    double alphasValue = parm("TopThreshold:alphasValue");
    topThreshold.setup( topModel, mt, gammat, thrWidth, qqSingletFrac,
      alphasOrder, alphasValue);
  }

}

//--------------------------------------------------------------------------

// Evaluate d(sigmaHat)/d(tHat) - no incoming flavour dependence.

void Sigma2qqbar2QQbar::sigmaKin() {

  // Modified Mandelstam variables for massive kinematics with m3 = m4.
  double s34Avg = 0.5 * (s3 + s4) - 0.25 * pow2(s3 - s4) / sH;
  double tHQ    = -0.5 * (sH - tH + uH);
  double uHQ    = -0.5 * (sH + tH - uH);
  double tHQ2   = tHQ * tHQ;
  double uHQ2   = uHQ * uHQ;

  // Calculate kinematics dependence.
  double sigS = (4./9.) * ((tHQ2 + uHQ2) / sH2 + 2. * s34Avg / sH);

  // Answer.
  sigma = (M_PI / sH2) * pow2(alpS) * sigS * openFracPair;

  // Special top threshold enhancement.
  if (topModel > 0) sigma *= topThreshold.multiplySigmaBy(
    infoPtr->getInInit(), sqrt(sH), m3, m4, infoPtr->toponiumE);

}

//--------------------------------------------------------------------------

// Select identity, colour and anticolour.

void Sigma2qqbar2QQbar::setIdColAcol() {

  // Set outgoing flavours.
  id3 = (id1 > 0) ? idNew : -idNew;
  setId( id1, id2, id3, -id3);

  // Colour flow topologies. Swap when antiquarks.
  setColAcol( 1, 0, 0, 2, 1, 0, 0, 2);
  // Special ttbar singlet setup.
  if (topModel > 0 && rndmPtr->flat() < qqSingletFrac)
    setColAcol( 1, 0, 0, 1, 2, 0, 0, 2);
  if (id1 < 0) swapColAcol();

}

//--------------------------------------------------------------------------

// Evaluate weight for decay angles of W in top decay.

double Sigma2qqbar2QQbar::weightDecay( Event& process, int iResBeg,
  int iResEnd) {

  // For top decay hand over to standard routine, else done.
  if (idNew == 6 && process[process[iResBeg].mother1()].idAbs() == 6)
       return weightTopDecay( process, iResBeg, iResEnd);
  else return 1.;

}

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

// Sigma3gg2ggg class.
// Cross section for g g -> g g g.

//--------------------------------------------------------------------------

// Evaluate |M|^2 - no incoming flavour dependence.

void Sigma3gg2ggg::sigmaKin() {

  // Calculate all four-vector products.
  Vec4 p1cm( 0., 0.,  0.5 * mH, 0.5 * mH);
  Vec4 p2cm( 0., 0., -0.5 * mH, 0.5 * mH);
  pp[1][2] = p1cm * p2cm;
  pp[1][3] = p1cm * p3cm;
  pp[1][4] = p1cm * p4cm;
  pp[1][5] = p1cm * p5cm;
  pp[2][3] = p2cm * p3cm;
  pp[2][4] = p2cm * p4cm;
  pp[2][5] = p2cm * p5cm;
  pp[3][4] = p3cm * p4cm;
  pp[3][5] = p3cm * p5cm;
  pp[4][5] = p4cm * p5cm;
  for (int i = 1; i < 5; ++i)
    for (int j = i + 1; j < 6; ++j) pp[j][i] = pp[i][j];

  // Cross section, in three main sections.
  double num1 = cycle(1,2,3,4,5) + cycle(1,2,3,5,4) + cycle(1,2,4,3,5)
              + cycle(1,2,4,5,3) + cycle(1,2,5,3,4) + cycle(1,2,5,4,3)
              + cycle(1,3,2,4,5) + cycle(1,3,2,5,4) + cycle(1,3,4,2,5)
              + cycle(1,3,5,2,4) + cycle(1,4,2,3,5) + cycle(1,4,3,2,5);
  double num2 = pow4(pp[1][2]) + pow4(pp[1][3]) + pow4(pp[1][4])
              + pow4(pp[1][5]) + pow4(pp[2][3]) + pow4(pp[2][4])
              + pow4(pp[2][5]) + pow4(pp[3][4]) + pow4(pp[3][5])
              + pow4(pp[4][5]);
  double den  = pp[1][2] * pp[1][3] * pp[1][4] * pp[1][5] * pp[2][3]
              * pp[2][4] * pp[2][5] * pp[3][4] * pp[3][5] * pp[4][5];

  // Answer has a factor 6 due to identical gluons
  // This is cancelled by phase space factor (1 / 6)
  sigma = pow3(4. * M_PI * alpS) * (27./16.) * num1 * num2 / den;

}

//--------------------------------------------------------------------------

// Select identity, colour and anticolour.

void Sigma3gg2ggg::setIdColAcol() {

  // Flavours are trivial.
  setId( id1, id2, 21, 21, 21);

  // Three colour flow topologies, each with two orientations.
  /*
  double sigRand = sigSum * rndmPtr->flat();
  if (sigRand < sigTS) setColAcol( 1, 2, 2, 3, 1, 4, 4, 3);
  else if (sigRand < sigTS + sigUS)
                       setColAcol( 1, 2, 3, 1, 3, 4, 4, 2);
  else                 setColAcol( 1, 2, 3, 4, 1, 4, 3, 2);
  if (rndmPtr->flat() > 0.5) swapColAcol();
  */

  // Temporary solution.
  setColAcol( 1, 2, 2, 3, 1, 4, 4, 5, 5, 3);
}


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

// Sigma3qqbar2ggg class.
// Cross section for q qbar -> g g g.

//--------------------------------------------------------------------------

// Evaluate |M|^2 - no incoming flavour dependence.
void Sigma3qqbar2ggg::sigmaKin() {

  // Setup four-vectors
  pCM[0] = Vec4( 0., 0.,  0.5 * mH, 0.5 * mH);
  pCM[1] = Vec4( 0., 0., -0.5 * mH, 0.5 * mH);
  pCM[2] = p3cm;
  pCM[3] = p4cm;
  pCM[4] = p5cm;

  // Calculate |M|^2
  // Answer has a factor 6 due to identical gluons,
  // which is cancelled by phase space factor (1 / 6)
  sigma = m2Calc();

}

//--------------------------------------------------------------------------

// |M|^2

inline double Sigma3qqbar2ggg::m2Calc() {

  // Calculate four-products
  double sHnow  = (pCM[0] + pCM[1]).m2Calc();
  double sHhalf = sH / 2.;

  // qbar (p+) + q(p-) -> g(k1) g(k2) g(k3)
  // a_i = (p+ . ki), i = 1, 2, 3
  // b_i = (p- . ki), i = 1, 2, 3
  a[0] = pCM[0] * pCM[2];
  a[1] = pCM[0] * pCM[3];
  a[2] = pCM[0] * pCM[4];
  b[0] = pCM[1] * pCM[2];
  b[1] = pCM[1] * pCM[3];
  b[2] = pCM[1] * pCM[4];

  pp[0][1] = pCM[2] * pCM[3];
  pp[1][2] = pCM[3] * pCM[4];
  pp[2][0] = pCM[4] * pCM[2];

  // ab[i][j] = a_i * b_j + a_j * b_i
  ab[0][1] = a[0] * b[1] + a[1] * b[0];
  ab[1][2] = a[1] * b[2] + a[2] * b[1];
  ab[2][0] = a[2] * b[0] + a[0] * b[2];

  // Cross section
  double num1 = a[0] * b[0] * (a[0] * a[0] + b[0] * b[0]) +
                a[1] * b[1] * (a[1] * a[1] + b[1] * b[1]) +
                a[2] * b[2] * (a[2] * a[2] + b[2] * b[2]);
  double den1 = a[0] * a[1] * a[2] * b[0] * b[1] * b[2];
  double num2 = - ( ab[0][1] / pp[0][1] )
                - ( ab[1][2] / pp[1][2] )
                - ( ab[2][0] / pp[2][0] );
  double num3 = a[2] * b[2] * ab[0][1] / (pp[1][2] * pp[2][0] ) +
                a[0] * b[0] * ab[1][2] / (pp[2][0] * pp[0][1] ) +
                a[1] * b[1] * ab[2][0] / (pp[0][1] * pp[1][2] );

  // Final answer
  return  pow3(4. * M_PI * alpS) * (8. / 324.) * (num1 / den1) *
          ( sHhalf + 9. * (sHhalf + num2) + (2. * 81. / sHnow) * num3 );

}

//--------------------------------------------------------------------------

// Select identity, colour and anticolour.

void Sigma3qqbar2ggg::setIdColAcol(){

  // Flavours are trivial.
  setId( id1, id2, 21, 21, 21);

  // Temporary solution.
  setColAcol( 1, 0, 0, 2, 1, 3, 3, 4, 4, 2);
  if (id1 < 0) swapColAcol();
}

//--------------------------------------------------------------------------

// Map a final state configuration

inline void Sigma3qqbar2ggg::mapFinal() {
  switch (config) {
  case 0: pCM[2] = p3cm; pCM[3] = p4cm; pCM[4] = p5cm; break;
  case 1: pCM[2] = p3cm; pCM[3] = p5cm; pCM[4] = p4cm; break;
  case 2: pCM[2] = p4cm; pCM[3] = p3cm; pCM[4] = p5cm; break;
  case 3: pCM[2] = p4cm; pCM[3] = p5cm; pCM[4] = p3cm; break;
  case 4: pCM[2] = p5cm; pCM[3] = p3cm; pCM[4] = p4cm; break;
  case 5: pCM[2] = p5cm; pCM[3] = p4cm; pCM[4] = p3cm; break;
  }
}

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

// Sigma3qg2qgg class.
// Cross section for q g -> q g g.
// Crossed relation from q qbar -> g g g:
//   qbar(p+) q(p-) -> g(k1) g(k2) g(k3)

//--------------------------------------------------------------------------

// Evaluate |M|^2 - no incoming flavour dependence
// Note: two different contributions from gq and qg incoming

void Sigma3qg2qgg::sigmaKin() {

  // Pick a final state configuration
  pickFinal();

  // gq and qg incoming
  for (int i = 0; i < 2; i++) {

    // Map incoming four-vectors to p+, p-, k1, k2, k3
    pCM[0] = Vec4( 0., 0.,  0.5 * mH, 0.5 * mH);
    pCM[1] = Vec4( 0., 0., -0.5 * mH, 0.5 * mH);
    mapFinal();

    // Crossing
    swap(pCM[i], pCM[2]);

    // |M|^2
    // XXX - Extra factor of (3) from selecting a final state
    // configuration (already a factor of 2 in the original
    // answer due to two identical final state gluons)???
    // Extra factor of (3 / 8) as average over incoming gluon
    sigma[i] = 3. * (3. / 8.) * m2Calc();

  } // for (int i = 0; i < 2; i++)

}

//--------------------------------------------------------------------------

// Evaluate |M|^2 - incoming flavour dependence
// Pick from two configurations calculated previously

double Sigma3qg2qgg::sigmaHat() {
  // gq or qg incoming
  return (id1 == 21) ? sigma[0] : sigma[1];
}

//--------------------------------------------------------------------------

// Select identity, colour and anticolour.

void Sigma3qg2qgg::setIdColAcol(){
  // Outgoing flavours; only need to know where the quark is
  int qIdx    = config / 2;
  int idTmp[3] = { 21, 21, 21 };
  idTmp[qIdx]  = (id1 == 21) ? id2 : id1;
  setId( id1, id2, idTmp[0], idTmp[1], idTmp[2]);

  // Temporary solution
  if      (qIdx == 0) setColAcol(1, 0, 2, 1, 4, 0, 3, 4, 2, 3);
  else if (qIdx == 1) setColAcol(1, 0, 2, 1, 3, 4, 4, 0, 2, 3);
  else                setColAcol(1, 0, 2, 1, 3, 4, 2, 3, 4, 0);
  // gq or qg incoming
  if (id1 == 21) {
    swap( colSave[1], colSave[2]);
    swap(acolSave[1], acolSave[2]);
  }
  // qbar rather than q incoming
  if (id1 < 0 || id2 < 0) swapColAcol();

}

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

// Sigma3gg2qqbarg class.
// Cross section for g g -> q qbar g
// Crossed relation from q qbar -> g g g

//--------------------------------------------------------------------------

// Initialize process.

void Sigma3gg2qqbarg::initProc() {

  // Read number of quarks to be considered in massless approximation.
  nQuarkNew       = mode("HardQCD:nQuarkNew");

}

//--------------------------------------------------------------------------

// Evaluate |M|^2 - no incoming flavour dependence.

void Sigma3gg2qqbarg::sigmaKin() {

  // Incoming four-vectors
  pCM[0] = Vec4( 0., 0.,  0.5 * mH, 0.5 * mH);
  pCM[1] = Vec4( 0., 0., -0.5 * mH, 0.5 * mH);

  // Pick and map a final state configuration
  pickFinal();
  mapFinal();

  // Crossing
  swap(pCM[0], pCM[2]);
  swap(pCM[1], pCM[3]);

  // |M|^2
  // Extra factor of (6.) from picking a final state configuration
  // Extra factor of nQuarkNew
  // Extra factor of (3. / 8.) ^ 2 as averaging over two incoming gluons
  sigma = 6. * nQuarkNew * (3. / 8.) * (3. / 8.) * m2Calc();

}

//--------------------------------------------------------------------------

// Select identity, colour and anticolour.

void Sigma3gg2qqbarg::setIdColAcol(){

  // Pick new flavour
  int idNew = 1 + int( nQuarkNew * rndmPtr->flat() );

  // Outgoing flavours; easiest just to map by hand
  switch (config) {
  case 0: id3 =  idNew; id4 = -idNew; id5 =  21;    break;
  case 1: id3 =  idNew; id4 =  21;    id5 = -idNew; break;
  case 2: id3 = -idNew; id4 =  idNew; id5 =  21;    break;
  case 3: id3 =  21;    id4 =  idNew; id5 = -idNew; break;
  case 4: id3 = -idNew; id4 =  21;    id5 =  idNew; break;
  case 5: id3 =  21;    id4 = -idNew; id5 =  idNew; break;
  }
  setId(id1, id2, id3, id4, id5);

  // Temporary solution
  switch (config) {
  case 0: setColAcol( 1, 2, 2, 3, 4, 0, 0, 3, 1, 4 ); break;
  case 1: setColAcol( 1, 2, 2, 3, 4, 0, 1, 4, 0, 3 ); break;
  case 2: setColAcol( 1, 2, 2, 3, 0, 3, 4, 0, 1, 4 ); break;
  case 3: setColAcol( 1, 2, 2, 3, 1, 4, 4, 0, 0, 3 ); break;
  case 4: setColAcol( 1, 2, 2, 3, 0, 3, 1, 4, 4, 0 ); break;
  case 5: setColAcol( 1, 2, 2, 3, 1, 4, 0, 3, 4, 0 ); break;
  }

}

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

// Sigma3qq2qqgDiff class.
// Cross section for q q' -> q q' g, q != q'

//--------------------------------------------------------------------------

// Evaluate |M|^2 - no incoming flavour dependence

void Sigma3qq2qqgDiff::sigmaKin() {

  // q1(p+) q2(p-) -> q1(q+) q2(q-) g(k)

  // Incoming four-vectors
  pCM[0] = Vec4( 0., 0.,  0.5 * mH, 0.5 * mH);
  pCM[1] = Vec4( 0., 0., -0.5 * mH, 0.5 * mH);
  // Pick and map a final state configuration
  pickFinal();
  mapFinal();

  // |M|^2
  // Extra factor of (6.) from picking a final state configuration
  sigma = 6. * m2Calc();
}

//--------------------------------------------------------------------------

// |M|^2

inline double Sigma3qq2qqgDiff::m2Calc() {

  // Four-products
  s  = (pCM[0] + pCM[1]).m2Calc();
  t  = (pCM[0] - pCM[2]).m2Calc();
  u  = (pCM[0] - pCM[3]).m2Calc();
  up = (pCM[1] - pCM[2]).m2Calc();
  sp = (pCM[2] + pCM[3]).m2Calc();
  tp = (pCM[1] - pCM[3]).m2Calc();

  // |M|^2
  double num1 = (s * s + sp * sp + u * u + up * up) / (t * tp);
  double den1 = (pCM[0] * pCM[4]) * (pCM[1] * pCM[4]) *
                (pCM[2] * pCM[4]) * (pCM[3] * pCM[4]);
  double num2 = (u + up) * (s * sp + t * tp - u * up) +
                u * (s * t + sp * tp) + up * (s * tp + sp * t);
  double num3 = (s + sp) * (s * sp - t * tp - u * up) +
                2. * t * tp * (u + up) + 2. * u * up * (t + tp);

  // (N^2 - 1)^2 / 4N^3 = 16. / 27.
  // (N^2 - 1)   / 4N^3 =  2. / 27.
  return (1. / 8.) * pow3(4. * M_PI * alpS) * num1 / den1 *
         ( (16. / 27.) * num2 - (2. / 27.) * num3 );

}

//--------------------------------------------------------------------------

// Evaluate |M|^2 - incoming flavour dependence

double Sigma3qq2qqgDiff::sigmaHat() {
  // Different incoming flavours only
  if (abs(id1) == abs(id2)) return 0.;
  return sigma;
}

//--------------------------------------------------------------------------

// Select identity, colour and anticolour.

void Sigma3qq2qqgDiff::setIdColAcol(){

  // Outgoing flavours; easiest just to map by hand
  switch (config) {
  case 0: id3 = id1; id4 = id2; id5 = 21;  break;
  case 1: id3 = id1; id4 = 21;  id5 = id2; break;
  case 2: id3 = id2; id4 = id1; id5 = 21;  break;
  case 3: id3 = 21;  id4 = id1; id5 = id2; break;
  case 4: id3 = id2; id4 = 21;  id5 = id1; break;
  case 5: id3 = 21;  id4 = id2; id5 = id1; break;
  }
  setId(id1, id2, id3, id4, id5);

  // Temporary solution; id1 and id2 can be q/qbar independently
  int cols[5][2];
  if (id1 > 0) {
    cols[0][0] = 1; cols[0][1] = 0;
    cols[2][0] = 1; cols[2][1] = 0;
  } else {
    cols[0][0] = 0; cols[0][1] = 1;
    cols[2][0] = 0; cols[2][1] = 1;
  }
  if (id2 > 0) {
    cols[1][0] = 2; cols[1][1] = 0;
    cols[3][0] = 3; cols[3][1] = 0;
    cols[4][0] = 2; cols[4][1] = 3;
  } else {
    cols[1][0] = 0; cols[1][1] = 2;
    cols[3][0] = 0; cols[3][1] = 3;
    cols[4][0] = 3; cols[4][1] = 2;
  }
  // Map correct final state configuration
  int i3 = 0, i4 = 0, i5 = 0;
  switch (config) {
  case 0: i3 = 2; i4 = 3; i5 = 4; break;
  case 1: i3 = 2; i4 = 4; i5 = 3; break;
  case 2: i3 = 3; i4 = 2; i5 = 4; break;
  case 3: i3 = 4; i4 = 2; i5 = 3; break;
  case 4: i3 = 3; i4 = 4; i5 = 2; break;
  case 5: i3 = 4; i4 = 3; i5 = 2; break;
  }
  // Put colours in place
  setColAcol(cols[0][0],  cols[0][1],  cols[1][0],  cols[1][1],
             cols[i3][0], cols[i3][1], cols[i4][0], cols[i4][1],
             cols[i5][0], cols[i5][1]);

}

//--------------------------------------------------------------------------

// Map a final state configuration

inline void Sigma3qq2qqgDiff::mapFinal() {
  switch (config) {
  case 0: pCM[2] = p3cm; pCM[3] = p4cm; pCM[4] = p5cm; break;
  case 1: pCM[2] = p3cm; pCM[3] = p5cm; pCM[4] = p4cm; break;
  case 2: pCM[2] = p4cm; pCM[3] = p3cm; pCM[4] = p5cm; break;
  case 3: pCM[2] = p4cm; pCM[3] = p5cm; pCM[4] = p3cm; break;
  case 4: pCM[2] = p5cm; pCM[3] = p3cm; pCM[4] = p4cm; break;
  case 5: pCM[2] = p5cm; pCM[3] = p4cm; pCM[4] = p3cm; break;
  }
}


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

// Sigma3qqbar2qqbargDiff
// Cross section for q qbar -> q' qbar' g
// Crossed relation from q q' -> q q' g, q != q'

//--------------------------------------------------------------------------

// Initialize process.

void Sigma3qqbar2qqbargDiff::initProc() {

  // Read number of quarks to be considered in massless approximation.
  nQuarkNew       = mode("HardQCD:nQuarkNew");

}

//--------------------------------------------------------------------------

// Evaluate |M|^2 - no incoming flavour dependence.

void Sigma3qqbar2qqbargDiff::sigmaKin() {
  // Overall 6 possibilities for final state ordering
  // To keep symmetry between final states, always map to:
  //  1) q1(p+)    qbar1(p-)  -> qbar2(q+)  q2(q-)     g(k)
  //  2) qbar1(p+) q1(p-)     -> q2(q+)     qbar2(q-)  g(k)
  // Crossing p- and q+ gives:
  //  1) q1(p+)    q2(-q+)    -> q1(-p-)    q2(q-)     g(k)
  //  2) qbar1(p+) qbar2(-q+) -> qbar1(-p-) qbar2(q-)  g(k)

  // Incoming four-vectors
  pCM[0] = Vec4( 0., 0.,  0.5 * mH, 0.5 * mH);
  pCM[1] = Vec4( 0., 0., -0.5 * mH, 0.5 * mH);
  // Pick and map a final state configuration
  pickFinal();
  mapFinal();

  // Crossing
  swap(pCM[1], pCM[2]);
  pCM[1] = -pCM[1];
  pCM[2] = -pCM[2];

  // |M|^2
  // Extra factor of (6.) from picking a final state configuration
  // Extra factor of (nQuarkNew - 1) from new q/qbar pairs
  // XXX - Extra factor of (2.) from second possible crossing???
  sigma = 6. * (nQuarkNew - 1) * 2. * m2Calc();

}

//--------------------------------------------------------------------------

// Select identity, colour and anticolour.

void Sigma3qqbar2qqbargDiff::setIdColAcol(){

  // Pick new q qbar flavour with incoming flavour disallowed
  int idNew = 1 + int( (nQuarkNew - 1) * rndmPtr->flat() );
  if (idNew >= abs(id1)) ++idNew;
  // For qbar q incoming, q+ is always mapped to q2
  // For q qbar incoming, q+ is always mapped to qbar2
  if (id1 > 0) idNew = -idNew;

  // Outgoing flavours; easiest just to map by hand
  switch (config) {
  case 0: id3 =  idNew; id4 = -idNew; id5 =  21;    break;
  case 1: id3 =  idNew; id4 =  21;    id5 = -idNew; break;
  case 2: id3 = -idNew; id4 =  idNew; id5 =  21;    break;
  case 3: id3 =  21;    id4 =  idNew; id5 = -idNew; break;
  case 4: id3 = -idNew; id4 =  21;    id5 =  idNew; break;
  case 5: id3 =  21;    id4 = -idNew; id5 =  idNew; break;
  }
  setId(id1, id2, id3, id4, id5);

  // Temporary solution; start with q qbar -> qbar q g
  int cols[5][2];
  cols[0][0] = 1; cols[0][1] = 0;
  cols[1][0] = 0; cols[1][1] = 2;
  cols[2][0] = 0; cols[2][1] = 3;
  cols[3][0] = 1; cols[3][1] = 0;
  cols[4][0] = 3; cols[4][1] = 2;
  // Map into correct place
  int i3 = 0, i4 = 0, i5 = 0;
  switch (config) {
  case 0: i3 = 2; i4 = 3; i5 = 4; break;
  case 1: i3 = 2; i4 = 4; i5 = 3; break;
  case 2: i3 = 3; i4 = 2; i5 = 4; break;
  case 3: i3 = 4; i4 = 2; i5 = 3; break;
  case 4: i3 = 3; i4 = 4; i5 = 2; break;
  case 5: i3 = 4; i4 = 3; i5 = 2; break;
  }
  setColAcol(cols[0][0], cols[0][1], cols[1][0], cols[1][1],
             cols[i3][0], cols[i3][1], cols[i4][0], cols[i4][1],
             cols[i5][0], cols[i5][1]);
  // Swap for qbar q incoming
  if (id1 < 0) swapColAcol();

}

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

// Sigma3qg2qqqbarDiff class.
// Cross section for q g -> q q' qbar'
// Crossed relation from q q' -> q q' g, q != q'
//   q1(p+) q2(p-) -> q1(q+) q2(q-) g(k)

//--------------------------------------------------------------------------

// Initialize process.

void Sigma3qg2qqqbarDiff::initProc() {

  // Read number of quarks to be considered in massless approximation.
  nQuarkNew       = mode("HardQCD:nQuarkNew");

}

//--------------------------------------------------------------------------

// Evaluate |M|^2 - no incoming flavour dependence

void Sigma3qg2qqqbarDiff::sigmaKin() {

  // Pick a final state configuration
  pickFinal();

  // gq or qg incoming
  for (int i = 0; i < 2; i++) {

    // Map incoming four-vectors
    pCM[0] = Vec4( 0., 0.,  0.5 * mH, 0.5 * mH);
    pCM[1] = Vec4( 0., 0., -0.5 * mH, 0.5 * mH);
    mapFinal();

    // Crossing (note extra -ve sign in total sigma)
    swap(pCM[i], pCM[4]);
    pCM[i] = -pCM[i];
    pCM[4] = -pCM[4];

    // |M|^2
    // Extra factor of (6) from picking a final state configuration
    // Extra factor of (3 / 8) as averaging over incoming gluon
    // Extra factor of (nQuarkNew - 1) due to new q/qbar pair
    sigma[i] = -6. * (3. / 8.) * (nQuarkNew - 1) * m2Calc();

  }

}

//--------------------------------------------------------------------------

// Evaluate |M|^2 - incoming flavour dependence

double Sigma3qg2qqqbarDiff::sigmaHat() {
  // gq or qg incoming
  return (id1 == 21) ? sigma[0] : sigma[1];
}

//--------------------------------------------------------------------------

// Select identity, colour and anticolour.

void Sigma3qg2qqqbarDiff::setIdColAcol(){
  // Pick new q qbar flavour with incoming flavour disallowed
  int sigmaIdx = (id1 == 21) ? 0 : 1;
  int idIn     = (id1 == 21) ? id2 : id1;
  int idNew    = 1 + int( (nQuarkNew - 1) * rndmPtr->flat() );
  if (idNew >= abs(idIn)) ++idNew;

  // qbar instead of q incoming means swap outgoing q/qbar pair
  int id3Tmp = idIn, id4Tmp = idNew, id5Tmp = -idNew;
  if (idIn < 0)  swap(id4Tmp, id5Tmp);
  // If g q incoming rather than q g, idIn and idNew
  // should be exchanged (see sigmaKin)
  if (sigmaIdx == 0) swap(id3Tmp, id4Tmp);
  // Outgoing flavours; now just map as if q g incoming
  switch (config) {
  case 0: id3 = id3Tmp; id4 = id4Tmp; id5 = id5Tmp; break;
  case 1: id3 = id3Tmp; id4 = id5Tmp; id5 = id4Tmp; break;
  case 2: id3 = id4Tmp; id4 = id3Tmp; id5 = id5Tmp; break;
  case 3: id3 = id5Tmp; id4 = id3Tmp; id5 = id4Tmp; break;
  case 4: id3 = id4Tmp; id4 = id5Tmp; id5 = id3Tmp; break;
  case 5: id3 = id5Tmp; id4 = id4Tmp; id5 = id3Tmp; break;
  }
  setId(id1, id2, id3, id4, id5);

  // Temporary solution; start with either
  // g q1    -> q1    q2 qbar2
  // g qbar1 -> qbar1 qbar2 q2
  int cols[5][2];
  cols[0][0] = 1; cols[0][1] = 2;
  if (idIn > 0) {
    cols[1][0] = 3; cols[1][1] = 0;
    cols[2][0] = 1; cols[2][1] = 0;
    cols[3][0] = 3; cols[3][1] = 0;
    cols[4][0] = 0; cols[4][1] = 2;
  } else {
    cols[1][0] = 0; cols[1][1] = 3;
    cols[2][0] = 0; cols[2][1] = 2;
    cols[3][0] = 0; cols[3][1] = 3;
    cols[4][0] = 1; cols[4][1] = 0;
  }
  // Swap incoming if q/qbar g instead
  if (id2 == 21) {
    swap(cols[0][0], cols[1][0]);
    swap(cols[0][1], cols[1][1]);
  }
  // Map final state
  int i3 = 0, i4 = 0, i5 = 0;
  if (sigmaIdx == 0) {
    switch (config) {
    case 0: i3 = 3; i4 = 2; i5 = 4; break;
    case 1: i3 = 3; i4 = 4; i5 = 2; break;
    case 2: i3 = 2; i4 = 3; i5 = 4; break;
    case 3: i3 = 4; i4 = 3; i5 = 2; break;
    case 4: i3 = 2; i4 = 4; i5 = 3; break;
    case 5: i3 = 4; i4 = 2; i5 = 3; break;
    }
  } else {
    switch (config) {
    case 0: i3 = 2; i4 = 3; i5 = 4; break;
    case 1: i3 = 2; i4 = 4; i5 = 3; break;
    case 2: i3 = 3; i4 = 2; i5 = 4; break;
    case 3: i3 = 4; i4 = 2; i5 = 3; break;
    case 4: i3 = 3; i4 = 4; i5 = 2; break;
    case 5: i3 = 4; i4 = 3; i5 = 2; break;
    }
  }
  setColAcol(cols[0][0],  cols[0][1],  cols[1][0],  cols[1][1],
             cols[i3][0], cols[i3][1], cols[i4][0], cols[i4][1],
             cols[i5][0], cols[i5][1]);
}

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

// Sigma3qq2qqgSame class.
// Cross section for q q' -> q q' g, q == q'.

//--------------------------------------------------------------------------

// Evaluate |M|^2 - no incoming flavour dependence

void Sigma3qq2qqgSame::sigmaKin() {
  // q1(p+) q2(p-) -> q1(q+) q2(q-) g(k)

  // Incoming four-vectors
  pCM[0] = Vec4( 0., 0.,  0.5 * mH, 0.5 * mH);
  pCM[1] = Vec4( 0., 0., -0.5 * mH, 0.5 * mH);
  // Pick/map a final state configuration
  pickFinal();
  mapFinal();

  // |M|^2
  // Extra factor (3) from picking final state configuration
  // (original answer already has a factor 2 from identical
  // quarks in the final state)
  sigma = 3. * m2Calc();

}

//--------------------------------------------------------------------------

// |M|^2

inline double Sigma3qq2qqgSame::m2Calc() {

  // Four-products
  s  = (pCM[0] + pCM[1]).m2Calc();
  t  = (pCM[0] - pCM[2]).m2Calc();
  u  = (pCM[0] - pCM[3]).m2Calc();
  sp = (pCM[2] + pCM[3]).m2Calc();
  tp = (pCM[1] - pCM[3]).m2Calc();
  up = (pCM[1] - pCM[2]).m2Calc();

  // |M|^2
  ssp  = s * sp;
  ttp  = t * tp;
  uup  = u * up;
  s_sp = s + sp;
  t_tp = t + tp;
  u_up = u + up;

  double den1 = (pCM[0] * pCM[4]) * (pCM[1] * pCM[4]) *
                (pCM[2] * pCM[4]) * (pCM[3] * pCM[4]);

  double fac1 = s * (t * u + tp * up) + sp * (t * up + tp * u);
  double fac2 = ssp - ttp - uup;
  double fac3 = 2. * (ttp * u_up + uup * t_tp);

  double num1 = u_up * (ssp + ttp - uup) + fac1;
  double num2 = s_sp * fac2 + fac3;
  double num3 = (s * s + sp * sp + u * u + up * up) / (t * tp);

  double num4 = t_tp * (ssp - ttp + uup) + fac1;
  double num5 = (s * s + sp * sp + t * t + tp * tp) / (u * up);

  double num6 = s_sp * fac2 - fac3 - 2. * fac1;
  double num7 = (s * s + sp * sp) * fac2;
  double den7 = (ttp * uup);

  // C1 = (N^2 - 1)^2 / 4N^3 = 16. / 27.
  // C2 = (N^2 - 1)   / 4N^3 =  2. / 27.
  // C3 = (N^4 - 1)   / 8N^4 = 10. / 81.
  // C4 = (N^2 - 1)^2 / 8N^4 =  8. / 81.
  return (1. / 8.) * pow3(4. * M_PI * alpS) *
         ( ( (16. / 27.) * num1 - (2. / 27.) * num2 ) * num3 +
         ( (16. / 27.) * num4 - (2. / 27.) * num2 ) * num5 +
         ( (10. / 81.) * num2 + (8. / 81.) * num6 ) *
         ( num7 / den7 ) ) / den1;

}

//--------------------------------------------------------------------------

// Evaluate |M|^2 - incoming flavour dependence

double Sigma3qq2qqgSame::sigmaHat() {
  // q q / qbar qbar incoming states only
  if (id1 != id2) return 0.;
  return sigma;
}

//--------------------------------------------------------------------------

// Select identity, colour and anticolour.

void Sigma3qq2qqgSame::setIdColAcol(){

  // Need to know where the gluon was mapped (pCM[4])
  int gIdx = 0;
  switch (config) {
  case 3: case 5: gIdx = 0; break;
  case 1: case 4: gIdx = 1; break;
  case 0: case 2: gIdx = 2; break;
  }

  // Outgoing flavours
  int idTmp[3] = { id1, id1, id1 };
  idTmp[gIdx]  = 21;
  setId(id1, id2, idTmp[0], idTmp[1], idTmp[2]);

  // Temporary solution; start with q q -> q q g
  setColAcol(1, 0, 2, 0, 1, 0, 3, 0, 2, 3);
  // Map gluon
  swap( colSave[5],  colSave[gIdx + 3]);
  swap(acolSave[5], acolSave[gIdx + 3]);
  // Swap if qbar qbar incoming
  if (id1 < 0) swapColAcol();

}

//--------------------------------------------------------------------------

// Map a final state configuration
inline void Sigma3qq2qqgSame::mapFinal() {
  switch (config) {
  case 0: pCM[2] = p3cm; pCM[3] = p4cm; pCM[4] = p5cm; break;
  case 1: pCM[2] = p3cm; pCM[3] = p5cm; pCM[4] = p4cm; break;
  case 2: pCM[2] = p4cm; pCM[3] = p3cm; pCM[4] = p5cm; break;
  case 3: pCM[2] = p4cm; pCM[3] = p5cm; pCM[4] = p3cm; break;
  case 4: pCM[2] = p5cm; pCM[3] = p3cm; pCM[4] = p4cm; break;
  case 5: pCM[2] = p5cm; pCM[3] = p4cm; pCM[4] = p3cm; break;
  }
}

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

// Sigma3qqbar2qqbargSame class.
// Cross section for q qbar -> q qbar g
// Crossed relation from q(bar) q(bar) -> q(bar) q(bar) g:
//   q1(p+) q2(p-) -> q1(q+) q2(q-) g(k)
//--------------------------------------------------------------------------

// Evaluate |M|^2 - no incoming flavour dependence

void Sigma3qqbar2qqbargSame::sigmaKin() {

  // Incoming four-vectors
  pCM[0] = Vec4( 0., 0.,  0.5 * mH, 0.5 * mH);
  pCM[1] = Vec4( 0., 0., -0.5 * mH, 0.5 * mH);

  // Pick and map a final state configuration
  pickFinal();
  mapFinal();

  // Crossing
  swap(pCM[1], pCM[3]);
  pCM[1] = -pCM[1];
  pCM[3] = -pCM[3];

  // |M|^2
  // Extra factor of (6) from picking a final state configuration
  sigma = 6. * m2Calc();

}

//--------------------------------------------------------------------------

// Select identity, colour and anticolour.

void Sigma3qqbar2qqbargSame::setIdColAcol(){
  // Outgoing flavours; easiest to map by hand
  switch (config) {
  case 0: id3 = id1; id4 = id2; id5 = 21;  break;
  case 1: id3 = id1; id4 = 21;  id5 = id2; break;
  case 2: id3 = id2; id4 = id1; id5 = 21;  break;
  case 3: id3 = 21;  id4 = id1; id5 = id2; break;
  case 4: id3 = id2; id4 = 21;  id5 = id1; break;
  case 5: id3 = 21;  id4 = id2; id5 = id1; break;
  }
  setId(id1, id2, id3, id4, id5);

  // Temporary solution; start with q qbar -> q qbar g
  int cols[5][2];
  cols[0][0] = 1; cols[0][1] = 0;
  cols[1][0] = 0; cols[1][1] = 2;
  cols[2][0] = 1; cols[2][1] = 0;
  cols[3][0] = 0; cols[3][1] = 3;
  cols[4][0] = 3; cols[4][1] = 2;
  // Map final state
  int i3 = 0, i4 = 0, i5 = 0;
  switch (config) {
  case 0: i3 = 2; i4 = 3; i5 = 4; break;
  case 1: i3 = 2; i4 = 4; i5 = 3; break;
  case 2: i3 = 3; i4 = 2; i5 = 4; break;
  case 3: i3 = 4; i4 = 2; i5 = 3; break;
  case 4: i3 = 3; i4 = 4; i5 = 2; break;
  case 5: i3 = 4; i4 = 3; i5 = 2; break;
  }
  setColAcol(cols[0][0],  cols[0][1],  cols[1][0],  cols[1][1],
             cols[i3][0], cols[i3][1], cols[i4][0], cols[i4][1],
             cols[i5][0], cols[i5][1]);
  // Swap for qbar q incoming
  if (id1 < 0) swapColAcol();
}

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

// Sigma3qg2qqqbarSame class.
// Cross section for q g -> q q qbar.
// Crossed relation from q(bar) q(bar) -> q(bar) q(bar) g:
//   q1(p+) q1(p-) -> q1(q+) q1(q-) g(k)

//--------------------------------------------------------------------------

// Evaluate |M|^2 - no incoming flavour dependence

void Sigma3qg2qqqbarSame::sigmaKin() {

  // Pick a final state configuration
  pickFinal();

  // gq and qg incoming
  for (int i = 0; i < 2; i++) {

    // Map incoming four-vectors
    pCM[0] = Vec4( 0., 0.,  0.5 * mH, 0.5 * mH);
    pCM[1] = Vec4( 0., 0., -0.5 * mH, 0.5 * mH);
    mapFinal();

    // Crossing (note extra -ve sign in total sigma)
    swap(pCM[i], pCM[4]);
    pCM[i] = -pCM[i];
    pCM[4] = -pCM[4];

    // |M|^2
    // XXX - Extra factor of (3) from picking a final state configuration???
    // Extra factor of (3 / 8) as averaging over incoming gluon
    sigma[i] = -3. * (3. / 8.) * m2Calc();

  }

}

//--------------------------------------------------------------------------

// Evaluate |M|^2 - incoming flavour dependence

double Sigma3qg2qqqbarSame::sigmaHat() {
  // gq or qg incoming
  return (id1 == 21) ? sigma[0] : sigma[1];
}

//--------------------------------------------------------------------------

// Select identity, colour and anticolour.

void Sigma3qg2qqqbarSame::setIdColAcol(){

  // Pick outgoing flavour configuration
  int idIn = (id1 == 21) ? id2 : id1;

  // Outgoing flavours; easiest just to map by hand
  switch (config) {
  case 0: id3 =  idIn; id4 =  idIn;   id5 = -idIn; break;
  case 1: id3 =  idIn; id4 = -idIn;   id5 =  idIn; break;
  case 2: id3 =  idIn; id4 =  idIn;   id5 = -idIn; break;
  case 3: id3 = -idIn; id4 =  idIn;   id5 =  idIn; break;
  case 4: id3 =  idIn; id4 = -idIn;   id5 =  idIn; break;
  case 5: id3 = -idIn; id4 =  idIn;   id5 =  idIn; break;
  }
  setId(id1, id2, id3, id4, id5);

  // Temporary solution; start with either
  // g q1    -> q1    q2 qbar2
  // g qbar1 -> qbar1 qbar2 q2
  int cols[5][2];
  cols[0][0] = 1; cols[0][1] = 2;
  if (idIn > 0) {
    cols[1][0] = 3; cols[1][1] = 0;
    cols[2][0] = 1; cols[2][1] = 0;
    cols[3][0] = 3; cols[3][1] = 0;
    cols[4][0] = 0; cols[4][1] = 2;
  } else {
    cols[1][0] = 0; cols[1][1] = 3;
    cols[2][0] = 0; cols[2][1] = 2;
    cols[3][0] = 0; cols[3][1] = 3;
    cols[4][0] = 1; cols[4][1] = 0;
  }
  // Swap incoming if q/qbar g instead
  if (id2 == 21) {
    swap(cols[0][0], cols[1][0]);
    swap(cols[0][1], cols[1][1]);
  }
  // Map final state
  int i3 = 0, i4 = 0, i5 = 0;
  switch (config) {
  case 0: i3 = 2; i4 = 3; i5 = 4; break;
  case 1: i3 = 2; i4 = 4; i5 = 3; break;
  case 2: i3 = 3; i4 = 2; i5 = 4; break;
  case 3: i3 = 4; i4 = 2; i5 = 3; break;
  case 4: i3 = 3; i4 = 4; i5 = 2; break;
  case 5: i3 = 4; i4 = 3; i5 = 2; break;
  }
  setColAcol(cols[0][0],  cols[0][1],  cols[1][0],  cols[1][1],
             cols[i3][0], cols[i3][1], cols[i4][0], cols[i4][1],
             cols[i5][0], cols[i5][1]);
}

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

} // end namespace Pythia8
