diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..27ce9b2
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,17 @@
+## Authors of the HEJ collaboration
+(sorted by first name)
+
+# Current contributors
+
+Andreas Maier
+Helen Brooks
+James Black
+Jennifer Smillie
+Jeppe R. Andersen
+Marian Heil
+
+# Former contributors
+
+James Cockburn
+Tuomas Hapola
+Jack J. Medley
diff --git a/FixedOrderGen/cmake/Templates/Version.hh.in b/FixedOrderGen/cmake/Templates/Version.hh.in
index 0d3e63a..ad8fc13 100644
--- a/FixedOrderGen/cmake/Templates/Version.hh.in
+++ b/FixedOrderGen/cmake/Templates/Version.hh.in
@@ -1,47 +1,50 @@
-/** \file Version.hh
- *  \brief The file gives the current HEJ Fixed Order Generator Version
+/** \file      Version.hh
+ *  \brief     The file gives the current HEJ Fixed Order Generator Version
+ *
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
+ *  \date      2019
+ *  \copyright GPLv2 or later
  */
-
 #pragma once
 
 #include <string>
 
 /// @brief Full name of this package.
 #define HEJFOG_PACKAGE_NAME  "@PROJECT_NAME@"
 
 /// @brief Version string of this package
 #define HEJFOG_VERSION "@PROJECT_VERSION@"
 
 /// @brief Full name and version of this package.
 #define HEJFOG_PACKAGE_STRING  "@PROJECT_NAME@ @PROJECT_VERSION@"
 
 /// @brief Major version of this package
 #define HEJFOG_VERSION_MAJOR  @PROJECT_VERSION_MAJOR@
 
 /// @brief Minor version of this package
 #define HEJFOG_VERSION_MINOR  @PROJECT_VERSION_MINOR@
 
 /// @brief Patch version of this package
 #define HEJFOG_VERSION_PATCH  @PROJECT_VERSION_PATCH@
 
 /// @brief Git revision of this package
 #define HEJFOG_GIT_revision  "@PROJECT_GIT_REVISION@"
 
 /// @brief Git branch name of this package
 #define HEJFOG_GIT_branch  "@PROJECT_GIT_BRANCH@"
 
 
 namespace HEJFOG {
 
   namespace Version {
 
     inline std::string String()             { return HEJFOG_VERSION; }
     inline std::string package_name()       { return HEJFOG_PACKAGE_NAME; }
     inline std::string package_name_full()  { return HEJFOG_PACKAGE_STRING; }
     inline int Major()                      { return HEJFOG_VERSION_MAJOR; }
     inline int Minor()                      { return HEJFOG_VERSION_MINOR; }
     inline int Patch()                      { return HEJFOG_VERSION_PATCH; }
     inline std::string revision()           { return HEJFOG_GIT_revision; }
 
   };
 }
diff --git a/FixedOrderGen/include/Beam.hh b/FixedOrderGen/include/Beam.hh
index 3d61ace..73f7380 100644
--- a/FixedOrderGen/include/Beam.hh
+++ b/FixedOrderGen/include/Beam.hh
@@ -1,14 +1,19 @@
+/**
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
+ *  \date      2019
+ *  \copyright GPLv2 or later
+ */
 #pragma once
 
 #include <array>
 
 #include "HEJ/PDG_codes.hh"
 
 namespace HEJFOG{
   struct Beam{
     double energy;
     std::array<HEJ::ParticleID, 2> particles{{
       HEJ::pid::proton, HEJ::pid::proton
     }};
   };
 }
diff --git a/FixedOrderGen/include/Decay.hh b/FixedOrderGen/include/Decay.hh
index ffe883c..64cae8d 100644
--- a/FixedOrderGen/include/Decay.hh
+++ b/FixedOrderGen/include/Decay.hh
@@ -1,11 +1,16 @@
+/**
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
+ *  \date      2019
+ *  \copyright GPLv2 or later
+ */
 #pragma once
 
 #include "HEJ/PDG_codes.hh"
 #include <vector>
 
 namespace HEJFOG{
   struct Decay{
     std::vector<HEJ::pid::ParticleID> products;
     double branching_ratio;
   };
 }
diff --git a/FixedOrderGen/include/EventGenerator.hh b/FixedOrderGen/include/EventGenerator.hh
index e2064c7..896f0f6 100644
--- a/FixedOrderGen/include/EventGenerator.hh
+++ b/FixedOrderGen/include/EventGenerator.hh
@@ -1,57 +1,62 @@
+/**
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
+ *  \date      2019
+ *  \copyright GPLv2 or later
+ */
 #pragma once
 
 #include "HEJ/MatrixElement.hh"
 #include "HEJ/optional.hh"
 #include "HEJ/PDF.hh"
 #include "HEJ/RNG.hh"
 
 #include "Beam.hh"
 #include "JetParameters.hh"
 #include "ParticleProperties.hh"
 #include "Process.hh"
 #include "Status.hh"
 
 namespace HEJ{
   class Event;
   class HiggsCouplingSettings;
   class ScaleGenerator;
 }
 
 //! Namespace for HEJ Fixed Order Generator
 namespace HEJFOG{
   class EventGenerator{
   public:
     EventGenerator(
         Process process,
         Beam beam,
         HEJ::ScaleGenerator scale_gen,
         JetParameters jets,
         int pdf_id,
         double subl_change,
         unsigned int subl_channels,
         ParticlesPropMap particles_properties,
         HEJ::HiggsCouplingSettings Higgs_coupling,
         HEJ::RNG & ran
     );
 
     HEJ::optional<HEJ::Event> gen_event();
 
     Status status() const {
       return status_;
     }
 
   private:
     HEJ::PDF pdf_;
     HEJ::MatrixElement ME_;
     HEJ::ScaleGenerator scale_gen_;
     Process process_;
     JetParameters jets_;
     Beam beam_;
     Status status_;
     double subl_change_;
     unsigned int subl_channels_;
     ParticlesPropMap particles_properties_;
     std::reference_wrapper<HEJ::RNG> ran_;
   };
 
 }
diff --git a/FixedOrderGen/include/JetParameters.hh b/FixedOrderGen/include/JetParameters.hh
index 4c65067..8649fff 100644
--- a/FixedOrderGen/include/JetParameters.hh
+++ b/FixedOrderGen/include/JetParameters.hh
@@ -1,14 +1,19 @@
+/**
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
+ *  \date      2019
+ *  \copyright GPLv2 or later
+ */
 #pragma once
 
 #include "fastjet/JetDefinition.hh"
 
 #include "HEJ/optional.hh"
 
 namespace HEJFOG{
   struct JetParameters{
     fastjet::JetDefinition def;
     double min_pt;
     double max_y;
     HEJ::optional<double> peak_pt;
   };
 }
diff --git a/FixedOrderGen/include/ParticleProperties.hh b/FixedOrderGen/include/ParticleProperties.hh
index 08a296d..d38a0ec 100644
--- a/FixedOrderGen/include/ParticleProperties.hh
+++ b/FixedOrderGen/include/ParticleProperties.hh
@@ -1,23 +1,28 @@
+/**
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
+ *  \date      2019
+ *  \copyright GPLv2 or later
+ */
 #pragma once
 
 #include <vector>
 #include <unordered_map>
 
 #include "Decay.hh"
 
 namespace HEJFOG{
   struct ParticleProperties{
     double mass;
     double width;
     std::vector<Decay> decays;
   };
   #if !defined(__clang__) && defined(__GNUC__) && (__GNUC__ < 6)
   // gcc version < 6 explicitly needs hash function for enum
   // see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60970
   using ParticlesPropMap
     = std::unordered_map<HEJ::ParticleID, ParticleProperties, std::hash<int>>;
   #else
   using ParticlesPropMap
     = std::unordered_map<HEJ::ParticleID, ParticleProperties>;
   #endif
 }
diff --git a/FixedOrderGen/include/PhaseSpacePoint.hh b/FixedOrderGen/include/PhaseSpacePoint.hh
index 32f6378..7587acb 100644
--- a/FixedOrderGen/include/PhaseSpacePoint.hh
+++ b/FixedOrderGen/include/PhaseSpacePoint.hh
@@ -1,219 +1,222 @@
-/** \file PhaseSpacePoint.hh
- *  \brief Contains the PhaseSpacePoint Class
+/** \file      PhaseSpacePoint.hh
+ *  \brief     Contains the PhaseSpacePoint Class
+ *
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
+ *  \date      2019
+ *  \copyright GPLv2 or later
  */
-
 #pragma once
 
 #include <bitset>
 #include <vector>
 
 #include "HEJ/Event.hh"
 #include "HEJ/Particle.hh"
 #include "HEJ/PDF.hh"
 #include "HEJ/PDG_codes.hh"
 #include "HEJ/RNG.hh"
 
 #include "JetParameters.hh"
 #include "ParticleProperties.hh"
 #include "Status.hh"
 
 namespace HEJFOG{
   class Process;
 
   using HEJ::Particle;
   //! A point in resummation phase space
   class PhaseSpacePoint{
   public:
     //! Default PhaseSpacePoint Constructor
     PhaseSpacePoint() = default;
 
     //! PhaseSpacePoint Constructor
     /**
      * @param proc                  The process to generate
      * @param jet_properties        Jet defintion & cuts
      * @param pdf                   The pdf set (used for sampling)
      * @param E_beam                Energie of the beam
      * @param subl_chance           Chance to turn a potentially unordered
      *                              emission into an actual one
      * @param subl_channels         Possible subleading channels.
      *                              see HEJFOG::Subleading
      * @param particle_properties   Properties of producted boson
      *
      * Initially, only FKL phase space points are generated. subl_chance gives
      * the change of turning one emissions into a subleading configuration,
      * i.e. either unordered or central quark/anti-quark pair. Unordered
      * emissions require that the most extremal emission in any direction is
      * a quark or anti-quark and the next emission is a gluon. Quark/anti-quark
      * pairs are only generated for W processes. At most one subleading
      * emission will be generated in this way.
      */
     PhaseSpacePoint(
       Process const & proc,
       JetParameters const & jet_properties,
       HEJ::PDF & pdf, double E_beam,
       double subl_chance,
       unsigned int subl_channels,
       ParticlesPropMap const & particles_properties,
       HEJ::RNG & ran
     );
 
     //! Get Weight Function
     /**
      * @returns        Weight of Event
      */
     double weight() const{
       return weight_;
     }
 
     Status status() const{
       return status_;
     }
 
     //! Get Incoming Function
     /**
      * @returns        Incoming Particles
      */
     std::array<Particle, 2> const & incoming() const{
       return incoming_;
     }
 
     //! Get Outgoing Function
     /**
      * @returns        Outgoing Particles
      */
     std::vector<Particle> const & outgoing() const{
       return outgoing_;
     }
 
     std::unordered_map<size_t, std::vector<Particle>> const & decays() const{
       return decays_;
     }
 
   private:
     /**
      * @internal
      * @brief Generate LO parton momentum
      *
      * @param count             Number of partons to generate
      * @param is_pure_jets      If true ensures momentum conservation in x and y
      * @param jet_param         Jet properties to fulfil
      * @param max_pt            max allowed pt for a parton (typically E_CMS)
      * @param ran               Random Number Generator
      *
      * @returns                 Momentum of partons
      *
      * Ensures that each parton is in its own jet.
      * Generation is independent of parton flavour. Output is sorted in rapidity.
      */
     std::vector<fastjet::PseudoJet> gen_LO_partons(
         int count, bool is_pure_jets,
         JetParameters const & jet_param,
         double max_pt,
         HEJ::RNG & ran
     );
     std::vector<Particle> gen_enu(
       std::vector<HEJ::pid::ParticleID> const & pair, HEJ::RNG & ran
     );
     Particle gen_boson(
         HEJ::ParticleID bosonid, double mass, double width,
         HEJ::RNG & ran
     );
     template<class ParticleMomenta>
     fastjet::PseudoJet gen_last_momentum(
         ParticleMomenta const & other_momenta,
         double mass_square, double y
     ) const;
 
     bool jets_ok(
         std::vector<fastjet::PseudoJet> const & Born_jets,
         std::vector<fastjet::PseudoJet> const & partons
     ) const;
     /**
      * @internal
      * @brief Generate incoming partons according to the PDF
      *
      * @param uf                Scale used in the PDF
      */
     void reconstruct_incoming(
         Process const & proc, unsigned int subl_channels,
         HEJ::PDF & pdf, double E_beam,
         double uf,
         HEJ::RNG & ran
     );
     /**
      * @internal
      * @brief Returns list of all allowed initial states partons
      */
     std::array<std::bitset<11>,2> filter_partons(
         Process const & proc, unsigned int const subl_channels,
         HEJ::RNG & ran
     );
     HEJ::ParticleID generate_incoming_id(
         size_t beam_idx, double x, double uf, HEJ::PDF & pdf,
         std::bitset<11> allowed_partons, HEJ::RNG & ran
     );
 
     bool momentum_conserved(double ep) const;
 
     HEJ::Particle const & most_backward_FKL(
         std::vector<HEJ::Particle> const & partons
     ) const;
     HEJ::Particle const & most_forward_FKL(
         std::vector<HEJ::Particle> const & partons
     ) const;
     HEJ::Particle & most_backward_FKL(std::vector<HEJ::Particle> & partons) const;
     HEJ::Particle & most_forward_FKL(std::vector<HEJ::Particle> & partons) const;
     bool extremal_FKL_ok(
         std::vector<fastjet::PseudoJet> const & partons
     ) const;
     double random_normal(double stddev, HEJ::RNG & ran);
     /**
      * @internal
      * @brief Turns a FKL configuration into a subleading one
      *
      * @param chance            Change to switch to subleading configuration
      * @param channels          Allowed channels for subleading process
      * @param proc              Process to decide which subleading
      *                          configurations are allowed
      *
      * With a chance of "chance" the FKL configuration is either turned into
      * a unordered configuration or, for A/W/Z bosons, a configuration with
      * a central quark/anti-quark pair.
      */
     void maybe_turn_to_subl(double chance, unsigned int channels,
         Process const & proc, HEJ::RNG & ran);
     void turn_to_uno(bool can_be_uno_backward, bool can_be_uno_forward, HEJ::RNG & ran);
     void turn_to_qqx(bool allow_strange, HEJ::RNG & ran);
     std::vector<Particle> decay_boson(
         HEJ::Particle const & parent,
         std::vector<Decay> const & decays,
         HEJ::RNG & ran
     );
     /// @brief setup outgoing partons to ensure correct coupling to boson
     void couple_boson(HEJ::ParticleID boson, HEJ::RNG & ran);
     Decay select_decay_channel(
         std::vector<Decay> const & decays,
         HEJ::RNG & ran
     );
     double gen_hard_pt(
         int np, double ptmin, double ptmax, double y,
         HEJ::RNG & ran
     );
     double gen_soft_pt(int np, double ptmax, HEJ::RNG & ran);
     double gen_parton_pt(
         int count, JetParameters const & jet_param, double ptmax, double y,
         HEJ::RNG & ran
     );
 
 
     double weight_;
 
     Status status_;
 
     std::array<Particle, 2> incoming_;
     std::vector<Particle> outgoing_;
     //! Particle decays in the format {outgoing index, decay products}
     std::unordered_map<size_t, std::vector<Particle>> decays_;
   };
   HEJ::Event::EventData to_EventData(PhaseSpacePoint const & psp);
 }
diff --git a/FixedOrderGen/include/Process.hh b/FixedOrderGen/include/Process.hh
index 33d0a48..d2b0beb 100644
--- a/FixedOrderGen/include/Process.hh
+++ b/FixedOrderGen/include/Process.hh
@@ -1,17 +1,22 @@
+/**
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
+ *  \date      2019
+ *  \copyright GPLv2 or later
+ */
 #pragma once
 
 #include <array>
 #include <vector>
 
 #include "HEJ/PDG_codes.hh"
 #include "HEJ/optional.hh"
 
 namespace HEJFOG{
   struct Process{
     std::array<HEJ::ParticleID, 2> incoming;
     int njets;
     HEJ::optional<HEJ::ParticleID> boson;
     std::vector <HEJ::ParticleID> leptons;
   };
 
 }
diff --git a/FixedOrderGen/include/Status.hh b/FixedOrderGen/include/Status.hh
index 599af0b..5639b49 100644
--- a/FixedOrderGen/include/Status.hh
+++ b/FixedOrderGen/include/Status.hh
@@ -1,22 +1,27 @@
+/**
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
+ *  \date      2019
+ *  \copyright GPLv2 or later
+ */
 #pragma once
 
 #include <string>
 #include <stdexcept>
 
 namespace HEJFOG{
   enum Status{
     good,
     not_enough_jets,
     too_much_energy
   };
 
   inline std::string to_string(Status s){
     switch(s){
       case good: return "good";
       case not_enough_jets: return "not enough jets";
       case too_much_energy: return "too much energy";
       default:;
     }
     throw std::logic_error{"unreachable"};
   }
 }
diff --git a/FixedOrderGen/include/Subleading.hh b/FixedOrderGen/include/Subleading.hh
index 57ea71a..c209b2d 100644
--- a/FixedOrderGen/include/Subleading.hh
+++ b/FixedOrderGen/include/Subleading.hh
@@ -1,15 +1,20 @@
+/**
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
+ *  \date      2019
+ *  \copyright GPLv2 or later
+ */
 #pragma once
 
 namespace HEJFOG{
   /**
    * Bit position of different subleading channels
    * e.g. (unsigned int) 1 => only unordered
    */
   enum Subleading: unsigned {
     none = 0u,
     all = ~0u,
     uno = 1u,
     unordered = uno,
     qqx = 2u
   };
 }
diff --git a/FixedOrderGen/include/UnweightSettings.hh b/FixedOrderGen/include/UnweightSettings.hh
index a633622..f552638 100644
--- a/FixedOrderGen/include/UnweightSettings.hh
+++ b/FixedOrderGen/include/UnweightSettings.hh
@@ -1,8 +1,13 @@
+/**
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
+ *  \date      2019
+ *  \copyright GPLv2 or later
+ */
 #pragma once
 
 namespace HEJFOG {
   struct UnweightSettings {
     int sample_size;
     double max_dev;
   };
 }
diff --git a/FixedOrderGen/include/Unweighter.hh b/FixedOrderGen/include/Unweighter.hh
index e7fe40b..5223e9d 100644
--- a/FixedOrderGen/include/Unweighter.hh
+++ b/FixedOrderGen/include/Unweighter.hh
@@ -1,72 +1,77 @@
+/**
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
+ *  \date      2019
+ *  \copyright GPLv2 or later
+ */
 #pragma once
 
 #include <limits>
 #include <cmath>
 
 #include "HEJ/optional.hh"
 #include "HEJ/RNG.hh"
 
 namespace HEJ {
   class Event;
 }
 
 namespace HEJFOG {
   namespace detail {
 
     bool has_jet_softer_than(HEJ::Event const & ev, double pt);
 
     template<typename Iterator>
     double calc_cut(
         Iterator begin, Iterator end, double max_dev,
         double min_pt
     ) {
       double mean = 0.;
       double err = 0.;
       double awt_sum = 0.;
       for(; begin != end; ++begin){
         if(has_jet_softer_than(*begin, min_pt)) continue;
         const double awt = std::abs(begin->central().weight);
         const double tmp = awt*std::log(awt);
         mean += tmp;
         err += tmp*tmp;
         awt_sum += awt;
       }
       mean /= awt_sum;
       err = std::sqrt(err)/awt_sum;
       return std::exp(mean + max_dev*err);
     }
   }
 
   class Unweighter {
 
   public:
     template<typename Iterator>
     Unweighter(
         Iterator begin, Iterator end, double max_dev,
         HEJ::RNG & ran,
         /* minimum pt of jets for an event to be considered for unweighting
          *
          * If the 'jets: peak pt' option is set to the *resummation* jet
          * threshold, events with softer jets will have a spurious
          * large weight, although they hardly contribute after resummation.
          * This destroys the unweighting efficiency.
          * By setting min_unweight_pt to the same threshold, we can exclude
          * these events from unweighting.
          */
         double min_unweight_pt = 0.
     ):
       cut_{detail::calc_cut(begin, end, max_dev, min_unweight_pt)},
       min_unweight_pt_{min_unweight_pt},
       ran_{ran}
     {}
 
     HEJ::optional<HEJ::Event> unweight(HEJ::Event ev) const;
 
   private:
     double cut_;
     double min_unweight_pt_;
     std::reference_wrapper<HEJ::RNG> ran_;
     std::function<bool(HEJ::Event const &)> unweight_ok_;
   };
 
 }
diff --git a/FixedOrderGen/include/config.hh b/FixedOrderGen/include/config.hh
index e34b2ec..71c748b 100644
--- a/FixedOrderGen/include/config.hh
+++ b/FixedOrderGen/include/config.hh
@@ -1,38 +1,43 @@
+/**
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
+ *  \date      2019
+ *  \copyright GPLv2 or later
+ */
 #pragma once
 
 #include "yaml-cpp/yaml.h"
 
 #include "HEJ/HiggsCouplingSettings.hh"
 #include "HEJ/optional.hh"
 #include "HEJ/config.hh"
 #include "HEJ/output_formats.hh"
 #include "HEJ/exceptions.hh"
 
 #include "Process.hh"
 #include "JetParameters.hh"
 #include "Beam.hh"
 #include "ParticleProperties.hh"
 #include "UnweightSettings.hh"
 
 namespace HEJFOG{
 
   struct Config{
     Process process;
     int events;
     JetParameters jets;
     Beam beam;
     int pdf_id;
     double subleading_fraction;
     unsigned int subleading_channels; //! < see HEJFOG::Subleading
     ParticlesPropMap particles_properties;
     YAML::Node analysis_parameters;
     HEJ::ScaleConfig scales;
     std::vector<HEJ::OutputFile> output;
     HEJ::RNGConfig rng;
     HEJ::HiggsCouplingSettings Higgs_coupling;
     HEJ::optional<UnweightSettings> unweight;
   };
 
   Config load_config(std::string const & config_file);
 
 }
diff --git a/FixedOrderGen/src/EventGenerator.cc b/FixedOrderGen/src/EventGenerator.cc
index c97839a..da0f2a5 100644
--- a/FixedOrderGen/src/EventGenerator.cc
+++ b/FixedOrderGen/src/EventGenerator.cc
@@ -1,80 +1,85 @@
+/**
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
+ *  \date      2019
+ *  \copyright GPLv2 or later
+ */
 #include "EventGenerator.hh"
 
 #include "Process.hh"
 #include "Beam.hh"
 #include "JetParameters.hh"
 #include "PhaseSpacePoint.hh"
 
 #include "HEJ/Event.hh"
 #include "HEJ/config.hh"
 
 namespace HEJFOG{
   EventGenerator::EventGenerator(
       Process process,
       Beam beam,
       HEJ::ScaleGenerator scale_gen,
       JetParameters jets,
       int pdf_id,
       double subl_change,
       unsigned int subl_channels,
       ParticlesPropMap particles_properties,
       HEJ::HiggsCouplingSettings Higgs_coupling,
       HEJ::RNG & ran
   ):
     pdf_{pdf_id, beam.particles[0], beam.particles[1]},
     ME_{
       [this](double mu){ return pdf_.Halphas(mu); },
       HEJ::MatrixElementConfig{
         false,
         std::move(Higgs_coupling)
       }
     },
     scale_gen_{std::move(scale_gen)},
     process_{std::move(process)},
     jets_{std::move(jets)},
     beam_{std::move(beam)},
     subl_change_{subl_change},
     subl_channels_{subl_channels},
     particles_properties_{std::move(particles_properties)},
     ran_{ran}
   {
   }
 
   HEJ::optional<HEJ::Event> EventGenerator::gen_event(){
     HEJFOG::PhaseSpacePoint psp{
       process_,
       jets_,
       pdf_, beam_.energy,
       subl_change_, subl_channels_,
       particles_properties_,
       ran_
     };
     status_ = psp.status();
     if(status_ != good) return {};
 
     HEJ::Event ev = scale_gen_(
         HEJ::Event{
           to_EventData( std::move(psp) ).cluster( jets_.def, jets_.min_pt)
         }
     );
 
     ev.generate_colours(ran_);
 
     const double shat = HEJ::shat(ev);
     const double xa = (ev.incoming()[0].E()-ev.incoming()[0].pz())/(2.*beam_.energy);
     const double xb = (ev.incoming()[1].E()+ev.incoming()[1].pz())/(2.*beam_.energy);
 
     // evaluate matrix element
     ev.parameters() *= ME_.tree(ev)/(shat*shat);
     // and PDFs
     ev.central().weight *= pdf_.pdfpt(0,xa,ev.central().muf, ev.incoming()[0].type);
     ev.central().weight *= pdf_.pdfpt(0,xb,ev.central().muf, ev.incoming()[1].type);
     for(size_t i = 0; i < ev.variations().size(); ++i){
       auto & var = ev.variations(i);
       var.weight *= pdf_.pdfpt(0,xa,var.muf, ev.incoming()[0].type);
       var.weight *= pdf_.pdfpt(0,xb,var.muf, ev.incoming()[1].type);
     }
     return ev;
   }
 
 }
diff --git a/FixedOrderGen/src/PhaseSpacePoint.cc b/FixedOrderGen/src/PhaseSpacePoint.cc
index 46f2473..d5d80d5 100644
--- a/FixedOrderGen/src/PhaseSpacePoint.cc
+++ b/FixedOrderGen/src/PhaseSpacePoint.cc
@@ -1,737 +1,742 @@
+/**
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
+ *  \date      2019
+ *  \copyright GPLv2 or later
+ */
 #include "PhaseSpacePoint.hh"
 
 #include <algorithm>
 
 #include "CLHEP/Vector/LorentzVector.h"
 
 #include "HEJ/Constants.hh"
 #include "HEJ/exceptions.hh"
 #include "HEJ/kinematics.hh"
 #include "HEJ/Particle.hh"
 #include "HEJ/utility.hh"
 
 #include "Process.hh"
 #include "Subleading.hh"
 
 using namespace HEJ;
 
 namespace HEJFOG{
 
   static_assert(
       std::numeric_limits<double>::has_quiet_NaN,
       "no quiet NaN for double"
   );
   constexpr double NaN = std::numeric_limits<double>::quiet_NaN();
 
   HEJ::Event::EventData to_EventData(PhaseSpacePoint const & psp){
     HEJ::Event::EventData result;
     result.incoming = psp.incoming();
     assert(result.incoming.size() == 2);
     result.outgoing=psp.outgoing();
       // technically Event::EventData doesn't have to be sorted,
       // but PhaseSpacePoint should be anyway
     assert(
         std::is_sorted(
             begin(result.outgoing), end(result.outgoing),
             HEJ::rapidity_less{}
         )
     );
     assert(result.outgoing.size() >= 2);
     result.decays=psp.decays();
     result.parameters.central= {NaN, NaN, psp.weight() };
     return result;
   }
 
   namespace{
     bool can_swap_to_uno(
         HEJ::Particle const & p1, HEJ::Particle const & p2
     ){
       return is_parton(p1)
         && p1.type != pid::gluon
         && p2.type == pid::gluon;
     }
 
     size_t count_gluons(std::vector<Particle>::const_iterator first,
       std::vector<Particle>::const_iterator last){
       return std::count_if(first, last, [](Particle const & p)
         {return p.type == pid::gluon;});
     }
 
     /** assumes FKL configurations between first and last,
      * else there can be a quark in a non-extreme position
      * e.g. uno configuration gqg would pass
      */
     bool can_change_to_qqx(
       std::vector<Particle>::const_iterator first,
       std::vector<Particle>::const_iterator last){
       return 1 < count_gluons(first,last);
     }
     bool is_AWZ_proccess(Process const & proc){
       return proc.boson && is_AWZ_boson(*proc.boson);
     }
 
     bool is_up_type(Particle const & part){
       return HEJ::is_anyquark(part) && !(abs(part.type)%2);
     }
     bool is_down_type(Particle const & part){
       return HEJ::is_anyquark(part) && abs(part.type)%2;
     }
     bool can_couple_to_W(Particle const & part, pid::ParticleID const W_id){
       const int W_charge = W_id>0?1:-1;
       return abs(part.type)<5
         && ( (W_charge*part.type > 0 && is_up_type(part))
           || (W_charge*part.type < 0 && is_down_type(part)) );
     }
   }
 
   void PhaseSpacePoint::maybe_turn_to_subl(
       double chance,
       unsigned int const channels,
       Process const & proc,
       HEJ::RNG & ran
   ){
     if(proc.njets <= 2) return;
     assert(outgoing_.size() >= 2);
 
     // decide what kind of subleading process is allowed
     bool allow_uno = false;
     bool allow_strange = true;
     const size_t nout = outgoing_.size();
     const bool can_be_uno_backward = (channels&Subleading::uno)
       && can_swap_to_uno(outgoing_[0], outgoing_[1]);
     const bool can_be_uno_forward = (channels&Subleading::uno)
       && can_swap_to_uno(outgoing_[nout-1], outgoing_[nout-2]);
     allow_uno = can_be_uno_backward || can_be_uno_forward;
 
     bool allow_qqx = false;
     if(is_AWZ_proccess(proc)) {
       allow_qqx = (channels&Subleading::qqx)
         && can_change_to_qqx(outgoing_.cbegin(), outgoing_.cend());
       if(std::none_of(outgoing_.cbegin(), outgoing_.cend(),
           [&proc](Particle const & p){ return can_couple_to_W(p, *proc.boson);})) {
         // enforce qqx if A/W/Z can't couple somewhere else
         assert(allow_qqx);
         allow_uno = false;
         chance = 1.;
         // strange not allowed for W
         if(abs(*proc.boson)== pid::Wp) allow_strange = false;
       }
     }
 
     if(!allow_uno && !allow_qqx) return;
     if(ran.flat() < chance){
       weight_ /= chance;
       if(allow_uno && !allow_qqx){
           turn_to_uno(can_be_uno_backward, can_be_uno_forward, ran);
       } else if (!allow_uno && allow_qqx) {
         turn_to_qqx(allow_strange, ran);
       } else {
         assert( allow_uno && allow_qqx);
         if(ran.flat() < 0.5) turn_to_uno(can_be_uno_backward, can_be_uno_forward, ran);
         else turn_to_qqx(allow_strange, ran);
         weight_ *= 2.;
       }
     } else weight_ /= 1 - chance;
 
   }
 
   void PhaseSpacePoint::turn_to_uno(
       const bool can_be_uno_backward, const bool can_be_uno_forward,
       HEJ::RNG & ran
   ){
     if(!can_be_uno_backward && !can_be_uno_forward) return;
     const size_t nout = outgoing_.size();
     if(can_be_uno_backward && can_be_uno_forward){
       if(ran.flat() < 0.5){
         std::swap(outgoing_[0].type, outgoing_[1].type);
       } else {
         std::swap(outgoing_[nout-1].type, outgoing_[nout-2].type);
       }
       weight_ *= 2.;
     } else if(can_be_uno_backward){
       std::swap(outgoing_[0].type, outgoing_[1].type);
     } else {
       assert(can_be_uno_forward);
       std::swap(outgoing_[nout-1].type, outgoing_[nout-2].type);
     }
   }
 
   void PhaseSpacePoint::turn_to_qqx(const bool allow_strange, HEJ::RNG & ran){
     /// find first and last gluon in FKL chain
     auto first = std::find_if(outgoing_.begin(), outgoing_.end(),
       [](Particle const & p){return p.type == pid::gluon;});
     std::vector<Particle*> FKL_gluons;
     for(auto p = first; p!=outgoing_.end(); ++p){
       if(p->type == pid::gluon) FKL_gluons.push_back(&*p);
       else if(is_anyquark(*p)) break;
     }
     const size_t ng = FKL_gluons.size();
     if(ng < 2)
       throw std::logic_error("not enough gluons to create qqx");
     // select flavour of quark
     const double r1 = 2.*ran.flat()-1.;
     const double max_flavour = allow_strange?n_f:n_f-1;
     weight_ *= max_flavour*2;
     int flavour = pid::down + std::floor(std::abs(r1)*max_flavour);
     flavour*=r1<0.?-1:1;
     // select gluon for switch
     const size_t idx = floor((ng-1) * ran.flat());
     weight_ *= (ng-1);
     FKL_gluons[idx]->type = ParticleID(flavour);
     FKL_gluons[idx+1]->type = ParticleID(-flavour);
   }
 
   template<class ParticleMomenta>
   fastjet::PseudoJet PhaseSpacePoint::gen_last_momentum(
       ParticleMomenta const & other_momenta,
       const double mass_square, const double y
   ) const {
     std::array<double,2> pt{0.,0.};
     for (auto const & p: other_momenta) {
       pt[0]-= p.px();
       pt[1]-= p.py();
     }
 
     const double mperp = sqrt(pt[0]*pt[0]+pt[1]*pt[1]+mass_square);
     const double pz=mperp*sinh(y);
     const double E=mperp*cosh(y);
 
     return {pt[0], pt[1], pz, E};
   }
 
   namespace {
     //! adds a particle to target (in correct rapidity ordering)
     //! @returns    positon of insertion
     auto insert_particle(std::vector<HEJ::Particle> & target,
       HEJ::Particle && particle
     ){
       const auto pos = std::upper_bound(
           begin(target),end(target),particle,rapidity_less{}
       );
       target.insert(pos, std::move(particle));
       return pos;
     }
   }
 
   PhaseSpacePoint::PhaseSpacePoint(
     Process const & proc,
     JetParameters const & jet_param,
     HEJ::PDF & pdf, double E_beam,
     double const subl_chance,
     unsigned int const subl_channels,
     ParticlesPropMap const & particles_properties,
     HEJ::RNG & ran
   )
   {
     assert(proc.njets >= 2);
     if(proc.boson
       && particles_properties.find(*(proc.boson))
         == particles_properties.end())
       throw HEJ::missing_option("Boson "
         +std::to_string(*(proc.boson))+" can't be generated: missing properties");
     status_ = good;
     weight_ = 1;
 
     const int nout = proc.njets + (proc.boson?1:0) + proc.leptons.size();
     outgoing_.reserve(nout);
 
     // generate parton momenta
     const bool is_pure_jets = (nout == proc.njets);
     auto partons = gen_LO_partons(
         proc.njets, is_pure_jets, jet_param, E_beam, ran
     );
     // pre fill flavour with gluons
     for(auto&& p_out: partons) {
       outgoing_.emplace_back(Particle{pid::gluon, std::move(p_out), {}});
     }
     if(status_ != good) return;
 
     // create phase space point for (lepton,neutrino)-pair of W+/W-
     // @TODO this have massive overlap with the "create boson" section
     if(proc.leptons.size()==2){
       auto pair(gen_enu(proc.leptons,ran));
       for(auto&& p: pair)
         insert_particle(outgoing_, std::move(p));
     }
 
     // create boson
     if(proc.boson){
       const auto & boson_prop = particles_properties.at(*proc.boson);
       auto boson(gen_boson(*proc.boson, boson_prop.mass, boson_prop.width, ran));
       const auto pos{insert_particle(outgoing_, std::move(boson))};
       if(! boson_prop.decays.empty()){
         const size_t boson_idx = std::distance(begin(outgoing_), pos);
         decays_.emplace(
             boson_idx,
             decay_boson(outgoing_[boson_idx], boson_prop.decays, ran)
         );
       }
     }
     // normalisation of momentum-conserving delta function
     weight_ *= pow(2*M_PI, 4);
 
     /** @TODO
      *  uf (jet_param.min_pt) doesn't correspond to our final scale choice.
      *  The HEJ scale generators currently expect a full event as input,
      *  so fixing this is not completely trivial
      */
     reconstruct_incoming(proc, subl_channels, pdf, E_beam, jet_param.min_pt, ran);
     if(status_ != good) return;
     // set outgoing states
     most_backward_FKL(outgoing_).type = incoming_[0].type;
     most_forward_FKL(outgoing_).type = incoming_[1].type;
 
     maybe_turn_to_subl(subl_chance, subl_channels, proc, ran);
 
     if(proc.boson) couple_boson(*proc.boson, ran);
   }
 
   double PhaseSpacePoint::gen_hard_pt(
       int np , double ptmin, double ptmax, double y,
       HEJ::RNG & ran
   ) {
     // heuristic parameters for pt sampling
     const double ptpar = ptmin + np/5.;
     const double arg_small_y = atan((ptmax - ptmin)/ptpar);
     const double y_cut = 3.;
 
     const double r1 = ran.flat();
     if(y < y_cut){
       const double pt = ptmin + ptpar*tan(r1*arg_small_y);
       const double temp = cos(r1*arg_small_y);
       weight_ *= pt*ptpar*arg_small_y/(temp*temp);
       return pt;
     }
 
     const double ptpar2 = ptpar/(1 + 5*(y-y_cut));
     const double temp = 1. - std::exp((ptmin-ptmax)/ptpar2);
     const double pt = ptmin - ptpar2*std::log(1-r1*temp);
     weight_ *= pt*ptpar2*temp/(1-r1*temp);
     return pt;
   }
 
   double PhaseSpacePoint::gen_soft_pt(int np, double max_pt, HEJ::RNG & ran) {
     constexpr double ptpar = 4.;
 
     const double r = ran.flat();
     const double pt = max_pt + ptpar/np*std::log(r);
     weight_ *= pt*ptpar/(np*r);
     return pt;
   }
 
   double PhaseSpacePoint::gen_parton_pt(
       int count, JetParameters const & jet_param, double max_pt, double y,
       HEJ::RNG & ran
   ) {
     constexpr double p_small_pt = 0.02;
 
     if(! jet_param.peak_pt) {
        return gen_hard_pt(count, jet_param.min_pt, max_pt, y, ran);
     }
     const double r = ran.flat();
     if(r > p_small_pt) {
       weight_ /= 1. - p_small_pt;
       return gen_hard_pt(count, *jet_param.peak_pt, max_pt, y, ran);
     }
     weight_ /= p_small_pt;
     const double pt =  gen_soft_pt(count, *jet_param.peak_pt, ran);
     if(pt < jet_param.min_pt) {
       weight_=0.0;
       status_ = not_enough_jets;
       return jet_param.min_pt;
     }
     return pt;
   }
 
   std::vector<fastjet::PseudoJet> PhaseSpacePoint::gen_LO_partons(
       int np, bool is_pure_jets,
       JetParameters const & jet_param,
       double max_pt,
       HEJ::RNG & ran
   ){
     if (np<2) throw std::invalid_argument{"Not enough partons in gen_LO_partons"};
 
     weight_ /= pow(16.*pow(M_PI,3),np);
     weight_ /= std::tgamma(np+1); //remove rapidity ordering
     std::vector<fastjet::PseudoJet> partons;
     partons.reserve(np);
     for(int i = 0; i < np; ++i){
       const double y = -jet_param.max_y + 2*jet_param.max_y*ran.flat();
       weight_ *= 2*jet_param.max_y;
 
       const bool is_last_parton = i+1 == np;
       if(is_pure_jets && is_last_parton) {
         constexpr double parton_mass_sq = 0.;
         partons.emplace_back(gen_last_momentum(partons, parton_mass_sq, y));
         break;
       }
 
       const double phi = 2*M_PI*ran.flat();
       weight_ *= 2.0*M_PI;
 
       const double pt = gen_parton_pt(np, jet_param, max_pt, y, ran);
       if(weight_ == 0.0) return {};
 
       partons.emplace_back(fastjet::PtYPhiM(pt, y, phi));
       assert(jet_param.min_pt <= partons[i].pt());
       assert(partons[i].pt() <= max_pt+1e-5);
     }
     // Need to check that at LO, the number of jets = number of partons;
     fastjet::ClusterSequence cs(partons, jet_param.def);
     auto cluster_jets=cs.inclusive_jets(jet_param.min_pt);
     if (cluster_jets.size()!=unsigned(np)){
       weight_=0.0;
       status_ = not_enough_jets;
       return {};
     }
 
     std::sort(begin(partons), end(partons), rapidity_less{});
 
     return partons;
   }
 
   std::vector<Particle> PhaseSpacePoint::gen_enu(
       std::vector<HEJ::pid::ParticleID> const & pair, HEJ::RNG & ran
   ){
     assert(pair.size() == 2);
     // Now we know the transverse momentum of the W, given by the array kt
     // double pW[4];
     // Choose its mass according to Breit-Wigner
     double r1=ran.flat();
     double sW=HEJ::MW*(HEJ::MW + HEJ::GammaW*tan((M_PI*r1)/2. + (-1. + r1)*atan(HEJ::MW/HEJ::GammaW)));
     // Multiply by derivate (d sap)/(d r)
     {
       static double temp=atan(HEJ::MW/HEJ::GammaW);
       weight_*=(HEJ::GammaW*HEJ::MW*(M_PI+2.*temp))/(1.+cos(M_PI*r1+2.*(-1.+r1)*temp));
     }
 
     // Generate a yW Gaussian distributed around 0
     double yW;
     {
       double lninvr1,r1,r2,temp,a;
       r1=ran.flat();
       r2=ran.flat();
       lninvr1=-log(r1);
       a=0.7; // tuned number
       temp=a*sqrt(2.*lninvr1)*cos(2.*M_PI*r2);
       yW=temp;
       weight_=weight_*(exp(temp*temp/2./a/a))*sqrt(2.*M_PI)*a;
     }
     auto p = gen_last_momentum(outgoing_, sW, yW);
     CLHEP::HepLorentzVector pWv(p.px(),p.py(),p.pz(),p.e());
 
     double ppW[4],ppWs;
     CLHEP::HepLorentzVector ppWv,appWv,pd1,pd2;
     ppWs=sqrt(sW)/2.;
     // Choose theta and phi
     double pptheta=2.*M_PI*ran.flat();
     double cosppphi=2.*ran.flat()-1.;
     weight_*=2.*M_PI*2.;
     double sinphi=sqrt(1.-cosppphi*cosppphi);  // Know 0 < phi < pi
     weight_*=1./(pow(2.*M_PI,3)*2.)/(4.);   // Divide by 8, *2, see WPhaseSpace.tex @TODO where?!?
     // (phi -> cosphi now so no sinphi in J)
     // construct 4-vector in W rest frame
     ppW[0]=ppWs;
     ppW[1]=ppWs*cos(pptheta)*sinphi;
     ppW[2]=ppWs*sin(pptheta)*sinphi;
     ppW[3]=ppWs*cosppphi;
     ppWv.set(ppW[1],ppW[2],ppW[3],ppW[0]);
     appWv.set(-ppW[1],-ppW[2],-ppW[3],ppW[0]);
 
     // translate to lab frame
     pd1=ppWv.boost(pWv.boostVector()); //particle
     pd2=appWv.boost(pWv.boostVector()); //anti-particle
 
     // assign particle and anti-particle momentum
     // find the particle, assuming the pair is ordered (anti-lepton,lepton)
     HEJ::Particle alepton{pair[0],
       fastjet::PseudoJet{pd2.px(),pd2.py(),pd2.pz(),pd2.e()}, {}};
     HEJ::Particle lepton{pair[1],
       fastjet::PseudoJet{pd1.px(),pd1.py(),pd1.pz(),pd1.e()}, {}};
 
     return std::vector<HEJ::Particle>{alepton,lepton};
   }
 
   Particle PhaseSpacePoint::gen_boson(
       HEJ::ParticleID bosonid, double mass, double width,
       HEJ::RNG & ran
   ){
     // Usual phase space measure
     weight_ /= 16.*pow(M_PI, 3);
 
     // Generate a y Gaussian distributed around 0
     /// @TODO: magic number only for Higgs
     /// @TODO better sampling for W
     const double y = random_normal(1.6, ran);
     const double r1 = ran.flat();
     const double sH = mass*(
         mass + width*tan(M_PI/2.*r1 + (r1-1.)*atan(mass/width))
     );
 
     auto p = gen_last_momentum(outgoing_, sH, y);
 
     return Particle{bosonid, std::move(p), {}};
   }
 
   Particle const & PhaseSpacePoint::most_backward_FKL(
       std::vector<Particle> const & partons
   ) const{
     if(!HEJ::is_parton(partons[0])) return partons[1];
     return partons[0];
   }
 
   Particle const & PhaseSpacePoint::most_forward_FKL(
       std::vector<Particle> const & partons
   ) const{
     const size_t last_idx = partons.size() - 1;
     if(!HEJ::is_parton(partons[last_idx])) return partons[last_idx-1];
     return partons[last_idx];
   }
 
   Particle & PhaseSpacePoint::most_backward_FKL(
       std::vector<Particle> & partons
   ) const{
     if(!HEJ::is_parton(partons[0])) return partons[1];
     return partons[0];
   }
 
   Particle & PhaseSpacePoint::most_forward_FKL(
       std::vector<Particle> & partons
   ) const{
     const size_t last_idx = partons.size() - 1;
     if(!HEJ::is_parton(partons[last_idx])) return partons[last_idx-1];
     return partons[last_idx];
   }
 
   namespace {
     /// partons are ordered: even = anti, 0 = gluon
     ParticleID index_to_pid(size_t i){
       if(!i) return pid::gluon;
       return static_cast<ParticleID>(i%2?(i+1)/2:-i/2);
     }
 
     /// partons are ordered: even = anti, 0 = gluon
     size_t pid_to_index(ParticleID id){
       if(id==pid::gluon) return 0;
       return id>0?id*2-1:abs(id)*2;
     }
 
     std::bitset<11> init_allowed(ParticleID const id){
       if(abs(id) == pid::proton)
         return ~0;
       std::bitset<11> out = 0;
       if(is_parton(id))
         out[pid_to_index(id)] = 1;
       return out;
     }
 
     /// decides which "index" (see index_to_pid) are allowed for process
     std::bitset<11> allowed_quarks(ParticleID const boson){
       std::bitset<11> allowed = ~0;
       if(abs(boson) == pid::Wp){
         // special case W:
         // Wp: anti-down or up-type quark, no b/t -> 0001100110(1) = 205
         // Wm: down or anti-up-type quark, no b/t -> 0010011001(1) = 307
         allowed = boson>0?205:307;
       }
       return allowed;
     }
   }
 
   /**
    * checks which partons are allowed as initial state:
    * 1. only allow what is given in the Runcard (p -> all)
    * 2. A/W/Z require something to couple to
    *  a) no qqx => no incoming gluon
    *  b) 2j     => no incoming gluon
    *  c) 3j     => can couple OR is gluon => 2 gluons become qqx later
    */
   std::array<std::bitset<11>,2> PhaseSpacePoint::filter_partons(
       Process const & proc, unsigned int const subl_channels, HEJ::RNG & ran
   ){
     std::array<std::bitset<11>,2> allowed_partons{
         init_allowed(proc.incoming[0]),
         init_allowed(proc.incoming[1])
       };
     bool const allow_qqx = subl_channels&Subleading::qqx;
     // special case A/W/Z
     if(is_AWZ_proccess(proc) && ((proc.njets < 4) || !allow_qqx)){
       // all possible incoming states
       auto allowed(allowed_quarks(*proc.boson));
       if(proc.njets == 2 || !allow_qqx) allowed[0]=0;
 
       // possible states per leg
       std::array<std::bitset<11>,2> const maybe_partons{
         allowed_partons[0]&allowed, allowed_partons[1]&allowed};
 
       if(maybe_partons[0].any() && maybe_partons[1].any()){
         // two options to get allowed initial state => choose one at random
         const size_t idx = ran.flat() < 0.5;
         allowed_partons[idx] = maybe_partons[idx];
         // else choose the possible
       } else if(maybe_partons[0].any()) {
         allowed_partons[0] = maybe_partons[0];
       } else if(maybe_partons[1].any()) {
         allowed_partons[1] = maybe_partons[1];
       } else{
         throw std::invalid_argument{"Incoming state not allowed."};
       }
     }
     return allowed_partons;
   }
 
   void PhaseSpacePoint::reconstruct_incoming(
       Process const & proc, unsigned int const subl_channels,
       HEJ::PDF & pdf, double E_beam,
       double uf,
       HEJ::RNG & ran
   ){
     std::tie(incoming_[0].p, incoming_[1].p) = incoming_momenta(outgoing_);
     // calculate xa, xb
     const double sqrts=2*E_beam;
     const double xa=(incoming_[0].p.e()-incoming_[0].p.pz())/sqrts;
     const double xb=(incoming_[1].p.e()+incoming_[1].p.pz())/sqrts;
     // abort if phase space point is outside of collider energy reach
     if (xa>1. || xb>1.){
       weight_=0;
       status_ = too_much_energy;
       return;
     }
     auto const & ids = proc.incoming;
     std::array<std::bitset<11>,2> allowed_partons(
       filter_partons(proc, subl_channels, ran));
     for(size_t i = 0; i < 2; ++i){
       if(ids[i] == pid::proton || ids[i] == pid::p_bar){
       // pick ids according to pdfs
         incoming_[i].type =
           generate_incoming_id(i, i?xb:xa, uf, pdf, allowed_partons[i], ran);
       } else {
         assert(allowed_partons[i][pid_to_index(ids[i])]);
         incoming_[i].type = ids[i];
       }
     }
     assert(momentum_conserved(1e-7));
   }
 
   HEJ::ParticleID PhaseSpacePoint::generate_incoming_id(
       size_t const beam_idx, double const x, double const uf,
       HEJ::PDF & pdf, std::bitset<11> allowed_partons, HEJ::RNG & ran
   ){
     std::array<double,11> pdf_wt;
     pdf_wt[0] = allowed_partons[0]?fabs(pdf.pdfpt(beam_idx,x,uf,pid::gluon)):0.;
     double pdftot = pdf_wt[0];
     for(size_t i = 1; i < pdf_wt.size(); ++i){
       pdf_wt[i] = allowed_partons[i]?4./9.*fabs(pdf.pdfpt(beam_idx,x,uf,index_to_pid(i))):0;
       pdftot += pdf_wt[i];
     }
     const double r1 = pdftot * ran.flat();
     double sum = 0;
     for(size_t i=0; i < pdf_wt.size(); ++i){
       if (r1 < (sum+=pdf_wt[i])){
         weight_*= pdftot/pdf_wt[i];
         return index_to_pid(i);
       }
     }
     std::cerr << "Error in choosing incoming parton: "<<x<<" "<<uf<<" "
       <<sum<<" "<<pdftot<<" "<<r1<<std::endl;
     throw std::logic_error{"Failed to choose parton flavour"};
   }
 
   void PhaseSpacePoint::couple_boson(
     HEJ::ParticleID const boson, HEJ::RNG & ran
   ){
     if(abs(boson) != pid::Wp) return; // only matters for W
     /// @TODO this could be use to sanity check gamma and Z
 
     // find all possible quarks
     std::vector<Particle*> allowed_parts;
     for(auto & part: outgoing_){
       // Wp -> up OR anti-down, Wm -> anti-up OR down, no bottom
       if ( can_couple_to_W(part, boson) )
         allowed_parts.push_back(&part);
     }
     if(allowed_parts.size() == 0){
       throw std::logic_error{"Found no parton for coupling with boson"};
     }
 
     // select one and flip it
     size_t idx = 0;
     if(allowed_parts.size() > 1){
       /// @TODO more efficient sampling
       ///       old code: probability[i] = exp(parton[i].y - W.y)
       idx = floor(ran.flat()*allowed_parts.size());
       weight_ *= allowed_parts.size();
     }
     const int W_charge = boson>0?1:-1;
     allowed_parts[idx]->type =
       static_cast<ParticleID>( allowed_parts[idx]->type - W_charge );
   }
 
   double PhaseSpacePoint::random_normal(
       double stddev,
       HEJ::RNG & ran
   ){
     const double r1 = ran.flat();
     const double r2 = ran.flat();
     const double lninvr1 = -log(r1);
     const double result = stddev*sqrt(2.*lninvr1)*cos(2.*M_PI*r2);
     weight_ *= exp(result*result/(2*stddev*stddev))*sqrt(2.*M_PI)*stddev;
     return result;
   }
 
   bool PhaseSpacePoint::momentum_conserved(double ep) const{
     fastjet::PseudoJet diff;
     for(auto const & in: incoming()) diff += in.p;
     for(auto const & out: outgoing()) diff -= out.p;
     return nearby_ep(diff, fastjet::PseudoJet{}, ep);
   }
 
   Decay PhaseSpacePoint::select_decay_channel(
       std::vector<Decay> const & decays,
       HEJ::RNG & ran
   ){
     double br_total = 0.;
     for(auto const & decay: decays) br_total += decay.branching_ratio;
     // adjust weight
     // this is given by (channel branching ratio)/(chance to pick channel)
     // where (chance to pick channel) =
     //       (channel branching ratio)/(total branching ratio)
     weight_ *= br_total;
     const double r1 = br_total*ran.flat();
     double br_sum = 0.;
     for(auto const & decay: decays){
       br_sum += decay.branching_ratio;
       if(r1 < br_sum) return decay;
     }
     throw std::logic_error{"unreachable"};
   }
 
   std::vector<Particle> PhaseSpacePoint::decay_boson(
       HEJ::Particle const & parent,
       std::vector<Decay> const & decays,
       HEJ::RNG & ran
   ){
     const auto channel = select_decay_channel(decays, ran);
     if(channel.products.size() != 2){
       throw HEJ::not_implemented{
         "only decays into two particles are implemented"
       };
     }
     std::vector<Particle> decay_products(channel.products.size());
     for(size_t i = 0; i < channel.products.size(); ++i){
       decay_products[i].type = channel.products[i];
     }
     // choose polar and azimuth angle in parent rest frame
     const double E = parent.m()/2;
     const double theta = 2.*M_PI*ran.flat();
     const double cos_phi = 2.*ran.flat()-1.;
     const double sin_phi = sqrt(1. - cos_phi*cos_phi);  // Know 0 < phi < pi
     const double px = E*cos(theta)*sin_phi;
     const double py = E*sin(theta)*sin_phi;
     const double pz = E*cos_phi;
     decay_products[0].p.reset(px, py, pz, E);
     decay_products[1].p.reset(-px, -py, -pz, E);
     for(auto & particle: decay_products) particle.p.boost(parent.p);
     return decay_products;
   }
 }
diff --git a/FixedOrderGen/src/Unweighter.cc b/FixedOrderGen/src/Unweighter.cc
index 7bb1695..16996e5 100644
--- a/FixedOrderGen/src/Unweighter.cc
+++ b/FixedOrderGen/src/Unweighter.cc
@@ -1,26 +1,31 @@
+/**
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
+ *  \date      2019
+ *  \copyright GPLv2 or later
+ */
 #include "Unweighter.hh"
 
 #include <cassert>
 
 #include "HEJ/Event.hh"
 
 namespace HEJFOG {
 
   namespace detail {
     bool has_jet_softer_than(HEJ::Event const & ev, double pt) {
       assert(! ev.jets().empty());
       const auto softest_jet = fastjet::sorted_by_pt(ev.jets()).back();
       return softest_jet.pt() < pt;
     }
   }
 
   HEJ::optional<HEJ::Event> Unweighter::unweight(HEJ::Event ev) const {
     if(detail::has_jet_softer_than(ev, min_unweight_pt_)) return ev;
     const double awt = std::abs(ev.central().weight);
     if(ran_.get().flat() < awt/cut_) {
       if(awt < cut_) ev.parameters() *= cut_/awt;
       return ev;
     }
     return {};
   }
 }
diff --git a/FixedOrderGen/src/config.cc b/FixedOrderGen/src/config.cc
index c3b3057..cf43551 100644
--- a/FixedOrderGen/src/config.cc
+++ b/FixedOrderGen/src/config.cc
@@ -1,388 +1,393 @@
+/**
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
+ *  \date      2019
+ *  \copyright GPLv2 or later
+ */
 #include "config.hh"
 
 #include <cctype>
 
 #include "Subleading.hh"
 
 #include "HEJ/config.hh"
 #include "HEJ/YAMLreader.hh"
 
 namespace HEJFOG{
   using HEJ::set_from_yaml;
   using HEJ::set_from_yaml_if_defined;
   using HEJ::pid::ParticleID;
 
   namespace{
     //! Get YAML tree of supported options
     /**
      * The configuration file is checked against this tree of options
      * in assert_all_options_known.
      */
     YAML::Node const & get_supported_options(){
       const static YAML::Node supported = [](){
         YAML::Node supported;
         static const auto opts = {
           "process", "events", "subleading fraction","subleading channels",
           "scales", "scale factors", "max scale ratio", "pdf",
           "event output", "analysis", "import scales"
         };
         // add subnodes to "supported" - the assigned value is irrelevant
         for(auto && opt: opts) supported[opt] = "";
         for(auto && jet_opt: {"min pt", "peak pt", "algorithm", "R", "max rapidity"}){
           supported["jets"][jet_opt] = "";
         }
         for(auto && particle_type: {"Higgs", "Wp", "W+", "Wm", "W-", "Z"}){
           for(auto && particle_opt: {"mass", "width"}){
             supported["particle properties"][particle_type][particle_opt] = "";
           }
           supported["particle properties"][particle_type]["decays"]["into"] = "";
           supported["particle properties"][particle_type]["decays"]["branching ratio"] = "";
         }
         for(auto && opt: {"mt", "use impact factors", "include bottom", "mb"}){
           supported["Higgs coupling"][opt] = "";
         }
         for(auto && beam_opt: {"energy", "particles"}){
           supported["beam"][beam_opt] = "";
         }
         for(auto && unweight_opt: {"sample size", "max deviation"}){
           supported["unweight"][unweight_opt] = "";
         }
         for(auto && opt: {"name", "seed"}){
           supported["random generator"][opt] = "";
         }
         return supported;
       }();
       return supported;
     }
 
     JetParameters get_jet_parameters(
         YAML::Node const & node, std::string const & entry
     ){
       const auto p = HEJ::get_jet_parameters(node, entry);
       JetParameters result;
       result.def = p.def;
       result.min_pt = p.min_pt;
       set_from_yaml(result.max_y, node, entry, "max rapidity");
       set_from_yaml_if_defined(result.peak_pt, node, entry, "peak pt");
       if(result.peak_pt && *result.peak_pt <= result.min_pt)
         throw std::invalid_argument{
           "Value of option 'peak pt' has to be larger than 'min pt'."
         };
       return result;
     }
 
     Beam get_Beam(
         YAML::Node const & node, std::string const & entry
     ){
       Beam beam;
       std::vector<HEJ::ParticleID> particles;
       set_from_yaml(beam.energy, node, entry, "energy");
       set_from_yaml_if_defined(particles, node, entry, "particles");
       if(! particles.empty()){
         for(HEJ::ParticleID particle: particles){
           if(particle != HEJ::pid::p && particle != HEJ::pid::p_bar){
             throw std::invalid_argument{
               "Unsupported value in option " + entry + ": particles:"
               " only proton ('p') and antiproton ('p_bar') beams are supported"
             };
           }
         }
         if(particles.size() != 2){
           throw std::invalid_argument{"Not exactly two beam particles"};
         }
         beam.particles.front() = particles.front();
         beam.particles.back() = particles.back();
       }
       return beam;
     }
 
     std::vector<std::string> split(
         std::string const & str, std::string const & delims
     ){
       std::vector<std::string> result;
       for(size_t begin, end = 0; end != str.npos;){
         begin = str.find_first_not_of(delims, end);
         if(begin == str.npos) break;
         end = str.find_first_of(delims, begin + 1);
         result.emplace_back(str.substr(begin, end - begin));
       }
       return result;
     }
 
     std::invalid_argument invalid_incoming(std::string const & what){
       return std::invalid_argument{
         "Incoming particle type " + what + " not supported,"
         " incoming particles have to be 'p', 'p_bar' or partons"
       };
     }
 
     std::invalid_argument invalid_outgoing(std::string const & what){
       return std::invalid_argument{
         "Outgoing particle type " + what + " not supported,"
         " outgoing particles have to be 'j', 'photon', 'H', 'e-', 'e+', 'nu_e', 'nu_e_bar'"
       };
     }
 
     bool leptons_flavours_OK(std::vector<ParticleID> const & pid) {
       assert(pid.size()==2);
       assert(is_sorted(begin(pid),end(pid)));
       // Check it is a W+/W-
       const int pidsum = pid[0]+pid[1];
       if (HEJ::is_antineutrino(pid[0])){
         if (pidsum!=-1) return false;
       } else {
         if (pidsum!=1) return false;
       }
       return true;
     }
 
     Process get_process(
         YAML::Node const & node, std::string const & entry
     ){
       Process result;
 
       std::string process_string;
       set_from_yaml(process_string, node, entry);
       assert(! process_string.empty());
       const auto particles = split(process_string, " \n\t\v=>");
       if(particles.size() < 3){
         throw std::invalid_argument{
           "Bad format in option process: '" + process_string
           + "', expected format is 'in1 in2 => out1 ...'"
         };
       }
       result.incoming.front() = HEJ::to_ParticleID(particles[0]);
       result.incoming.back() = HEJ::to_ParticleID(particles[1]);
 
       for(size_t i = 0; i < result.incoming.size(); ++i){
         const HEJ::ParticleID in = result.incoming[i];
         if(
             in != HEJ::pid::proton && in != HEJ::pid::p_bar
             && !HEJ::is_parton(in)
         ){
           throw invalid_incoming(particles[i]);
         }
       }
       result.njets = 0;
       for(size_t i = result.incoming.size(); i < particles.size(); ++i){
         assert(! particles[i].empty());
         if(particles[i] == "j") ++result.njets;
         else if(std::isdigit(particles[i].front())
                 && particles[i].back() == 'j')
           result.njets += std::stoi(particles[i]);
         else{
           const auto pid = HEJ::to_ParticleID(particles[i]);
           if(pid==HEJ::pid::Higgs){
             if(result.boson){
               throw std::invalid_argument{
                 "More than one outgoing boson is not supported"
                   };
             }
             result.boson = pid;
           } else if (HEJ::is_anylepton(pid)){
             // Do not accept more leptons, if two leptons are already mentioned
             if (result.leptons.size()==2||result.boson){
               throw std::invalid_argument {
                 "Too many leptons required "
                   };
             }
             result.leptons.emplace_back(pid);
           } else {
             throw invalid_outgoing(particles[i]);
           }
         }
       }
       std::sort(std::begin(result.leptons),std::end(result.leptons));
       if(result.njets < 2){
         throw std::invalid_argument{
           "Process has to include at least two jets ('j')"
         };
       }
       if((result.leptons.size()>0) && (!leptons_flavours_OK(result.leptons))){
         throw std::invalid_argument{
           "Requested process has unsupported combinations of leptons"
         };
       }
       return result;
     }
 
     HEJFOG::Subleading to_subleading_channel(YAML::Node const & yaml){
       std::string name;
       using HEJFOG::Subleading;
       set_from_yaml(name, yaml);
       if(name == "none")
         return none;
       if(name == "all")
         return all;
       if(name == "unordered" || name == "uno")
         return uno;
       if(name == "qqx")
         return qqx;
       throw HEJ::unknown_option("Unknown subleading channel '"+name+"'");
 
     }
 
     unsigned int get_subleading_channels(YAML::Node const & node){
       using YAML::NodeType;
       using HEJFOG::Subleading;
       // all channels allowed by default
       if(!node) return all;
       switch(node.Type()){
       case NodeType::Undefined:
         return  all;
       case NodeType::Null:
         return none;
       case NodeType::Scalar:
         return to_subleading_channel(node);
       case NodeType::Map:
         throw HEJ::invalid_type{"map is not a valid option for subleading channels"};
       case NodeType::Sequence:
         unsigned int channels = HEJFOG::Subleading::none;
         for(auto && channel_node: node){
           channels |= get_subleading_channels(channel_node);
         }
         return channels;
       }
       throw std::logic_error{"unreachable"};
     }
 
     Decay get_decay(YAML::Node const & node){
       Decay decay;
       set_from_yaml(decay.products, node, "into");
       set_from_yaml(decay.branching_ratio, node, "branching ratio");
       return decay;
     }
 
     std::vector<Decay> get_decays(YAML::Node const & node){
       using YAML::NodeType;
       if(!node) return {};
       switch(node.Type()){
       case NodeType::Null:
       case NodeType::Undefined:
         return {};
       case NodeType::Scalar:
         throw HEJ::invalid_type{"value is not a list of decays"};
       case NodeType::Map:
         return {get_decay(node)};
       case NodeType::Sequence:
         std::vector<Decay> result;
         for(auto && decay_str: node){
           result.emplace_back();
           set_from_yaml(result.back().products, decay_str, "into");
           set_from_yaml(result.back().branching_ratio, decay_str, "branching ratio");
         }
         return result;
       }
       throw std::logic_error{"unreachable"};
     }
 
     ParticleProperties get_particle_properties(
         YAML::Node const & node, std::string const & entry
     ){
       ParticleProperties result;
       set_from_yaml(result.mass, node, entry, "mass");
       set_from_yaml(result.width, node, entry, "width");
       try{
         result.decays = get_decays(node[entry]["decays"]);
       }
       catch(HEJ::missing_option const & ex){
         throw HEJ::missing_option{entry + ": decays: " + ex.what()};
       }
       catch(HEJ::invalid_type const & ex){
         throw HEJ::invalid_type{entry + ": decays: " + ex.what()};
       }
       return result;
     }
 
     ParticlesPropMap get_all_particles_properties(YAML::Node const & node){
       ParticlesPropMap result;
       for(auto const & entry: node) {
         const auto name = entry.first.as<std::string>();
         const auto id = HEJ::to_ParticleID(name);
         result.emplace(id, get_particle_properties(node,name));
       }
       return result;
     }
 
     UnweightSettings get_unweight(
         YAML::Node const & node, std::string const & entry
     ){
       UnweightSettings result;
       set_from_yaml(result.sample_size, node, entry, "sample size");
       if(result.sample_size <= 0){
         throw std::invalid_argument{
           "negative sample size " + std::to_string(result.sample_size)
         };
       }
       set_from_yaml(result.max_dev, node, entry, "max deviation");
       return result;
     }
 
     Config to_Config(YAML::Node const & yaml){
       try{
         HEJ::assert_all_options_known(yaml, get_supported_options());
       }
       catch(HEJ::unknown_option const & ex){
         throw HEJ::unknown_option{std::string{"Unknown option '"} + ex.what() + "'"};
       }
 
       Config config;
       config.process = get_process(yaml, "process");
       set_from_yaml(config.events, yaml, "events");
       config.jets = get_jet_parameters(yaml, "jets");
       config.beam = get_Beam(yaml, "beam");
       for(size_t i = 0; i < config.process.incoming.size(); ++i){
         const auto & in = config.process.incoming[i];
         using namespace HEJ::pid;
         if( (in == p || in == p_bar) && in != config.beam.particles[i]){
           throw std::invalid_argument{
             "Particle type of beam " + std::to_string(i+1) + " incompatible"
             + " with type of incoming particle " + std::to_string(i+1)
           };
         }
       }
       set_from_yaml(config.pdf_id, yaml, "pdf");
 
       set_from_yaml(config.subleading_fraction, yaml, "subleading fraction");
       if(config.subleading_fraction < 0 || config.subleading_fraction > 1){
         throw std::invalid_argument{
           "subleading fraction has to be between 0 and 1"
         };
       }
       if(config.subleading_fraction == 0)
         config.subleading_channels = Subleading::none;
       else
         config.subleading_channels = get_subleading_channels(yaml["subleading channels"]);
       if(!config.process.boson && config.subleading_channels != Subleading::none)
         throw HEJ::not_implemented("Subleading processes for pure Jet production not implemented yet");
 
       if(yaml["particle properties"]){
         config.particles_properties = get_all_particles_properties(
           yaml["particle properties"]);
       }
       if(config.process.boson
         && config.particles_properties.find(*(config.process.boson))
           == config.particles_properties.end())
         throw HEJ::missing_option("Process wants to generate boson "
           +std::to_string(*(config.process.boson))+", but particle properties are missing");
       set_from_yaml_if_defined(config.analysis_parameters, yaml, "analysis");
       config.scales = HEJ::to_ScaleConfig(yaml);
       set_from_yaml_if_defined(config.output, yaml, "event output");
       config.rng = HEJ::to_RNGConfig(yaml, "random generator");
       config.Higgs_coupling = HEJ::get_Higgs_coupling(yaml, "Higgs coupling");
       if(yaml["unweight"]) config.unweight = get_unweight(yaml, "unweight");
       return config;
     }
 
   } // namespace anonymous
 
   Config load_config(std::string const & config_file){
     try{
       return to_Config(YAML::LoadFile(config_file));
     }
     catch(...){
       std::cerr << "Error reading " << config_file << ":\n  ";
       throw;
     }
   }
 }
diff --git a/FixedOrderGen/src/main.cc b/FixedOrderGen/src/main.cc
index 01b9b80..806d0d8 100644
--- a/FixedOrderGen/src/main.cc
+++ b/FixedOrderGen/src/main.cc
@@ -1,228 +1,228 @@
 /**
- * Name: main.cc
- * Authors: Jeppe R. Andersen
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
+ *  \date      2019
+ *  \copyright GPLv2 or later
  */
-
 #include <algorithm>
 #include <chrono>
 #include <fstream>
 #include <iostream>
 #include <map>
 #include <memory>
 
 #include "yaml-cpp/yaml.h"
 
 #include "LHEF/LHEF.h"
 
 #include "HEJ/CombinedEventWriter.hh"
 #include "HEJ/CrossSectionAccumulator.hh"
 #include "HEJ/get_analysis.hh"
 #include "HEJ/LesHouchesWriter.hh"
 #include "HEJ/make_RNG.hh"
 #include "HEJ/ProgressBar.hh"
 #include "HEJ/stream.hh"
 
 #include "config.hh"
 #include "EventGenerator.hh"
 #include "PhaseSpacePoint.hh"
 #include "Unweighter.hh"
 #include "Version.hh"
 
 namespace{
   constexpr auto banner =
     "     __  ___       __       ______                                   __     __                               \n"
     "    / / / (_)___ _/ /_     / ____/___  ___  _________ ___  __       / /__  / /______                         \n"
     "   / /_/ / / __ `/ __ \\   / __/ / __ \\/ _ \\/ ___/ __ `/ / / /  __  / / _ \\/ __/ ___/                         \n"
     "  / __  / / /_/ / / / /  / /___/ / / /  __/ /  / /_/ / /_/ /  / /_/ /  __/ /_(__  )                          \n"
     " /_/ /_/_/\\__, /_/ /_/  /_____/_/ /_/\\___/_/   \\__, /\\__, /   \\____/\\___/\\__/____/                           \n"
     "     ____///__/            __   ____          ///__//____/    ______                           __            \n"
     "    / ____(_)  _____  ____/ /  / __ \\_________/ /__  _____   / ____/__  ____  ___  _________ _/ /_____  _____\n"
     "   / /_  / / |/_/ _ \\/ __  /  / / / / ___/ __  / _ \\/ ___/  / / __/ _ \\/ __ \\/ _ \\/ ___/ __ `/ __/ __ \\/ ___/\n"
     "  / __/ / />  </  __/ /_/ /  / /_/ / /  / /_/ /  __/ /     / /_/ /  __/ / / /  __/ /  / /_/ / /_/ /_/ / /    \n"
     " /_/   /_/_/|_|\\___/\\__,_/   \\____/_/   \\__,_/\\___/_/      \\____/\\___/_/ /_/\\___/_/   \\__,_/\\__/\\____/_/     \n"
     ;
 
   constexpr double invGeV2_to_pb = 389379292.;
   constexpr long long max_warmup_events = 10000;
 }
 
 HEJFOG::Config load_config(char const * filename){
   try{
     return HEJFOG::load_config(filename);
   }
   catch(std::exception const & exc){
     std::cerr << "Error: " << exc.what() << '\n';
     std::exit(EXIT_FAILURE);
   }
 }
 
 std::unique_ptr<HEJ::Analysis> get_analysis(
     YAML::Node const & parameters
 ){
   try{
     return HEJ::get_analysis(parameters);
   }
   catch(std::exception const & exc){
     std::cerr << "Failed to load analysis: " << exc.what() << '\n';
     std::exit(EXIT_FAILURE);
   }
 }
 
 int main(int argn, char** argv) {
   using namespace std::string_literals;
   if (argn < 2) {
     std::cerr << "\n# Usage:\n." << argv[0] << " config_file\n";
     return EXIT_FAILURE;
   }
   std::cout << banner;
   std::cout << "Version " << HEJFOG::Version::String()
              << ", revision " << HEJFOG::Version::revision() << std::endl;
   fastjet::ClusterSequence::print_banner();
   using clock = std::chrono::system_clock;
 
   const auto start_time = clock::now();
 
   // read configuration
   auto config = load_config(argv[1]);
 
   std::unique_ptr<HEJ::Analysis> analysis = get_analysis(
       config.analysis_parameters
   );
   assert(analysis != nullptr);
 
   auto ran = HEJ::make_RNG(config.rng.name, config.rng.seed);
   assert(ran != nullptr);
 
   HEJ::ScaleGenerator scale_gen{
     config.scales.base,
     config.scales.factors,
     config.scales.max_ratio
   };
 
   HEJFOG::EventGenerator generator{
     config.process,
     config.beam,
     std::move(scale_gen),
     config.jets,
     config.pdf_id,
     config.subleading_fraction,
     config.subleading_channels,
     config.particles_properties,
     config.Higgs_coupling,
     *ran
   };
 
   LHEF::HEPRUP heprup;
   heprup.IDBMUP=std::pair<long,long>(config.beam.particles[0], config.beam.particles[1]);
   heprup.EBMUP=std::make_pair(config.beam.energy, config.beam.energy);
   heprup.PDFGUP=std::make_pair(0,0);
   heprup.PDFSUP=std::make_pair(config.pdf_id,config.pdf_id);
   heprup.NPRUP=1;
   heprup.XSECUP=std::vector<double>(1.);
   heprup.XERRUP=std::vector<double>(1.);
   heprup.LPRUP=std::vector<int>{1};
   heprup.generators.emplace_back(LHEF::XMLTag{});
   heprup.generators.back().name = HEJFOG::Version::package_name();
   heprup.generators.back().version = HEJFOG::Version::String();
 
   HEJ::CombinedEventWriter writer{config.output, heprup};
 
   HEJ::optional<HEJFOG::Unweighter> unweighter{};
   std::map<HEJFOG::Status, int> status_counter;
 
   std::vector<HEJ::Event> events;
   int trials = 0;
   // warm-up phase to train unweighter
   if(config.unweight) {
     std::cout << "Calibrating unweighting ...\n";
     const auto warmup_start = clock::now();
     const size_t warmup_events = config.unweight->sample_size;
     HEJ::ProgressBar<size_t> warmup_progress{std::cout, warmup_events};
     for(; events.size() < warmup_events; ++trials){
       auto ev = generator.gen_event();
       ++status_counter[generator.status()];
       assert( (generator.status() == HEJFOG::good) == bool(ev) );
       if(generator.status() == HEJFOG::good && analysis->pass_cuts(*ev, *ev)) {
         events.emplace_back(std::move(*ev));
         ++warmup_progress;
       }
     }
     std::cout << std::endl;
     unweighter = HEJFOG::Unweighter{
       begin(events), end(events), config.unweight->max_dev, *ran,
       config.jets.peak_pt?(*config.jets.peak_pt):0.
     };
     std::vector<HEJ::Event> unweighted_events;
     for(auto && ev: events) {
       auto unweighted = unweighter->unweight(std::move(ev));
       if(unweighted) {
         unweighted_events.emplace_back(std::move(*unweighted));
       }
     }
     events = std::move(unweighted_events);
     if(events.empty()) {
       std::cerr <<
         "Failed to generate events. Please increase \"unweight: sample size\""
         " or reduce \"unweight: max deviation\"\n";
       return EXIT_FAILURE;
     }
     const auto warmup_end = clock::now();
     const double completion = static_cast<double>(events.size())/config.events;
     const std::chrono::duration<double> remaining_time =
       (warmup_end- warmup_start)*(1./completion - 1);
     const auto finish = clock::to_time_t(
         std::chrono::time_point_cast<std::chrono::seconds>(warmup_end + remaining_time)
     );
     std::cout
       << "Generated " << events.size() << "/" << config.events << " events ("
       << static_cast<int>(std::round(100*completion)) << "%)\n"
       << "Estimated remaining generation time: "
       << remaining_time.count() << " seconds ("
       << std::put_time(std::localtime(&finish), "%c") << ")\n\n";
   }
 
   HEJ::ProgressBar<long long> progress{std::cout, config.events};
   progress.increment(events.size());
   events.reserve(config.events);
   for(; events.size() < static_cast<size_t>(config.events); ++trials){
     auto ev = generator.gen_event();
     ++status_counter[generator.status()];
     assert( (generator.status() == HEJFOG::good) == bool(ev) );
     if(generator.status() == HEJFOG::good && analysis->pass_cuts(*ev, *ev)) {
       if(unweighter) {
         auto unweighted = unweighter->unweight(std::move(*ev));
         if(! unweighted) continue;
         ev = std::move(unweighted);
       }
       events.emplace_back(std::move(*ev));
       ++progress;
     }
   }
   std::cout << std::endl;
 
   HEJ::CrossSectionAccumulator xs;
   for(auto & ev: events){
     ev.parameters() *= invGeV2_to_pb/trials;
     analysis->fill(ev, ev);
     writer.write(ev);
     xs.fill(ev);
   }
   analysis->finalise();
 
   const std::chrono::duration<double> run_time = (clock::now() - start_time);
   std::cout << "\nTask Runtime: " << run_time.count() << " seconds.\n\n";
 
   std::cout << xs << '\n';
 
   for(auto && entry: status_counter){
     const double fraction = static_cast<double>(entry.second)/trials;
     const int percent = std::round(100*fraction);
     std::cout << "status "
               << std::left << std::setw(16) << (to_string(entry.first) + ":")
               << " [";
     for(int i = 0; i < percent/2; ++i) std::cout << '#';
     for(int i = percent/2; i < 50; ++i) std::cout << ' ';
     std::cout << "] " << percent << "%\n";
   }
 
 }
diff --git a/FixedOrderGen/t/2j.cc b/FixedOrderGen/t/2j.cc
index ea68d04..ae63974 100644
--- a/FixedOrderGen/t/2j.cc
+++ b/FixedOrderGen/t/2j.cc
@@ -1,60 +1,65 @@
+/**
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
+ *  \date      2019
+ *  \copyright GPLv2 or later
+ */
 #ifdef NDEBUG
 #undef NDEBUG
 #endif
 
 #include <algorithm>
 #include <cmath>
 #include <cassert>
 #include <iostream>
 
 #include "config.hh"
 #include "EventGenerator.hh"
 #include "HEJ/Mixmax.hh"
 
 #include "HEJ/Event.hh"
 #include "HEJ/PDF.hh"
 #include "HEJ/MatrixElement.hh"
 
 using namespace HEJFOG;
 
 int main(){
   constexpr double invGeV2_to_pb = 389379292.;
   constexpr double xs_ref = 86.42031848*1e6; //calculated with "combined" HEJ svn r3480
 
   auto config = load_config("config_2j.yml");
 
   HEJ::Mixmax ran{};
   HEJFOG::EventGenerator generator{
     config.process,
     config.beam,
     HEJ::ScaleGenerator{
       config.scales.base,
       config.scales.factors,
       config.scales.max_ratio
     },
     config.jets,
     config.pdf_id,
     config.subleading_fraction,
     config.subleading_channels,
     config.particles_properties,
     config.Higgs_coupling,
     ran
   };
 
   double xs = 0., xs_err = 0.;
   for (int trials = 0; trials < config.events; ++trials){
     auto ev = generator.gen_event();
     if(generator.status() != good) continue;
     assert(ev);
     ev->central().weight *= invGeV2_to_pb;
     ev->central().weight /= config.events;
 
     xs += ev->central().weight;
     xs_err += ev->central().weight*ev->central().weight;
   }
   xs_err = std::sqrt(xs_err);
   std::cout << xs_ref << " ~ " << xs << " +- " << xs_err << '\n';
 
   assert(std::abs(xs - xs_ref) < 3*xs_err);
   assert(xs_err < 0.01*xs);
 }
diff --git a/FixedOrderGen/t/4j.cc b/FixedOrderGen/t/4j.cc
index a597242..09919f1 100644
--- a/FixedOrderGen/t/4j.cc
+++ b/FixedOrderGen/t/4j.cc
@@ -1,61 +1,66 @@
+/**
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
+ *  \date      2019
+ *  \copyright GPLv2 or later
+ */
 #ifdef NDEBUG
 #undef NDEBUG
 #endif
 
 #include <algorithm>
 #include <cmath>
 #include <cassert>
 #include <iostream>
 
 #include "config.hh"
 #include "EventGenerator.hh"
 #include "HEJ/Mixmax.hh"
 
 #include "HEJ/Event.hh"
 #include "HEJ/PDF.hh"
 #include "HEJ/MatrixElement.hh"
 
 using namespace HEJFOG;
 
 int main(){
   constexpr double invGeV2_to_pb = 389379292.;
   constexpr double xs_ref = 0.81063619*1e6; //calculated with "combined" HEJ svn r3480
 
   auto config = load_config("config_2j.yml");
   config.process.njets = 4;
 
   HEJ::Mixmax ran{};
   HEJFOG::EventGenerator generator{
     config.process,
     config.beam,
     HEJ::ScaleGenerator{
       config.scales.base,
       config.scales.factors,
       config.scales.max_ratio
     },
     config.jets,
     config.pdf_id,
     config.subleading_fraction,
     config.subleading_channels,
     config.particles_properties,
     config.Higgs_coupling,
     ran
   };
 
   double xs = 0., xs_err = 0.;
   for (int trials = 0; trials < config.events; ++trials){
     auto ev = generator.gen_event();
     if(generator.status() != good) continue;
     assert(ev);
     ev->central().weight *= invGeV2_to_pb;
     ev->central().weight /= config.events;
 
     xs += ev->central().weight;
     xs_err += ev->central().weight*ev->central().weight;
   }
   xs_err = std::sqrt(xs_err);
   std::cout << xs_ref << " ~ " << xs << " +- " << xs_err << '\n';
 
   assert(std::abs(xs - xs_ref) < 3*xs_err);
   assert(xs_err < 0.03*xs);
 }
diff --git a/FixedOrderGen/t/W_2j_classify.cc b/FixedOrderGen/t/W_2j_classify.cc
index 4cddbf0..5dfc5c8 100644
--- a/FixedOrderGen/t/W_2j_classify.cc
+++ b/FixedOrderGen/t/W_2j_classify.cc
@@ -1,147 +1,152 @@
-// check that the PSP generates only "valid" W + 2 jets events
-
+/**
+ * \brief      check that the PSP generates only "valid" W + 2 jets events
+ *
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
+ *  \date      2019
+ *  \copyright GPLv2 or later
+ */
 #ifdef NDEBUG
 #undef NDEBUG
 #endif
 
 #include "JetParameters.hh"
 #include "ParticleProperties.hh"
 #include "PhaseSpacePoint.hh"
 #include "Process.hh"
 #include "Subleading.hh"
 
 #include "HEJ/Mixmax.hh"
 #include "HEJ/PDF.hh"
 #include "HEJ/utility.hh"
 
 using namespace HEJFOG;
 using namespace HEJ;
 
 namespace {
   void print_psp(PhaseSpacePoint const & psp){
     std::cerr << "Process:\n"
       << psp.incoming()[0].type << " + "<< psp.incoming()[1].type << " -> ";
     for(auto const & out: psp.outgoing()){
       std::cerr << out.type << " ";
     }
     std::cerr << "\n";
   }
 
   void bail_out(PhaseSpacePoint const & psp, std::string msg){
     print_psp(psp);
     throw std::logic_error{msg};
   }
 
   bool is_up_type(Particle const & part){
     return HEJ::is_anyquark(part) && !(abs(part.type)%2);
   }
   bool is_down_type(Particle const & part){
     return HEJ::is_anyquark(part) && abs(part.type)%2;
   }
 
   bool check_W2j(PhaseSpacePoint const & psp, ParticleID const W_type){
     bool found_quark = false;
     bool found_anti = false;
     std::vector<Particle> out_partons;
     std::vector<Particle> Wp;
     for(auto const & p: psp.outgoing()){
       if(p.type == W_type) Wp.push_back(p);
       else if(is_parton(p)) out_partons.push_back(p);
       else bail_out(psp, "Found particle with is not "
             +std::to_string(int(W_type))+" or parton");
     }
     if(Wp.size() != 1 || out_partons.size() != 2){
       bail_out(psp, "Found wrong number of outgoing partons");
     }
     for(size_t j=0; j<2; ++j){
       auto const & in = psp.incoming()[j];
       auto const & out = out_partons[j];
       if(is_quark(in) || is_antiquark(in)) {
         found_quark = true;
         if(in.type != out.type) { // switch in quark type -> Wp couples to it
           if(found_anti){ // already found qq for coupling to W
             bail_out(psp, "Found second up/down pair");
           } else if(abs(in.type)>4 || abs(out.type)>4){
             bail_out(psp, "Found bottom/top pair");
           }
           found_anti = true;
           if( is_up_type(in)) { // "up" in
             if(W_type > 0){
               // -> only allowed u -> Wp + d
               if(in.type < 0 || is_up_type(out) || out.type < 0)
                 bail_out(psp, "u -/> Wp + d");
 
             } else {
               // -> only allowed ux -> Wm + dx
               if(in.type > 0 || is_up_type(out) || out.type > 0)
                 bail_out(psp, "ux -/> Wm + dx");
             }
           } else { // "down" in
             if(W_type > 0){
               // -> only allowed dx -> Wp + ux
               if(in.type > 0 || is_down_type(out) || out.type > 0)
                 bail_out(psp, "dx -/> Wp + ux");
             } else {
               // -> only allowed d -> Wm + u
               if(in.type < 0 || is_down_type(out) || out.type < 0)
                 bail_out(psp, "d -/> Wm + u");
             }
           }
         }
       }
     }
     if(!found_quark) {
       bail_out(psp, "Found no initial quarks");
     } else if(!found_anti){
       bail_out(psp, "Found no up/down pair");
     }
     return true;
   }
 }
 
 int main(){
   constexpr size_t n_psp_base = 1337;
   const JetParameters jet_para{
     fastjet::JetDefinition(fastjet::JetAlgorithm::antikt_algorithm, 0.4), 30, 5, 30};
   PDF pdf(11000, pid::proton, pid::proton);
   constexpr double E_cms = 13000.;
   constexpr double subl_change = 0.5;
   constexpr auto subl_channels = Subleading::all;
   const ParticlesPropMap boson_prop{
       {pid::Wp, {91.1876, 2.085, {Decay{ {pid::e_bar, pid::nu_e}, 1.}} }},
       {pid::Wm, {91.1876, 2.085, {Decay{ {pid::e, pid::nu_e_bar}, 1.}} }}
     };
   HEJ::Mixmax ran{};
 
   // Wp2j
   Process proc {{pid::proton,pid::proton}, 2, pid::Wp};
   size_t n_psp = n_psp_base;
   for( size_t i = 0; i<n_psp; ++i){
     const PhaseSpacePoint psp{proc,jet_para,pdf,E_cms, subl_change,subl_channels,
       boson_prop, ran};
     if(psp.status()==good){
       check_W2j(psp, *proc.boson);
     } else { // bad process -> try again
       ++n_psp;
     }
   }
   std::cout << "Wp+2j: Took " << n_psp << " to generate "
     << n_psp_base << " successfully PSP (" << 1.*n_psp/n_psp_base << " trials/PSP)" << std::endl;
   // Wm2j
   proc = Process{{pid::proton,pid::proton}, 2, pid::Wm};
   n_psp = n_psp_base;
   for( size_t i = 0; i<n_psp; ++i){
     const PhaseSpacePoint psp{proc,jet_para,pdf,E_cms, subl_change,subl_channels,
       boson_prop, ran};
     if(psp.status()==good){
       check_W2j(psp, *proc.boson);
     } else { // bad process -> try again
       ++n_psp;
     }
   }
   std::cout << "Wm+2j: Took " << n_psp << " to generate "
     << n_psp_base << " successfully PSP (" << 1.*n_psp/n_psp_base << " trials/PSP)" << std::endl;
 
   std::cout << "All processes passed." << std::endl;
   return EXIT_SUCCESS;
 }
diff --git a/FixedOrderGen/t/W_nj_classify.cc b/FixedOrderGen/t/W_nj_classify.cc
index 2da7126..9e832ab 100644
--- a/FixedOrderGen/t/W_nj_classify.cc
+++ b/FixedOrderGen/t/W_nj_classify.cc
@@ -1,179 +1,184 @@
-// check that the PSP generates the all W+jet subleading processes
-
+/**
+ *  \brief     check that the PSP generates the all W+jet subleading processes
+ *
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
+ *  \date      2019
+ *  \copyright GPLv2 or later
+ */
 #ifdef NDEBUG
 #undef NDEBUG
 #endif
 
 #include <algorithm>
 
 #include "JetParameters.hh"
 #include "ParticleProperties.hh"
 #include "PhaseSpacePoint.hh"
 #include "Process.hh"
 #include "Subleading.hh"
 
 #include "HEJ/Event.hh"
 #include "HEJ/Mixmax.hh"
 #include "HEJ/PDF.hh"
 #include "HEJ/utility.hh"
 
 using namespace HEJFOG;
 using namespace HEJ;
 
 namespace {
   void print_psp(PhaseSpacePoint const & psp){
     std::cerr << "Process:\n"
       << psp.incoming()[0].type << " + "<< psp.incoming()[1].type << " -> ";
     for(auto const & out: psp.outgoing()){
       std::cerr << out.type << " ";
     }
     std::cerr << "\n";
   }
   void bail_out(PhaseSpacePoint const & psp, std::string msg){
     print_psp(psp);
     throw std::logic_error{msg};
   }
 }
 
 int main(){
   constexpr size_t n_psp_base = 10375;
   const JetParameters jet_para{
     fastjet::JetDefinition(fastjet::JetAlgorithm::antikt_algorithm, 0.4), 30, 5, 30};
   PDF pdf(11000, pid::proton, pid::proton);
   constexpr double E_cms = 13000.;
   constexpr double subl_change = 0.8;
   const ParticlesPropMap boson_prop{
       {pid::Wp, {91.1876, 2.085, {Decay{ {pid::e_bar, pid::nu_e}, 1.}} }},
       {pid::Wm, {91.1876, 2.085, {Decay{ {pid::e, pid::nu_e_bar}, 1.}} }}
     };
   HEJ::Mixmax ran{};
 
   auto subl_channels = Subleading::all;
   std::vector<event_type::EventType> allowed_types{event_type::FKL,
     event_type::unob, event_type::unof, event_type::qqxexb, event_type::qqxexf};
 
   std::cout << "Wp3j" << std::endl;
   // Wp3j
   Process proc {{pid::proton,pid::proton}, 3, pid::Wp};
   size_t n_psp = n_psp_base;
   std::unordered_map<event_type::EventType, size_t> type_counter;
   for( size_t i = 0; i<n_psp; ++i){
     const PhaseSpacePoint psp{proc,jet_para,pdf,E_cms, subl_change,subl_channels,
       boson_prop, ran};
     if(psp.status()==good){
       const Event ev{ to_EventData(psp).cluster(jet_para.def, jet_para.min_pt) };
       ++type_counter[ev.type()];
       if( std::find(allowed_types.cbegin(), allowed_types.cend(), ev.type())
         == allowed_types.cend()) {
         bail_out(psp, "Found not allowed event of type "
           +std::string(event_type::names[ev.type()]));
       }
     } else { // bad process -> try again
       ++n_psp;
     }
   }
   std::cout << "Wp+3j: Took " << n_psp << " to generate "
     << n_psp_base << " successfully PSP (" << 1.*n_psp/n_psp_base << " trials/PSP)" << std::endl;
   std::cout << "States by classification:\n";
   for(auto const & entry: type_counter){
     const double fraction = static_cast<double>(entry.second)/n_psp_base;
     const int percent = std::round(100*fraction);
     std::cout << std::left << std::setw(25)
               << (event_type::names[entry.first] + std::string(":"))
               << entry.second << " (" << percent << "%)\n";
 
   }
   for(auto const & t: allowed_types){
     if(type_counter[t] < 0.05 * n_psp_base){
       std::cerr << "Less than 5% of the events are of type " << event_type::names[t] << std::endl;
       return EXIT_FAILURE;
     }
   }
 
   // Wm3j - only uno
   proc = Process{{pid::proton,pid::proton}, 3, pid::Wm};
   n_psp = n_psp_base;
 
   subl_channels = Subleading::uno;
   allowed_types = {event_type::FKL, event_type::unob, event_type::unof};
   type_counter.clear();
 
   for( size_t i = 0; i<n_psp; ++i){
     const PhaseSpacePoint psp{proc,jet_para,pdf,E_cms, subl_change,subl_channels,
       boson_prop, ran};
     if(psp.status()==good){
       const Event ev{ to_EventData(psp).cluster(jet_para.def, jet_para.min_pt) };
       ++type_counter[ev.type()];
       if( std::find(allowed_types.cbegin(), allowed_types.cend(), ev.type())
         == allowed_types.cend()) {
         bail_out(psp, "Found not allowed event of type "
           +std::string(event_type::names[ev.type()]));
       }
     } else { // bad process -> try again
       ++n_psp;
     }
   }
   std::cout << "Wm+3j (only uno): Took " << n_psp << " to generate "
     << n_psp_base << " successfully PSP (" << 1.*n_psp/n_psp_base << " trials/PSP)" << std::endl;
   std::cout << "States by classification:\n";
   for(auto const & entry: type_counter){
     const double fraction = static_cast<double>(entry.second)/n_psp_base;
     const int percent = std::round(100*fraction);
     std::cout << std::left << std::setw(25)
               << (event_type::names[entry.first] + std::string(":"))
               << entry.second << " (" << percent << "%)\n";
 
   }
   for(auto const & t: allowed_types){
     if(type_counter[t] < 0.05 * n_psp_base){
       std::cerr << "Less than 5% of the events are of type " << event_type::names[t] << std::endl;
       return EXIT_FAILURE;
     }
   }
 
   // Wm4j
   proc = Process{{pid::proton,pid::proton}, 4, pid::Wm};
   n_psp = n_psp_base;
 
   subl_channels = Subleading::all;
   allowed_types = {event_type::FKL,
     event_type::unob, event_type::unof, event_type::qqxexb, event_type::qqxexf,
     event_type::qqxmid};
   type_counter.clear();
 
   for( size_t i = 0; i<n_psp; ++i){
     const PhaseSpacePoint psp{proc,jet_para,pdf,E_cms, subl_change,subl_channels,
       boson_prop, ran};
     if(psp.status()==good){
       const Event ev{ to_EventData(psp).cluster(jet_para.def, jet_para.min_pt)};
       ++type_counter[ev.type()];
       if( std::find(allowed_types.cbegin(), allowed_types.cend(), ev.type())
         == allowed_types.cend()) {
         bail_out(psp, "Found not allowed event of type "
           +std::string(event_type::names[ev.type()]));
       }
     } else { // bad process -> try again
       ++n_psp;
     }
   }
   std::cout << "Wm+4j: Took " << n_psp << " to generate "
     << n_psp_base << " successfully PSP (" << 1.*n_psp/n_psp_base << " trials/PSP)" << std::endl;
   std::cout << "States by classification:\n";
   for(auto const & entry: type_counter){
     const double fraction = static_cast<double>(entry.second)/n_psp_base;
     const int percent = std::round(100*fraction);
     std::cout << std::left << std::setw(25)
               << (event_type::names[entry.first] + std::string(":"))
               << entry.second << " (" << percent << "%)\n";
 
   }
   for(auto const & t: allowed_types){
     if(type_counter[t] < 0.03 * n_psp_base){
       std::cerr << "Less than 3% of the events are of type " << event_type::names[t] << std::endl;
       return EXIT_FAILURE;
     }
   }
 
   std::cout << "All processes passed." << std::endl;
   return EXIT_SUCCESS;
 }
diff --git a/FixedOrderGen/t/h_2j.cc b/FixedOrderGen/t/h_2j.cc
index 4ad315d..8bdc382 100644
--- a/FixedOrderGen/t/h_2j.cc
+++ b/FixedOrderGen/t/h_2j.cc
@@ -1,68 +1,73 @@
+/**
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
+ *  \date      2019
+ *  \copyright GPLv2 or later
+ */
 #ifdef NDEBUG
 #undef NDEBUG
 #endif
 
 #include <algorithm>
 #include <cmath>
 #include <cassert>
 #include <iostream>
 
 #include "config.hh"
 #include "EventGenerator.hh"
 #include "HEJ/Ranlux64.hh"
 
 #include "HEJ/Event.hh"
 #include "HEJ/PDF.hh"
 #include "HEJ/MatrixElement.hh"
 
 using namespace HEJFOG;
 
 int main(){
   constexpr double invGeV2_to_pb = 389379292.;
   constexpr double xs_ref = 2.04928; // +- 0.00377252
   //calculated with HEJ revision 9570e3809613272ac4b8bf3236279ba23cf64d20
 
   auto config = load_config("config_h_2j.yml");
 
   HEJ::Ranlux64 ran{};
   HEJFOG::EventGenerator generator{
     config.process,
     config.beam,
     HEJ::ScaleGenerator{
       config.scales.base,
       config.scales.factors,
       config.scales.max_ratio
     },
     config.jets,
     config.pdf_id,
     config.subleading_fraction,
     config.subleading_channels,
     config.particles_properties,
     config.Higgs_coupling,
     ran
   };
 
   double xs = 0., xs_err = 0.;
   for (int trials = 0; trials < config.events; ++trials){
     auto ev = generator.gen_event();
     if(generator.status() != good) continue;
     assert(ev);
     ev->central().weight *= invGeV2_to_pb;
     ev->central().weight /= config.events;
 
     const auto the_Higgs = std::find_if(
         begin(ev->outgoing()), end(ev->outgoing()),
         [](HEJ::Particle const & p){ return p.type == HEJ::ParticleID::h; }
     );
     assert(the_Higgs != end(ev->outgoing()));
     if(std::abs(the_Higgs->rapidity()) > 5.) continue;
 
     xs += ev->central().weight;
     xs_err += ev->central().weight*ev->central().weight;
   }
   xs_err = std::sqrt(xs_err);
   std::cout << xs_ref << " ~ " << xs << " +- " << xs_err << std::endl;
 
   assert(std::abs(xs - xs_ref) < 3*xs_err);
   assert(xs_err < 0.01*xs);
 }
diff --git a/FixedOrderGen/t/h_2j_decay.cc b/FixedOrderGen/t/h_2j_decay.cc
index e404707..82de38a 100644
--- a/FixedOrderGen/t/h_2j_decay.cc
+++ b/FixedOrderGen/t/h_2j_decay.cc
@@ -1,87 +1,92 @@
+/**
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
+ *  \date      2019
+ *  \copyright GPLv2 or later
+ */
 #ifdef NDEBUG
 #undef NDEBUG
 #endif
 
 #include <algorithm>
 #include <cmath>
 #include <cassert>
 #include <iostream>
 
 #include "config.hh"
 #include "EventGenerator.hh"
 
 #include "HEJ/Event.hh"
 #include "HEJ/MatrixElement.hh"
 #include "HEJ/Particle.hh"
 #include "HEJ/PDF.hh"
 #include "HEJ/Ranlux64.hh"
 #include "HEJ/utility.hh"
 
 using namespace HEJFOG;
 
 bool pass_dR_cut(
     std::vector<fastjet::PseudoJet> const & jets,
     std::vector<HEJ::Particle> const & photons
 ){
   constexpr double delta_R_min = 0.7;
   for(auto const & jet: jets){
     for(auto const & photon: photons){
       if(jet.delta_R(photon.p) < delta_R_min) return false;
     }
   }
   return true;
 }
 
 int main(){
   constexpr double invGeV2_to_pb = 389379292.;
   constexpr double xs_ref = 0.00429198; // +- 1.0488e-05
   //calculated with HEJ revision 9570e3809613272ac4b8bf3236279ba23cf64d20
 
   auto config = load_config("config_h_2j_decay.yml");
 
   HEJ::Ranlux64 ran{};
   HEJFOG::EventGenerator generator{
     config.process,
     config.beam,
     HEJ::ScaleGenerator{
       config.scales.base,
       config.scales.factors,
       config.scales.max_ratio
     },
     config.jets,
     config.pdf_id,
     config.subleading_fraction,
     config.subleading_channels,
     config.particles_properties,
     config.Higgs_coupling,
     ran
   };
 
   double xs = 0., xs_err = 0.;
   for (int trials = 0; trials < config.events; ++trials){
     auto ev = generator.gen_event();
     if(generator.status() != good) continue;
     assert(ev);
     assert(ev->decays().size() == 1);
     const auto decay = begin(ev->decays());
     assert(ev->outgoing().size() > decay->first);
     const auto & the_Higgs = ev->outgoing()[decay->first];
     assert(the_Higgs.type == HEJ::pid::Higgs);
     assert(decay->second.size() == 2);
     auto const & gamma = decay->second;
     assert(gamma[0].type == HEJ::pid::photon);
     assert(gamma[1].type == HEJ::pid::photon);
     assert(HEJ::nearby_ep(gamma[0].p + gamma[1].p, the_Higgs.p, 1e-6));
     if(!pass_dR_cut(ev->jets(), gamma)) continue;
     ev->central().weight *= invGeV2_to_pb;
     ev->central().weight /= config.events;
 
     xs += ev->central().weight;
     xs_err += ev->central().weight*ev->central().weight;
   }
   xs_err = std::sqrt(xs_err);
   std::cout << xs_ref << " ~ " << xs << " +- " << xs_err << std::endl;
 
   assert(std::abs(xs - xs_ref) < 3*xs_err);
   assert(xs_err < 0.012*xs);
 }
diff --git a/FixedOrderGen/t/h_3j.cc b/FixedOrderGen/t/h_3j.cc
index bb2baff..459dfae 100644
--- a/FixedOrderGen/t/h_3j.cc
+++ b/FixedOrderGen/t/h_3j.cc
@@ -1,69 +1,74 @@
+/**
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
+ *  \date      2019
+ *  \copyright GPLv2 or later
+ */
 #ifdef NDEBUG
 #undef NDEBUG
 #endif
 
 #include <algorithm>
 #include <cmath>
 #include <cassert>
 #include <iostream>
 
 #include "config.hh"
 #include "EventGenerator.hh"
 #include "HEJ/Ranlux64.hh"
 
 #include "HEJ/Event.hh"
 #include "HEJ/MatrixElement.hh"
 #include "HEJ/PDF.hh"
 
 using namespace HEJFOG;
 
 int main(){
   constexpr double invGeV2_to_pb = 389379292.;
   constexpr double xs_ref = 1.07807; // +- 0.0071
   //calculated with HEJ revision 93efdc851b02a907a6fcc63956387f9f4c1111c2 +1
 
   auto config = load_config("config_h_2j.yml");
   config.process.njets = 3;
 
   HEJ::Ranlux64 ran{};
   HEJFOG::EventGenerator generator{
     config.process,
     config.beam,
     HEJ::ScaleGenerator{
       config.scales.base,
       config.scales.factors,
       config.scales.max_ratio
     },
     config.jets,
     config.pdf_id,
     config.subleading_fraction,
     config.subleading_channels,
     config.particles_properties,
     config.Higgs_coupling,
     ran
   };
 
   double xs = 0., xs_err = 0.;
   for (int trials = 0; trials < config.events; ++trials){
     auto ev = generator.gen_event();
     if(generator.status() != good) continue;
     assert(ev);
     ev->central().weight *= invGeV2_to_pb;
     ev->central().weight /= config.events;
 
     const auto the_Higgs = std::find_if(
         begin(ev->outgoing()), end(ev->outgoing()),
         [](HEJ::Particle const & p){ return p.type == HEJ::ParticleID::h; }
     );
     assert(the_Higgs != end(ev->outgoing()));
     if(std::abs(the_Higgs->rapidity()) > 5.) continue;
 
     xs += ev->central().weight;
     xs_err += ev->central().weight*ev->central().weight;
   }
   xs_err = std::sqrt(xs_err);
   std::cout << xs_ref << " ~ " << xs << " +- " << xs_err << std::endl;
 
   assert(std::abs(xs - xs_ref) < 3*xs_err);
   assert(xs_err < 0.02*xs);
 }
diff --git a/FixedOrderGen/t/h_3j_uno1.cc b/FixedOrderGen/t/h_3j_uno1.cc
index c100dcb..2005230 100644
--- a/FixedOrderGen/t/h_3j_uno1.cc
+++ b/FixedOrderGen/t/h_3j_uno1.cc
@@ -1,73 +1,79 @@
+/**
+ *  check that adding uno emissions doesn't change the FKL cross section
+ *
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
+ *  \date      2019
+ *  \copyright GPLv2 or later
+ */
 #ifdef NDEBUG
 #undef NDEBUG
 #endif
 
-// check that adding uno emissions doesn't change the FKL cross section
 
 #include <algorithm>
 #include <cassert>
 #include <cmath>
 #include <iostream>
 
 #include "config.hh"
 #include "EventGenerator.hh"
 #include "HEJ/Ranlux64.hh"
 #include "Subleading.hh"
 
 #include "HEJ/Event.hh"
 #include "HEJ/MatrixElement.hh"
 #include "HEJ/PDF.hh"
 
 using namespace HEJFOG;
 
 int main(){
   constexpr double invGeV2_to_pb = 389379292.;
   constexpr double xs_ref = 0.0243548; // +- 0.000119862
   //calculated with HEJ revision 9570e3809613272ac4b8bf3236279ba23cf64d20
 
   auto config = load_config("config_h_2j.yml");
   config.process.njets = 3;
   config.process.incoming = {HEJ::pid::u, HEJ::pid::u};
   config.subleading_channels = HEJFOG::Subleading::uno;
 
   HEJ::Ranlux64 ran{};
   HEJFOG::EventGenerator generator{
     config.process,
     config.beam,
     HEJ::ScaleGenerator{
       config.scales.base,
       config.scales.factors,
       config.scales.max_ratio
     },
     config.jets,
     config.pdf_id,
     config.subleading_fraction,
     config.subleading_channels,
     config.particles_properties,
     config.Higgs_coupling,
     ran
   };
 
   double xs = 0., xs_err = 0.;
   int uno_found = 0;
   for (int trials = 0; trials < config.events; ++trials){
     auto ev = generator.gen_event();
     if(generator.status() != good) continue;
     assert(ev);
     if(ev->type() != HEJ::event_type::FKL){
       ++uno_found;
       continue;
     }
     ev->central().weight *= invGeV2_to_pb;
     ev->central().weight /= config.events;
 
     xs += ev->central().weight;
     xs_err += ev->central().weight*ev->central().weight;
   }
   xs_err = std::sqrt(xs_err);
   std::cout << xs_ref << " ~ " << xs << " +- " << xs_err << '\n';
   std::cout << uno_found << " events with unordered emission" << std::endl;
   assert(uno_found > 0);
   assert(std::abs(xs - xs_ref) < 3*xs_err);
   assert(xs_err < 0.05*xs);
 }
diff --git a/FixedOrderGen/t/h_3j_uno2.cc b/FixedOrderGen/t/h_3j_uno2.cc
index 4dc4770..5341658 100644
--- a/FixedOrderGen/t/h_3j_uno2.cc
+++ b/FixedOrderGen/t/h_3j_uno2.cc
@@ -1,68 +1,74 @@
+/**
+ * check uno cross section
+ *
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
+ *  \date      2019
+ *  \copyright GPLv2 or later
+ */
 #ifdef NDEBUG
 #undef NDEBUG
 #endif
 
-// check uno cross section
 
 #include <algorithm>
 #include <cassert>
 #include <cmath>
 #include <iostream>
 
 #include "config.hh"
 #include "EventGenerator.hh"
 #include "HEJ/Ranlux64.hh"
 #include "Subleading.hh"
 
 #include "HEJ/Event.hh"
 #include "HEJ/MatrixElement.hh"
 #include "HEJ/PDF.hh"
 
 using namespace HEJFOG;
 
 int main(){
   constexpr double invGeV2_to_pb = 389379292.;
   constexpr double xs_ref = 0.00347538; // +- 3.85875e-05
   //calculated with HEJ revision 9570e3809613272ac4b8bf3236279ba23cf64d20
 
   auto config = load_config("config_h_2j.yml");
   config.process.njets = 3;
   config.process.incoming = {HEJ::pid::u, HEJ::pid::u};
   config.subleading_fraction = 1.;
   config.subleading_channels = HEJFOG::Subleading::uno;
 
   HEJ::Ranlux64 ran{};
   HEJFOG::EventGenerator generator{
     config.process,
     config.beam,
     HEJ::ScaleGenerator{
       config.scales.base,
       config.scales.factors,
       config.scales.max_ratio
     },
     config.jets,
     config.pdf_id,
     config.subleading_fraction,
     config.subleading_channels,
     config.particles_properties,
     config.Higgs_coupling,
     ran
   };
 
   double xs = 0., xs_err = 0.;
   for (int trials = 0; trials < config.events; ++trials){
     auto ev = generator.gen_event();
     if(generator.status() != good) continue;
     assert(ev);
     if(ev->type() == HEJ::event_type::FKL) continue;
     ev->central().weight *= invGeV2_to_pb;
     ev->central().weight /= config.events;
 
     xs += ev->central().weight;
     xs_err += ev->central().weight*ev->central().weight;
   }
   xs_err = std::sqrt(xs_err);
   std::cout << xs_ref << " ~ " << xs << " +- " << xs_err << std::endl;
   assert(std::abs(xs - xs_ref) < 3*xs_err);
   assert(xs_err < 0.05*xs);
 }
diff --git a/FixedOrderGen/t/h_5j.cc b/FixedOrderGen/t/h_5j.cc
index a1b60e8..f280fc9 100644
--- a/FixedOrderGen/t/h_5j.cc
+++ b/FixedOrderGen/t/h_5j.cc
@@ -1,64 +1,70 @@
+/**
+ *  This is a regression test
+ *  the reference cross section has not been checked against any other program
+ *
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
+ *  \date      2019
+ *  \copyright GPLv2 or later
+ */
 #ifdef NDEBUG
 #undef NDEBUG
 #endif
-// This is a regression test
-// the reference cross section has not been checked against any other program
 
 #include <algorithm>
 #include <cassert>
 #include <cmath>
 #include <iostream>
 
 #include "config.hh"
 #include "EventGenerator.hh"
 #include "HEJ/Ranlux64.hh"
 
 #include "HEJ/Event.hh"
 #include "HEJ/MatrixElement.hh"
 #include "HEJ/PDF.hh"
 
 using namespace HEJFOG;
 
 int main(){
   constexpr double invGeV2_to_pb = 389379292.;
   constexpr double xs_ref = 0.252273; // +- 0.00657742
   //calculated with HEJ revision 9570e3809613272ac4b8bf3236279ba23cf64d20
 
   auto config = load_config("config_h_2j.yml");
   config.process.njets = 5;
 
   HEJ::Ranlux64 ran{};
   HEJFOG::EventGenerator generator{
     config.process,
     config.beam,
     HEJ::ScaleGenerator{
       config.scales.base,
       config.scales.factors,
       config.scales.max_ratio
     },
     config.jets,
     config.pdf_id,
     config.subleading_fraction,
     config.subleading_channels,
     config.particles_properties,
     config.Higgs_coupling,
     ran
   };
 
   double xs = 0., xs_err = 0.;
   for (int trials = 0; trials < config.events; ++trials){
     auto ev = generator.gen_event();
     if(generator.status() != good) continue;
     assert(ev);
     ev->central().weight *= invGeV2_to_pb;
     ev->central().weight /= config.events;
 
     xs += ev->central().weight;
     xs_err += ev->central().weight*ev->central().weight;
   }
   xs_err = std::sqrt(xs_err);
   std::cout << xs_ref << " ~ " << xs << " +- " << xs_err << std::endl;
 
   assert(std::abs(xs - xs_ref) < 3*xs_err);
   assert(xs_err < 0.06*xs);
 }
diff --git a/README b/README
new file mode 100644
index 0000000..f66566a
--- /dev/null
+++ b/README
@@ -0,0 +1,60 @@
+------------------------------------------
+-            High Energy Jets            -
+------------------------------------------
+
+High Energy Jets (HEJ) is a Monte Carlo generator for all-order summation of
+high-energy logarithms. It can be used as both a C++ library and standalone
+executable.
+
+For further informations and questions please visit
+
+    http://hej.web.cern.ch/
+
+The latest version can be downloaded from
+
+    https://phab.hepforge.org/source/hej/repository/v2.0/
+
+-------------  Installation  -------------
+
+HEJ can be installed via CMake version 3.1 or later (https://cmake.org/) by
+running
+
+  mkdir build
+  cd build
+  cmake .. -DCMAKE_INSTALL_PREFIX=target/directory
+  make install
+
+Replace "target/directory" with the directory where HEJ should be installed to.
+
+HEJ depends on multiple external packages, a full list is given in the user
+documentation (i.e. http://hej.web.cern.ch/). The minimal requirements are:
+
+    A compiler supporting the C++14 standard (e.g. gcc 5 or later)
+    CLHEP (https://gitlab.cern.ch/CLHEP/CLHEP)
+    FastJet (http://fastjet.fr/)
+    IOStreams and uBLAS for the boost library (https://boost.org/)
+    LHAPDF (https://lhapdf.hepforge.org/)
+    yaml-cpp (https://github.com/jbeder/yaml-cpp)
+
+We also provide a Fixed Order Generator for the HEJ matrix elements as a
+separate executable. To install it run the same commands as above in the
+"FixedOrderGen" directory.
+
+-------------  Documentation -------------
+
+All documentation is hosted on
+
+    http://hej.web.cern.ch/
+
+To generate the user documentation locally run
+(requires sphinx http://sphinx-doc.org/)
+
+    cd doc/sphinx
+    make html
+    firefox _build/html/index.html
+
+The code documentation can be build through doxygen (http://doxygen.org/);
+
+    cd doc/doxygen
+    doxygen Doxyfile
+    firefox html/index.html
diff --git a/cmake/Templates/Version.hh.in b/cmake/Templates/Version.hh.in
index 6babcbe..76e0a6e 100644
--- a/cmake/Templates/Version.hh.in
+++ b/cmake/Templates/Version.hh.in
@@ -1,47 +1,50 @@
-/** \file Version.hh
- *  \brief The file gives the current HEJ Version
+/** \file      Version.hh
+ *  \brief     The file gives the current HEJ Version
+ *
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
+ *  \date      2019
+ *  \copyright GPLv2 or later
  */
-
 #pragma once
 
 #include <string>
 
 /// @brief Full name of this package.
 #define HEJ_PACKAGE_NAME  "@PROJECT_NAME@"
 
 /// @brief HEJ version string
 #define HEJ_VERSION "@PROJECT_VERSION@"
 
 /// @brief Full name and version of this package.
 #define HEJ_PACKAGE_STRING  "@PROJECT_NAME@ @PROJECT_VERSION@"
 
 /// @brief Major version of this package
 #define HEJ_VERSION_MAJOR  @PROJECT_VERSION_MAJOR@
 
 /// @brief Minor version of this package
 #define HEJ_VERSION_MINOR  @PROJECT_VERSION_MINOR@
 
 /// @brief Patch version of this package
 #define HEJ_VERSION_PATCH  @PROJECT_VERSION_PATCH@
 
 /// @brief Git revision of this package
 #define HEJ_GIT_revision  "@PROJECT_GIT_REVISION@"
 
 /// @brief Git branch name of this package
 #define HEJ_GIT_branch  "@PROJECT_GIT_BRANCH@"
 
 
 namespace HEJ {
 
   namespace Version {
 
     inline std::string String()             { return HEJ_VERSION; }
     inline std::string package_name()       { return HEJ_PACKAGE_NAME; }
     inline std::string package_name_full()  { return HEJ_PACKAGE_STRING; }
     inline int Major()                      { return HEJ_VERSION_MAJOR; }
     inline int Minor()                      { return HEJ_VERSION_MINOR; }
     inline int Patch()                      { return HEJ_VERSION_PATCH; }
     inline std::string revision()           { return HEJ_GIT_revision; }
 
   };
 }
diff --git a/doc/doxygen/biblio.bib b/doc/doxygen/biblio.bib
index 3e960be..e41ce28 100644
--- a/doc/doxygen/biblio.bib
+++ b/doc/doxygen/biblio.bib
@@ -1,86 +1,97 @@
 @article{Andersen:2011hs,
       author         = "Andersen, Jeppe R. and Smillie, Jennifer M.",
       title          = "{Multiple Jets at the LHC with High Energy Jets}",
       journal        = "JHEP",
       volume         = "06",
       year           = "2011",
       pages          = "010",
       doi            = "10.1007/JHEP06(2011)010",
       eprint         = "1101.5394",
       archivePrefix  = "arXiv",
       primaryClass   = "hep-ph",
       reportNumber   = "CP3-ORIGINS-2011-02, EDINBURGH-2011-03",
       SLACcitation   = "%%CITATION = ARXIV:1101.5394;%%"
 }
 @article{James:1993np,
       author         = "James, F.",
       title          = "{RANLUX: A FORTRAN implementation of the high quality
                         pseudorandom number generator of Luscher}",
       journal        = "Comput. Phys. Commun.",
       volume         = "79",
       year           = "1994",
       pages          = "111-114",
       doi            = "10.1016/0010-4655(94)90233-X",
       note           = "[Erratum: Comput. Phys. Commun.97,357(1996)]",
       reportNumber   = "CERN-CN-93-13",
       SLACcitation   = "%%CITATION = CPHCB,79,111;%%"
 }
 @article{Luscher:1993dy,
       author         = "Luscher, Martin",
       title          = "{A Portable high quality random number generator for
                         lattice field theory simulations}",
       journal        = "Comput. Phys. Commun.",
       volume         = "79",
       year           = "1994",
       pages          = "100-110",
       doi            = "10.1016/0010-4655(94)90232-1",
       eprint         = "hep-lat/9309020",
       archivePrefix  = "arXiv",
       primaryClass   = "hep-lat",
       reportNumber   = "DESY-93-133",
       SLACcitation   = "%%CITATION = HEP-LAT/9309020;%%"
 }
 @article{Savvidy:2014ana,
       author         = "Savvidy, Konstantin G.",
       title          = "{The MIXMAX random number generator}",
       journal        = "Comput. Phys. Commun.",
       volume         = "196",
       year           = "2015",
       pages          = "161-165",
       doi            = "10.1016/j.cpc.2015.06.003",
       eprint         = "1403.5355",
       archivePrefix  = "arXiv",
       primaryClass   = "hep-lat",
       reportNumber   = "NITS-PHY-2014, NITS-PHY-2014003",
       SLACcitation   = "%%CITATION = ARXIV:1403.5355;%%"
 }
 @inproceedings{Boos:2001cv,
       author         = "Boos, E. and others",
       title          = "{Generic user process interface for event generators}",
       booktitle      = "{Physics at TeV colliders. Proceedings, Euro Summer
                         School, Les Houches, France, May 21-June 1, 2001}",
       url            = "http://lss.fnal.gov/archive/preprint/fermilab-conf-01-496-t.shtml",
       year           = "2001",
       eprint         = "hep-ph/0109068",
       archivePrefix  = "arXiv",
       primaryClass   = "hep-ph",
       reportNumber   = "FERMILAB-CONF-01-496-T",
       SLACcitation   = "%%CITATION = HEP-PH/0109068;%%"
 }
 @article{Andersen:2011zd,
       author         = "Andersen, Jeppe R. and Lonnblad, Leif and Smillie,
                         Jennifer M.",
       title          = "{A Parton Shower for High Energy Jets}",
       journal        = "JHEP",
       volume         = "07",
       year           = "2011",
       pages          = "110",
       doi            = "10.1007/JHEP07(2011)110",
       eprint         = "1104.1316",
       archivePrefix  = "arXiv",
       primaryClass   = "hep-ph",
       reportNumber   = "CERN-PH-TH-2011-072, CP3-ORIGINS-2011-14,
                         EDINBURGH-2011-16, LU-TP-11-15, MCNET-11-12, LU-TP
                         --11-15",
       SLACcitation   = "%%CITATION = ARXIV:1104.1316;%%"
 }
+@article{Hoeche:2019rti,
+      author         = "Höche, Stefan and Prestel, Stefan and Schulz, Holger",
+      title          = "{Simulation of vector boson plus many jet final states at
+                        the high luminosity LHC}",
+      year           = "2019",
+      eprint         = "1905.05120",
+      archivePrefix  = "arXiv",
+      primaryClass   = "hep-ph",
+      reportNumber   = "FERMILAB-PUB-19-192-T, LU-TP 19-14, MCNET-19-09",
+      SLACcitation   = "%%CITATION = ARXIV:1905.05120;%%"
+}
diff --git a/doc/doxygen/mainpage.dox b/doc/doxygen/mainpage.dox
index 60cbb24..dceb8d4 100644
--- a/doc/doxygen/mainpage.dox
+++ b/doc/doxygen/mainpage.dox
@@ -1,225 +1,224 @@
 namespace HEJ { // so that doxygen links names in this namespace
 /**
  * @mainpage
  *
  * @section intro Introduction
  *
  * HEJ 2 is a library for all-order resummation of high-energy
  * logarithms. It includes a program to add resummation to fixed-order
  * events. User documentation for the program can be found <a
  * href="https://hej.web.cern.ch/HEJ/doc/2.0/user/">here</a>. This
  * documentation is instead aimed at users of the library itself.
  *
  * @section overview Overview
  *
- * The main functionality is contained in the HEJ namespace. Particles
- * are defined via the Particle struct, which consists of the particle
- * four-momentum and its identifier according to the <a
+ * The main functionality is contained in the HEJ namespace. Particles are
+ * defined via the Particle struct, which consists of the particle
+ * four-momentum, its identifier according to the <a
  * href="http://pdg.lbl.gov/2017/reviews/rpp2017-rev-monte-carlo-numbering.pdf">
- * PDG Monte Carlo numbering scheme </a>. Given a number of incoming and
- * outgoing particles, the square of the resummation matrix element can
- * be calculated with the help of the MatrixElement class.
+ * PDG Monte Carlo numbering scheme </a> and an optional Colour charge. Given a
+ * number of incoming and outgoing particles, the square of the resummation
+ * matrix element can be calculated with the help of the MatrixElement class.
  *
  * The EventReweighter class adds resummation to existing fixed-order
  * events. Both fixed-order and resummation events are objects of the
- * Event class, which are created from UnclusteredEvent objects with the
+ * Event class, which are created from EventData objects with the
  * help of a <a
  * href="http://fastjet.fr/repo/doxygen-3.3.1/classfastjet_1_1JetDefinition.html">jet
- * definition according to the fastjet</a> library. UnclusteredEvent
+ * definition according to the fastjet</a> library. EventData
  * objects can be assembled manually or converted from input events in
- * the LesHouches standard, read from file with a LHEF::Reader.
+ * the LesHouches standard, read from file with a EventReader (e.g.
+ * LesHouchesReader or HDF5Reader).
  *
  * Events can be saved with one of the EventWriter classes. Currently,
  * there is support for the Les Houches event file format with the
  * LesHouchesWriter class. If HEJ 2 was installed with HepMC 2 or 3
  * support, the respective format is available through the HepMCWriter
  * class.
  *
  * Further classes of interest are the interfaces to the Mixmax and
  * Ranlux64 random number generators, the PDF class to interact with <a
  * href="https://lhapdf.hepforge.org/"> LHAPDF </a> and the ScaleGenerator
  * and ScaleConfig classes to calculate renormalisation and factorisation
  * scales for a given Event.
  *
  * @section example Example
  *
  * As an example, we show a toy program that computes the square of a
  * matrix element in the HEJ approximation for a single event. First, we
  * include the necessary header files:
  * @code{.cpp}
  * #include "HEJ/Event.hh"
  * #include "HEJ/MatrixElement.hh"
  * @endcode
  * We then specify the incoming and outgoing particles. A particle
- * has a type and four-momentum \f$(p_x, p_y, p_z, E)\f$. For instance, an
- * incoming gluon could be defined as
+ * has a type, a four-momentum \f$(p_x, p_y, p_z, E)\f$ and optionally a colour
+ * charge. For instance, an incoming gluon could be defined as
  * @code{.cpp}
  * fastjet::PseudoJet momentum{0, 0, 308., 308.};
- * HEJ::Particle gluon_in{HEJ::ParticleID::gluon, momentum};
+ * HEJ::Colour colours{501,502};
+ * HEJ::Particle gluon_in{HEJ::ParticleID::gluon, momentum, colours};
  * @endcode
  * We collect all incoming and outgoing particles in a partonic event. Here
- * is an example for a partonic \f$gu \to gghu\f$ event:
+ * is an example for a partonic \f$gu \to gghu\f$ event (omitting colours):
  * @code{.cpp}
- * HEJ::UnclusteredEvent partonic_event;
+ * HEJ::Event::EventData partonic_event;
  *
  * // incoming particles
  * partonic_event.incoming[0] = {
  *   HEJ::ParticleID::gluon,
  *   { 0., 0., 308., 308.}
  * };
  * partonic_event.incoming[1] = {
  *   HEJ::ParticleID::up,
  *   { 0., 0.,-164., 164.}
  * };
  * // outgoing particles
  * partonic_event.outgoing.push_back({
  *   HEJ::ParticleID::higgs,
  *   { 98., 82., 14., 180.}
  * });
  * partonic_event.outgoing.push_back({
  *   HEJ::ParticleID::up,
  *   { 68.,-54., 36.,  94.}
  * });
  * partonic_event.outgoing.push_back({
  *   HEJ::ParticleID::gluon,
  *   {-72.,  9., 48.,  87.}
  * });
  * partonic_event.outgoing.push_back({
  *   HEJ::ParticleID::gluon,
  *   {-94.,-37., 46., 111.}
  * });
  * @endcode
  * Alternatively, we could read the event from a Les Houches event file,
- * possibly compressed with gzip. For this, the additional header
- * files @c HEJ/stream.hh and @c LHEF/LHEF.h have to be
- * included.
+ * possibly compressed with gzip. For this, the additional header file
+ * @c HEJ/LesHouchesReader.hh have to be included.
  * @code{.cpp}
- * HEJ::istream in{"events.lhe.gz"};
- * LHEF::Reader reader{in};
- * reader.readEvent();
- * HEJ::UnclusteredEvent partonic_event{reader.hepeup};
+ * HEJ::LesHouchesReader reader{"events.lhe.gz"};
+ * reader.read_event();
+ * HEJ::Event::EventData partonic_event{reader.hepeup()};
  * @endcode
  *
  * In this specific example we will later choose a constant value for the
  * strong coupling, so that the HEJ matrix element does not depend on the
  * renormalisation scale. However, in a more general scenario, we will want
  * to set a central scale:
  * @code{.cpp}
- * partonic_event.central.mur = 50.;
+ * partonic_event.parameters.central.mur = 50.;
  * @endcode
  * It is possible to add more scales in order to perform scale variation:
  * @code{.cpp}
- * partonic_event.variations.resize(2);
- * partonic_event.variations[0].mur = 25.;
- * partonic_event.variations[1].mur = 100.;
+ * partonic_event.parameters.variations.resize(2);
+ * partonic_event.parameters.variations[0].mur = 25.;
+ * partonic_event.parameters.variations[1].mur = 100.;
  * @endcode
  *
  * In the next step, we leverage FastJet to construct an event with
  * clustered jets. Here, we use antikt jets with R=0.4 and transverse
  * momenta of at least 30 GeV.
  * @code{.cpp}
  * const fastjet::JetDefinition jet_def{
  *  fastjet::JetAlgorithm::antikt_algorithm, 0.4
  * };
  * const double min_jet_pt = 30.;
- * HEJ::Event event{partonic_event, jet_def, min_jet_pt};
+ * HEJ::Event event{partonic_event.cluster(jet_def, min_jet_pt)};
  * @endcode
  * In order to calculate the Matrix element, we now have to fix the physics
  * parameters. For the sake of simplicity, we assume an effective coupling
  * of the Higgs boson to gluons in the limit of an infinite top-quark mass
- * and a fixed value of $\alpha_s = 0.118$ for the strong coupling.
+ * and a fixed value of \f$\alpha_s = 0.118\f$ for the strong coupling.
  * @code{.cpp}
  * const auto alpha_s = [](double /* mu_r */) { return 0.118; };
  * HEJ::MatrixElementConfig ME_config;
  * // whether to include corrections from the
  * // evolution of \alpha_s in virtual corrections
  * ME_config.log_correction = false;
  * HEJ::MatrixElement ME{alpha_s, ME_config};
  * @endcode
  * If QCDLoop is installed, we can also take into account the full loop
  * effects with finite top and bottom quark masses:
  * @code{.cpp}
  * HEJ::MatrixElementConfig ME_config;
  * ME_config.Higgs_coupling.use_impact_factors = false;
  * ME_config.Higgs_coupling.mt = 163;
  * ME_config.Higgs_coupling.include_bottom = true;
  * ME_config.Higgs_coupling.mb = 2.8;
  * @endcode
  * Finally, we can compute and print the square of the matrix element with
  * @code{.cpp}
  * std::cout << "HEJ ME: " << ME(event).central << '\n';
  * @endcode
  * In the case of scale variation, the weight associated with the scale
  * @c event.variations[i].mur is @c ME(event).variations[i].
  *
  * Collecting the above pieces, we have the following program:
  * @code{.cpp}
  * #include "HEJ/Event.hh"
  * #include "HEJ/MatrixElement.hh"
  *
  * int main(){
- *   HEJ::UnclusteredEvent partonic_event;
+ *   HEJ::Event::EventData partonic_event;
  *   // incoming particles
  *   partonic_event.incoming[0] = {
  *     HEJ::ParticleID::gluon,
  *     { 0., 0., 308., 308.}
  *   };
  *   partonic_event.incoming[1] = {
  *     HEJ::ParticleID::up,
  *     { 0., 0.,-164., 164.}
  *   };
  *   // outgoing particles
  *   partonic_event.outgoing.push_back({
  *     HEJ::ParticleID::higgs,
  *     { 98., 82., 14., 180.}
  *   });
  *   partonic_event.outgoing.push_back({
  *     HEJ::ParticleID::up,
  *     { 68.,-54., 36.,  94.}
  *   });
  *   partonic_event.outgoing.push_back({
  *     HEJ::ParticleID::gluon,
  *     {-72.,  9., 48.,  87.}
  *   });
  *   partonic_event.outgoing.push_back({
  *     HEJ::ParticleID::gluon,
  *     {-94.,-37., 46., 111.}
  *   });
  *
  *   const fastjet::JetDefinition jet_def{
  *     fastjet::JetAlgorithm::antikt_algorithm, 0.4
  *   };
  *   const double min_jet_pt = 30.;
- *   HEJ::Event event{partonic_event, jet_def, min_jet_pt};
- *
+ *   HEJ::Event event{partonic_event.cluster(jet_def, min_jet_pt)};
  *   const auto alpha_s = [](double /* mu_r */) { return 0.118; };
  *   HEJ::MatrixElementConfig ME_config;
  *   // whether to include corrections from the
  *   // evolution of \alpha_s in virtual corrections
  *   ME_config.log_correction = false;
  *   HEJ::MatrixElement ME{alpha_s, ME_config};
  *
  *   std::cout
  *     << "HEJ ME: " << ME(event).central
  *     << " = tree * virtual = " << ME.tree(event).central
  *     << " * " << ME.virtual_corrections(event).central
  *     << '\n';
  * }
  * @endcode
  * After saving the above code to a file @c matrix_element.cc, it
  * can be compiled into an executable @c matrix_element with a
  * suitable compiler. For example, with @c g++ this can be done
  * with the command
  * @code{.sh}
  * g++ -o matrix_element matrix_element.cc -lHEJ -lfastjet
  * @endcode
  * If HEJ or any of the required libraries was installed to a
  * non-standard location, it may be necessary to explicitly specify the
  * paths to the required header and library files. This can be done with
  * the @c HEJ-config executable and similar programs for the
  * other dependencies:
  * @code{.sh}
  * g++ $(fastjet-config --cxxflags) $(HEJ-config --cxxflags) -o matrix_element matrix_element.cc $(HEJ-config --libs) $(fastjet-config --libs)
  * @endcode
  */
 }
diff --git a/include/HEJ/Analysis.hh b/include/HEJ/Analysis.hh
index 003667d..f00d7fc 100644
--- a/include/HEJ/Analysis.hh
+++ b/include/HEJ/Analysis.hh
@@ -1,47 +1,47 @@
 /** \file
  *  \brief Header file for the Analysis interface
  *
  * This header contains declarations that faciliate creating custom analyses
  * to be used with HEJ 2.
  * \todo link to user documentation
  *
- *  \authors   Jeppe Andersen, Tuomas Hapola, Marian Heil, Andreas Maier, Jennifer Smillie
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
  *  \date      2019
  *  \copyright GPLv2 or later
  */
 #pragma once
 
 //! Main HEJ 2 Namespace
 namespace HEJ{
   class Event;
 
   //! Analysis base class
   /**
    *  This is the interface that all analyses should implement,
    *  i.e. all custom analyses have to be derived from this struct.
    */
   struct Analysis{
     //! Fill event into analysis (e.g. to histograms)
     /**
      * @param res_event     The event in resummation phase space
      * @param FO_event      The original fixed-order event
      */
     virtual void fill(Event const & res_event, Event const & FO_event) = 0;
     //! Decide whether an event passes the cuts
     /**
      * @param res_event     The event in resummation phase space
      * @param FO_event      The original fixed-order event
      * @returns             Whether the event passes all cuts
      */
     virtual bool pass_cuts(Event const & res_event, Event const & FO_event) = 0;
     //! Finalise analysis
     /**
      * This function is called after all events have been processed and
      * can be used for example to print out or save the results.
      */
     virtual void finalise() = 0;
 
     virtual ~Analysis() = default;
   };
 
 }
diff --git a/include/HEJ/CombinedEventWriter.hh b/include/HEJ/CombinedEventWriter.hh
index 160da19..5f1ba18 100644
--- a/include/HEJ/CombinedEventWriter.hh
+++ b/include/HEJ/CombinedEventWriter.hh
@@ -1,45 +1,44 @@
 /** \file
  *  \brief Declares the CombinedEventWriter class
  *
- *  \authors   Jeppe Andersen, Tuomas Hapola, Marian Heil, Andreas Maier, Jennifer Smillie
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
  *  \date      2019
  *  \copyright GPLv2 or later
  */
-
 #pragma once
 
 #include <memory>
 #include <vector>
 
 #include "HEJ/EventWriter.hh"
 #include "HEJ/output_formats.hh"
 
 namespace LHEF{
   struct HEPRUP;
 }
 
 namespace HEJ{
 
   //! Write event output to zero or more output files.
   class CombinedEventWriter: public EventWriter{
   public:
     //!Constructor
     /**
      * @param outfiles     Specifies files output should be written to.
      *                     Each entry in the vector contains a file name
      *                     and output format.
      * @param heprup       General process information
      */
     CombinedEventWriter(
         std::vector<OutputFile> const & outfiles,
         LHEF::HEPRUP const & heprup
     );
 
     //! Write one event to all output files
     void write(Event const &) override;
 
   private:
     std::vector<std::unique_ptr<EventWriter>> writers_;
   };
 
 }
diff --git a/include/HEJ/Constants.hh b/include/HEJ/Constants.hh
index 4568911..7b43915 100644
--- a/include/HEJ/Constants.hh
+++ b/include/HEJ/Constants.hh
@@ -1,39 +1,39 @@
 /** \file
  *  \brief Header file defining all global constants used for HEJ
  *
- *  \authors   Jeppe Andersen, Tuomas Hapola, Marian Heil, Andreas Maier, Jennifer Smillie
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
  *  \date      2019
  *  \copyright GPLv2 or later
  */
 #pragma once
 
 namespace HEJ{
 /// @name QCD parameters
 //@{
   constexpr double N_C = 3.;    //!< number of Colours
   constexpr double C_A = N_C;    //!< \f$C_A\f$
   constexpr double C_F = (N_C*N_C - 1.)/(2.*N_C); //!< \f$C_F\f$
   constexpr double t_f = 0.5; //!< \f$t_f\f$
   constexpr double n_f = 5.;    //!< number light flavours
   constexpr double beta0 = 11./3.*C_A - 4./3.*t_f*n_f;  //!< \f$\beta_0\f$
 //@}
 /// @name QFT parameters
 //@{
   constexpr double vev = 246.2196508; //!< Higgs vacuum expectation value in GeV
   constexpr double gw = 0.653233;
   constexpr double MW = 80.419; // The W mass in GeV/c^2
   constexpr double GammaW = 2.0476; // the W width in GeV/c^2
 
   //@}
 /// @name Generation Parameters
 //@{
   //! Default scale for virtual correction, \f$\lambda\f$ cf. eq. (20) in \cite Andersen:2011hs
   constexpr double CLAMBDA = 0.2;
   constexpr double CMINPT = 0.2;  //!< minimal \f$p_t\f$ of all partons
 //@}
 /// @name Conventional Parameters
 //@{
   //! Value of first colour for colour dressing, according to LHE convention \cite Boos:2001cv
   constexpr int COLOUR_OFFSET = 501;
 //@}
 }
diff --git a/include/HEJ/CrossSectionAccumulator.hh b/include/HEJ/CrossSectionAccumulator.hh
index 013ffa5..2b76fd5 100644
--- a/include/HEJ/CrossSectionAccumulator.hh
+++ b/include/HEJ/CrossSectionAccumulator.hh
@@ -1,69 +1,74 @@
+/**
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
+ *  \date      2019
+ *  \copyright GPLv2 or later
+ */
 #pragma once
 
 #include <map>
 #include <ostream>
 #include <iomanip>
 #include <string>
 
 #include "HEJ/Event.hh"
 #include "HEJ/event_types.hh"
 
 namespace HEJ {
   template<typename T>
   struct XSWithError {
     T value = T{};
     T error = T{};
   };
 
   /**
    * @brief Sum of Cross Section for different subproccess
    */
   class CrossSectionAccumulator {
   public:
     void fill(HEJ::Event const & ev) {
       const double wt = ev.central().weight;
       auto & entry = xs_[ev.type()];
       entry.value += wt;
       entry.error += wt*wt;
       total_.value += wt;
       total_.error += wt*wt;
     }
 
     auto begin() const {
       return std::begin(xs_);
     }
 
     auto end() const {
       return std::end(xs_);
     }
 
     //! total Cross Section and error
     XSWithError<double> total() const {
       return total_;
     }
 
   private:
     std::map<HEJ::event_type::EventType, XSWithError<double>> xs_;
     XSWithError<double> total_;
   };
 
   std::ostream& operator<<(std::ostream& os, const CrossSectionAccumulator& xs){
     const std::streamsize orig_prec = os.precision();
     os << std::scientific << std::setprecision(3)
       << "    " << std::left << std::setw(25)
       << "Cross section: " << xs.total().value
       << " +- " << std::sqrt(xs.total().error) << " (pb)\n";
     for(auto const & xs_type: xs) {
       os << "    " << std::left << std::scientific <<std::setw(25)
         << (HEJ::event_type::names[xs_type.first] + std::string(": "));
       os << xs_type.second.value << " +- "
         << std::sqrt(xs_type.second.error) << " (pb) "
         << std::fixed << std::setprecision(3)
         << "[" <<std::setw(6) <<std::right<< ((xs_type.second.value)/xs.total().value)*100 << "%]"
         << std::endl;
     }
     os << std::defaultfloat;
     os.precision(orig_prec);
     return os;
   }
 }
diff --git a/include/HEJ/EmptyAnalysis.hh b/include/HEJ/EmptyAnalysis.hh
index f0d1933..6905b0e 100644
--- a/include/HEJ/EmptyAnalysis.hh
+++ b/include/HEJ/EmptyAnalysis.hh
@@ -1,48 +1,47 @@
 /** \file
  *  \brief Declaration of the trivial (empty) analysis
  *
- *  \authors   Jeppe Andersen, Tuomas Hapola, Marian Heil, Andreas Maier, Jennifer Smillie
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
  *  \date      2019
  *  \copyright GPLv2 or later
  */
-
 #pragma once
 
 #include <memory>
 
 #include "HEJ/Analysis.hh"
 
 //! YAML Namespace
 namespace YAML{
   class Node;
 }
 
 namespace HEJ{
   /** An analysis that does nothing
    *
    *  This analysis is used by default if no user analysis is specified.
    *  The member functions don't do anything and events passed to the
    *  analysis are simply ignored.
    */
   struct EmptyAnalysis: Analysis{
     static std::unique_ptr<Analysis> create(YAML::Node const & parameters);
 
     //! Fill event into analysis (e.g. to histograms)
     /**
      *  This function does nothing
      */
     virtual void fill(Event const &, Event const &) override;
     //! Whether a resummation event passes all cuts
     /**
      *  There are no cuts, so all events pass
      */
     virtual bool pass_cuts(Event const &, Event const &) override;
     //! Finalise analysis
     /**
      * This function does nothing
      */
     virtual void finalise() override;
 
     virtual ~EmptyAnalysis() override = default;
   };
 }
diff --git a/include/HEJ/Event.hh b/include/HEJ/Event.hh
index 5140f8f..3b08c7e 100644
--- a/include/HEJ/Event.hh
+++ b/include/HEJ/Event.hh
@@ -1,282 +1,281 @@
 /** \file
  *  \brief Declares the Event class and helpers
  *
- *  \authors   Jeppe Andersen, Tuomas Hapola, Marian Heil, Andreas Maier, Jennifer Smillie
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
  *  \date      2019
  *  \copyright GPLv2 or later
  */
-
 #pragma once
 
 #include <array>
 #include <memory>
 #include <string>
 #include <unordered_map>
 #include <vector>
 
 #include "HEJ/event_types.hh"
 #include "HEJ/Parameters.hh"
 #include "HEJ/Particle.hh"
 #include "HEJ/RNG.hh"
 
 #include "fastjet/ClusterSequence.hh"
 
 namespace LHEF{
   class HEPEUP;
   class HEPRUP;
 }
 
 namespace fastjet{
   class JetDefinition;
 }
 
 namespace HEJ{
 
   struct UnclusteredEvent;
 
   /** @brief An event with clustered jets
     *
     * This is the main HEJ 2 event class.
     * It contains kinematic information including jet clustering,
     * parameter (e.g. scale) settings and the event weight.
     */
   class Event{
   public:
     class EventData;
     //! No default Constructor
     Event() = delete;
     //! Event Constructor adding jet clustering to an unclustered event
     //! @deprecated UnclusteredEvent will be replaced by EventData in HEJ 2.2.0
     [[deprecated("UnclusteredEvent will be replaced by EventData")]]
     Event(
       UnclusteredEvent const & ev,
       fastjet::JetDefinition const & jet_def, double min_jet_pt
     );
 
     //! Incoming particles
     std::array<Particle, 2> const &  incoming() const{
       return incoming_;
     }
     //! Outgoing particles
     std::vector<Particle> const &  outgoing() const{
       return outgoing_;
     }
     //! Particle decays
     /**
      *  The key in the returned map corresponds to the index in the
      *  vector returned by outgoing()
      */
     std::unordered_map<size_t, std::vector<Particle>> const &  decays() const{
       return decays_;
     }
     //! The jets formed by the outgoing partons, sorted in rapidity
     std::vector<fastjet::PseudoJet> const & jets() const{
       return jets_;
     }
 
     //! All chosen parameter, i.e. scale choices (const version)
     Parameters<EventParameters> const & parameters() const{
       return parameters_;
     }
     //! All chosen parameter, i.e. scale choices
     Parameters<EventParameters> & parameters(){
       return parameters_;
     }
 
     //! Central parameter choice (const version)
     EventParameters const & central() const{
       return parameters_.central;
     }
     //! Central parameter choice
     EventParameters & central(){
       return parameters_.central;
     }
 
     //! Parameter (scale) variations (const version)
     std::vector<EventParameters> const & variations() const{
       return parameters_.variations;
     }
     //! Parameter (scale) variations
     std::vector<EventParameters> & variations(){
       return parameters_.variations;
     }
 
     //! Parameter (scale) variation (const version)
     /**
      *  @param i   Index of the requested variation
      */
     EventParameters const & variations(size_t i) const{
       return parameters_.variations[i];
     }
     //! Parameter (scale) variation
     /**
      *  @param i   Index of the requested variation
      */
     EventParameters & variations(size_t i){
       return parameters_.variations[i];
     }
 
     //! Indices of the jets the outgoing partons belong to
     /**
      *  @param jets   Jets to be tested
      *  @returns      A vector containing, for each outgoing parton,
      *                the index in the vector of jets the considered parton
      *                belongs to. If the parton is not inside any of the
      *                passed jets, the corresponding index is set to -1.
      */
     std::vector<int> particle_jet_indices(
         std::vector<fastjet::PseudoJet> const & jets
     ) const{
       return cs_.particle_jet_indices(jets);
     }
 
     //! Jet definition used for clustering
     fastjet::JetDefinition const & jet_def() const{
       return cs_.jet_def();
     }
 
     //! Minimum jet transverse momentum
     double min_jet_pt() const{
       return min_jet_pt_;
     }
 
     //! Event type
     event_type::EventType type() const{
       return type_;
     }
 
     //! Give colours to each particle
     /**
      * @returns true if new colours are generated, i.e. same as is_HEJ()
      * @details Colour ordering is done according to leading colour in the MRK
      *          limit, see \cite Andersen:2011zd. This only affects \ref
      *          is_HEJ() "HEJ" configurations, all other \ref event_type
      *          "EventTypes" will be ignored.
      * @note    This overwrites all previously set colours.
      */
     bool generate_colours(HEJ::RNG &);
 
   private:
     //! \internal
     //! @brief Construct Event explicitly from input.
     /** This is only intended to be called from EventData.
      *
      * \warning The input is taken _as is_, sorting and classification has to be
      *          done externally, i.e. by EventData
      */
     Event(
       std::array<Particle, 2> && incoming,
       std::vector<Particle> && outgoing,
       std::unordered_map<size_t, std::vector<Particle>> && decays,
       Parameters<EventParameters> && parameters,
       fastjet::JetDefinition const & jet_def,
       double const min_jet_pt
     );
 
     std::array<Particle, 2> incoming_;
     std::vector<Particle> outgoing_;
     std::unordered_map<size_t, std::vector<Particle>> decays_;
     std::vector<fastjet::PseudoJet> jets_;
     Parameters<EventParameters> parameters_;
     fastjet::ClusterSequence cs_;
     double min_jet_pt_;
     event_type::EventType type_;
   }; // end class Event
 
   //! Class to store general Event setup, i.e. Phase space and weights
   class Event::EventData{
   public:
     //! Default Constructor
     EventData() = default;
     //! Constructor from LesHouches event information
     EventData(LHEF::HEPEUP const & hepeup);
     //! Constructor with all values given
     EventData(
       std::array<Particle, 2> const & incoming_,
       std::vector<Particle> const & outgoing_,
       std::unordered_map<size_t, std::vector<Particle>> const & decays_,
       Parameters<EventParameters> const & parameters_
     ):
       incoming(incoming_), outgoing(outgoing_),
       decays(decays_), parameters(parameters_)
     {};
     //! Move Constructor with all values given
     EventData(
       std::array<Particle, 2> && incoming_,
       std::vector<Particle> && outgoing_,
       std::unordered_map<size_t, std::vector<Particle>> && decays_,
       Parameters<EventParameters> && parameters_
     ):
       incoming(std::move(incoming_)), outgoing(std::move(outgoing_)),
       decays(std::move(decays_)), parameters(std::move(parameters_))
     {};
 
     //! Generate an Event from the stored EventData.
     /**
      * @details          Do jet clustering and classification.
      *                   Use this to generate an Event.
      *
      * @note             Calling this function destroys EventData
      *
      * @param jet_def    Jet definition
      * @param min_jet_pt minimal \f$p_T\f$ for each jet
      *
      * @returns          Full clustered and classified event.
      */
     Event cluster(
       fastjet::JetDefinition const & jet_def, double const min_jet_pt);
 
     //! Alias for cluster()
     Event operator()(
       fastjet::JetDefinition const & jet_def, double const min_jet_pt){
       return cluster(jet_def, min_jet_pt);
     };
 
     //! Sort particles in rapidity
     void sort();
 
     //! Reconstruct intermediate particles from final-state leptons
     /**
      *  Final-state leptons are created from virtual photons, W, or Z bosons.
      *  This function tries to reconstruct such intermediate bosons if they
      *  are not part of the event record.
      */
     void reconstruct_intermediate();
 
     std::array<Particle, 2> incoming;
     std::vector<Particle> outgoing;
     std::unordered_map<size_t, std::vector<Particle>> decays;
     Parameters<EventParameters> parameters;
   }; // end class EventData
 
   //! Print Event
   std::ostream& operator<<(std::ostream & os, Event const & ev);
 
   //! Square of the partonic centre-of-mass energy \f$\hat{s}\f$
   double shat(Event const & ev);
 
   //! Convert an event to a LHEF::HEPEUP
   LHEF::HEPEUP to_HEPEUP(Event const & event, LHEF::HEPRUP *);
 
   // put deprecated warning at the end, so don't get the warning inside Event.hh,
   // additionally doxygen can not identify [[deprecated]] correctly
   struct [[deprecated("UnclusteredEvent will be replaced by EventData")]]
     UnclusteredEvent;
   //! An event before jet clustering
   //! @deprecated UnclusteredEvent will be replaced by EventData in HEJ 2.2.0
   struct UnclusteredEvent{
     //! Default Constructor
     UnclusteredEvent() = default;
     //! Constructor from LesHouches event information
     UnclusteredEvent(LHEF::HEPEUP const & hepeup);
 
     std::array<Particle, 2> incoming;          /**< Incoming Particles */
     std::vector<Particle> outgoing;            /**< Outgoing Particles */
     //! Particle decays in the format {outgoing index, decay products}
     std::unordered_map<size_t, std::vector<Particle>> decays;
     //! Central parameter (e.g. scale) choice
     EventParameters central;
     std::vector<EventParameters> variations;    /**< For parameter variation */
   };
 
 }
diff --git a/include/HEJ/EventReader.hh b/include/HEJ/EventReader.hh
index ff920b6..f001f6a 100644
--- a/include/HEJ/EventReader.hh
+++ b/include/HEJ/EventReader.hh
@@ -1,44 +1,44 @@
 /** \file
  *  \brief Header file for event reader interface
  *
  *  This header defines an abstract base class for reading events from files.
  *
- *  \authors   Jeppe Andersen, Marian Heil, Andreas Maier, Jennifer Smillie
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
  *  \date      2019
  *  \copyright GPLv2 or later
  */
 #pragma once
 
 #include <memory>
 #include <string>
 
 #include "LHEF/LHEF.h"
 
 namespace HEJ{
   class EventData;
 
   // Abstract base class for reading events from files
   struct EventReader {
     //! Read an event
     virtual bool read_event() = 0;
 
     //! Access header text
     virtual std::string const & header() const = 0;
 
     //! Access run information
     virtual LHEF::HEPRUP const & heprup() const = 0;
 
     //! Access last read event
     virtual LHEF::HEPEUP const & hepeup() const = 0;
 
     virtual ~EventReader() = default;
   };
 
   //! Factory function for event readers
   /**
    *  @param infile   The name of the input file
    *  @returns        A pointer to an instance of an EventReader
    *                  for the input file
    */
   std::unique_ptr<EventReader> make_reader(std::string const & filename);
 }
diff --git a/include/HEJ/EventReweighter.hh b/include/HEJ/EventReweighter.hh
index e139ce1..feb50bb 100644
--- a/include/HEJ/EventReweighter.hh
+++ b/include/HEJ/EventReweighter.hh
@@ -1,190 +1,201 @@
 /** \file
  *  \brief Declares the EventReweighter class
  *
  *  EventReweighter is the main class used within HEJ 2. It reweights the
  *  resummation events.
  *
- *  \authors   Jeppe Andersen, Tuomas Hapola, Marian Heil, Andreas Maier, Jennifer Smillie
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
  *  \date      2019
  *  \copyright GPLv2 or later
  */
 #pragma once
 
 #include <array>
 #include <functional>
 #include <utility>
 #include <vector>
 
 #include "HEJ/config.hh"
 #include "HEJ/event_types.hh"
 #include "HEJ/MatrixElement.hh"
+#include "HEJ/Parameters.hh"
 #include "HEJ/PDF.hh"
 #include "HEJ/PDG_codes.hh"
 #include "HEJ/RNG.hh"
 #include "HEJ/ScaleFunction.hh"
-#include "HEJ/Parameters.hh"
+#include "HEJ/StatusCode.hh"
 
 namespace LHEF {
   class HEPRUP;
 }
 
 namespace HEJ{
   class Event;
 
   //! Beam parameters
   /**
    *  Currently, only symmetric beams are supported,
    *  so there is a single beam energy.
    */
   struct Beam{
     double E;                                /**< Beam energy */
     std::array<ParticleID, 2> type;          /**< Beam particles */
   };
 
   //! Main class for reweighting events in HEJ.
   class EventReweighter{
     using EventType = event_type::EventType;
   public:
 
     EventReweighter(
         Beam beam,                            /**< Beam Energy */
         int pdf_id,                           /**< PDF ID */
         ScaleGenerator scale_gen,             /**< Scale settings */
         EventReweighterConfig conf,           /**< Configuration parameters */
         HEJ::RNG & ran                       /**< Random number generator */
     );
 
     EventReweighter(
         LHEF::HEPRUP const & heprup,          /**< LHEF event header */
         ScaleGenerator scale_gen,             /**< Scale settings */
         EventReweighterConfig conf,           /**< Configuration parameters */
         HEJ::RNG & ran                       /**< Random number generator */
     );
 
     //! Get the used pdf
     PDF const & pdf() const;
 
 
     //! Generate resummation events for a given fixed-order event
     /**
      *  @param ev             Fixed-order event corresponding
      *                        to the resummation events
      *  @param num_events     Number of trial resummation configurations.
      *  @returns              A vector of resummation events.
      *
      *  The result vector depends on the type of the input event and the
      *  treatment of different types as specified in the constructor:
      *
      *  \ref reweight  The result vector contains between
      *                 0 and num_events resummation events.
      *
      *  \ref keep  If the input event passes the resummation jet cuts
      *             the result vector contains one event. Otherwise it is empty.
      *
      *  \ref discard   The result vector is empty
      */
     std::vector<Event> reweight(
         Event const & ev,
         int num_events
     );
 
+    //! Gives all StatusCodes of the last reweight()
+    /**
+     * Each StatusCode corresponds to one tried generation. Only good
+     * StatusCodes generated an event.
+     */
+    std::vector<StatusCode> const & status() const {
+        return status_;
+    }
+
   private:
 
     template<typename... T>
     PDF const & pdf(T&& ...);
 
     /** \internal
      * \brief main generation/reweighting function:
      * generate phase space points and divide out Born factors
      */
     std::vector<Event> gen_res_events(
         Event const & ev, int num_events
     );
     std::vector<Event> rescale(
         Event const & Born_ev, std::vector<Event> events
     ) const;
 
     /** \internal
      * \brief Do the Jets pass the resummation Cuts?
      *
      * @param ev               Event in Question
      * @returns                0 or 1 depending on if ev passes Jet Cuts
      */
     bool jets_pass_resummation_cuts(Event const & ev) const;
 
     /** \internal
     * \brief pdf_factors Function
     *
     * @param ev         Event in Question
     * @returns          EventFactor due to PDFs
     *
     * Calculates the Central value and the variation due
     * to the PDF choice made.
     */
     Weights pdf_factors(Event const & ev) const;
 
     /** \internal
      * \brief matrix_elements Function
      *
      * @param ev         Event in question
      * @returns          EventFactor due to MatrixElements
      *
      * Calculates the Central value and the variation due
      * to the Matrix Element.
      */
     Weights matrix_elements(Event const & ev) const;
 
     /** \internal
      * \brief Scale-dependent part of fixed-order matrix element
      *
      * @param ev         Event in question
      * @returns          EventFactor scale variation due to FO-ME.
      *
      * This is only called to compute the scale variation for events where
      * we don't do resummation (e.g. non-FKL).
      * Since at tree level the scale dependence is just due to alpha_s,
      * it is enough to return the alpha_s(mur) factors in the matrix element.
      * The rest drops out in the ratio of (output event ME)/(input event ME),
      * so we never have to compute it.
      */
     Weights fixed_order_scale_ME(Event const & ev) const;
 
     /** \internal
      * \brief Computes the tree level matrix element
      *
      * @param ev                Event in Question
      * @returns                 HEJ approximation to Tree level Matrix Element
      *
      * This computes the HEJ approximation to the tree level FO
      * Matrix element which is used within the LO weighting process.
      */
     double tree_matrix_element(Event const & ev) const;
 
 
     //! \internal General parameters
     EventReweighterConfig param_;
 
     //! \internal Beam energy
     double E_beam_;
 
     //! \internal PDF
     PDF pdf_;
 
     //! \internal Object to calculate the square of the matrix element
     MatrixElement MEt2_;
     //! \internal Object to calculate event renormalisation and factorisation scales
     ScaleGenerator scale_gen_;
     /** \internal random number generator
      *
      *  \note We use a reference_wrapper so that EventReweighter objects can
      *        still be copied (which would be impossible with a reference).
      */
     std::reference_wrapper<HEJ::RNG> ran_;
+    std::vector<StatusCode> status_;
   };
 
   template<typename... T>
   PDF const & EventReweighter::pdf(T&&... t){
     return pdf_ = PDF{std::forward<T>(t)...};
   }
 
 }
diff --git a/include/HEJ/EventWriter.hh b/include/HEJ/EventWriter.hh
index 997a510..b335dd4 100644
--- a/include/HEJ/EventWriter.hh
+++ b/include/HEJ/EventWriter.hh
@@ -1,21 +1,21 @@
 /** \file
  *  \brief Header file for the EventWriter interface.
  *
- *  \authors   Jeppe Andersen, Tuomas Hapola, Marian Heil, Andreas Maier, Jennifer Smillie
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
  *  \date      2019
  *  \copyright GPLv2 or later
  */
 #pragma once
 
 namespace HEJ{
   class Event;
 
   //! Pure abstract base class for event writers
   struct EventWriter{
     //! Write an event
     virtual void write(Event const &) = 0;
 
     virtual ~EventWriter() = default;
   };
 
 }
diff --git a/include/HEJ/HDF5Reader.hh b/include/HEJ/HDF5Reader.hh
index c3eba20..13bc75e 100644
--- a/include/HEJ/HDF5Reader.hh
+++ b/include/HEJ/HDF5Reader.hh
@@ -1,43 +1,44 @@
 /** \file
  *  \brief Header file for reading events in the HDF5 event format.
  *
- *  This format is specified in arXiv:1905.05120.
- *
- *  \authors   Jeppe Andersen, Marian Heil, Andreas Maier, Jennifer Smillie
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
  *  \date      2019
  *  \copyright GPLv2 or later
  */
 #pragma once
 
 #include "HEJ/EventReader.hh"
 
 namespace HEJ{
 
-  //! Class for writing events to a file in the Les Houches Event File format
+  //! Class for reading events from a file in the HDF5 file format
+  /**
+   * @details This format is specified in \cite Hoeche:2019rti.
+   */
   class HDF5Reader : public EventReader{
   public:
     //! Contruct object reading from the given file
     explicit HDF5Reader(std::string const & filename);
 
     //! Read an event
     bool read_event() override;
 
     //! Access header text
     std::string const & header() const override;
 
     //! Access run information
     LHEF::HEPRUP const & heprup() const override;
 
     //! Access last read event
     LHEF::HEPEUP const & hepeup() const override;
 
   private:
     struct HDF5ReaderImpl;
     struct HDF5ReaderImplDeleter {
       void operator()(HDF5ReaderImpl* p);
     };
 
     std::unique_ptr<HDF5ReaderImpl, HDF5ReaderImplDeleter> impl_;
   };
 
 }
diff --git a/include/HEJ/HepMCInterface.hh b/include/HEJ/HepMCInterface.hh
index b920dbf..94c788e 100644
--- a/include/HEJ/HepMCInterface.hh
+++ b/include/HEJ/HepMCInterface.hh
@@ -1,73 +1,72 @@
 /** \file
  *  \brief Header file for the HepMCInterface
  *
- *  \authors   Jeppe Andersen, Tuomas Hapola, Marian Heil, Andreas Maier, Jennifer Smillie
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
  *  \date      2019
  *  \copyright GPLv2 or later
  */
-
 #pragma once
 
 #include <sys/types.h>
 #include <vector>
 
 namespace HepMC{
   class GenCrossSection;
   class GenEvent;
 }
 
 namespace HEJ{
   class Event;
   class EventParameters;
   //! This class converts the Events into HepMC::GenEvents
   /**
   *   \details The output is depended on the HepMC version HEJ is compiled with,
   *   both HepMC 2 and HepMC 3 are supported. If HEJ 2 is compiled
   *   without HepMC calling this interface will throw an error.
   *
   *   This interface will also keep track of the cross section of all the events that
   *   being fed into it.
   */
 
   class HepMCInterface{
   public:
     HepMCInterface();
     /**
      * \brief main function to convert an event into HepMC::GenEvent
      *
      * \param event          Event to convert
      * \param weight_index   optional selection of specific weight
      *                       (negative value gives central weight)
      */
     HepMC::GenEvent operator()(Event const & event, ssize_t weight_index = -1);
     /**
      * \brief initialise the event kinematics (everything but the weights)
      *
      * \param event          Event to convert
      * \param weight_index   optional selection of specific weight
      *                       (negative value gives central weight)
      */
     HepMC::GenEvent init_kinematics(Event const & event);
     /**
      * \brief Sets the central value from \p event to \p out_ev
      *
      * \param out_ev         HepMC::GenEvent to write to
      * \param event          Event to convert
      * \param weight_index   optional selection of specific weight
      *                       (negative value gives "central")
      */
     void set_central(HepMC::GenEvent & out_ev, Event const & event,
       ssize_t weight_index = -1);
     /**
      * \brief Add the event \p variations to \p out_ev
      */
     void add_variation(HepMC::GenEvent & out_ev,
       std::vector<EventParameters> const & variations);
   private:
     size_t event_count_;
     double tot_weight_;
     double tot_weight2_;
     HepMC::GenCrossSection cross_section() const;
 
   };
 }
diff --git a/include/HEJ/HepMCWriter.hh b/include/HEJ/HepMCWriter.hh
index f696713..0c5ce91 100644
--- a/include/HEJ/HepMCWriter.hh
+++ b/include/HEJ/HepMCWriter.hh
@@ -1,55 +1,54 @@
 /** \file
  *  \brief Contains the EventWriter for HepMC Output.
  *
- *  \authors   Jeppe Andersen, Tuomas Hapola, Marian Heil, Andreas Maier, Jennifer Smillie
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
  *  \date      2019
  *  \copyright GPLv2 or later
  */
-
 #pragma once
 
 #include <memory>
 #include <string>
 
 #include "HEJ/EventWriter.hh"
 
 namespace LHEF {
   class HEPRUP;
 }
 
 namespace HEJ{
   class Event;
 
   //! This is an event writer specifically for HepMC output.
   /**
    * \internal Implementation note:
    * This uses the pimpl ("pointer to implementation") idiom.
    * HepMC support is optional and the implementation depends on the
    * HepMC version. Without pimpl, we would have to specify the HepMC version
    * via the preprocessor whenever this header is included. We don't want to
    * burden users of the HEJ library (for example the HEJ fixed-order generator)
    * with those details
    */
   class HepMCWriter: public EventWriter{
   public:
     //! Constructor
     /**
      * @param file      name of the output file
      * @param heprup    general process information
      */
     HepMCWriter(std::string const & file, LHEF::HEPRUP heprup);
     ~HepMCWriter() override = default;
 
     //! Write an event to the output file
     void write(Event const & ev) override;
 
   private:
     struct HepMCWriterImpl;
     struct HepMCWriterImplDeleter {
       void operator()(HepMCWriterImpl* p);
     };
 
     std::unique_ptr<HepMCWriterImpl, HepMCWriterImplDeleter> impl_;
   };
 
 }
diff --git a/include/HEJ/HiggsCouplingSettings.hh b/include/HEJ/HiggsCouplingSettings.hh
index 9518ee4..1bcabb9 100644
--- a/include/HEJ/HiggsCouplingSettings.hh
+++ b/include/HEJ/HiggsCouplingSettings.hh
@@ -1,24 +1,24 @@
 /** \file
  *  \brief Defines the settings for Higgs boson coupling to gluons
  *
- *  \authors   Jeppe Andersen, Tuomas Hapola, Marian Heil, Andreas Maier, Jennifer Smillie
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
  *  \date      2019
  *  \copyright GPLv2 or later
  */
 #pragma once
 
 #include <limits>
 
 namespace HEJ{
   //! Settings for Higgs boson coupling to gluons
   struct HiggsCouplingSettings{
     //! Top quark mass
     double mt = std::numeric_limits<double>::infinity();
     //! Bottom quark mass
     double mb = 4.7;
     //! Whether to use impact factors
     bool use_impact_factors = true;
     //! Whether to include bottom quark effects
     bool include_bottom = false;
   };
 }
diff --git a/include/HEJ/JetSplitter.hh b/include/HEJ/JetSplitter.hh
index 71e0183..e3246ff 100644
--- a/include/HEJ/JetSplitter.hh
+++ b/include/HEJ/JetSplitter.hh
@@ -1,78 +1,77 @@
 /**
  * \file
  * \brief Declaration of the JetSplitter class
  *
- *  \authors   Jeppe Andersen, Tuomas Hapola, Marian Heil, Andreas Maier, Jennifer Smillie
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
  *  \date      2019
  *  \copyright GPLv2 or later
  */
-
 #pragma once
 
 #include <functional>
 #include <vector>
 
 #include "fastjet/JetDefinition.hh"
 
 #include "HEJ/RNG.hh"
 
 namespace fastjet {
   class PseudoJet;
 }
 
 namespace HEJ {
   //! Class to split jets into their constituents
   class JetSplitter {
 
   public:
     struct SplitResult {
       std::vector<fastjet::PseudoJet> constituents;
       double weight;
     };
 
     //! Constructor
     /**
      *  @param jet_def   Jet definition
      *  @param min_pt    Minimum jet transverse momentum
      *  @param ran       Random number generator
      */
     JetSplitter(
         fastjet::JetDefinition jet_def, double min_pt,
         HEJ::RNG & ran
     ):
       R_{jet_def.R()},
       min_jet_pt_{min_pt},
       jet_def_{jet_def},
       ran_{ran}
     {}
 
     //! Split a get into constituents
     /**
      *  @param j2split      Jet to be split
      *  @param ncons        Number of constituents
      *  @returns            The constituent momenta
      *                      together with the associated weight
      */
     SplitResult split(fastjet::PseudoJet const & j2split, int ncons) const;
 
     //! Maximum distance of constituents to jet axis
     static constexpr double R_factor = 5./3.;
   private:
     //! \internal split jet into two partons
     SplitResult Split2(fastjet::PseudoJet const & j2split) const;
 
     /** \internal
      * @brief sample y-phi distance to jet pt axis for a jet splitting into two
      *        partons
      *
      * @param wt    Multiplied by the weight of the sampling point
      * @returns     The distance in units of the jet radius
      */
     double sample_distance_2p(double & wt) const;
 
     double R_;
     double min_jet_pt_;
     fastjet::JetDefinition jet_def_;
     std::reference_wrapper<HEJ::RNG> ran_;
   };
 }
diff --git a/include/HEJ/LesHouchesReader.hh b/include/HEJ/LesHouchesReader.hh
index 56656f6..982172a 100644
--- a/include/HEJ/LesHouchesReader.hh
+++ b/include/HEJ/LesHouchesReader.hh
@@ -1,53 +1,52 @@
 /** \file
  *  \brief Header file for reading events in the Les Houches Event File format.
  *
- *  \authors   Jeppe Andersen, Marian Heil, Andreas Maier, Jennifer Smillie
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
  *  \date      2019
  *  \copyright GPLv2 or later
  */
 #pragma once
 
 #include "LHEF/LHEF.h"
 
 #include "HEJ/Event.hh"
 #include "HEJ/EventReader.hh"
 #include "HEJ/stream.hh"
 
-
 namespace HEJ{
 
-  //! Class for writing events to a file in the Les Houches Event File format
+  //! Class for reading events from a file in the Les Houches Event File format
   class LesHouchesReader : public EventReader{
   public:
     //! Contruct object reading from the given file
     explicit LesHouchesReader(std::string const & filename):
       stream_{filename},
       reader_{stream_}
     {}
 
     //! Read an event
     bool read_event() override {
       return reader_.readEvent();
     }
 
     //! Access header text
     std::string const & header() const override {
       return reader_.headerBlock;
     }
 
     //! Access run information
     LHEF::HEPRUP const & heprup() const override {
       return reader_.heprup;
     }
 
     //! Access last read event
     LHEF::HEPEUP const & hepeup() const override {
       return reader_.hepeup;
     }
 
   private:
     HEJ::istream stream_;
     LHEF::Reader reader_;
   };
 
 }
diff --git a/include/HEJ/LesHouchesWriter.hh b/include/HEJ/LesHouchesWriter.hh
index 7d23e79..777d34f 100644
--- a/include/HEJ/LesHouchesWriter.hh
+++ b/include/HEJ/LesHouchesWriter.hh
@@ -1,61 +1,60 @@
 /** \file
  *  \brief Contains the writer for LesHouches output
  *
- *  \authors   Jeppe Andersen, Tuomas Hapola, Marian Heil, Andreas Maier, Jennifer Smillie
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
  *  \date      2019
  *  \copyright GPLv2 or later
  */
-
 #pragma once
 
 #include <fstream>
 #include <memory>
 #include <string>
 
 #include "LHEF/LHEF.h"
 
 #include "HEJ/EventWriter.hh"
 
 namespace HEJ{
   class Event;
 
   //! Class for writing events to a file in the Les Houches Event File format
   class LesHouchesWriter : public EventWriter{
   public:
     //! Constructor
     /**
      * @param file    Name of output file
      * @param heprup  General process information
      */
     LesHouchesWriter(std::string const & file, LHEF::HEPRUP heprup);
     LesHouchesWriter(LesHouchesWriter const & other) = delete;
     LesHouchesWriter & operator=(LesHouchesWriter const & other) = delete;
     /** @TODO in principle, this class should be movable
      *       but that somehow(?) breaks the write member function
      */
     LesHouchesWriter(LesHouchesWriter && other) = delete;
     LesHouchesWriter & operator=(LesHouchesWriter && other) = delete;
     ~LesHouchesWriter() override;
 
     //! Write an event to the file specified in the constructor
     void write(Event const & ev) override;
 
   private:
     void write_init(){
       writer_->init();
     }
 
     void rewrite_init();
 
     LHEF::HEPRUP & heprup(){
       return writer_->heprup;
     }
 
     LHEF::HEPEUP & hepeup(){
       return writer_->hepeup;
     }
 
     std::fstream out_;
     std::unique_ptr<LHEF::Writer> writer_;
   };
 }
diff --git a/include/HEJ/MatrixElement.hh b/include/HEJ/MatrixElement.hh
index cfcd15f..de56a71 100644
--- a/include/HEJ/MatrixElement.hh
+++ b/include/HEJ/MatrixElement.hh
@@ -1,191 +1,191 @@
 /** \file
  *  \brief Contains the MatrixElement Class
  *
- *  \authors   Jeppe Andersen, Tuomas Hapola, Marian Heil, Andreas Maier, Jennifer Smillie
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
  *  \date      2019
  *  \copyright GPLv2 or later
  */
 #pragma once
 
 #include <functional>
 #include <vector>
 
 #include "fastjet/PseudoJet.hh"
 
 #include "HEJ/PDG_codes.hh"
 #include "HEJ/Parameters.hh"
 #include "HEJ/config.hh"
 
 namespace CLHEP {
   class HepLorentzVector;
 }
 
 namespace HEJ{
   class Event;
   class Particle;
 
   //! Class to calculate the squares of matrix elements
   class MatrixElement{
   public:
     /** \brief MatrixElement Constructor
      * @param alpha_s        Function taking the renormalisation scale
      *                       and returning the strong coupling constant
      * @param conf           General matrix element settings
      */
     MatrixElement(
         std::function<double (double)> alpha_s,
         MatrixElementConfig conf
     );
 
   /**
    * \brief squares of regulated HEJ matrix elements
    * @param event          The event for which to calculate matrix elements
    * @returns              The squares of HEJ matrix elements including virtual corrections
    *
    * This function returns one value for the central parameter choice
    * and one additional value for each entry in \ref Event.variations().
    * See eq. (22) in \cite Andersen:2011hs for the definition of the squared
    * matrix element.
    *
    * \internal Relation to standard HEJ Met2: MatrixElement = Met2*shat^2/(pdfta*pdftb)
    */
     Weights operator()(Event const & event) const;
 
   //! Squares of HEJ tree-level matrix elements
   /**
    * @param event          The event for which to calculate matrix elements
    * @returns              The squares of HEJ matrix elements without virtual corrections
    *
    * cf. eq. (22) in \cite Andersen:2011hs
    */
     Weights tree(Event const & event) const;
 
    /**
     * \brief Virtual corrections to matrix element squares
     * @param event         The event for which to calculate matrix elements
     * @returns             The virtual corrections to the squares of the matrix elements
     *
     * The all order virtual corrections to LL in the MRK limit is
     * given by replacing 1/t in the scattering amplitude according to the
     * lipatov ansatz.
     *
     * cf. second-to-last line of eq. (22) in \cite Andersen:2011hs
     * note that indices are off by one, i.e. out[0].p corresponds to p_1
     */
     Weights virtual_corrections(Event const & event) const;
 
    /**
     * \brief Scale-dependent part of tree-level matrix element squares
     * @param event         The event for which to calculate matrix elements
     * @returns             The scale-dependent part of the squares of the
     *                      tree-level matrix elements
     *
     * The tree-level matrix elements factorises into a renormalisation-scale
     * dependent part, given by the strong coupling to some power, and a
     * scale-independent remainder. This function only returns the former parts
     * for the central scale choice and all \ref Event.variations().
     *
     * @see tree, tree_kin
     */
     Weights tree_param(
         Event const & event
     ) const;
 
    /**
     * \brief Kinematic part of tree-level matrix element squares
     * @param event         The event for which to calculate matrix elements
     * @returns             The kinematic part of the squares of the
     *                      tree-level matrix elements
     *
     * The tree-level matrix elements factorises into a renormalisation-scale
     * dependent part, given by the strong coupling to some power, and a
     * scale-independent remainder. This function only returns the latter part.
     * Since it does not depend on the parameter variations, only a single value
     * is returned.
     *
     * @see tree, tree_param
     */
     double tree_kin(Event const & event) const;
 
   private:
 
     double tree_param(
         Event const & event,
         double mur
     ) const;
 
     double virtual_corrections_W(
         Event const & event,
         double mur,
         Particle const & WBoson
     ) const;
     double virtual_corrections(
         Event const & event,
         double mur
     ) const;
 
     //! \internal cf. last line of eq. (22) in \cite Andersen:2011hs
     double omega0(
         double alpha_s, double mur,
         fastjet::PseudoJet const & q_j
     ) const;
 
     double tree_kin_jets(
         Event const & ev
     ) const;
     double tree_kin_W(
         Event const & ev
     ) const;
     double tree_kin_Higgs(
         Event const & ev
     ) const;
     double tree_kin_Higgs_first(
         Event const & ev
     ) const;
     double tree_kin_Higgs_last(
         Event const & ev
     ) const;
 
     /**
      * \internal
      * \brief Higgs inbetween extremal partons.
      *
      * Note that in the case of unordered emission, the Higgs is *always*
      * treated as if in between the extremal (FKL) partons, even if its
      * rapidity is outside the extremal parton rapidities
      */
     double tree_kin_Higgs_between(
         Event const & ev
     ) const;
 
 
     double tree_param_partons(
         double alpha_s, double mur,
         std::vector<Particle> const & partons
     ) const;
 
 
     std::vector<int> in_extremal_jet_indices(
         std::vector<fastjet::PseudoJet> const & partons
     ) const;
 
 
     std::vector<Particle> tag_extremal_jet_partons(
         Event const & ev
     ) const;
 
     double MH2_forwardH(
         CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector p1in,
         pid::ParticleID type2,
         CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in,
         CLHEP::HepLorentzVector pH,
         double t1, double t2
     ) const;
 
     std::function<double (double)> alpha_s_;
 
     MatrixElementConfig param_;
   };
 
 
 }
diff --git a/include/HEJ/Mixmax.hh b/include/HEJ/Mixmax.hh
index 5f9677e..6fdb84c 100644
--- a/include/HEJ/Mixmax.hh
+++ b/include/HEJ/Mixmax.hh
@@ -1,35 +1,35 @@
 /** \file
  * \brief The Mixmax random number generator
  *
- *  \authors   Jeppe Andersen, Tuomas Hapola, Marian Heil, Andreas Maier, Jennifer Smillie
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
  *  \date      2019
  *  \copyright GPLv2 or later
  */
 #pragma once
 
 #include <CLHEP/Random/Randomize.h>
 #include <CLHEP/Random/MixMaxRng.h>
 
 #include "HEJ/RNG.hh"
 
 namespace HEJ {
 
   //! MIXMAX random number generator
   /**
    *  For details on MIXMAX, see \cite Savvidy:2014ana
    */
   class Mixmax : public DefaultRNG {
   public:
     Mixmax() = default;
     Mixmax(long seed): ran_{seed} {};
 
     //! Generate pseudorandom number between 0 and 1
     double flat() override {
       return ran_.flat();
     };
 
   private:
     CLHEP::MixMaxRng ran_;
   };
 
 }
diff --git a/include/HEJ/PDF.hh b/include/HEJ/PDF.hh
index c5dddbf..63ad94e 100644
--- a/include/HEJ/PDF.hh
+++ b/include/HEJ/PDF.hh
@@ -1,77 +1,76 @@
 /** \file
  *
  * \brief Contains all the necessary classes and functions for interaction with PDFs.
  *
- *  \authors   Jeppe Andersen, Tuomas Hapola, Marian Heil, Andreas Maier, Jennifer Smillie
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
  *  \date      2019
  *  \copyright GPLv2 or later
  */
-
 #pragma once
 
 #include <array>
 #include <memory>
 
 #include "LHAPDF/LHAPDF.h"
 
 #include "HEJ/PDG_codes.hh"
 
 namespace HEJ{
   //! Class for interaction with a PDF set
   class PDF {
   public:
     /**
      * \brief PDF Constructor
      * @param id         Particle ID according to PDG
      * @param beam1      Particle ID of particle in beam 1
      * @param beam2      Particle ID of particle in beam 2
      */
     PDF(int id, ParticleID beam1, ParticleID beam2);
 
     /**
      * \brief Calculate the pdf value x*f(x, q)
      * @param beam_idx  Beam number (0 or 1)
      * @param x         Momentum fraction
      * @param q         Energy scale
      * @param id        PDG particle id
      * @returns         x*f(x, q)
      *
      * Returns 0 if x or q are outside the range covered by the PDF set
      */
     double pdfpt(size_t beam_idx, double x, double q, ParticleID id) const;
 
     /**
      * \brief Value of the strong coupling \f$\alpha_s(q)\f$ at the given scale
      * @param q         Renormalisation scale
      * @returns         Value of the strong coupling constant
      */
     double Halphas(double q) const;
 
     //! Check if the energy scale is within the range covered by the PDF set
     /**
      * @param q        Energy Scale
      * @returns        true if q is within the covered range, false otherwise
      */
     bool inRangeQ(double q) const;
 
     //! Check if the momentum fraction is within the range covered by the PDF set
     /**
      * @param x        Momentum Fraction
      * @returns        true if x is within the covered range, false otherwise
      */
     bool inRangeX(double x) const;
 
 #if defined LHAPDF_MAJOR_VERSION && LHAPDF_MAJOR_VERSION == 6
     //! PDF id of the current set
     int id() const;
 #endif
 
   private:
 
 #if defined LHAPDF_MAJOR_VERSION && LHAPDF_MAJOR_VERSION == 6
     std::unique_ptr<LHAPDF::PDF> pdf;
 #endif
 
     std::array<int, 2> beamtype;
   };
 }
diff --git a/include/HEJ/PDG_codes.hh b/include/HEJ/PDG_codes.hh
index ffdad16..97a6c4c 100644
--- a/include/HEJ/PDG_codes.hh
+++ b/include/HEJ/PDG_codes.hh
@@ -1,217 +1,216 @@
 /** \file PDG_codes.hh
  *  \brief Contains the Particle IDs of all relevant SM particles.
  *
  *  Large enumeration included which has multiple entries for potential
  *  alternative names of different particles. There are also functions
  *  which can be used to determine if a particle is a parton or if
  *  it is a non-gluon boson.
  *
- *  \authors   Jeppe Andersen, Tuomas Hapola, Marian Heil, Andreas Maier, Jennifer Smillie
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
  *  \date      2019
  *  \copyright GPLv2 or later
  */
-
 #pragma once
 
 #include <string>
 
 namespace HEJ {
 
   //! particle ids according to PDG
   namespace pid {
     //! The possible particle identities. We use PDG IDs as standard.
     enum ParticleID{
       d = 1,                           /*!< Down Quark */
       down = d,                        /*!< Down Quark */
       u = 2,                           /*!< Up Quark */
       up = u,                          /*!< Up Quark */
       s = 3,                           /*!< Strange Quark */
       strange = s,                     /*!< Strange Quark */
       c = 4,                           /*!< Charm Quark */
       charm = c,                       /*!< Charm Quark */
       b = 5,                           /*!< Bottom Quark */
       bottom = b,                      /*!< Bottom Quark */
       t = 6,                           /*!< Top Quark */
       top = t,                         /*!< Top Quark */
       e = 11,                          /*!< Electron */
       electron = e,                    /*!< Electron */
       nu_e = 12,                       /*!< Electron Neutrino */
       electron_neutrino = nu_e,        /*!< Electron neutrino */
       mu = 13,                         /*!< Muon */
       muon = mu,                       /*!< Muon */
       nu_mu = 14,                      /*!< Muon Neutrino */
       muon_neutrino = nu_mu,           /*!< Muon Neutrino */
       tau = 15,                        /*!< Tau */
       nu_tau = 16,                     /*!< Tau Neutrino */
       tau_neutrino = nu_tau,           /*!< Tau Neutrino */
       d_bar = -d,                      /*!< Anti-Down Quark */
       antidown = d_bar,                /*!< Anti-Down Quark */
       u_bar = -u,                      /*!< Anti-Up quark */
       antiup = -u,                     /*!< Anti-Up quark */
       s_bar = -s,                      /*!< Anti-Strange Quark */
       antistrange = -s,                /*!< Anti-Strange Quark */
       c_bar = -c,                      /*!< Anti-Charm Quark */
       anticharm = -c,                  /*!< Anti-Charm Quark */
       b_bar = -b,                      /*!< Anti-Bottom Quark */
       antibottom = -b,                 /*!< Anti-Bottom Quark */
       t_bar = -t,                      /*!< Anti-Top Quark */
       antitop = -t,                    /*!< Anti-Top Quark */
       e_bar = -e,                      /*!< Positron */
       positron = e_bar,                /*!< Positron */
       antielectron = positron,         /*!< Positron */
       nu_e_bar = -nu_e,                /*!< Electron Anti-Neutrino */
       electron_antineutrino = nu_e_bar,/*!< Electron Anti-Neutrino */
       mu_bar = -mu,                    /*!< Anti-Muon */
       antimuon = -mu,                  /*!< Anti-Muon */
       nu_mu_bar = -nu_mu,              /*!< Muon Anti-Neutrino */
       muon_antineutrino = nu_mu_bar,   /*!< Muon Anti-Neutrino */
       tau_bar = -tau,                  /*!< Anti-Tau */
       antitau = tau_bar,               /*!< Anti-Tau */
       nu_tau_bar = -nu_tau,            /*!< Tau Anti-Neutrino */
       tau_antineutrino = nu_tau_bar,   /*!< Tau Anti-Neutrino */
       gluon = 21,                      /*!< Gluon */
       g = gluon,                       /*!< Gluon */
       photon = 22,                     /*!< Photon */
       gamma = photon,                  /*!< Photon */
       Z = 23,                          /*!< Z Boson */
       Wp = 24,                         /*!< W- Boson */
       Wm = -Wp,                         /*!< W+ Boson */
       h = 25,                          /*!< Higgs Boson */
       Higgs = h,                       /*!< Higgs Boson */
       higgs = h,                       /*!< Higgs Boson */
       p = 2212,                        /*!< Proton */
       proton = p,                      /*!< Proton */
       p_bar = -p,                      /*!< Anti-Proton */
       antiproton = p_bar,              /*!< Anti-Proton */
     };
 
   }
 
   using ParticleID = pid::ParticleID;
 
   //! Convert a particle name to the corresponding PDG particle ID
   ParticleID to_ParticleID(std::string const & name);
 
   //! Get the of the particle with the given PDG ID
   std::string name(ParticleID id);
 
   /**
    * \brief Function to determine if particle is a parton
    * @param id        PDG ID of particle
    * @returns         true if the particle is a parton, false otherwise
    */
   inline
   constexpr bool is_parton(ParticleID id){
     return id == pid::gluon || std::abs(id) <= pid::top;
   }
 
   /**
    * \brief Function to determine if particle is a quark
    * @param id        PDG ID of particle
    * @returns         true if the particle is a quark, false otherwise
    */
   inline
   constexpr bool is_quark(ParticleID id){
     return (id >= pid::down && id <= pid::top);
   }
 
   /**
    * \brief Function to determine if particle is an antiquark
    * @param id        PDG ID of particle
    * @returns         true if the particle is an antiquark, false otherwise
    */
   inline
   constexpr bool is_antiquark(ParticleID id){
     return (id <= pid::d_bar && id >= pid::t_bar);
   }
 
   /**
    * \brief Function to determine if particle is an (anti-)quark
    * @param id        PDG ID of particle
    * @returns         true if the particle is a quark or antiquark, false otherwise
    */
   inline
   constexpr bool is_anyquark(ParticleID id){
     return (id && id >= pid::t_bar && id <= pid::t);
   }
 
   /**
    * \brief function to determine if the particle is a photon, W, Z, or Higgs boson
    * @param id        PDG ID of particle
    * @returns         true if the partice is an A,W,Z, or H, false otherwise
    */
   inline
   constexpr bool is_AWZH_boson(ParticleID id){
     return id == pid::Wm || (id >= pid::photon && id <= pid::Higgs);
   }
 
   /**
    * \brief function to determine if the particle is a photon, W or Z
    * @param id        PDG ID of particle
    * @returns         true if the partice is an A,W,Z, or H, false otherwise
    */
   inline
   constexpr bool is_AWZ_boson(ParticleID id){
     return id == pid::Wm || (id >= pid::photon && id <= pid::Wp);
   }
 
   /**
    * \brief Function to determine if particle is a lepton
    * @param id        PDG ID of particle
    * @returns         true if the particle is a lepton, false otherwise
    */
   inline
   constexpr bool is_lepton(ParticleID id){
     return (id >= pid::electron && id <= pid::tau_neutrino);
   }
 
   /**
    * \brief Function to determine if particle is an antilepton
    * @param id        PDG ID of particle
    * @returns         true if the particle is an antilepton, false otherwise
    */
   inline
   constexpr bool is_antilepton(ParticleID id){
     return (id <= pid::positron && id >= pid::nu_tau_bar);
   }
 
   /**
    * \brief Function to determine if particle is an (anti-)lepton
    * @param id        PDG ID of particle
    * @returns         true if the particle is a lepton or antilepton, false otherwise
    */
   inline
   constexpr bool is_anylepton(ParticleID id){
     return ( is_lepton(id) || is_antilepton(id));
   }
 
   /**
    * \brief Function to determine if particle is a neutrino
    * @param id        PDG ID of particle
    * @returns         true if the particle is a neutrino, false otherwise
    */
   inline
   constexpr bool is_neutrino(ParticleID id){
     return (id == pid::nu_e || id == pid::tau_neutrino || id == pid::muon_neutrino);
   }
 
   /**
    * \brief Function to determine if particle is an antineutrino
    * @param id        PDG ID of particle
    * @returns         true if the particle is an antineutrino, false otherwise
    */
   inline
   constexpr bool is_antineutrino(ParticleID id){
     return (id == pid::nu_e_bar || id == pid::nu_tau_bar || id == pid::nu_mu_bar);
   }
 
   /**
    * \brief Function to determine if particle is an (anti-)neutrino
    * @param id        PDG ID of particle
    * @returns         true if the particle is a neutrino or antineutrino, false otherwise
    */
   inline
   constexpr bool is_anyneutrino(ParticleID id){
     return ( is_neutrino(id)||is_antineutrino(id));
   }
 }
diff --git a/include/HEJ/Parameters.hh b/include/HEJ/Parameters.hh
index b9ed559..840d2e4 100644
--- a/include/HEJ/Parameters.hh
+++ b/include/HEJ/Parameters.hh
@@ -1,155 +1,155 @@
 /** \file
  *  \brief Containers for Parameter variations, e.g. different Weights
  *
- *  \authors   Jeppe Andersen, Tuomas Hapola, Marian Heil, Andreas Maier, Jennifer Smillie
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
  *  \date      2019
  *  \copyright GPLv2 or later
  */
 #pragma once
 
 #include <memory>
 #include <vector>
 
 #include "HEJ/exceptions.hh"
 
 namespace HEJ{
   //! Collection of parameters, e.g. Weights, assigned to a single event
   /**
    * A number of member functions of the MatrixElement class return Parameters
    * objects containing the squares of the matrix elements for the various
    * scale choices.
    */
   template<class T>
   struct Parameters {
     T central;
     std::vector<T> variations;
 
     template<class T_ext>
     Parameters<T>& operator*=(Parameters<T_ext> const & other);
     Parameters<T>& operator*=(double factor);
     template<class T_ext>
     Parameters<T>& operator/=(Parameters<T_ext> const & other);
     Parameters<T>& operator/=(double factor);
   };
 
   template<class T1, class T2> inline
   Parameters<T1> operator*(Parameters<T1> a, Parameters<T2> const & b) {
     a*=b;
     return a;
   }
   template<class T> inline
   Parameters<T> operator*(Parameters<T> a, double b) {
     a*=b;
     return a;
   }
   template<class T> inline
   Parameters<T> operator*(double b, Parameters<T> a) {
     a*=b;
     return a;
   }
   template<class T1, class T2> inline
   Parameters<T1> operator/(Parameters<T1> a, Parameters<T2> const & b) {
     a/=b;
     return a;
   }
   template<class T> inline
   Parameters<T> operator/(Parameters<T> a, double b) {
     a/=b;
     return a;
   }
 
   //! Alias for weight container, e.g. used by the MatrixElement
   using Weights = Parameters<double>;
 
   //! Description of event parameters, see also EventParameters
   struct ParameterDescription {
     //! Name of central scale choice (e.g. "H_T/2")
     std::string scale_name;
     //! Actual renormalisation scale divided by central scale
     double mur_factor;
     //! Actual factorisation scale divided by central scale
     double muf_factor;
 
     ParameterDescription() = default;
     ParameterDescription(
       std::string scale_name, double mur_factor, double muf_factor
     ):
       scale_name{scale_name}, mur_factor{mur_factor}, muf_factor{muf_factor}
     {};
   };
 
   //! Event parameters
   struct EventParameters{
     double mur;              /**< Value of the Renormalisation Scale */
     double muf;              /**< Value of the Factorisation Scale */
     double weight;           /**< Event Weight */
     //! Optional description
     std::shared_ptr<ParameterDescription> description = nullptr;
 
     //! multiply weight by factor
     EventParameters& operator*=(double factor){
       weight*=factor;
       return *this;
     };
     //! divide weight by factor
     EventParameters& operator/=(double factor){
       weight/=factor;
       return *this;
     };
   };
   inline EventParameters operator*(EventParameters a, double b){
     a*=b;
     return a;
   }
   inline EventParameters operator*(double b, EventParameters a){
     a*=b;
     return a;
   }
   inline EventParameters operator/(EventParameters a, double b){
     a/=b;
     return a;
   }
 
   //! @{
   //! @internal Implementation of template functions
   template<class T>
   template<class T_ext>
   Parameters<T>& Parameters<T>::operator*=(Parameters<T_ext> const & other) {
     if(other.variations.size() != variations.size()) {
       throw std::invalid_argument{"Wrong number of Parameters"};
     }
     central *= other.central;
     for(std::size_t i = 0; i < variations.size(); ++i) {
       variations[i] *= other.variations[i];
     }
     return *this;
   };
 
   template<class T>
   Parameters<T>& Parameters<T>::operator*=(double factor) {
     central *= factor;
     for(auto & wt: variations) wt *= factor;
     return *this;
   };
 
   template<class T>
   template<class T_ext>
   Parameters<T>& Parameters<T>::operator/=(Parameters<T_ext> const & other) {
     if(other.variations.size() != variations.size()) {
       throw std::invalid_argument{"Wrong number of Parameters"};
     }
     central /= other.central;
     for(std::size_t i = 0; i < variations.size(); ++i) {
       variations[i] /= other.variations[i];
     }
     return *this;
   };
 
   template<class T>
   Parameters<T>& Parameters<T>::operator/=(double factor) {
     central /= factor;
     for(auto & wt: variations) wt /= factor;
     return *this;
   };
   //! @}
 }
diff --git a/include/HEJ/Particle.hh b/include/HEJ/Particle.hh
index f5a3843..3dcd0fd 100644
--- a/include/HEJ/Particle.hh
+++ b/include/HEJ/Particle.hh
@@ -1,208 +1,207 @@
 /**
  * \file Particle.hh
  * \brief Contains the particle struct
  *
- *  \authors   Jeppe Andersen, Tuomas Hapola, Marian Heil, Andreas Maier, Jennifer Smillie
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
  *  \date      2019
  *  \copyright GPLv2 or later
  */
-
 #pragma once
 
 #include <utility>
 
 #include "fastjet/PseudoJet.hh"
 
 #include "HEJ/optional.hh"
 #include "HEJ/PDG_codes.hh"
 
 namespace HEJ {
 
   using Colour = std::pair<int,int>;
 
   //! Class representing a particle
   struct Particle {
     //! particle type
     ParticleID type;
     //! particle momentum
     fastjet::PseudoJet p;
     //! (optional) colour & anti-colour
     optional<Colour> colour;
 
     //! get rapidity
     double rapidity() const{
       return p.rapidity();
     }
     //! get transverse momentum
     double perp() const{
       return p.perp();
     }
     //! get momentum in x direction
     double px() const{
       return p.px();
     }
     //! get momentum in y direction
     double py() const{
       return p.py();
     }
     //! get momentum in z direction
     double pz() const{
       return p.pz();
     }
     //! get energy
     double E() const{
       return p.E();
     }
     //! get mass
     double m() const{
       return p.m();
     }
   };
 
   //! Functor to compare rapidities
   /**
    *  This can be used whenever a rapidity comparison function is needed,
    *  for example in many standard library functions.
    *
    *  @see pz_less
    */
   struct rapidity_less{
     template<class FourVector>
     bool operator()(FourVector const & p1, FourVector const & p2){
       return p1.rapidity() < p2.rapidity();
     }
   };
 
   //! Functor to compare momenta in z direction
   /**
    *  This can be used whenever a pz comparison function is needed,
    *  for example in many standard library functions.
    *
    *  @see rapidity_less
    */
   struct pz_less{
     template<class FourVector>
     bool operator()(FourVector const & p1, FourVector const & p2){
       return p1.pz() < p2.pz();
     }
   };
 
 
   //! Convert a vector of Particles to a vector of particle momenta
   inline
   std::vector<fastjet::PseudoJet> to_PseudoJet(
       std::vector<Particle> const & v
   ){
     std::vector<fastjet::PseudoJet> result;
     for(auto && sp: v) result.emplace_back(sp.p);
     return result;
   }
 
   //! Check if a particle is a parton, i.e. quark, antiquark, or gluon
   inline
   bool is_parton(Particle const & p){
     return is_parton(p.type);
   }
 
   //! Check if a particle is a quark
   inline
   bool is_quark(Particle const & p){
     return is_quark(p.type);
   }
 
   //! Check if a particle is an anti-quark
   inline
   bool is_antiquark(Particle const & p){
     return is_antiquark(p.type);
   }
 
   //! Check if a particle is a quark or anit-quark
   inline
   bool is_anyquark(Particle const & p){
     return is_anyquark(p.type);
   }
 
   /**
    * \brief Function to determine if particle is a lepton
    * @param p         the particle
    * @returns         true if the particle is a lepton, false otherwise
    */
   inline
   constexpr bool is_lepton(Particle const & p){
     return is_lepton(p.type);
   }
 
   /**
    * \brief Function to determine if particle is an antilepton
    * @param p         the particle
    * @returns         true if the particle is an antilepton, false otherwise
    */
   inline
   constexpr bool is_antilepton(Particle const & p){
     return is_antilepton(p.type);
   }
 
   /**
    * \brief Function to determine if particle is an (anti-)lepton
    * @param p         the particle
    * @returns         true if the particle is a lepton or antilepton, false otherwise
    */
   inline
   constexpr bool is_anylepton(Particle const & p){
     return is_anylepton(p.type);
   }
 
   /**
    * \brief Function to determine if particle is a neutrino
    * @param p         the particle
    * @returns         true if the particle is a neutrino, false otherwise
    */
   inline
   constexpr bool is_neutrino(Particle const & p){
     return is_neutrino(p.type);
   }
 
   /**
    * \brief Function to determine if particle is an antineutrino
    * @param p         the particle
    * @returns         true if the particle is an antineutrino, false otherwise
    */
   inline
   constexpr bool is_antineutrino(Particle const & p){
     return is_antineutrino(p.type);
   }
 
   /**
    * \brief Function to determine if particle is an (anti-)neutrino
    * @param p         the particle
    * @returns         true if the particle is a neutrino or antineutrino, false otherwise
    */
   inline
   constexpr bool is_anyneutrino(Particle const & p){
     return is_anyneutrino(p.type);
   }
 
   //! Check if a particle is a photon, W or Z boson
   inline bool is_AWZ_boson(Particle const & particle){
     return is_AWZ_boson(particle.type);
   }
 
   //! Check if a particle is a photon, W, Z, or Higgs boson
   inline bool is_AWZH_boson(Particle const & particle){
     return is_AWZH_boson(particle.type);
   }
 
   //! Extract all partons from a vector of particles
   inline
   std::vector<Particle> filter_partons(
       std::vector<Particle> const & v
   ){
     std::vector<Particle> result;
     result.reserve(v.size());
     std::copy_if(
         begin(v), end(v), std::back_inserter(result),
         [](Particle const & p){ return is_parton(p); }
     );
     return result;
   }
 }
diff --git a/include/HEJ/PhaseSpacePoint.hh b/include/HEJ/PhaseSpacePoint.hh
index 85ba9b6..1f51139 100644
--- a/include/HEJ/PhaseSpacePoint.hh
+++ b/include/HEJ/PhaseSpacePoint.hh
@@ -1,175 +1,182 @@
 /** \file
  *  \brief Contains the PhaseSpacePoint Class
  *
- *  \authors   Jeppe Andersen, Tuomas Hapola, Marian Heil, Andreas Maier, Jennifer Smillie
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
  *  \date      2019
  *  \copyright GPLv2 or later
  */
-
 #pragma once
 
 #include <array>
 #include <functional>
 #include <unordered_map>
 #include <vector>
 
 #include "HEJ/config.hh"
 #include "HEJ/Particle.hh"
 #include "HEJ/RNG.hh"
+#include "HEJ/StatusCode.hh"
 
 namespace HEJ{
   class Event;
 
   //! A point in resummation phase space
   class PhaseSpacePoint{
   public:
     //! Default PhaseSpacePoint Constructor
     PhaseSpacePoint() = default;
 
     //! PhaseSpacePoint Constructor
     /**
      * @param ev               Clustered Jet Event
      * @param conf             Configuration parameters
      * @param ran              Random number generator
      */
     PhaseSpacePoint(
         Event const & ev,
         PhaseSpacePointConfig conf,
         RNG & ran
     );
 
     //! Get phase space point weight
     double weight() const{
       return weight_;
     }
 
     //! Access incoming particles
     std::array<Particle, 2> const & incoming() const{
       return incoming_;
     }
 
     //! Access outgoing particles
     std::vector<Particle> const & outgoing() const{
       return outgoing_;
     }
 
 
     //! Particle decays
     /**
      *  The key in the returned map corresponds to the index in the
      *  vector returned by outgoing()
      */
     std::unordered_map<size_t, std::vector<Particle>> const &  decays() const{
       return decays_;
     }
 
+    //! Status code of generation
+    StatusCode status() const{
+        return status_;
+    }
+
     static constexpr int ng_max = 1000;    //< maximum number of extra gluons
 
   private:
 
     //! /internal returns the clustered jets sorted in rapidity
     std::vector<fastjet::PseudoJet> cluster_jets(
         std::vector<fastjet::PseudoJet> const & partons
     ) const;
     bool pass_resummation_cuts(
         std::vector<fastjet::PseudoJet> const & jets
     ) const;
     bool pass_extremal_cuts(
         fastjet::PseudoJet const & ext_parton,
         fastjet::PseudoJet const & jet
     ) const;
     int sample_ng(std::vector<fastjet::PseudoJet> const & Born_jets);
     int sample_ng_jets(int ng, std::vector<fastjet::PseudoJet> const & Born_jets);
     double probability_in_jet(
         std::vector<fastjet::PseudoJet> const & Born_jets
     ) const;
     std::vector<fastjet::PseudoJet> gen_non_jet(
         int ng_non_jet,
         double ptmin, double ptmax
     );
     void rescale_rapidities(
       std::vector<fastjet::PseudoJet> & partons,
       double ymin, double ymax
     );
     std::vector<fastjet::PseudoJet> reshuffle(
         std::vector<fastjet::PseudoJet> const & Born_jets,
         fastjet::PseudoJet const & q
     );
 
   /** \internal
    * final jet test:
    *   - number of jets must match Born kinematics
    *   - no partons designated as nonjet may end up inside jets
    *   - all other outgoing partons *must* end up inside jets
    *   - the extremal (in rapidity) partons must be inside the extremal jets
    *   - rapidities must be the same (by construction)
    */
     bool jets_ok(
         std::vector<fastjet::PseudoJet> const & Born_jets,
         std::vector<fastjet::PseudoJet> const & partons
     ) const;
     void reconstruct_incoming(std::array<Particle, 2> const & Born_incoming);
     double phase_space_normalisation(
         int num_Born_jets,
         int num_res_partons
     ) const;
     std::vector<fastjet::PseudoJet> split(
         std::vector<fastjet::PseudoJet> const & jets, int ng_jets
     );
     std::vector<int> distribute_jet_partons(
         int ng_jets, std::vector<fastjet::PseudoJet> const & jets
     );
     std::vector<fastjet::PseudoJet> split(
         std::vector<fastjet::PseudoJet> const & jets,
         std::vector<int> const & np_in_jet
     );
     bool split_preserved_jets(
         std::vector<fastjet::PseudoJet> const & jets,
         std::vector<fastjet::PseudoJet> const & jet_partons
     ) const;
     template<class Particle>
     Particle const & most_backward_FKL(
         std::vector<Particle> const & partons
     ) const;
     template<class Particle>
     Particle const & most_forward_FKL(
         std::vector<Particle> const & partons
     ) const;
     template<class Particle>
     Particle & most_backward_FKL(std::vector<Particle> & partons) const;
     template<class Particle>
     Particle & most_forward_FKL(std::vector<Particle> & partons) const;
     bool extremal_ok(
         std::vector<fastjet::PseudoJet> const & partons
     ) const;
     /** \internal
      * \brief relabels qqx-pair with its PDG IDs.
      * \param event               Born Event
      *
      * This function will label the qqx pair in a qqx event back to their
      * original types from the input event.
      *
      * \note This function assumes outgoing_ to be pure partonic when called,
      *       i.e.  A/W/Z/h bosons should _not be set_ at this stage
      */
     void label_qqx(Event const & event);
     void copy_AWZH_boson_from(Event const & event);
 
     bool momentum_conserved() const;
 
     bool unob_, unof_, qqxb_, qqxf_, qqxmid_;
 
     double weight_;
 
     PhaseSpacePointConfig param_;
 
 
     std::array<Particle, 2> incoming_;
     std::vector<Particle> outgoing_;
     //! \internal Particle decays in the format {outgoing index, decay products}
     std::unordered_map<size_t, std::vector<Particle>> decays_;
 
     std::reference_wrapper<HEJ::RNG> ran_;
+
+    StatusCode status_;
   };
 
 }
diff --git a/include/HEJ/ProgressBar.hh b/include/HEJ/ProgressBar.hh
index 5f7f32d..aeaa9d3 100644
--- a/include/HEJ/ProgressBar.hh
+++ b/include/HEJ/ProgressBar.hh
@@ -1,93 +1,93 @@
 /** \file
  *  \brief Contains the ProgressBar class
  *
- *  \authors   Jeppe Andersen, Tuomas Hapola, Marian Heil, Andreas Maier, Jennifer Smillie
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
  *  \date      2019
  *  \copyright GPLv2 or later
  */
 #pragma once
 
 #include <ostream>
 #include <functional>
 #include <stdexcept>
 
 namespace HEJ {
 
   //! Class representing (and printing) a progress bar
   template<typename T>
   class ProgressBar {
   public:
     //! Constructor
     /**
      * @param out    Output stream
      * @param max    Maximum value of the progress parameter
      *
      * This will print a fixed-width progress bar, which is initially at 0%.
      */
     ProgressBar(std::ostream & out, T max) :
       out_{out}, max_{max}
     {
       if(max <= 0) {
         throw std::invalid_argument{
           "Maximum in progress bar has to be positive"
         };
       }
       print_bar();
     }
 
     //! Increment progress
     /**
      *  @param count    Value to add to the current progress parameter
      *
      *  After updating the progess parameter, the progress bar is updated
      *  to a percentage that corresponds to the ratio of the current and
      *  maximum progress parameters.
      */
     ProgressBar & increment(T count) {
       counter_ += count;
       update_progress();
       return *this;
     }
 
     //! Increase progress by one unit
     /**
      *  After updating the progess parameter, the progress bar is updated
      *  to a percentage that corresponds to the ratio of the current and
      *  maximum progress parameters.
      */
     ProgressBar & operator++() {
       ++counter_;
       update_progress();
       return *this;
     }
 
   private:
     void update_progress() {
       counter_ = std::min(counter_, max_);
       const int ndots = (100*counter_)/max_;
       const int new_dots = ndots - ndots_;
       if(new_dots > 0) {
         for(int dot = 0; dot < new_dots; ++dot) out_.get() << '.';
         out_.get().flush();
         ndots_ = ndots;
       }
     }
 
     void print_bar() const {
       out_.get() << "0% ";
       for(int i = 10; i <= 100; i+= 10){
         out_.get() << "       " + std::to_string(i) + "%";
       }
       out_.get() << "\n|";
       for(int i = 10; i <= 100; i+= 10){
         out_.get() << "---------|";
       }
       out_.get() << '\n';
     }
 
     std::reference_wrapper<std::ostream> out_;
     T counter_ = 0;
     T ndots_ = 0;
     T max_;
   };
 }
diff --git a/include/HEJ/RNG.hh b/include/HEJ/RNG.hh
index 9cd36f6..120b126 100644
--- a/include/HEJ/RNG.hh
+++ b/include/HEJ/RNG.hh
@@ -1,45 +1,45 @@
 /** \file
  *  \brief Interface for pseudorandom number generators
  *
  *  We select our random number generator at runtime according to the
  *  configuration file. This interface guarantees that we can use all
  *  generators in the same way.
  *
- *  \authors   Jeppe Andersen, Tuomas Hapola, Marian Heil, Andreas Maier, Jennifer Smillie
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
  *  \date      2019
  *  \copyright GPLv2 or later
  */
 #pragma once
 
 #include <limits>
 
 namespace HEJ {
 
   //! Interface for random number generator
   struct RNG {
     //! Generate random number in (0,1]
     virtual double flat() = 0;
 
     //! Minimum number that can be generated
     virtual unsigned min() const = 0;
     //! Maximum number that can be generated
     virtual unsigned max() const = 0;
     //! Generate random number in [min(), max()]
     virtual unsigned operator()() = 0;
 
     virtual ~RNG() = default;
   };
 
   //! Helper struct with default implementations
   struct DefaultRNG : virtual RNG {
     unsigned min() const override {
       return 0u;
     }
     unsigned max() const override {
       return std::numeric_limits<unsigned>::max() - 1;
     }
     unsigned operator()() override {
       return flat()*std::numeric_limits<unsigned int>::max();
     }
   };
 }
diff --git a/include/HEJ/Ranlux64.hh b/include/HEJ/Ranlux64.hh
index c11c3c7..687e38e 100644
--- a/include/HEJ/Ranlux64.hh
+++ b/include/HEJ/Ranlux64.hh
@@ -1,34 +1,34 @@
 /** \file
  * \brief Contains a class for the ranlux64 random number generator
  *
- *  \authors   Jeppe Andersen, Tuomas Hapola, Marian Heil, Andreas Maier, Jennifer Smillie
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
  *  \date      2019
  *  \copyright GPLv2 or later
  */
 #pragma once
 
 #include <string>
 
 #include <CLHEP/Random/Ranlux64Engine.h>
 
 #include "HEJ/RNG.hh"
 
 namespace HEJ {
 
   //! Ranlux64 random number generator
   /**
    *  For details on ranlux64, see \cite Luscher:1993dy, \cite James:1993np
    */
   class Ranlux64 : public DefaultRNG {
   public:
     Ranlux64();
     Ranlux64(std::string const & seed_file);
 
     //! Generate pseudorandom number between 0 and 1
     double flat() override;
 
   private:
     CLHEP::Ranlux64Engine ran_;
   };
 
 }
diff --git a/include/HEJ/RivetAnalysis.hh b/include/HEJ/RivetAnalysis.hh
index e36b88d..cac5738 100644
--- a/include/HEJ/RivetAnalysis.hh
+++ b/include/HEJ/RivetAnalysis.hh
@@ -1,69 +1,68 @@
 /** \file
  *  \brief HEJ 2 interface to rivet analyses
  *
- *  \authors   Jeppe Andersen, Tuomas Hapola, Marian Heil, Andreas Maier, Jennifer Smillie
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
  *  \date      2019
  *  \copyright GPLv2 or later
  */
-
 #pragma once
 
 #include <memory>
 #include <string>
 #include <vector>
 
 #include "HEJ/Analysis.hh"
 #include "HEJ/HepMCInterface.hh"
 
 namespace Rivet {
   class AnalysisHandler;
 }
 namespace YAML {
   class Node;
 }
 
 namespace HEJ {
   /**
     * @brief Class representing a Rivet analysis
     *
     * This class inherits from Analysis and can therefore be used
     * like any other HEJ 2 analysis.
     */
   class RivetAnalysis: public HEJ::Analysis {
   public:
     static std::unique_ptr<Analysis> create(YAML::Node const & config);
 
     //! Constructor
     /**
      *  @param config    Configuration parameters
      *
      * config["analysis"] should be the name of a single Rivet analysis or
      * a list of Rivet analyses. config["output"] is the prefix for
      * the .yoda output files.
      */
     RivetAnalysis(YAML::Node const & config);
     //! Pass an event to the underlying Rivet analysis
     void fill(HEJ::Event const & event, HEJ::Event const &) override;
     bool pass_cuts(HEJ::Event const &, HEJ::Event const &) override
        {return true;} //< no additional cuts are applied
     void finalise() override;
   private:
     std::vector<std::string> analyses_names_;
     const std::string output_name_;
 
     /// struct to organise the infos per rivet run/scale setting
     struct rivet_info {
       std::unique_ptr<Rivet::AnalysisHandler> handler;
       std::string name;
       HEJ::HepMCInterface hepmc;
     };
     std::vector<rivet_info> rivet_runs_;
 
     /**
      *  \internal
      * @brief Calculates the scale variation from the first event for the output file
      */
     void init(HEJ::Event const & event);
     bool first_event_;
   };
 }
diff --git a/include/HEJ/ScaleFunction.hh b/include/HEJ/ScaleFunction.hh
index 42c2c99..e84b7a7 100644
--- a/include/HEJ/ScaleFunction.hh
+++ b/include/HEJ/ScaleFunction.hh
@@ -1,174 +1,174 @@
 /** \file
  *  \brief Functions to calculate the (renormalisation and factorisation) scales for an event
  *
- *  \authors   Jeppe Andersen, Tuomas Hapola, Marian Heil, Andreas Maier, Jennifer Smillie
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
  *  \date      2019
  *  \copyright GPLv2 or later
  */
 #pragma once
 
 #include <functional>
 #include <memory>
 #include <string>
 #include <utility>
 #include <vector>
 
 namespace HEJ{
   class Event;
 
   //! Class to calculate the scale associated with an event
   class ScaleFunction {
   public:
     //! Constructor
     /**
      *  @param name     Name of the scale choice (e.g. H_T)
      *  @param fun      Function used to calculate the scale
      */
     ScaleFunction(std::string name, std::function<double(Event const &)> fun):
       name_{std::move(name)},
       fun_{std::move(fun)}
     {}
 
     //! Name of the scale choice
     std::string const & name() const {
       return name_;
     }
 
     //! Calculate the scale associated with an event
     double operator()(Event const & ev) const {
       return fun_(ev);
     }
 
   private:
     friend ScaleFunction operator*(double factor, ScaleFunction base_scale);
     friend ScaleFunction operator/(ScaleFunction base_scale, double denom);
     friend ScaleFunction operator*(ScaleFunction func1, ScaleFunction func2);
     friend ScaleFunction operator/(ScaleFunction func1, ScaleFunction func2);
 
     std::string name_;
     std::function<double(Event const &)> fun_;
   };
 
   //! Multiply a scale choice by a constant factor
   /**
    * For example, multiplying 0.5 and a scale function for H_T
    * will result in a scale function for H_T/2.
    */
   ScaleFunction operator*(double factor, ScaleFunction base_scale);
   //! Multiply a scale choice by a second one
   /**
    * For example, multiplying H_T and m_j1j2
    * will result in a scale function for H_T*m_j1j2.
    */
   ScaleFunction operator*(ScaleFunction factor, ScaleFunction base_scale);
   //! Divide a scale choice by a constant factor
   /**
    * For example, dividing a scale function for H_T by 2
    * will result in a scale function for H_T/2.
    */
   ScaleFunction operator/(ScaleFunction base_scale, double denom);
   //! Divide a scale choice by a second one
   /**
    * For example, dividing a scale function for H_T by m_j1j2
    * will result in a scale function for H_T/m_j1j2.
    */
   ScaleFunction operator/(ScaleFunction base_scale, ScaleFunction denom);
 
   //! Calculate \f$H_T\f$ for the input event
   /**
    * \f$H_T\f$ is the sum of the (scalar) transverse momenta of all
    * final-state particles
    */
   double H_T(Event const &);
   //! The maximum of all (scalar) jet transverse momentum
   double max_jet_pt(Event const &);
   //! The invariant mass of the sum of all jet momenta
   double jet_invariant_mass(Event const &);
   //! Invariant mass of the two hardest jets
   double m_j1j2(Event const &);
 
   //! Functor that returns a fixed scale regardless of the input event
   class FixedScale {
   public:
     explicit FixedScale(double mu): mu_{mu} {}
     double operator()(Event const &) const {
       return mu_;
     }
   private:
     double mu_;
   };
 
   class ParameterDescription;
 
   //! Generate combinations of renormalisation and factorisation scales
   class ScaleGenerator{
   public:
     ScaleGenerator() = default;
 
     /** \brief Constructor
      * @param scale_functions_begin   Iterator to first base scale
      * @param scale_functions_end     Iterator past last base scale
      * @param scale_factors_begin     Iterator to first scale factor
      * @param scale_factors_end       Iterator past last scale factor
      * @param max_scale_ratio         Maximum ratio between renormalisation
      *                                and factorisation scale
      */
     template<class ScaleFunIterator, class FactorIterator>
     ScaleGenerator(
         ScaleFunIterator scale_functions_begin,
         ScaleFunIterator scale_functions_end,
         FactorIterator scale_factors_begin,
         FactorIterator scale_factors_end,
         double max_scale_ratio
     ):
       scales_(scale_functions_begin, scale_functions_end),
       scale_factors_(scale_factors_begin, scale_factors_end),
       max_scale_ratio_{max_scale_ratio}
     {
       gen_descriptions();
     }
 
     /** \brief Constructor
      * @param scales            Base scales
      * @param scale_factors     Factors to multiply the base scales
      * @param max_scale_ratio   Maximum ratio between renormalisation
      *                          and factorisation scale
      */
     ScaleGenerator(
         std::vector<ScaleFunction> scales,
         std::vector<double> scale_factors,
         double max_scale_ratio
     ):
       scales_(std::move(scales)),
       scale_factors_(std::move(scale_factors)),
       max_scale_ratio_{max_scale_ratio}
     {
       gen_descriptions();
     }
 
     /** \brief Adjust event parameters, adding scale variation
      *
      * The central renormalisation and factorisation scale of the returned
      * event is given be the first base scale passed to the constructor.
      * The scale variation (stored in event.variation()) is constructed as
      * follows: For each base scale according to the arguments of the
      * constructor we add one variation where both renormalisation and
      * factorisation scale are set according to the current base scale.
      * Then, all combinations where the base renormalisation and factorisation
      * scales are multiplied by one of the scale factors are added.
      * The case were both scales are multiplied by one is skipped.
      * Scale combinations where the ratio is larger than the maximum scale ratio
      * set in the constructor are likewise discarded.
      */
     Event operator()(Event event) const;
 
   private:
     void gen_descriptions();
 
     std::vector<ScaleFunction> scales_;
     std::vector<double> scale_factors_;
     std::vector<std::shared_ptr<ParameterDescription>> descriptions_;
     double max_scale_ratio_;
   };
 
 }
diff --git a/include/HEJ/StatusCode.hh b/include/HEJ/StatusCode.hh
new file mode 100644
index 0000000..9e04007
--- /dev/null
+++ b/include/HEJ/StatusCode.hh
@@ -0,0 +1,39 @@
+/** \file
+ *  \brief Header file for status codes of event generation
+ *
+ *  \authors   Jeppe Andersen, Marian Heil, Andreas Maier, Jennifer Smillie
+ *  \date      2019
+ *  \copyright GPLv2 or later
+ */
+#pragma once
+
+namespace HEJ {
+  enum StatusCode{
+    good,
+    discard,
+    empty_jets,
+    failed_reshuffle,
+    failed_resummation_cuts,
+    failed_split,
+    too_much_energy,
+    wrong_jets,
+    unspecified // should never appear
+  };
+
+  // @TODO better names
+  inline std::string to_string(StatusCode s){
+    switch(s){
+      case good:                    return "good";
+      case discard:                 return "discard";
+      case empty_jets:              return "empty jets";
+      case failed_reshuffle:        return "failed reshuffle";
+      case failed_resummation_cuts: return "below cuts";
+      case failed_split:            return "failed split";
+      case too_much_energy:         return "too much energy";
+      case wrong_jets:              return "wrong jets";
+      case unspecified:             return "unspecified";
+      default:;
+    }
+    throw std::logic_error{"unreachable"};
+  }
+ } // HEJ
diff --git a/include/HEJ/Tensor.hh b/include/HEJ/Tensor.hh
index b02ae30..470539e 100644
--- a/include/HEJ/Tensor.hh
+++ b/include/HEJ/Tensor.hh
@@ -1,201 +1,204 @@
 /** \file
  *  \brief Tensor Template Class declaration.
  *
  *  This file contains the declaration of the Tensor Template class. This
  *  is used to calculate some of the more complex currents within the
  *  W+Jets implementation particularly.
+ *
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
+ *  \date      2019
+ *  \copyright GPLv2 or later
  */
-
 #pragma once
 #include <array>
 
 ///@TODO remove function implementation from header
 ///@TODO put in some namespace
 template <unsigned int N, unsigned int D>
 class Tensor{
 public:
 
   //Constructor
   Tensor();
   Tensor(COM x);
 
   //Destructor
   virtual ~Tensor();
 
   int rank(){
     return N;
   }
 
   int dim(){
     return D;
   }
 
   int len(){
     return size;
   }
 
   COM at(int i){
     return components[i];
   }
   COM at(int i, int j) {
     return components[D*i +j];
   }
   COM at(int i, int j, int k) {
     return components[D*(D*i + j)+ k];
   }
   COM at(int i,int j, int k,int l) {
     return components[D*(D*(D*i +j) + k) + l];
   }
   COM at(int i,int j, int k,int l, int m){
     return components[D*(D*(D*(D*i + j) + k) + l) + m];
   }
 
   bool isSet(){
     if(components.size()==0)
       return false;
     else
       return true;
   }
 
   void Fill(COM x){
     components=x;
   }
 
   //Set component indexed by i,j,k,l,m
   void Set(int i,COM x){
     components[i] = x;
   }
   void Set(int i, int j, COM x) {
     components[D*i +j] = x;
   }
   void Set(int i, int j, int k, COM x) {
     components[D*(D*i + j)+ k] = x;
   }
   void Set(int i,int j, int k,int l,COM x) {
     components[D*(D*(D*i +j) + k) + l] = x;
   }
   void Set(int i,int j, int k,int l, int m, COM x){
     components[D*(D*(D*(D*i + j) + k) + l) + m] = x;
   }
 
   Tensor<N,D> operator*(const double x){
     Tensor<N,D> newT;
     newT.components=components*COM(x,0);
     return newT;
   }
   Tensor<N,D> operator*(const COM x){
     Tensor<N,D> newT;
     newT.components=components*x;
     return newT;
   }
   Tensor<N,D> operator/(const double x){
     Tensor<N,D> newT;
     newT.components=components/COM(x,0);
     return newT;
   }
   Tensor<N,D> operator/(const COM x){
     Tensor<N,D> newT;
     newT.components=components/x;
     return newT;
   }
   Tensor<N,D> operator+(const Tensor<N,D> T2){
     Tensor<N,D> newT;
     newT.components=components+T2.components;
     return newT;
   }
   Tensor<N,D> operator-(const Tensor<N,D> T2){
     Tensor<N,D> newT;
     newT.components=components-T2.components;
     return newT;
   }
  void operator+=(const Tensor<N,D> T2){
    components = components+T2.components;
  }
   void operator-=(const Tensor<N,D> T2){
     components=components-T2.components;
   }
 
   Tensor<N+1,D> rightprod(const Tensor<1,D> T2){
     Tensor<N+1,D> newT;
     for(int i=0; i<size;i++){
       for(unsigned int j=0;j<D;j++){
         newT.components[i*D+j]=components[i]*T2.components[j];
       }
     }
     return newT;
   }
 
   Tensor<N+1,D> leftprod(const Tensor<1,D> T2){
     Tensor<N+1,D> newT;
     for(unsigned int j=0;j<D;j++){
       for(int i=0; i<size;i++){
         newT.components[j*size+i]=components[i]*T2.components[j];
       }
     }
     return newT;
   }
 
   //T^(mu1...mk..mN)T2_(muk) contract kth index, where k member of [1,N]
   Tensor<N-1,D> contract(const Tensor<1,D> T2, int k){
     Tensor<N-1,D> newT;
     for(int j=0; j<newT.len(); j++){
       COM temp;
       int itemp = pow(D,(N-k));
       for (unsigned int i=0; i<D; i++){
         int index = D*itemp*floor(j/itemp) + itemp*i +j%(itemp);
         temp+=components[index]*T2.components[i]*sign(i);
       }
       newT.components[j]=temp;
     }
     return newT;
   }
 
   std::valarray<COM> components;
 
 private:
 
   int size;
   COM sign(unsigned int i){
     if(i==0)
       return 1.;
     else
       return -1.;
   }
 
 };
 
 template <unsigned int N, unsigned int D> Tensor<N,D>::Tensor()
 {
   size = pow(D,N);
   components.resize(size);
 }
 template <unsigned int N, unsigned int D> Tensor<N,D>::Tensor(COM x) {
   size = pow(D,N);
   components.resize(size, x);
 }
 template <unsigned int N, unsigned int D> Tensor<N,D>::~Tensor() {}
 
 // Tensor Functions:
 // Tensor<1,4> Sigma(int i, int j, bool hel);
 // Tensor<2,4> Metric();
 // int tensor2listindex(std::array<int,5> indexlist);
 // int tensor2listindex(std::array<int,3> indexlist);
 // void perms41(int same4, int diff, std::vector<std::array<int,5>> * perms);
 // void perms32(int same3, int diff, std::vector<std::array<int,5>> * perms);
 // void perms311(int same3, int diff1, int diff2, std::vector<std::array<int,5>> * perms);
 // void perms221(int same2a, int same2b, int diff, std::vector<std::array<int,5>> * perms);
 // void perms2111(int same2, int diff1,int diff2,int diff3, std::vector<std::array<int,5>> * perms);
 // void perms21(int same, int diff, std::vector<std::array<int,3>> * perms);
 // void perms111(int diff1, int diff2, int diff3, std::vector<std::array<int,3>> * perms);
 
 Tensor<2,4> Metric();
 Tensor<1,4> TCurrent(CLHEP::HepLorentzVector p1, bool h1,
                      CLHEP::HepLorentzVector p2, bool h2);
 Tensor<3,4> T3Current(CLHEP::HepLorentzVector p1, bool h1,
                       CLHEP::HepLorentzVector p2, bool h2);
 Tensor<5,4> T5Current(CLHEP::HepLorentzVector p1, bool h1,
                       CLHEP::HepLorentzVector p2, bool h2);
 Tensor<1,4> Construct1Tensor(CCurrent j);
 Tensor<1,4> Construct1Tensor(CLHEP::HepLorentzVector p);
 Tensor<1,4> eps(CLHEP::HepLorentzVector k, CLHEP::HepLorentzVector ref, bool pol);
 bool init_sigma_index();
diff --git a/include/HEJ/Weights.hh b/include/HEJ/Weights.hh
index 2b0af46..98fb8d7 100644
--- a/include/HEJ/Weights.hh
+++ b/include/HEJ/Weights.hh
@@ -1,13 +1,13 @@
 /** \file
  *  \brief Legacy Header for Weights
  *  \note  This Header was moved to "HEJ/Parameters.hh"
  *  \TODO remove in HEJ 2.2.0
  *
- *  \authors   Jeppe Andersen, Tuomas Hapola, Marian Heil, Andreas Maier, Jennifer Smillie
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
  *  \date      2019
  *  \copyright GPLv2 or later
  */
 #warning "HEJ/Weights.hh is deprecated use HEJ/Parameters.hh instead"
 #pragma once
 
 #include "HEJ/Parameters.hh"
diff --git a/include/HEJ/YAMLreader.hh b/include/HEJ/YAMLreader.hh
index fd9b36a..06cb2f8 100644
--- a/include/HEJ/YAMLreader.hh
+++ b/include/HEJ/YAMLreader.hh
@@ -1,254 +1,253 @@
 /** \file
  *  \brief The file which handles the configuration file parameters
  *
  *  The configuration files parameters are read and then stored
  *  within this objects.
  *
- *  \authors   Jeppe Andersen, Tuomas Hapola, Marian Heil, Andreas Maier, Jennifer Smillie
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
  *  \date      2019
  *  \copyright GPLv2 or later
  */
-
 #pragma once
 
 #include <string>
 #include <utility>
 #include <vector>
 
 #include "yaml-cpp/yaml.h"
 
 #include "fastjet/JetDefinition.hh"
 
 #include "HEJ/config.hh"
 #include "HEJ/exceptions.hh"
 #include "HEJ/optional.hh"
 #include "HEJ/PDG_codes.hh"
 #include "HEJ/utility.hh"
 
 namespace HEJ{
   class OutputFile;
   //! Load configuration from file
   /**
    *  @param config_file   Name of the YAML configuration file
    *  @returns             The HEJ 2 configuration
    */
   Config load_config(std::string const & config_file);
 
   //! Set option using the corresponding YAML entry
   /**
    *  @param setting      Option variable to be set
    *  @param yaml         Root of the YAML configuration
    *  @param names        Name of the entry
    *
    *  If the entry does not exist or has the wrong type or format
    *  an exception is thrown.
    *
    *  For example
    *  @code
    *  set_from_yaml(foobar, yaml, "foo", "bar")
    *  @endcode
    *  is equivalent to
    *  @code
    *  foobar = yaml["foo"]["bar"].as<decltype(foobar)>()
    *  @endcode
    *  with improved diagnostics on errors.
    *
    * @see set_from_yaml_if_defined
    */
   template<typename T, typename... YamlNames>
   void set_from_yaml(
       T & setting,
       YAML::Node const & yaml, YamlNames const & ... names
   );
 
   //! Set option using the corresponding YAML entry, if present
   /**
    *  @param setting      Option variable to be set
    *  @param yaml         Root of the YAML configuration
    *  @param names        Name of the entry
    *
    *  This function works similar to set_from_yaml, but does not
    *  throw any exception if the requested YAML entry does not exist.
    *
    *  @see set_from_yaml
    */
   template<typename T, typename... YamlNames>
   void set_from_yaml_if_defined(
       T & setting,
       YAML::Node const & yaml, YamlNames const & ... names
   );
 
   //! Extract jet parameters from YAML configuration
   JetParameters get_jet_parameters(
       YAML::Node const & node, std::string const & entry
   );
 
   //! Extract Higgs coupling settings from YAML configuration
   HiggsCouplingSettings get_Higgs_coupling(
       YAML::Node const & node, std::string const & entry
   );
 
   //! Extract scale setting parameters from YAML configuration
   ScaleConfig to_ScaleConfig(YAML::Node const & yaml);
 
   //! Extract random number generator settings from YAML configuration
   RNGConfig to_RNGConfig(YAML::Node const & node, std::string const & entry);
 
   //! Check whether all options in configuration are supported
   /**
    *  @param conf       Configuration to be checked
    *  @param supported  Tree of supported options
    *
    *  If conf contains an entry that does not appear in supported
    *  an unknown_option exception is thrown. Sub-entries of "analysis"
    *  (if present) are not checked.
    *
    *  @see unknown_option
    */
   void assert_all_options_known(
       YAML::Node const & conf, YAML::Node const & supported
   );
 
   namespace detail{
     void set_from_yaml(fastjet::JetAlgorithm & setting, YAML::Node const & yaml);
     void set_from_yaml(EventTreatment & setting, YAML::Node const & yaml);
     void set_from_yaml(ParticleID & setting, YAML::Node const & yaml);
     void set_from_yaml(OutputFile & setting, YAML::Node const & yaml);
 
     inline
     void set_from_yaml(YAML::Node & setting, YAML::Node const & yaml){
       setting = yaml;
     }
 
     template<typename Scalar>
     void set_from_yaml(Scalar & setting, YAML::Node const & yaml){
       assert(yaml);
       if(!yaml.IsScalar()){
         throw invalid_type{"value is not a scalar"};
       }
       try{
         setting = yaml.as<Scalar>();
       }
       catch(...){
         throw invalid_type{
           "value " + yaml.as<std::string>()
           + " cannot be converted to a " + type_string(setting)
         };
       }
     }
 
     template<typename T>
     void set_from_yaml(optional<T> & setting, YAML::Node const & yaml){
       T tmp;
       set_from_yaml(tmp, yaml);
       setting = tmp;
     }
 
     template<typename T>
     void set_from_yaml(std::vector<T> & setting, YAML::Node const & yaml){
       assert(yaml);
       // special case: treat a single value like a vector with one element
       if(yaml.IsScalar()){
         setting.resize(1);
         return set_from_yaml(setting.front(), yaml);
       }
       if(yaml.IsSequence()){
         setting.resize(yaml.size());
         for(size_t i = 0; i < setting.size(); ++i){
           set_from_yaml(setting[i], yaml[i]);
         }
         return;
       }
       throw invalid_type{""};
     }
 
     template<typename T, typename FirstName, typename... YamlNames>
     void set_from_yaml(
         T & setting,
         YAML::Node const & yaml, FirstName const & name,
         YamlNames && ... names
     ){
       if(!yaml[name]) throw missing_option{""};
       set_from_yaml(
           setting,
           yaml[name], std::forward<YamlNames>(names)...
       );
     }
 
     template<typename T>
     void set_from_yaml_if_defined(T & setting, YAML::Node const & yaml){
       return set_from_yaml(setting, yaml);
     }
 
     template<typename T, typename FirstName, typename... YamlNames>
     void set_from_yaml_if_defined(
         T & setting,
         YAML::Node const & yaml, FirstName const & name,
         YamlNames && ... names
     ){
       if(!yaml[name]) return;
       set_from_yaml_if_defined(
           setting,
           yaml[name], std::forward<YamlNames>(names)...
       );
     }
   }
 
   template<typename T, typename... YamlNames>
   void set_from_yaml(
       T & setting,
       YAML::Node const & yaml, YamlNames const & ... names
   ){
     try{
       detail::set_from_yaml(setting, yaml, names...);
     }
     catch(invalid_type const & ex){
       throw invalid_type{
         "In option " + join(": ", names...) + ": " + ex.what()
       };
     }
     catch(missing_option const &){
       throw missing_option{
         "No entry for mandatory option " + join(": ", names...)
       };
     }
     catch(std::invalid_argument const & ex){
       throw missing_option{
         "In option " + join(": ", names...) + ":"
           " invalid value " + ex.what()
       };
     }
   }
 
   template<typename T, typename... YamlNames>
   void set_from_yaml_if_defined(
       T & setting,
       YAML::Node const & yaml, YamlNames const & ... names
   ){
     try{
       detail::set_from_yaml_if_defined(setting, yaml, names...);
     }
     catch(invalid_type const & ex){
       throw invalid_type{
         "In option " + join(": ", names...) + ": " + ex.what()
       };
     }
     catch(std::invalid_argument const & ex){
       throw missing_option{
         "In option " + join(": ", names...) + ":"
           " invalid value " + ex.what()
       };
     }
   }
 
 }
 
 
 namespace YAML {
 
   template<>
   struct convert<HEJ::OutputFile> {
     static Node encode(HEJ::OutputFile const & outfile);
     static bool decode(Node const & node, HEJ::OutputFile & out);
   };
 }
diff --git a/include/HEJ/config.hh b/include/HEJ/config.hh
index 51f973c..c6a002c 100644
--- a/include/HEJ/config.hh
+++ b/include/HEJ/config.hh
@@ -1,191 +1,190 @@
 /** \file
  *  \brief HEJ 2 configuration parameters
  *
- *  \authors   Jeppe Andersen, Tuomas Hapola, Marian Heil, Andreas Maier, Jennifer Smillie
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
  *  \date      2019
  *  \copyright GPLv2 or later
  */
-
 #pragma once
 
 #include <string>
 
 #include "fastjet/JetDefinition.hh"
 #include "yaml-cpp/yaml.h"
 
 #include "HEJ/Constants.hh"
 #include "HEJ/event_types.hh"
 #include "HEJ/HiggsCouplingSettings.hh"
 #include "HEJ/optional.hh"
 #include "HEJ/output_formats.hh"
 #include "HEJ/ScaleFunction.hh"
 
 namespace HEJ{
 
   //! Jet parameters
   struct JetParameters{
     fastjet::JetDefinition def;          /**< Jet Definition */
     double min_pt;                       /**< Minimum Jet Transverse Momentum */
   };
 
   //! Settings for scale variation
   struct ScaleConfig{
     //! Base scale choices
     std::vector<ScaleFunction> base;
     //! Factors for multiplicative scale variation
     std::vector<double> factors;
     //! Maximum ratio between renormalisation and factorisation scale
     double max_ratio;
   };
 
   //! Settings for random number generator
   struct RNGConfig {
     //! Random number generator name
     std::string name;
     //! Optional initial seed
     optional<std::string> seed;
   };
 
   /**! Possible treatments for fixed-order input events.
    *
    *  The program will decide on how to treat an event based on
    *  the value of this enumeration.
    */
   enum class EventTreatment{
     reweight,                                  /**< Perform resummation */
     keep,                                      /**< Keep the event */
     discard,                                   /**< Discard the event */
   };
 
   //! Container to store the treatments for various event types
   using EventTreatMap = std::map<event_type::EventType, EventTreatment>;
 
   /**! Input parameters.
    *
    * This struct handles stores all configuration parameters
    * needed in a HEJ 2 run.
    *
    * \internal To add a new option:
    *           1. Add a member to the Config struct.
    *           2. Inside "src/YAMLreader.cc":
    *              - Add the option name to the "supported" Node in
    *                get_supported_options.
    *              - Initialise the new Config member in to_Config.
    *                The functions set_from_yaml (for mandatory options) and
    *                set_from_yaml_if_defined (non-mandatory) may be helpful.
    *           3. Add a new entry (with short description) to config.yaml
    *           4. Update the user documentation in "doc/Sphinx/"
    */
   struct Config {
     //! Parameters for scale variation
     ScaleConfig scales;
     //! Resummation jet properties
     JetParameters resummation_jets;
     //! Fixed-order jet properties
     JetParameters fixed_order_jets;
     //! Minimum transverse momentum for extremal partons
     double min_extparton_pt;
     //! Maximum transverse momentum fraction from soft radiation in extremal jets
     double max_ext_soft_pt_fraction;
     //! The regulator lambda for the subtraction terms
     double regulator_lambda = CLAMBDA;
     //! Number of resummation configurations to generate per fixed-order event
     int trials;
     //! Whether to include the logarithmic correction from \f$\alpha_s\f$ running
     bool log_correction;
     //! Event output files names and formats
     std::vector<OutputFile> output;
     //! Parameters for random number generation
     RNGConfig rng;
     //! Map to decide what to do for different event types
     EventTreatMap treat;
     //! Parameters for custom analyses
     YAML::Node analysis_parameters;
     //! Settings for effective Higgs-gluon coupling
     HiggsCouplingSettings Higgs_coupling;
   };
 
   //! Configuration options for the PhaseSpacePoint class
   struct PhaseSpacePointConfig {
     //! Properties of resummation jets
     JetParameters jet_param;
     //! Minimum transverse momentum for extremal partons
     double min_extparton_pt;
     //! Maximum transverse momentum fraction from soft radiation in extremal jets
     double max_ext_soft_pt_fraction;
   };
 
   //! Configuration options for the MatrixElement class
   struct MatrixElementConfig {
     MatrixElementConfig() = default;
     MatrixElementConfig(
         bool log_correction,
         HiggsCouplingSettings Higgs_coupling,
         double regulator_lambda = CLAMBDA
     ):
     log_correction(log_correction),
     Higgs_coupling(Higgs_coupling),
     regulator_lambda(regulator_lambda)
     {}
 
     //! Whether to include the logarithmic correction from \f$\alpha_s\f$ running
     bool log_correction;
     //! Settings for effective Higgs-gluon coupling
     HiggsCouplingSettings Higgs_coupling;
     //! The regulator lambda for the subtraction terms
     double regulator_lambda = CLAMBDA;
   };
 
   //! Configuration options for the EventReweighter class
   struct EventReweighterConfig {
     //! Settings for phase space point generation
     PhaseSpacePointConfig psp_config;
     //! Settings for matrix element calculation
     MatrixElementConfig ME_config;
     //! Properties of resummation jets
     JetParameters jet_param;
     //! Treatment of the various event types
     EventTreatMap treat;
   };
 
   /**! Extract PhaseSpacePointConfig from Config
    *
    * \internal We do not provide a PhaseSpacePointConfig constructor from Config
    * so that PhaseSpacePointConfig remains an aggregate.
    * This faciliates writing client code (e.g. the HEJ fixed-order generator)
    * that creates a PhaseSpacePointConfig *without* a Config object.
    *
    * @see to_MatrixElementConfig, to_EventReweighterConfig
    */
   inline
   PhaseSpacePointConfig to_PhaseSpacePointConfig(Config const & conf) {
     return {
       conf.resummation_jets,
       conf.min_extparton_pt,
       conf.max_ext_soft_pt_fraction
     };
   }
 
   /**! Extract MatrixElementConfig from Config
    *
    * @see to_PhaseSpacePointConfig, to_EventReweighterConfig
    */
   inline
   MatrixElementConfig to_MatrixElementConfig(Config const & conf) {
     return {conf.log_correction, conf.Higgs_coupling, conf.regulator_lambda};
   }
 
   /**! Extract EventReweighterConfig from Config
    *
    * @see to_PhaseSpacePointConfig, to_MatrixElementConfig
    */
   inline
   EventReweighterConfig to_EventReweighterConfig(Config const & conf) {
     return {
       to_PhaseSpacePointConfig(conf),
       to_MatrixElementConfig(conf),
       conf.resummation_jets, conf.treat
     };
   }
 
 } // namespace HEJ
diff --git a/include/HEJ/currents.hh b/include/HEJ/currents.hh
index fb2ffaf..349518b 100644
--- a/include/HEJ/currents.hh
+++ b/include/HEJ/currents.hh
@@ -1,1333 +1,1319 @@
-//////////////////////////////////////////////////
-//////////////////////////////////////////////////
-// This source code is Copyright (2012) of      //
-//  Jeppe R. Andersen and Jennifer M. Smillie   //
-// and is distributed under the                 //
-// Gnu Public License version 2                 //
-// http://www.gnu.org/licenses/gpl-2.0.html     //
-// You are allowed to distribute and alter the  //
-// source under the conditions of the GPLv2     //
-// as long as this copyright notice             //
-// is unaltered and distributed with the source //
-// Any use should comply with the               //
-//             MCNET GUIDELINES                 //
-//    for Event Generator Authors and Users     //
-// as distributed with this source code         //
-//////////////////////////////////////////////////
-//////////////////////////////////////////////////
-
+/**
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
+ *  \date      2019
+ *  \copyright GPLv2 or later
+ */
 /** \file
  *  \brief Functions computing the square of current contractions.
  *
  *  This file contains all the necessary functions to compute the current
  *  contractions for all valid HEJ processes. PJETS, H+JETS and W+JETS along with
  *  some unordered counterparts.
  *
  *  @TODO add a namespace
  */
-
 #pragma once
 
 #include <complex>
 #include <vector>
 #include <valarray>
 #include <limits>
 
 #include <ostream>
 
 #include <CLHEP/Vector/LorentzVector.h>
 
 typedef std::complex<double> COM;
 typedef COM current[4];
 typedef CLHEP::HepLorentzVector HLV;
 
 //! Square of qQ->qenuQ W+Jets Scattering Current
 /**
  *  @param p1out                Momentum of final state quark
  *  @param pe                   Momentum of final state electron
  *  @param pnu                  Momentum of final state Neutrino
  *  @param p1in                 Momentum of initial state quark
  *  @param p2out                Momentum of final state quark
  *  @param p2in                 Momentum of intial state quark
  *  @returns                    Square of the current contractions for qQ->qenuQ Scattering
  *
  *  This returns the square of the current contractions in qQ->qenuQ scattering
  *  with an emission of a W Boson.
  */
 double jMWqQ (CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector pe,
               CLHEP::HepLorentzVector pnu,   CLHEP::HepLorentzVector p1in,
               CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in);
 
 
 //! Square of qbarQ->qbarenuQ W+Jets Scattering Current
 /**
  *  @param p1out                Momentum of final state anti-quark
  *  @param pe                   Momentum of final state electron
  *  @param pnu                  Momentum of final state Neutrino
  *  @param p1in                 Momentum of initial state anti-quark
  *  @param p2out                Momentum of final state quark
  *  @param p2in                 Momentum of intial state quark
  *  @returns                    Square of the current contractions for qbarQ->qbarenuQ Scattering
  *
  *  This returns the square of the current contractions in qbarQ->qbarenuQ scattering
  *  with an emission of a W Boson.
  */
 double jMWqbarQ (CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector pe,
                  CLHEP::HepLorentzVector pnu, CLHEP::HepLorentzVector p1in,
                  CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in);
 
 
 //! Square of qQbar->qenuQbar W+Jets Scattering Current
 /**
  *  @param p1out                Momentum of final state quark
  *  @param pe                   Momentum of final state electron
  *  @param pnu                  Momentum of final state Neutrino
  *  @param p1in                 Momentum of initial state quark
  *  @param p2out                Momentum of final state anti-quark
  *  @param p2in                 Momentum of intial state anti-quark
  *  @returns                    Square of the current contractions for qQbar->qenuQbar Scattering
  *
  *  This returns the square of the current contractions in qQbar->qenuQbar scattering
  *  with an emission of a W Boson.
  */
 double jMWqQbar (CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector pe,
                  CLHEP::HepLorentzVector pnu, CLHEP::HepLorentzVector p1in,
                  CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in);
 
 
 //! Square of qbarQbar->qbarenuQbar W+Jets Scattering Current
 /**
  *  @param p1out                Momentum of final state anti-quark
  *  @param pe                   Momentum of final state electron
  *  @param pnu                  Momentum of final state Neutrino
  *  @param p1in                 Momentum of initial state anti-quark
  *  @param p2out                Momentum of final state anti-quark
  *  @param p2in                 Momentum of intial state anti-quark
  *  @returns                    Square of the current contractions for qbarQbar->qbarenuQbar Scattering
  *
  *  This returns the square of the current contractions in qbarQbar->qbarenuQbar scattering
  *  with an emission of a W Boson.
  */
 double jMWqbarQbar (CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector pe,
                     CLHEP::HepLorentzVector pnu, CLHEP::HepLorentzVector p1in,
                     CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in);
 
 //! Square of qg->qenug W+Jets Scattering Current
 /**
  *  @param p1out                Momentum of final state quark
  *  @param pe                   Momentum of final state electron
  *  @param pnu                  Momentum of final state Neutrino
  *  @param p1in                 Momentum of initial state quark
  *  @param p2out                Momentum of final state gluon
  *  @param p2in                 Momentum of intial state gluon
  *  @returns                    Square of the current contractions for qg->qenug Scattering
  *
  *  This returns the square of the current contractions in qg->qenug scattering
  *  with an emission of a W Boson.
  */
 double jMWqg (CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector pe,
               CLHEP::HepLorentzVector pnu, CLHEP::HepLorentzVector p1in,
               CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in);
 
 //! Square of qbarg->qbarenug W+Jets Scattering Current
 /**
  *  @param p1out                Momentum of final state anti-quark
  *  @param pe                   Momentum of final state electron
  *  @param pnu                  Momentum of final state Neutrino
  *  @param p1in                 Momentum of initial state anti-quark
  *  @param p2out                Momentum of final state gluon
  *  @param p2in                 Momentum of intial state gluon
  *  @returns                    Square of the current contractions for qbarg->qbarenug Scattering
  *
  *  This returns the square of the current contractions in qbarg->qbarenug scattering
  *  with an emission of a W Boson.
  */
 double jMWqbarg (CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector pe,
                  CLHEP::HepLorentzVector pnu, CLHEP::HepLorentzVector p1in,
                  CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in);
 
 
 // W+Jets Unordered Functions
 
 //! qQg Wjets Unordered backwards opposite leg to W
 /**
  *  @param p1out                Momentum of final state quark a
  *  @param pe                   Momentum of final state electron
  *  @param pnu                  Momentum of final state Neutrino
  *  @param p1in                 Momentum of initial state quark a
  *  @param p2out                Momentum of final state quark b
  *  @param p2in                 Momentum of intial state quark b
  *  @param pg                   Momentum of final state unordered gluon
  *  @returns                    Square of the current contractions for qQ->qQg Scattering
  *
  *  This returns the square of the current contractions in qQg->qQg scattering
  *  with an emission of a W Boson.
  */
 double junobMWqQg (CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector pe, CLHEP::HepLorentzVector pnu, CLHEP::HepLorentzVector p1in, CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in, CLHEP::HepLorentzVector pg);
 
 //! qbarQg Wjets Unordered backwards opposite leg to W
 /**
  *  @param p1out                Momentum of final state anti-quark a
  *  @param pe                   Momentum of final state electron
  *  @param pnu                  Momentum of final state Neutrino
  *  @param p1in                 Momentum of initial state anti-quark a
  *  @param p2out                Momentum of final state quark b
  *  @param p2in                 Momentum of intial state quark b
  *  @param pg                   Momentum of final state unordered gluon
  *  @returns                    Square of the current contractions for qbarQ->qbarQg Scattering
  *
  *  This returns the square of the current contractions in qbarQg->qbarQg scattering
  *  with an emission of a W Boson.
  */
 double junobMWqbarQg (CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector pe, CLHEP::HepLorentzVector pnu, CLHEP::HepLorentzVector p1in, CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in, CLHEP::HepLorentzVector pg);
 
 
 
 //! qQbarg Wjets Unordered backwards opposite leg to W
 /**
  *  @param p1out                Momentum of final state quark a
  *  @param pe                   Momentum of final state electron
  *  @param pnu                  Momentum of final state Neutrino
  *  @param p1in                 Momentum of initial state quark a
  *  @param p2out                Momentum of final state anti-quark b
  *  @param p2in                 Momentum of intial state anti-quark b
  *  @param pg                   Momentum of final state unordered gluon
  *  @returns                    Square of the current contractions for qQbar->qQbarg Scattering
  *
  *  This returns the square of the current contractions in qQbarg->qQbarg scattering
  *  with an emission of a W Boson.
  */
 double junobMWqQbarg (CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector pe, CLHEP::HepLorentzVector pnu, CLHEP::HepLorentzVector p1in, CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in, CLHEP::HepLorentzVector pg);
 
 //! qbarQbarg Wjets Unordered backwards opposite leg to W
 /**
  *  @param p1out                Momentum of final state anti-quark a
  *  @param pe                   Momentum of final state electron
  *  @param pnu                  Momentum of final state Neutrino
  *  @param p1in                 Momentum of initial state anti-quark a
  *  @param p2out                Momentum of final state anti-quark b
  *  @param p2in                 Momentum of intial state anti-quark b
  *  @param pg                   Momentum of final state unordered gluon
  *  @returns                    Square of the current contractions for qbarQbar->qbarQbarg Scattering
  *
  *  This returns the square of the current contractions in qbarQbarg->qbarQbarg scattering
  *  with an emission of a W Boson.
  */
 double junobMWqbarQbarg (CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector pe, CLHEP::HepLorentzVector pnu, CLHEP::HepLorentzVector p1in, CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in, CLHEP::HepLorentzVector pg);
 
 
 //!Wjets Unordered forwards opposite leg to W
 /**
  *  @param pg                   Momentum of final state unordered gluon
  *  @param p1out                Momentum of final state quark a
  *  @param pe                   Momentum of final state electron
  *  @param pnu                  Momentum of final state Neutrino
  *  @param p1in                 Momentum of initial state quark a
  *  @param p2out                Momentum of final state quark b
  *  @param p2in                 Momentum of intial state quark b
  *  @returns                    Square of the current contractions for qQ->gqQ Scattering
  *
  *  This returns the square of the current contractions in qQg->gqQ scattering
  *  with an emission of a W Boson.
  */
 double junofMWgqQ (CLHEP::HepLorentzVector pg,CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector p1in, CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector pe, CLHEP::HepLorentzVector pnu, CLHEP::HepLorentzVector p2in);
 
 //!Wjets Unordered forwards opposite leg to W
 /**
  *  @param pg                   Momentum of final state unordered gluon
  *  @param p1out                Momentum of final state anti-quark a
  *  @param pe                   Momentum of final state electron
  *  @param pnu                  Momentum of final state Neutrino
  *  @param p1in                 Momentum of initial state anti-quark a
  *  @param p2out                Momentum of final state quark b
  *  @param p2in                 Momentum of intial state quark b
  *  @returns                    Square of the current contractions for qbarQ->gqbarQ Scattering
  *
  *  This returns the square of the current contractions in qbarQg->gqbarQ scattering
  *  with an emission of a W Boson.
  */
 double junofMWgqbarQ (CLHEP::HepLorentzVector pg,CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector p1in, CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector pe, CLHEP::HepLorentzVector pnu, CLHEP::HepLorentzVector p2in);
 
 //!Wjets Unordered forwards opposite leg to W
 /**
  *  @param pg                   Momentum of final state unordered gluon
  *  @param p1out                Momentum of final state quark a
  *  @param pe                   Momentum of final state electron
  *  @param pnu                  Momentum of final state Neutrino
  *  @param p1in                 Momentum of initial state quark a
  *  @param p2out                Momentum of final state anti-quark b
  *  @param p2in                 Momentum of intial state anti-quark b
  *  @returns                    Square of the current contractions for qQbar->gqQbar Scattering
  *
  *  This returns the square of the current contractions in qQbarg->gqQbar scattering
  *  with an emission of a W Boson.
  */
 double junofMWgqQbar (CLHEP::HepLorentzVector pg,CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector p1in, CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector pe, CLHEP::HepLorentzVector pnu, CLHEP::HepLorentzVector p2in);
 
 //!Wjets Unordered forwards opposite leg to W
 /**
  *  @param pg                   Momentum of final state unordered gluon
  *  @param p1out                Momentum of final state anti-quark a
  *  @param pe                   Momentum of final state electron
  *  @param pnu                  Momentum of final state Neutrino
  *  @param p1in                 Momentum of initial state anti-quark a
  *  @param p2out                Momentum of final state anti-quark b
  *  @param p2in                 Momentum of intial state anti-quark b
  *  @returns                    Square of the current contractions for qbarQbar->gqbarQbar Scattering
  *
  *  This returns the square of the current contractions in qbarQbarg->gqbarQbar scattering
  *  with an emission of a W Boson.
  */
 double junofMWgqbarQbar (CLHEP::HepLorentzVector pg,CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector p1in, CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector pe, CLHEP::HepLorentzVector pnu, CLHEP::HepLorentzVector p2in);
 
 //!W+uno same leg
 /**
  *  @param pg                   Momentum of final state unordered gluon
  *  @param p1out                Momentum of final state quark a
  *  @param plbar                Momentum of final state anti-lepton
  *  @param pl                   Momentum of final state lepton
  *  @param p1in                 Momentum of initial state quark a
  *  @param p2out                Momentum of final state quark b
  *  @param p2in                 Momentum of intial state quark b
  *  @returns                    Square of the current contractions for qQ->qQg Scattering
  *
  *  This returns the square of the current contractions in gqQ->gqQ scattering
  *  with an emission of a W Boson.
  */
 double jM2WunogqQ(CLHEP::HepLorentzVector pg, CLHEP::HepLorentzVector p1out,CLHEP::HepLorentzVector plbar,CLHEP::HepLorentzVector pl, CLHEP::HepLorentzVector p1in, CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in);
 
 //! @TODO What does this function do? Crossed contribution is Exqqx..?
 /**
  *  @param pg                   Momentum of final state unordered gluon
  *  @param p1out                Momentum of final state quark a
  *  @param plbar                Momentum of final state anti-lepton
  *  @param pl                   Momentum of final state lepton
  *  @param p1in                 Momentum of initial state quark a
  *  @param p2out                Momentum of final state quark b
  *  @param p2in                 Momentum of intial state quark b
  *  @returns                    Square of the current contractions for qQ->gqQ Scattering
  *
  *  This returns the square of the current contractions in gqQ->gqQ scattering
  *  with an emission of a W Boson.
  */
 double jM2WunogqQ_crossqQ(CLHEP::HepLorentzVector pg, CLHEP::HepLorentzVector p1out,CLHEP::HepLorentzVector plbar,CLHEP::HepLorentzVector pl, CLHEP::HepLorentzVector p1in, CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in);
 
 //! W+uno same leg. quark anti-quark
 /**
  *  @param pg                   Momentum of final state unordered gluon
  *  @param p1out                Momentum of final state quark a
  *  @param plbar                Momentum of final state anti-lepton
  *  @param pl                   Momentum of final state lepton
  *  @param p1in                 Momentum of initial state quark a
  *  @param p2out                Momentum of final state anti-quark b
  *  @param p2in                 Momentum of intial state anti-quark b
  *  @returns                    Square of the current contractions for qQbar->gqQbar Scattering
  *
  *  This returns the square of the current contractions in gqQbar->gqQbar scattering
  *  with an emission of a W Boson. (Unordered Same Leg)
  */
 double jM2WunogqQbar(CLHEP::HepLorentzVector pg, CLHEP::HepLorentzVector p1out,CLHEP::HepLorentzVector plbar,CLHEP::HepLorentzVector pl, CLHEP::HepLorentzVector p1in, CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in);
 
 //! W+uno same leg. quark gluon
 /**
  *  @param pg                   Momentum of final state unordered gluon
  *  @param p1out                Momentum of final state quark a
  *  @param plbar                Momentum of final state anti-lepton
  *  @param pl                   Momentum of final state lepton
  *  @param p1in                 Momentum of initial state quark a
  *  @param p2out                Momentum of final state gluon b
  *  @param p2in                 Momentum of intial state gluon b
  *  @returns                    Square of the current contractions for qg->gqg Scattering
  *
  *  This returns the square of the current contractions in qg->gqg scattering
  *  with an emission of a W Boson.
  */
 double jM2Wunogqg(CLHEP::HepLorentzVector pg, CLHEP::HepLorentzVector p1out,CLHEP::HepLorentzVector plbar,CLHEP::HepLorentzVector pl, CLHEP::HepLorentzVector p1in, CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in);
 
 //! W+uno same leg. anti-quark quark
 /**
  *  @param pg                   Momentum of final state unordered gluon
  *  @param p1out                Momentum of final state anti-quark a
  *  @param plbar                Momentum of final state anti-lepton
  *  @param pl                   Momentum of final state lepton
  *  @param p1in                 Momentum of initial state anti-quark a
  *  @param p2out                Momentum of final state quark b
  *  @param p2in                 Momentum of intial state quark b
  *  @returns                    Square of the current contractions for qbarQ->gqbarQ Scattering
  *
  *  This returns the square of the current contractions in qbarQ->gqbarQ scattering
  *  with an emission of a W Boson.
  */
 double jM2WunogqbarQ(CLHEP::HepLorentzVector pg, CLHEP::HepLorentzVector p1out,CLHEP::HepLorentzVector plbar,CLHEP::HepLorentzVector pl, CLHEP::HepLorentzVector p1in, CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in);
 
 //! W+uno same leg. anti-quark anti-quark
 /**
  *  @param pg                   Momentum of final state unordered gluon
  *  @param p1out                Momentum of final state anti-quark a
  *  @param plbar                Momentum of final state anti-lepton
  *  @param pl                   Momentum of final state lepton
  *  @param p1in                 Momentum of initial state anti-quark a
  *  @param p2out                Momentum of final state anti-quark b
  *  @param p2in                 Momentum of intial state anti-quark b
  *  @returns                    Square of the current contractions for qbarQbar->gqbarQbar Scattering
  *
  *  This returns the square of the current contractions in gqbarQbar->qbarQbar scattering
  *  with an emission of a W Boson.
  */
 double jM2WunogqbarQbar(CLHEP::HepLorentzVector pg, CLHEP::HepLorentzVector p1out,CLHEP::HepLorentzVector plbar,CLHEP::HepLorentzVector pl, CLHEP::HepLorentzVector p1in, CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in);
 
 //! W+uno same leg. anti-quark gluon
 /**
  *  @param pg                   Momentum of final state unordered gluon
  *  @param p1out                Momentum of final state anti-quark a
  *  @param plbar                Momentum of final state anti-lepton
  *  @param pl                   Momentum of final state lepton
  *  @param p1in                 Momentum of initial state anti-quark a
  *  @param p2out                Momentum of final state gluon b
  *  @param p2in                 Momentum of intial state gluon b
  *  @returns                    Square of the current contractions for ->gqbarg Scattering
  *
  *  This returns the square of the current contractions in qbarg->gqbarg scattering
  *  with an emission of a W Boson.
  */
 double jM2Wunogqbarg(CLHEP::HepLorentzVector pg, CLHEP::HepLorentzVector p1out,CLHEP::HepLorentzVector plbar,CLHEP::HepLorentzVector pl, CLHEP::HepLorentzVector p1in, CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in);
 
 //W+Jets qqxExtremal
 //! W+Extremal qqx. qxqQ
 /**
  *  @param pgin                 Momentum of initial state gluon
  *  @param pqout                Momentum of final state quark a
  *  @param plbar                Momentum of final state anti-lepton
  *  @param pl                   Momentum of final state lepton
  *  @param pqbarout             Momentum of final state anti-quark a
  *  @param p2out                Momentum of initial state anti-quark b
  *  @param p2in                 Momentum of final state gluon b
  *  @returns                    Square of the current contractions for ->qbarqQ Scattering
  *
  *  Calculates the square of the current contractions with extremal qqbar pair
  *  production. This is calculated through the use of crossing symmetry.
  */
 double jM2WgQtoqbarqQ(CLHEP::HepLorentzVector pgin, CLHEP::HepLorentzVector pqout,CLHEP::HepLorentzVector plbar,CLHEP::HepLorentzVector pl, CLHEP::HepLorentzVector pqbarout, CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in);
 
 //W+Jets qqxExtremal
 //! W+Extremal qqx. qqxQ
 /**
  *  @param pgin                 Momentum of initial state gluon
  *  @param pqout                Momentum of final state quark a
  *  @param plbar                Momentum of final state anti-lepton
  *  @param pl                   Momentum of final state lepton
  *  @param pqbarout             Momentum of final state anti-quark a
  *  @param p2out                Momentum of initial state anti-quark b
  *  @param p2in                 Momentum of final state gluon b
  *  @returns                    Square of the current contractions for ->qqbarQ Scattering
  *
  *  Calculates the square of the current contractions with extremal qqbar pair
  *  production. This is calculated through the use of crossing symmetry.
  */
 double jM2WgQtoqqbarQ(CLHEP::HepLorentzVector pgin, CLHEP::HepLorentzVector pqout,CLHEP::HepLorentzVector plbar,CLHEP::HepLorentzVector pl, CLHEP::HepLorentzVector pqbarout, CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in);
 
 //W+Jets qqxExtremal
 //! W+Extremal qqx. gg->qxqg
 /**
  *  @param pgin                 Momentum of initial state gluon
  *  @param pqout                Momentum of final state quark a
  *  @param plbar                Momentum of final state anti-lepton
  *  @param pl                   Momentum of final state lepton
  *  @param pqbarout             Momentum of final state anti-quark a
  *  @param p2out                Momentum of initial state gluon b
  *  @param p2in                 Momentum of final state gluon b
  *  @returns                    Square of the current contractions for gg->qbarqg Scattering
  *
  *  Calculates the square of the current contractions with extremal qqbar pair
  *  production. This is calculated through the use of crossing symmetry.
  */
 double jM2Wggtoqbarqg(CLHEP::HepLorentzVector pgin, CLHEP::HepLorentzVector pqout,CLHEP::HepLorentzVector plbar,CLHEP::HepLorentzVector pl, CLHEP::HepLorentzVector pqbarout, CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in);
 
 //W+Jets qqxExtremal
 //! W+Extremal qqx. gg->qqxg
 /**
  *  @param pgin                 Momentum of initial state gluon
  *  @param pqout                Momentum of final state quark a
  *  @param plbar                Momentum of final state anti-lepton
  *  @param pl                   Momentum of final state lepton
  *  @param pqbarout             Momentum of final state anti-quark a
  *  @param p2out                Momentum of initial state gluon a
  *  @param p2in                 Momentum of final state gluon b
  *  @returns                    Square of the current contractions for gg->qqbarg Scattering
  *
  *  Calculates the square of the current contractions with extremal qqbar pair
  *  production. This is calculated through the use of crossing symmetry.
  */
 double jM2Wggtoqqbarg(CLHEP::HepLorentzVector pgin, CLHEP::HepLorentzVector pqbarout,CLHEP::HepLorentzVector plbar,CLHEP::HepLorentzVector pl, CLHEP::HepLorentzVector pqout, CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in);
 
 //W+Jets qqxExtremal, W emission from opposite leg
 //! W+Extremal qqx. gg->qqxg. qqx on forwards leg, W emission backwards leg.
 /**
  *  @param pa                   Momentum of initial state (anti-)quark
  *  @param pb                   Momentum of initial state gluon
  *  @param p1                   Momentum of final state (anti-)quark (after W emission)
  *  @param p2                   Momentum of final state anti-quark
  *  @param p3                   Momentum of final state quark
  *  @param plbar                Momentum of final state anti-lepton
  *  @param pl                   Momentum of final state lepton
  *  @param aqlinepa             Is opposite extremal leg to qqx a quark or antiquark line
  *  @returns                    Square of the current contractions for gq->qqbarqW Scattering
  *
  *  Calculates the square of the current contractions with extremal qqbar pair
  *  production. This is calculated via current contraction of existing currents.
  *  Assumes qqx split from forwards leg, W emission from backwards leg.
  *  Switch input (pa<->pb, p1<->pn) if calculating forwards qqx.
  */
 double jM2WgqtoQQqW(CLHEP::HepLorentzVector pa, CLHEP::HepLorentzVector pb, CLHEP::HepLorentzVector p1,  CLHEP::HepLorentzVector p2, CLHEP::HepLorentzVector p3,CLHEP::HepLorentzVector plbar,CLHEP::HepLorentzVector pl, bool aqlinepa);
 
 //! W+Jets qqxCentral. qqx W emission.
 /**
  *  @param pa                Momentum of initial state particle a
  *  @param pb                Momentum of initial state particle b
  *  @param pl                Momentum of final state lepton
  *  @param plbar             Momentum of final state anti-lepton
  *  @param partons           Vector of outgoing parton momenta
  *  @param aqlinepa          Bool: True= pa is anti-quark
  *  @param aqlinepb          Bool: True= pb is anti-quark
  *  @param qqxmarker         Bool: Ordering of the qqbar pair produced (qqx vs qxq)
  *  @param nabove            Number of lipatov vertices "above" qqbar pair
  *  @param nbelow            Number of lipatov vertices "below" qqbar pair
  *  @returns                 Square of the current contractions for qq>qQQbarWq Scattering
  *
  *  Calculates the square of the current contractions with extremal qqbar pair
  *  production. This is calculated through the use of crossing symmetry.
  */
 double jM2WqqtoqQQq(HLV pa, HLV pb,HLV pl,HLV plbar, std::vector<HLV> partons, bool aqlinepa, bool aqlinepb, bool qqxmarker, int nabove);
 //emission from backwards leg
 
 //! W+Jets qqxCentral. W emission from backwards leg.
 /**
  *  @param ka                HLV: Momentum of initial state particle a
  *  @param kb                HLV: Momentum of initial state particle b
  *  @param pl                HLV: Momentum of final state lepton
  *  @param plbar             HLV: Momentum of final state anti-lepton
  *  @param partons           Vector(HLV): outgoing parton momenta
  *  @param aqlinepa          Bool: True= pa is anti-quark
  *  @param aqlinepb          Bool: True= pb is anti-quark
  *  @param qqxmarker         Bool: Ordering of the qqbar pair produced (qqx vs qxq)
  *  @param nabove            Int: Number of lipatov vertices "above" qqbar pair
  *  @param nbelow            Int: Number of lipatov vertices "below" qqbar pair
  *  @param forwards          Bool: Swap to emission off front leg TODO:remove so args can be const
  *  @returns                 Square of the current contractions for qq>qQQbarWq Scattering
  *
  *  Calculates the square of the current contractions with extremal qqbar pair
  *  production. This is calculated through the use of crossing symmetry.
  */
 double jM2WqqtoqQQqW(HLV ka, HLV kb,HLV pl,HLV plbar, std::vector<HLV> partons, bool aqlinepa, bool aqlinepb, bool qqxmarker, int nabove, int nbelow, bool forwards); //Doing
 
 
 
 //! Square of qQ->qQ Pure Jets Scattering Current
 /**
  *  @param p1out                Momentum of final state quark
  *  @param p1in                 Momentum of initial state quark
  *  @param p2out                Momentum of final state quark
  *  @param p2in                 Momentum of intial state quark
  *  @returns                    Square of the current contractions for qQ->qQ Scattering
  *
  *  This returns the square of the current contractions in qQ->qQ Pure Jet Scattering.
  */
 double jM2qQ (CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector p1in,
               CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in);
 
 
 //! Square of qQbar->qQbar Pure Jets Scattering Current
 /**
  *  @param p1out                Momentum of final state quark
  *  @param p1in                 Momentum of initial state quark
  *  @param p2out                Momentum of final state anti-quark
  *  @param p2in                 Momentum of intial state anti-quark
  *  @returns                    Square of the current contractions for qQbar->qQbar Scattering
  *
  *  This returns the square of the current contractions in qQbar->qQbar Pure Jet Scattering.
  *  Note this can be used for qbarQ->qbarQ Scattering by inputting arguments appropriately.
  */
 double jM2qQbar (CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector p1in,
                  CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in);
 
 
 //! Square of qbarQbar->qbarQbar Pure Jets Scattering Current
 /**
  *  @param p1out                Momentum of final state anti-quark
  *  @param p1in                 Momentum of initial state anti-quark
  *  @param p2out                Momentum of final state anti-quark
  *  @param p2in                 Momentum of intial state anti-quark
  *  @returns                    Square of the current contractions for qbarQbar->qbarQbar Scattering
  *
  *  This returns the square of the current contractions in qbarQbar->qbarQbar Pure Jet Scattering.
  */
 double jM2qbarQbar (CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector p1in,
                     CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in);
 
 
 //! Square of qg->qg Pure Jets Scattering Current
 /**
  *  @param p1out                Momentum of final state quark
  *  @param p1in                 Momentum of initial state quark
  *  @param p2out                Momentum of final state gluon
  *  @param p2in                 Momentum of intial state gluon
  *  @returns                    Square of the current contractions for qg->qg Scattering
  *
  *  This returns the square of the current contractions in qg->qg Pure Jet Scattering.
  *  Note this can be used for gq->gq Scattering by inputting arguments appropriately.
  */
 double jM2qg (CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector p1in,
               CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in);
 
 
 //! Square of qbarg->qbarg Pure Jets Scattering Current
 /**
  *  @param p1out                Momentum of final state anti-quark
  *  @param p1in                 Momentum of initial state anti-quark
  *  @param p2out                Momentum of final state gluon
  *  @param p2in                 Momentum of intial state gluon
  *  @returns                    Square of the current contractions for qbarg->qbarg Scattering
  *
  *  This returns the square of the current contractions in qbarg->qbarg Pure Jet Scattering.
  *  Note this can be used for gqbar->gqbar Scattering by inputting arguments appropriately.
  */
 double jM2qbarg (CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector p1in,
                  CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in);
 
 
 //! Square of gg->gg Pure Jets Scattering Current
 /**
  *  @param p1out                Momentum of final state gluon
  *  @param p1in                 Momentum of initial state gluon
  *  @param p2out                Momentum of final state gluon
  *  @param p2in                 Momentum of intial state gluon
  *  @returns                    Square of the current contractions for gg->gg Scattering
  *
  *  This returns the square of the current contractions in gg->gg Pure Jet Scattering.
  */
 double jM2gg (CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector p1in,
               CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in);
 
 
 //! Square of gg->gg Higgs+Jets Scattering Current
 /**
  *  @param p1out                Momentum of final state gluon
  *  @param p1in                 Momentum of initial state gluon
  *  @param p2out                Momentum of final state gluon
  *  @param p2in                 Momentum of intial state gluon
  *  @param q1                   Momentum of t-channel propagator before Higgs
  *  @param qH2                  Momentum of t-channel propagator after Higgs
  *  @param mt                   Top quark mass
  *  @param include_bottom       Specifies whether bottom corrections are included
  *  @param mb                   Bottom quark mass
  *  @returns                    Square of the current contractions for gg->gg Scattering
  *
  *  This returns the square of the current contractions in gg->gg Higgs+Jet Scattering.
  *
  *  g~p1 g~p2
  *  should be called with q1 meant to be contracted with p2 in first part of vertex
  *  (i.e. if g is backward, q1 is forward)
  */
 double MH2gg (CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector p1in,
               CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in,
               CLHEP::HepLorentzVector q1, CLHEP::HepLorentzVector qH2,
               double mt,
               bool include_bottom, double mb);
 
 //! Square of gq->gq Higgs+Jets Scattering Current with Higgs before Gluon
 /**
  *  @param p1out                Momentum of final state gluon
  *  @param p1in                 Momentum of initial state gluon
  *  @param p2out                Momentum of final state gluon
  *  @param p2in                 Momentum of intial state gluon
  *  @param pH                   Momentum of Higgs
  *  @param mt                   Top quark mass
  *  @param include_bottom       Specifies whether bottom corrections are included
  *  @param mb                   Bottom quark mass
  *  @returns                    Square of the current contraction
  *
  */
 double MH2gq_outsideH(CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector p1in,
                       CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in,
                       CLHEP::HepLorentzVector pH,
                       double mt,
                       bool include_bottom, double mb);
 
 
 //! Square of qg->qg Higgs+Jets Scattering Current
 /**
  *  @param p1out                Momentum of final state quark
  *  @param p1in                 Momentum of initial state quark
  *  @param p2out                Momentum of final state gluon
  *  @param p2in                 Momentum of intial state gluon
  *  @param q1                   Momentum of t-channel propagator before Higgs
  *  @param qH2                  Momentum of t-channel propagator after Higgs
  *  @param mt                   Top quark mass
  *  @param include_bottom       Specifies whether bottom corrections are included
  *  @param mb                   Bottom quark mass
  *  @returns                    Square of the current contractions for qg->qg Scattering
  *
  *  This returns the square of the current contractions in qg->qg Higgs+Jet Scattering.
  *
  *  q~p1 g~p2 (i.e. ALWAYS p1 for quark, p2 for gluon)
  *  should be called with q1 meant to be contracted with p2 in first part of vertex
  *  (i.e. if g is backward, q1 is forward)
  */
 double MH2qg (CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector p1in,
               CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in,
               CLHEP::HepLorentzVector q1, CLHEP::HepLorentzVector qH2,
               double mt,
               bool include_bottom, double mb);
 
 
 //! Square of qbarg->qbarg Higgs+Jets Scattering Current
 /**
  *  @param p1out                Momentum of final state anti-quark
  *  @param p1in                 Momentum of initial state anti-quark
  *  @param p2out                Momentum of final state gluon
  *  @param p2in                 Momentum of intial state gluon
  *  @param q1                   Momentum of t-channel propagator before Higgs
  *  @param qH2                  Momentum of t-channel propagator after Higgs
  *  @param mt                   Top quark mass
  *  @param include_bottom       Specifies whether bottom corrections are included
  *  @param mb                   Bottom quark mass
  *  @returns                    Square of the current contractions for qbarg->qbarg Scattering
  *
  *  This returns the square of the current contractions in qbarg->qbarg Higgs+Jet Scattering.
  *
  *  qbar~p1 g~p2 (i.e. ALWAYS p1 for anti-quark, p2 for gluon)
  *  should be called with q1 meant to be contracted with p2 in first part of vertex
  *  (i.e. if g is backward, q1 is forward)
  */
 double MH2qbarg (CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector p1in,
                  CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in,
                  CLHEP::HepLorentzVector q1, CLHEP::HepLorentzVector qH2,
                  double mt,
                  bool include_bottom, double mb);
 
 
 //! Square of qQ->qQ Higgs+Jets Scattering Current
 /**
  *  @param p1out                Momentum of final state quark
  *  @param p1in                 Momentum of initial state quark
  *  @param p2out                Momentum of final state quark
  *  @param p2in                 Momentum of intial state quark
  *  @param q1                   Momentum of t-channel propagator before Higgs
  *  @param qH2                  Momentum of t-channel propagator after Higgs
  *  @param mt                   Top quark mass
  *  @param include_bottom       Specifies whether bottom corrections are included
  *  @param mb                   Bottom quark mass
  *  @returns                    Square of the current contractions for qQ->qQ Scattering
  *
  *  This returns the square of the current contractions in qQ->qQ Higgs+Jet Scattering.
  *
  *  q~p1 Q~p2 (i.e. ALWAYS p1 for quark, p2 for quark)
  *  should be called with q1 meant to be contracted with p2 in first part of vertex
  *  (i.e. if Q is backward, q1 is forward)
  */
 double MH2qQ (CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector p1in,
               CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in,
               CLHEP::HepLorentzVector q1, CLHEP::HepLorentzVector qH2,
               double mt,
               bool include_bottom, double mb);
 
 //! Square of qQbar->qQbar Higgs+Jets Scattering Current
 /**
  *  @param p1out                Momentum of final state quark
  *  @param p1in                 Momentum of initial state quark
  *  @param p2out                Momentum of final state anti-quark
  *  @param p2in                 Momentum of intial state anti-quark
  *  @param q1                   Momentum of t-channel propagator before Higgs
  *  @param qH2                   Momentum of t-channel propagator after Higgs
  *  @param mt                   Top quark mass
  *  @param include_bottom       Specifies whether bottom corrections are included
  *  @param mb                   Bottom quark mass
  *  @returns                    Square of the current contractions for qQ->qQ Scattering
  *
  *  This returns the square of the current contractions in qQbar->qQbar Higgs+Jet Scattering.
  *
  *  q~p1 Qbar~p2 (i.e. ALWAYS p1 for quark, p2 for anti-quark)
  *  should be called with q1 meant to be contracted with p2 in first part of vertex
  *  (i.e. if Qbar is backward, q1 is forward)
  */
 double MH2qQbar (CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector p1in,
                  CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in,
                  CLHEP::HepLorentzVector q1, CLHEP::HepLorentzVector qH2,
                  double mt,
                  bool include_bottom, double mb);
 
 //! Square of qbarQ->qbarQ Higgs+Jets Scattering Current
 /**
  *  @param p1out                Momentum of final state anti-quark
  *  @param p1in                 Momentum of initial state anti-quark
  *  @param p2out                Momentum of final state quark
  *  @param p2in                 Momentum of intial state quark
  *  @param q1                   Momentum of t-channel propagator before Higgs
  *  @param qH2                  Momentum of t-channel propagator after Higgs
  *  @param mt                   Top quark mass
  *  @param include_bottom       Specifies whether bottom corrections are included
  *  @param mb                   Bottom quark mass
  *  @returns                    Square of the current contractions for qbarQ->qbarQ Scattering
  *
  *  This returns the square of the current contractions in qbarQ->qbarQ Higgs+Jet Scattering.
  *
  *  qbar~p1 Q~p2 (i.e. ALWAYS p1 for anti-quark, p2 for quark)
  *  should be called with q1 meant to be contracted with p2 in first part of vertex
  *  (i.e. if Q is backward, q1 is forward)
  */
 double MH2qbarQ (CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector p1in,
                  CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in,
                  CLHEP::HepLorentzVector q1, CLHEP::HepLorentzVector qH2,
                  double mt,
                  bool include_bottom, double mb);
 
 
 //! Square of qbarQbar->qbarQbar Higgs+Jets Scattering Current
 /**
  *  @param p1out                Momentum of final state anti-quark
  *  @param p1in                 Momentum of initial state anti-quark
  *  @param p2out                Momentum of final state anti-quark
  *  @param p2in                 Momentum of intial state anti-quark
  *  @param q1                   Momentum of t-channel propagator before Higgs
  *  @param qH2                  Momentum of t-channel propagator after Higgs
  *  @param mt                   Top quark mass
  *  @param include_bottom       Specifies whether bottom corrections are included
  *  @param mb                   Bottom quark mass
  *  @returns                    Square of the current contractions for qbarQbar->qbarQbar Scattering
  *
  *  This returns the square of the current contractions in qbarQbar->qbarQbar Higgs+Jet Scattering.
  *
  *  qbar~p1 Qbar~p2 (i.e. ALWAYS p1 for anti-quark, p2 for anti-quark)
  *  should be called with q1 meant to be contracted with p2 in first part of vertex
  *  (i.e. if Qbar is backward, q1 is forward)
  */
 double MH2qbarQbar (CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector p1in,
                     CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in,
                     CLHEP::HepLorentzVector q1, CLHEP::HepLorentzVector qH2,
                     double mt,
                     bool include_bottom, double mb);
 
 
 // Unordered f
 
 //! Square of qQ->gqQ Higgs+Jets Unordered f Scattering Current
 /**
  *  @param pg                   Momentum of unordered gluon
  *  @param p1out                Momentum of final state quark
  *  @param p1in                 Momentum of initial state quark
  *  @param p2out                Momentum of final state quark
  *  @param p2in                 Momentum of intial state quark
  *  @param qH1                  Momentum of t-channel propagator before Higgs
  *  @param qH2                  Momentum of t-channel propagator after Higgs
  *  @param mt                   Top quark mass
  *  @param include_bottom       Specifies whether bottom corrections are included
  *  @param mb                   Bottom quark mass
  *  @returns                    Square of the current contractions for qQ->gqQ Scattering
  *
  *  This returns the square of the current contractions in qQ->gqQ Higgs+Jet Scattering.
  *
  *  This construction is taking rapidity order: pg > p1out >> p2out
  */
 double jM2unogqHQ (CLHEP::HepLorentzVector pg, CLHEP::HepLorentzVector p1out,
                    CLHEP::HepLorentzVector p1in, CLHEP::HepLorentzVector p2out,
                    CLHEP::HepLorentzVector p2in, CLHEP::HepLorentzVector qH1,
                    CLHEP::HepLorentzVector qH2,
                    double mt,
                    bool include_bottom, double mb);
 
 
 //! Square of qQbar->gqQbar Higgs+Jets Unordered f Scattering Current
 /**
  *  @param pg                   Momentum of unordered gluon
  *  @param p1out                Momentum of final state quark
  *  @param p1in                 Momentum of initial state quark
  *  @param p2out                Momentum of final state anti-quark
  *  @param p2in                 Momentum of intial state anti-quark
  *  @param qH1                  Momentum of t-channel propagator before Higgs
  *  @param qH2                  Momentum of t-channel propagator after Higgs
  *  @param mt                   Top quark mass
  *  @param include_bottom       Specifies whether bottom corrections are included
  *  @param mb                   Bottom quark mass
  *  @returns                    Square of the current contractions for qQbar->gqQbar Scattering
  *
  *  This returns the square of the current contractions in qQbar->gqQbar Higgs+Jet Scattering.
  *
  *  This construction is taking rapidity order: pg > p1out >> p2out
  */
 double jM2unogqHQbar (CLHEP::HepLorentzVector pg, CLHEP::HepLorentzVector p1out,
                       CLHEP::HepLorentzVector p1in, CLHEP::HepLorentzVector p2out,
                       CLHEP::HepLorentzVector p2in, CLHEP::HepLorentzVector qH1,
                       CLHEP::HepLorentzVector qH2,
                       double mt,
                       bool include_bottom, double mb);
 
 //! Square of qbarQ->gqbarQ Higgs+Jets Unordered f Scattering Current
 /**
  *  @param pg                   Momentum of unordered gluon
  *  @param p1out                Momentum of final state anti-quark
  *  @param p1in                 Momentum of initial state anti-quark
  *  @param p2out                Momentum of final state quark
  *  @param p2in                 Momentum of intial state quark
  *  @param qH1                  Momentum of t-channel propagator before Higgs
  *  @param qH2                  Momentum of t-channel propagator after Higgs
  *  @param mt                   Top quark mass
  *  @param include_bottom       Specifies whether bottom corrections are included
  *  @param mb                   Bottom quark mass
  *  @returns                    Square of the current contractions for qbarQ->gqbarQ Scattering
  *
  *  This returns the square of the current contractions in qbarQ->gqbarQ Higgs+Jet Scattering.
  *
  *  This construction is taking rapidity order: pg > p1out >> p2out
  */
 double jM2unogqbarHQ (CLHEP::HepLorentzVector pg, CLHEP::HepLorentzVector p1out,
                       CLHEP::HepLorentzVector p1in, CLHEP::HepLorentzVector p2out,
                       CLHEP::HepLorentzVector p2in, CLHEP::HepLorentzVector qH1,
                       CLHEP::HepLorentzVector qH2,
                       double mt,
                       bool include_bottom, double mb);
 
 //! Square of qbarQbar->gqbarQbar Higgs+Jets Unordered f Scattering Current
 /**
  *  @param pg                   Momentum of unordered gluon
  *  @param p1out                Momentum of final state anti-quark
  *  @param p1in                 Momentum of initial state anti-quark
  *  @param p2out                Momentum of final state anti-quark
  *  @param p2in                 Momentum of intial state anti-quark
  *  @param qH1                  Momentum of t-channel propagator before Higgs
  *  @param qH2                  Momentum of t-channel propagator after Higgs
  *  @param mt                   Top quark mass
  *  @param include_bottom       Specifies whether bottom corrections are included
  *  @param mb                   Bottom quark mass
  *  @returns                    Square of the current contractions for qbarQbar->gqbarQbar Scattering
  *
  *  This returns the square of the current contractions in qbarQbar->gqbarQbar Higgs+Jet Scattering.
  *
  *  This construction is taking rapidity order: pg > p1out >> p2out
  */
 double jM2unogqbarHQbar (CLHEP::HepLorentzVector pg, CLHEP::HepLorentzVector p1out,
                          CLHEP::HepLorentzVector p1in, CLHEP::HepLorentzVector p2out,
                          CLHEP::HepLorentzVector p2in, CLHEP::HepLorentzVector qH1,
                          CLHEP::HepLorentzVector qH2,
                          double mt,
                          bool include_bottom, double mb);
 
 
 //! Square of qg->gqg Higgs+Jets Unordered f Scattering Current
 /**
  *  @param pg                   Momentum of unordered gluon
  *  @param p1out                Momentum of final state quark
  *  @param p1in                 Momentum of initial state quark
  *  @param p2out                Momentum of final state gluon
  *  @param p2in                 Momentum of intial state gluon
  *  @param qH1                  Momentum of t-channel propagator before Higgs
  *  @param qH2                  Momentum of t-channel propagator after Higgs
  *  @param mt                   Top quark mass
  *  @param include_bottom       Specifies whether bottom corrections are included
  *  @param mb                   Bottom quark mass
  *  @returns                    Square of the current contractions for qg->gqg Scattering
  *
  *  This returns the square of the current contractions in qg->gqg Higgs+Jet Scattering.
  *
  *  This construction is taking rapidity order: pg > p1out >> p2out
  */
 double jM2unogqHg (CLHEP::HepLorentzVector pg, CLHEP::HepLorentzVector p1out,
                    CLHEP::HepLorentzVector p1in, CLHEP::HepLorentzVector p2out,
                    CLHEP::HepLorentzVector p2in, CLHEP::HepLorentzVector qH1,
                    CLHEP::HepLorentzVector qH2,
                    double mt,
                    bool include_bottom, double mb);
 
 
 //! Square of qbarg->gqbarg Higgs+Jets Unordered f Scattering Current
 /**
  *  @param pg                   Momentum of unordered gluon
  *  @param p1out                Momentum of final state anti-quark
  *  @param p1in                 Momentum of initial state anti-quark
  *  @param p2out                Momentum of final state gluon
  *  @param p2in                 Momentum of intial state gluon
  *  @param qH1                  Momentum of t-channel propagator before Higgs
  *  @param qH2                  Momentum of t-channel propagator after Higgs
  *  @param mt                   Top quark mass
  *  @param include_bottom       Specifies whether bottom corrections are included
  *  @param mb                   Bottom quark mass
  *  @returns                    Square of the current contractions for qbarg->gbarg Scattering
  *
  *  This returns the square of the current contractions in qbarg->gqbarg Higgs+Jet Scattering.
  *
  *  This construction is taking rapidity order: pg > p1out >> p2out
  */
 double jM2unogqbarHg (CLHEP::HepLorentzVector pg, CLHEP::HepLorentzVector p1out,
                       CLHEP::HepLorentzVector p1in, CLHEP::HepLorentzVector p2out,
                       CLHEP::HepLorentzVector p2in, CLHEP::HepLorentzVector qH1,
                       CLHEP::HepLorentzVector qH2,
                       double mt,
                       bool include_bottom, double mb);
 
 
 //Unordered b
 
 //! Square of qbarQ->qbarQg Higgs+Jets Unordered b Scattering Current
 /**
  *  @param p1out                Momentum of final state anti-quark
  *  @param p1in                 Momentum of initial state anti-quark
  *  @param pg                   Momentum of unordered b gluon
  *  @param p2out                Momentum of final state quark
  *  @param p2in                 Momentum of intial state quark
  *  @param qH1                  Momentum of t-channel propagator before Higgs
  *  @param qH2                  Momentum of t-channel propagator after Higgs
  *  @param mt                   Top quark mass
  *  @param include_bottom       Specifies whether bottom corrections are included
  *  @param mb                   Bottom quark mass
  *  @returns                    Square of the current contractions for qbarQ->qbarQg Scattering
  *
  *  This returns the square of the current contractions in qbarQ->qbarQg Higgs+Jet Scattering.
  *
  *  This construction is taking rapidity order: p1out >> p2out > pg
  */
 double jM2unobqbarHQg (CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector p1in,
                        CLHEP::HepLorentzVector pg, CLHEP::HepLorentzVector p2out,
                        CLHEP::HepLorentzVector p2in, CLHEP::HepLorentzVector qH1,
                        CLHEP::HepLorentzVector qH2,
                        double mt,
                        bool include_bottom, double mb);
 
 
 //! Square of qQ->qQg Higgs+Jets Unordered b Scattering Current
 /**
  *  @param p1out                Momentum of final state quark
  *  @param p1in                 Momentum of initial state quark
  *  @param pg                   Momentum of unordered b gluon
  *  @param p2out                Momentum of final state quark
  *  @param p2in                 Momentum of intial state quark
  *  @param qH1                  Momentum of t-channel propagator before Higgs
  *  @param qH2                  Momentum of t-channel propagator after Higgs
  *  @param mt                   Top quark mass
  *  @param include_bottom       Specifies whether bottom corrections are included
  *  @param mb                   Bottom quark mass
  *  @returns                    Square of the current contractions for qQ->qQg Scattering
  *
  *  This returns the square of the current contractions in qQ->qQg Higgs+Jet Scattering.
  *
  *  This construction is taking rapidity order: p1out >> p2out > pg
  */
 double jM2unobqHQg (CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector p1in,
                     CLHEP::HepLorentzVector pg, CLHEP::HepLorentzVector p2out,
                     CLHEP::HepLorentzVector p2in, CLHEP::HepLorentzVector qH1,
                     CLHEP::HepLorentzVector qH2,
                     double mt,
                     bool include_bottom, double mb);
 
 
 //! Square of qQbar->qQbarg Higgs+Jets Unordered b Scattering Current
 /**
  *  @param p1out                Momentum of final state quark
  *  @param p1in                 Momentum of initial state quark
  *  @param pg                   Momentum of unordered b gluon
  *  @param p2out                Momentum of final state anti-quark
  *  @param p2in                 Momentum of intial state anti-quark
  *  @param qH1                  Momentum of t-channel propagator before Higgs
  *  @param qH2                  Momentum of t-channel propagator after Higgs
  *  @param mt                   Top quark mass
  *  @param include_bottom       Specifies whether bottom corrections are included
  *  @param mb                   Bottom quark mass
  *  @returns                    Square of the current contractions for qQbar->qQbarg Scattering
  *
  *  This returns the square of the current contractions in qQbar->qQbarg Higgs+Jet Scattering.
  *
  *  This construction is taking rapidity order: p1out >> p2out > pg
  */
 double jM2unobqHQbarg (CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector p1in,
                        CLHEP::HepLorentzVector pg, CLHEP::HepLorentzVector p2out,
                        CLHEP::HepLorentzVector p2in, CLHEP::HepLorentzVector qH1,
                        CLHEP::HepLorentzVector qH2,
                        double mt,
                        bool include_bottom, double mb);
 
 //! Square of qbarQbar->qbarQbarg Higgs+Jets Unordered b Scattering Current
 /**
  *  @param p1out                Momentum of final state anti-quark
  *  @param p1in                 Momentum of initial state anti-quark
  *  @param pg                   Momentum of unordered b gluon
  *  @param p2out                Momentum of final state anti-quark
  *  @param p2in                 Momentum of intial state anti-quark
  *  @param qH1                  Momentum of t-channel propagator before Higgs
  *  @param qH2                  Momentum of t-channel propagator after Higgs
  *  @param mt                   Top quark mass
  *  @param include_bottom       Specifies whether bottom corrections are included
  *  @param mb                   Bottom quark mass
  *  @returns                    Square of the current contractions for qbarQbar->qbarQbarg Scattering
  *
  *  This returns the square of the current contractions in qbarQbar->qbarQbarg Higgs+Jet Scattering.
  *
  *  This construction is taking rapidity order: p1out >> p2out > pg
  */
 double jM2unobqbarHQbarg (CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector p1in,
                           CLHEP::HepLorentzVector pg, CLHEP::HepLorentzVector p2out,
                           CLHEP::HepLorentzVector p2in, CLHEP::HepLorentzVector qH1,
                           CLHEP::HepLorentzVector qH2,
                           double mt,
                           bool include_bottom, double mb);
 
 
 //! Square of gQbar->gQbarg Higgs+Jets Unordered b Scattering Current
 /**
  *  @param p1out                Momentum of final state gluon
  *  @param p1in                 Momentum of initial state gluon
  *  @param pg                   Momentum of unordered b gluon
  *  @param p2out                Momentum of final state anti-quark
  *  @param p2in                 Momentum of intial state anti-quark
  *  @param qH1                  Momentum of t-channel propagator before Higgs
  *  @param qH2                  Momentum of t-channel propagator after Higgs
  *  @param mt                   Top quark mass
  *  @param include_bottom       Specifies whether bottom corrections are included
  *  @param mb                   Bottom quark mass
  *  @returns                    Square of the current contractions for gQbar->gQbarg Scattering
  *
  *  This returns the square of the current contractions in gQbar->gQbarg Higgs+Jet Scattering.
  *
  *  This construction is taking rapidity order: p1out >> p2out > pg
  */
 double jM2unobgHQbarg (CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector p1in,
                        CLHEP::HepLorentzVector pg, CLHEP::HepLorentzVector p2out,
                        CLHEP::HepLorentzVector p2in, CLHEP::HepLorentzVector qH1,
                        CLHEP::HepLorentzVector qH2,
                        double mt,
                        bool include_bottom, double mb);
 
 //! Square of gQ->gQg Higgs+Jets Unordered b Scattering Current
 /**
  *  @param p1out                Momentum of final state gluon
  *  @param p1in                 Momentum of initial state gluon
  *  @param pg                   Momentum of unordered b gluon
  *  @param p2out                Momentum of final state quark
  *  @param p2in                 Momentum of intial state quark
  *  @param qH1                  Momentum of t-channel propagator before Higgs
  *  @param qH2                  Momentum of t-channel propagator after Higgs
  *  @param mt                   Top quark mass
  *  @param include_bottom       Specifies whether bottom corrections are included
  *  @param mb                   Bottom quark mass
  *  @returns                    Square of the current contractions for gQ->gQg Scattering
  *
  *  This returns the square of the current contractions in gQ->gQg Higgs+Jet Scattering.
  *
  *  This construction is taking rapidity order: p1out >> p2out > pg
  */
 double jM2unobgHQg (CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector p1in,
                     CLHEP::HepLorentzVector pg, CLHEP::HepLorentzVector p2out,
                     CLHEP::HepLorentzVector p2in, CLHEP::HepLorentzVector qH1,
                     CLHEP::HepLorentzVector qH2,
                     double mt,
                     bool include_bottom, double mb);
 
 // impact factors for Higgs + jet
 
 
 //! Implements Eq. (4.22) in hep-ph/0301013 with modifications to incoming plus momenta
 /**
  * @param p2               Momentum of Particle 2
  * @param p1               Momentum of Particle 1
  * @param pH               Momentum of Higgs
  * @returns                Value of Eq. (4.22) in Hep-ph/0301013 with modifications
  *
  *  This gives the impact factor. First it determines first whether this is the case
  *  p1p\sim php>>p3p or the opposite
  */
 double C2gHgm(CLHEP::HepLorentzVector p2, CLHEP::HepLorentzVector p1,
               CLHEP::HepLorentzVector pH);
 
 
 //! Implements Eq. (4.23) in hep-ph/0301013 with modifications to incoming plus momenta
 /**
  * @param p2               Momentum of Particle 2
  * @param p1               Momentum of Particle 1
  * @param pH               Momentum of Higgs
  * @returns                Value of Eq. (4.23) in Hep-ph/0301013
  *
  *  This gives the impact factor. First it determines first whether this is the case
  *  p1p\sim php>>p3p or the opposite
  */
 double C2gHgp(CLHEP::HepLorentzVector p2, CLHEP::HepLorentzVector p1,
               CLHEP::HepLorentzVector pH);
 
 
 //! Implements Eq. (4.22) in hep-ph/0301013
 /**
  * @param p2               Momentum of Particle 2
  * @param p1               Momentum of Particle 1
  * @param pH               Momentum of Higgs
  * @returns                Value of Eq. (4.22) in Hep-ph/0301013
  *
  *  This gives the impact factor. First it determines first whether this is the case
  *  p1p\sim php>>p3p or the opposite
  */
 double C2qHqm(CLHEP::HepLorentzVector p2, CLHEP::HepLorentzVector p1,
               CLHEP::HepLorentzVector pH);
 
 /** \class CCurrent currents.hh "include/HEJ/currents.hh"
  *  \brief This is the a new class structure for currents.
  */
 class CCurrent
 {
 public:
     CCurrent(COM sc0, COM sc1, COM sc2, COM sc3)
     :c0(sc0),c1(sc1),c2(sc2),c3(sc3)
     {};
     CCurrent(const CLHEP::HepLorentzVector p)
     {
         c0=p.e();
         c1=p.px();
         c2=p.py();
         c3=p.pz();
     };
     CCurrent()
     {};
     CCurrent operator+(const CCurrent& other);
     CCurrent operator-(const CCurrent& other);
     CCurrent operator*(const double x);
     CCurrent operator*(const COM x);
     CCurrent operator/(const double x);
     CCurrent operator/(const COM x);
 
     friend std::ostream& operator<<(std::ostream& os, const CCurrent& cur);
     COM dot(CLHEP::HepLorentzVector p1);
     COM dot(CCurrent p1);
     COM c0,c1,c2,c3;
 private:
 };
 
 /* std::ostream& operator <<(std::ostream& os, const CCurrent& cur); */
 CCurrent operator * ( double x, CCurrent& m);
 CCurrent operator * ( COM x, CCurrent& m);
 CCurrent operator / ( double x, CCurrent& m);
 CCurrent operator / ( COM x, CCurrent& m);
 
 
 //! Current <outgoing state | mu | incoming state>
 /**
  * These functions are a mess. There are many more defined in the source file than declared in the
  * header - and the arguments are mislabelled in some cases. Need to investigate.
  */
 //! @TODO remove
 [[deprecated("Use joi instead")]]
 void j (CLHEP::HepLorentzVector pout, bool helout, CLHEP::HepLorentzVector pin, bool helin,current &cur);
 
 //! Current <incoming state | mu | outgoing state>
 /**
  * These functions are a mess. There are many more defined in the source file than declared in the
  * header - and the arguments are mislabelled in some cases. Need to investigate.
  */
 void jio(HLV pin, bool helin, HLV pout, bool helout, current &cur);
 
 //! Current <outgoing state | mu | outgoing state>
 /**
  * These functions are a mess. There are many more defined in the source file than declared in the
  * header - and the arguments are mislabelled in some cases. Need to investigate.
  */
 void joo(HLV pi, bool heli, HLV pj, bool helj, current &cur);
 
 //! Current <outgoing state | mu | incoming state>
 /**
  * These functions are a mess. There are many more defined in the source file than declared in the
  * header - and the arguments are mislabelled in some cases. Need to investigate.
  */
 void joi(HLV pout, bool helout, HLV pin, bool helin, current &cur);
 
 //! Current <outgoing state | mu | incoming state>
 /**
  * These functions are a mess. There are many more defined in the source file than declared in the
  * header - and the arguments are mislabelled in some cases. Need to investigate.
  */
 //! @TODO remove
 [[deprecated("Use joi instead")]]
 CCurrent j (CLHEP::HepLorentzVector pout, bool helout, CLHEP::HepLorentzVector pin, bool helin);
 
 //! Current <outgoing state | mu | incoming state>
 /**
  * These functions are a mess. There are many more defined in the source file than declared in the
  * header - and the arguments are mislabelled in some cases. Need to investigate.
  */
 CCurrent joi (CLHEP::HepLorentzVector pout, bool helout, CLHEP::HepLorentzVector pin, bool helin);
 
 //! Current <incoming state | mu | outgoing state>
 /**
  * These functions are a mess. There are many more defined in the source file than declared in the
  * header - and the arguments are mislabelled in some cases. Need to investigate.
  */
 CCurrent jio (CLHEP::HepLorentzVector pout, bool helout, CLHEP::HepLorentzVector pin, bool helin);
 
 //! Current <outgoing state | mu | outgoing state>
 /**
  * These functions are a mess. There are many more defined in the source file than declared in the
  * header - and the arguments are mislabelled in some cases. Need to investigate.
  */
 CCurrent joo (CLHEP::HepLorentzVector pout, bool helout, CLHEP::HepLorentzVector pin, bool helin);
 
 inline COM cdot(const current & j1, const current & j2)
 {
   return j1[0]*j2[0]-j1[1]*j2[1]-j1[2]*j2[2]-j1[3]*j2[3];
 }
 
 inline COM cdot(const HLV & p, const current & j1) {
   return j1[0]*p.e()-j1[1]*p.x()-j1[2]*p.y()-j1[3]*p.z();
 }
 
 inline void cmult(const COM & factor, const current & j1, current &cur)
 {
   cur[0]=factor*j1[0];
   cur[1]=factor*j1[1];
   cur[2]=factor*j1[2];
   cur[3]=factor*j1[3];
 }
 
 // WHY!?!
 inline void cadd(const current & j1, const current & j2, const current & j3,
           const current & j4, const current & j5, current &sum)
 {
   sum[0]=j1[0]+j2[0]+j3[0]+j4[0]+j5[0];
   sum[1]=j1[1]+j2[1]+j3[1]+j4[1]+j5[1];
   sum[2]=j1[2]+j2[2]+j3[2]+j4[2]+j5[2];
   sum[3]=j1[3]+j2[3]+j3[3]+j4[3]+j5[3];
 }
 
 inline void cadd(const current & j1, const current & j2, const current & j3,
           const current & j4, current &sum) {
   sum[0] = j1[0] + j2[0] + j3[0] + j4[0];
   sum[1] = j1[1] + j2[1] + j3[1] + j4[1];
   sum[2] = j1[2] + j2[2] + j3[2] + j4[2];
   sum[3] = j1[3] + j2[3] + j3[3] + j4[3];
 }
 
 inline void cadd(const current & j1, const current & j2, const current & j3,
          current &sum)
 {
   sum[0]=j1[0]+j2[0]+j3[0];
   sum[1]=j1[1]+j2[1]+j3[1];
   sum[2]=j1[2]+j2[2]+j3[2];
   sum[3]=j1[3]+j2[3]+j3[3];
 }
 
 inline void cadd(const current & j1, const current & j2, current &sum)
 {
   sum[0]=j1[0]+j2[0];
   sum[1]=j1[1]+j2[1];
   sum[2]=j1[2]+j2[2];
   sum[3]=j1[3]+j2[3];
 }
 
 inline double abs2(const COM & a)
 {
     return (a*conj(a)).real();
 }
 
 inline double vabs2(const CCurrent & cur)
 {
     return abs2(cur.c0)-abs2(cur.c1)-abs2(cur.c2)-abs2(cur.c3);
 }
 
 inline double vre(const CCurrent & a, const CCurrent & b)
 {
   return real(a.c0*conj(b.c0)-a.c1*conj(b.c1)-a.c2*conj(b.c2)-a.c3*conj(b.c3));
 }
 // @TODO: These are not currents and should be moved elsewhere.
 double K_g(double p1minus, double paminus);
 double K_g(CLHEP::HepLorentzVector const & pout, CLHEP::HepLorentzVector const & pin);
diff --git a/include/HEJ/event_types.hh b/include/HEJ/event_types.hh
index ac85fd6..783942d 100644
--- a/include/HEJ/event_types.hh
+++ b/include/HEJ/event_types.hh
@@ -1,82 +1,81 @@
 /** \file
  *  \brief Define different types of events.
  *
- *  \authors   Jeppe Andersen, Tuomas Hapola, Marian Heil, Andreas Maier, Jennifer Smillie
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
  *  \date      2019
  *  \copyright GPLv2 or later
  */
-
 #pragma once
 
 #include "HEJ/utility.hh"
 
 namespace HEJ{
 
   //! Namespace for event types
   namespace event_type{
     //! Possible event types
     enum EventType: size_t{
       FKL,                        /**< FKL-type event */
       unordered_backward,         /**< event with unordered backward emission */
       unordered_forward,          /**< event with unordered forward emission */
       extremal_qqxb,               /**< event with a backward extremal qqbar */
       extremal_qqxf,               /**< event with a forward extremal qqbar */
       central_qqx,                /**< event with a central qqbar */
       nonHEJ,                     /**< event configuration not covered by HEJ */
       no_2_jets,                  /**< event with less than two jets */
       bad_final_state,            /**< event with an unsupported final state */
       unob = unordered_backward,
       unof = unordered_forward,
       qqxexb = extremal_qqxb,
       qqxexf = extremal_qqxf,
       qqxmid = central_qqx,
       first_type = FKL,
       last_type = bad_final_state
     };
 
     //! Event type names
     /**
      * For example, names[FKL] is the string "FKL"
      */
     static constexpr auto names = make_array(
       "FKL",
       "unordered backward",
       "unordered forward",
       "extremal qqbar backward",
       "extremal qqbar forward",
       "central qqbar",
       "nonHEJ",
       "no 2 jets",
       "bad final state"
     );
 
     //! Returns True for a HEJ \ref event_type::EventType "EventType"
     inline
     bool is_HEJ(EventType type) {
       switch(type) {
       case FKL:
       case unordered_backward:
       case unordered_forward:
       case extremal_qqxb:
       case extremal_qqxf:
       case central_qqx:
         return true;
       default:
         return false;
       }
     }
 
     //! Returns True for an unordered \ref event_type::EventType "EventType"
     inline
     bool is_uno(EventType type) {
       return type == unordered_backward || type == unordered_forward;
     }
 
     inline
     bool is_qqx(EventType type) {
       return type == extremal_qqxb || type == extremal_qqxf || type == central_qqx;
     }
 
   }
 
 }
diff --git a/include/HEJ/exceptions.hh b/include/HEJ/exceptions.hh
index 8fdeabf..e5c2514 100644
--- a/include/HEJ/exceptions.hh
+++ b/include/HEJ/exceptions.hh
@@ -1,57 +1,57 @@
 /** \file
  *  \brief Custom exception classes
  *
- *  \authors   Jeppe Andersen, Tuomas Hapola, Marian Heil, Andreas Maier, Jennifer Smillie
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
  *  \date      2019
  *  \copyright GPLv2 or later
  */
 #pragma once
 
 #include <stdexcept>
 
 namespace HEJ{
   //! Exception indicating wrong option type
   /**
    * This exception is thrown if a configuration option has
    * the wrong type (e.g. 'trials' is not set to a number)
    */
   struct invalid_type: std::invalid_argument {
     explicit invalid_type(std::string const & what):
       std::invalid_argument{what} {};
     explicit invalid_type(char const * what):
       std::invalid_argument{what} {};
   };
 
   //! Exception indicating unknown option
   /**
    * This exception is thrown if an unknown configuration option
    * is set (e.g. the 'trials' setting is misspelt as 'trails')
    */
   struct unknown_option: std::invalid_argument {
     explicit unknown_option(std::string const & what):
       std::invalid_argument{what} {};
     explicit unknown_option(char const * what):
       std::invalid_argument{what} {};
   };
 
   //! Exception indicating missing option setting
   /**
    * This exception is thrown if a mandatory configuration option
    * (e.g. 'trials') is not set.
    */
   struct missing_option: std::logic_error {
     explicit missing_option(std::string const & what):
       std::logic_error{what} {};
     explicit missing_option(char const * what):
       std::logic_error{what} {};
   };
 
   //! Exception indicating functionality that has not been implemented yet
   struct not_implemented: std::logic_error {
     explicit not_implemented(std::string const & what):
       std::logic_error{what} {};
     explicit not_implemented(char const * what):
       std::logic_error{what} {};
   };
 
 }
diff --git a/include/HEJ/get_analysis.hh b/include/HEJ/get_analysis.hh
index 8572f44..11e92ec 100644
--- a/include/HEJ/get_analysis.hh
+++ b/include/HEJ/get_analysis.hh
@@ -1,32 +1,31 @@
 /** \file
  *  \brief Contains the get_analysis function
  *
- *  \authors   Jeppe Andersen, Tuomas Hapola, Marian Heil, Andreas Maier, Jennifer Smillie
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
  *  \date      2019
  *  \copyright GPLv2 or later
  */
-
 #pragma once
 
 #include <memory>
 
 #include "HEJ/Analysis.hh"
 
 namespace YAML{
   class Node;
 }
 
 namespace HEJ{
   //!  Load an analysis
   /**
    *  @param parameters    Analysis parameters
    *  @returns             A pointer to an Analysis instance
    *
    *  If parameters["plugin"] exists, an analysis (deriving from the
    *  \ref Analysis class) will be loaded from the library parameters["plugin"].
    *  Otherwise, if parameters["rivet"] exists, the corresponding RivetAnalysis
    *  will be loaded. If none of these parameters are specified, a pointer to
    *  the default EmptyAnalysis is returned.
    */
   std::unique_ptr<Analysis> get_analysis(YAML::Node const & parameters);
 }
diff --git a/include/HEJ/kinematics.hh b/include/HEJ/kinematics.hh
index 3a2b909..cb8aa9c 100644
--- a/include/HEJ/kinematics.hh
+++ b/include/HEJ/kinematics.hh
@@ -1,25 +1,25 @@
 /** \file
  *  \brief Contains function to compute the incoming momentum from outgoing.
  *
- *  \authors   Jeppe Andersen, Tuomas Hapola, Marian Heil, Andreas Maier, Jennifer Smillie
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
  *  \date      2019
  *  \copyright GPLv2 or later
  */
 #pragma once
 
 #include <tuple>
 #include <vector>
 
 namespace fastjet{
   class PseudoJet;
 }
 
 namespace HEJ{
   class Particle;
 
   /** \brief Compute the incoming momentum from momentum conservation.
    */
   std::tuple<fastjet::PseudoJet, fastjet::PseudoJet> incoming_momenta(
             std::vector<Particle> const & outgoing    /**< Outgoing particles */
   );
 }
diff --git a/include/HEJ/make_RNG.hh b/include/HEJ/make_RNG.hh
index b6ee632..ec7c026 100644
--- a/include/HEJ/make_RNG.hh
+++ b/include/HEJ/make_RNG.hh
@@ -1,32 +1,32 @@
 /** \file
  *  \brief Declares a factory function for random number generators
  *
- *  \authors   Jeppe Andersen, Tuomas Hapola, Marian Heil, Andreas Maier, Jennifer Smillie
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
  *  \date      2019
  *  \copyright GPLv2 or later
  */
 #pragma once
 
 #include <memory>
 #include <string>
 
 #include "HEJ/optional.hh"
 #include "HEJ/RNG.hh"
 
 namespace HEJ {
   //! Factory function for random number generators
   /**
    *  @param name      Name of the random number generator
    *  @param seed      Optional seed
    *  @returns         A pointer to an instance of a random number generator
    *
    *  At present, name should be one of "ranlux64" or "mixmax" (case insensitive).
    *  The interpretation of the seed depends on the random number generator.
    *  For ranlux64, it is the name of a seed file. For mixmax it should be a
    *  string convertible to a long integer.
    */
   std::unique_ptr<HEJ::RNG> make_RNG(
       std::string const & name,
       optional<std::string> const & seed
   );
 }
diff --git a/include/HEJ/make_writer.hh b/include/HEJ/make_writer.hh
index 71a7de5..3511f5a 100644
--- a/include/HEJ/make_writer.hh
+++ b/include/HEJ/make_writer.hh
@@ -1,37 +1,36 @@
 /** \file
  *  \brief Declares a factory function for event writers
  *
- *  \authors   Jeppe Andersen, Tuomas Hapola, Marian Heil, Andreas Maier, Jennifer Smillie
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
  *  \date      2019
  *  \copyright GPLv2 or later
  */
-
 #pragma once
 
 #include <memory>
 #include <string>
 
 #include "HEJ/EventWriter.hh"
 #include "HEJ/output_formats.hh"
 
 namespace LHEF{
   struct HEPRUP;
 }
 
 namespace HEJ{
 
   //! Factory function for event writers
   /**
    *  @param format    The format of the output file
    *  @param outfile   The name of the output file
    *  @param heprup    General process information
    *  @returns         A pointer to an instance of an EventWriter
    *                   for the desired format
    */
   std::unique_ptr<EventWriter> make_format_writer(
       FileFormat format,
       std::string const &  outfile,
       LHEF::HEPRUP const & heprup
   );
 
 }
diff --git a/include/HEJ/optional.hh b/include/HEJ/optional.hh
index 32be64e..619cf7f 100644
--- a/include/HEJ/optional.hh
+++ b/include/HEJ/optional.hh
@@ -1,28 +1,28 @@
 /** \file
  *  \brief Defines the optional type
  *
  *  The C++14 standard introduces the std::optional type.
  *  If C++14 is not available, we use the optional type from boost instead.
  *
- *  \authors   Jeppe Andersen, Tuomas Hapola, Marian Heil, Andreas Maier, Jennifer Smillie
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
  *  \date      2019
  *  \copyright GPLv2 or later
  */
 #pragma once
 
 #if __cplusplus <= 201402L
 #include <boost/optional.hpp>
 #else
 #include <optional>
 #endif
 
 namespace HEJ{
 
 #if __cplusplus <= 201402L
   template<typename T>
   using optional = boost::optional<T>;
 #else
   template<typename T>
   using optional = std::optional<T>;
 #endif
 }
diff --git a/include/HEJ/output_formats.hh b/include/HEJ/output_formats.hh
index 5ee6e51..8d5b005 100644
--- a/include/HEJ/output_formats.hh
+++ b/include/HEJ/output_formats.hh
@@ -1,38 +1,37 @@
 /** \file
  *  \brief Defines formats for output to event files
  *
- *  \authors   Jeppe Andersen, Tuomas Hapola, Marian Heil, Andreas Maier, Jennifer Smillie
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
  *  \date      2019
  *  \copyright GPLv2 or later
  */
-
 #pragma once
 
 #include <stdexcept>
 #include <string>
 
 namespace HEJ{
 
   //! Supported event file formats
   enum FileFormat{
     Les_Houches, /*!< Les Houches Output */
     HepMC        /*!< HepMC Output */
   };
 
   //! Convert a file format to a string
   inline std::string to_string(FileFormat f){
     switch(f){
     case Les_Houches: return "Les Houches";
     case HepMC: return "HepMC";
     default:
       throw std::logic_error("unhandled file format");
     }
   }
 
   //! Output file specification
   struct OutputFile{
     std::string name;     /**< Output File Name */
     FileFormat format;    /**< Output File Format */
   };
 
 }
diff --git a/include/HEJ/resummation_jet.hh b/include/HEJ/resummation_jet.hh
index 409b5a2..f2f8c31 100644
--- a/include/HEJ/resummation_jet.hh
+++ b/include/HEJ/resummation_jet.hh
@@ -1,43 +1,43 @@
 /** \file
  *  \brief Functions to calculate the kinematics of resummation jets,
  *         i.e. resuffling the jet momenta
  *
- *  \authors   Jeppe Andersen, Tuomas Hapola, Marian Heil, Andreas Maier, Jennifer Smillie
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
  *  \date      2019
  *  \copyright GPLv2 or later
  */
 #pragma once
 
 #include <vector>
 
 namespace fastjet{
   struct PseudoJet;
 }
 
 namespace HEJ{
   /**
    * \brief Calculate the resummation jet momenta
    * @param p_born              born Jet Momenta
    * @param qperp               Sum of non-jet Parton Transverse Momenta
    * @returns                   Resummation Jet Momenta
    */
   std::vector<fastjet::PseudoJet> resummation_jet_momenta(
       std::vector<fastjet::PseudoJet> const & p_born,
       fastjet::PseudoJet const & qperp
   );
 
   /**
    * \brief Calculate additional weight from changing the jet momenta
    * @param p_born              born Jet Momenta
    * @param qperp               Sum of non-jet Parton Transverse Momenta
    *
    *  Computes the Jacobian for changing the original delta functions
    *  expressed in terms of jet momenta to delta functions of the
    *  parton momenta in the resummation phase space
    */
   double resummation_jet_weight(
       std::vector<fastjet::PseudoJet> const & p_born,
       fastjet::PseudoJet const & qperp
   );
 
 }
diff --git a/include/HEJ/stream.hh b/include/HEJ/stream.hh
index 55ab077..e8aa707 100644
--- a/include/HEJ/stream.hh
+++ b/include/HEJ/stream.hh
@@ -1,39 +1,38 @@
 /** \file
  *  \brief Declares input streams
  *
- *  \authors   Jeppe Andersen, Tuomas Hapola, Marian Heil, Andreas Maier, Jennifer Smillie
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
  *  \date      2019
  *  \copyright GPLv2 or later
  */
-
 #pragma once
 
 #include <fstream>
 #include <memory>
 #include <string>
 
 #include <boost/iostreams/filtering_stream.hpp>
 
 namespace HEJ{
 
   //! Small wrapper around boost's filtering_istream
   class istream {
     using boost_istream = boost::iostreams::filtering_istream;
   public:
     //! Constructor
     /**
      *  @param filename   Name of input file
      */
     explicit istream(std::string const & filename);
 
     //! Conversion to boost_istream
     operator boost_istream& () const noexcept {
       return *stream_;
     }
 
   private:
     std::ifstream file_;
     std::unique_ptr<boost_istream> stream_;
   };
 
 }
diff --git a/include/HEJ/utility.hh b/include/HEJ/utility.hh
index 9d2d53e..c8583ca 100644
--- a/include/HEJ/utility.hh
+++ b/include/HEJ/utility.hh
@@ -1,104 +1,103 @@
 /**
  * \file
  * \brief Contains various utilities
  *
- *  \authors   Jeppe Andersen, Tuomas Hapola, Marian Heil, Andreas Maier, Jennifer Smillie
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
  *  \date      2019
  *  \copyright GPLv2 or later
  */
-
 #pragma once
 
 #include <memory>
 
 #include <boost/core/demangle.hpp>
 
 #include "fastjet/PseudoJet.hh"
 
 namespace HEJ{
 
   //! Create a std::unique_ptr to a T object
   /**
    *  For non-array types this works like std::make_unique,
    *  which is not available under C++11
    */
   template<class T, class... Args>
   std::unique_ptr<T> make_unique(Args&&... a){
     return std::unique_ptr<T>{new T{std::forward<Args>(a)...}};
   }
 
   //! Create an array containing the passed arguments
   template<typename T, typename... U>
   constexpr
   std::array<T, 1 + sizeof...(U)> make_array(T t, U&&... rest){
     return {{t, std::forward<U>(rest)...}};
   }
 
 
   inline
   std::string join(
       std::string const & /* delim */
   ){
     return "";
   }
 
   inline
   std::string join(
       std::string const & /* delim */, std::string const & str
   ){
     return str;
   }
 
   //! Join strings with a delimiter
   /**
    *   @param delim      Delimiter to be put between consecutive strings
    *   @param first      First string
    *   @param second     Second string
    *   @param rest       Remaining strings
    */
   template<typename... Strings>
   std::string join(
       std::string const & delim,
       std::string const & first, std::string const & second,
       Strings&&... rest
   ){
     return join(delim, first + delim + second, std::forward<Strings>(rest)...);
   }
 
   //! Return the name of the argument's type
   template<typename T>
   std::string type_string(T&&){
     return boost::core::demangle(typeid(T).name());
   }
 
   //! Eliminate compiler warnings for unused variables
   template<typename... T>
   constexpr void ignore(T&&...) {}
 
   //! Check whether two doubles are closer than ep > 0 to each other
   inline
   bool nearby_ep(double a, double b, double ep){
     assert(ep > 0);
     return std::abs(a-b) < ep;
   }
 
   //! Check whether all components of two PseudoJets are closer than ep to each other
   inline
   bool nearby_ep(
       fastjet::PseudoJet const & pa, fastjet::PseudoJet const & pb,
       double ep
   ){
     assert(ep > 0);
     for(size_t i = 0; i < 4; ++i){
       if(!nearby_ep(pa[i], pb[i], ep)) return false;
     }
     return true;
   }
 
   inline
   bool nearby(
       fastjet::PseudoJet const & pa, fastjet::PseudoJet const & pb, double const norm = 1.
   ){
     return nearby_ep(pa, pb, 1e-7*norm);
   }
 }
diff --git a/src/CombinedEventWriter.cc b/src/CombinedEventWriter.cc
index 6ea7623..ff99b96 100644
--- a/src/CombinedEventWriter.cc
+++ b/src/CombinedEventWriter.cc
@@ -1,28 +1,28 @@
 /**
- *  \authors   Jeppe Andersen, Tuomas Hapola, Marian Heil, Andreas Maier, Jennifer Smillie
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
  *  \date      2019
  *  \copyright GPLv2 or later
  */
 #include "HEJ/CombinedEventWriter.hh"
 
 #include "HEJ/make_writer.hh"
 
 namespace HEJ{
 
   CombinedEventWriter::CombinedEventWriter(
       std::vector<OutputFile> const & outfiles,
       LHEF::HEPRUP const & heprup
   ){
     writers_.reserve(outfiles.size());
     for(OutputFile const & outfile: outfiles){
       writers_.emplace_back(
           make_format_writer(outfile.format, outfile.name, heprup)
       );
     }
   }
 
   void CombinedEventWriter::write(Event const & ev){
     for(auto & writer: writers_) writer->write(ev);
   }
 
 }
diff --git a/src/EmptyAnalysis.cc b/src/EmptyAnalysis.cc
index bed8f77..39c4037 100644
--- a/src/EmptyAnalysis.cc
+++ b/src/EmptyAnalysis.cc
@@ -1,68 +1,68 @@
 /**
- *  \authors   Jeppe Andersen, Tuomas Hapola, Marian Heil, Andreas Maier, Jennifer Smillie
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
  *  \date      2019
  *  \copyright GPLv2 or later
  */
 #include "HEJ/EmptyAnalysis.hh"
 
 #include <string>
 #include <vector>
 
 #include "yaml-cpp/yaml.h"
 
 #include "HEJ/exceptions.hh"
 
 namespace HEJ{
   namespace{
     std::vector<std::string> param_as_strings(YAML::Node const & parameters){
       using YAML::NodeType;
       switch(parameters.Type()){
       case NodeType::Null:
       case NodeType::Undefined:
         return {};
       case NodeType::Scalar:
         return {parameters.as<std::string>()};
       case NodeType::Sequence: {
         std::vector<std::string> param_strings;
         for(auto && param: parameters){
           param_strings.emplace_back(param.as<std::string>());
         }
         return param_strings;
       }
       case NodeType::Map: {
         std::vector<std::string> param_strings;
         for(auto && param: parameters){
           param_strings.emplace_back(param.first.as<std::string>());
         }
         return param_strings;
       }
       default:;
       }
       throw std::logic_error{"unreachable"};
     }
   }
 
   std::unique_ptr<Analysis> EmptyAnalysis::create(
       YAML::Node const & parameters
   ){
     const auto param_strings = param_as_strings(parameters);
     if(! param_strings.empty()){
       std::string error{"Unknown analysis parameter(s):"};
       for(auto && p: param_strings) error += " " + p;
       throw unknown_option{error};
     }
     return std::unique_ptr<Analysis>{new EmptyAnalysis{}};
   }
 
   void EmptyAnalysis::fill(Event const &, Event const &){
     // do nothing
   }
 
   bool EmptyAnalysis::pass_cuts(Event const &, Event const &){
     return true;
   }
 
   void EmptyAnalysis::finalise(){
     // do nothing
   }
 }
diff --git a/src/EventReader.cc b/src/EventReader.cc
index 0067f31..ae4fc9b 100644
--- a/src/EventReader.cc
+++ b/src/EventReader.cc
@@ -1,19 +1,24 @@
+/**
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
+ *  \date      2019
+ *  \copyright GPLv2 or later
+ */
 #include "HEJ/EventReader.hh"
 #include "HEJ/HDF5Reader.hh"
 #include "HEJ/LesHouchesReader.hh"
 #include "HEJ/utility.hh"
 
 namespace HEJ {
   std::unique_ptr<EventReader> make_reader(std::string const & filename) {
     try {
       return std::make_unique<LesHouchesReader>(filename);
     }
     catch(std::runtime_error&) {
 #if HEJ_BUILD_WITH_HDF5
       return std::make_unique<HDF5Reader>(filename);
 #else
       throw;
 #endif
     }
   }
 }
diff --git a/src/EventReweighter.cc b/src/EventReweighter.cc
index 411bf0c..261807b 100644
--- a/src/EventReweighter.cc
+++ b/src/EventReweighter.cc
@@ -1,258 +1,275 @@
 /**
- *  \authors   Jeppe Andersen, Tuomas Hapola, Marian Heil, Andreas Maier, Jennifer Smillie
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
  *  \date      2019
  *  \copyright GPLv2 or later
  */
 #include "HEJ/EventReweighter.hh"
 
 #include <algorithm>
 #include <assert.h>
 #include <limits>
 #include <math.h>
 #include <stddef.h>
 #include <string>
 #include <unordered_map>
 
 #include "fastjet/ClusterSequence.hh"
 
 #include "LHEF/LHEF.h"
 
 #include "HEJ/Event.hh"
 #include "HEJ/exceptions.hh"
 #include "HEJ/Particle.hh"
 #include "HEJ/PDG_codes.hh"
 #include "HEJ/PhaseSpacePoint.hh"
 
 namespace HEJ{
   using EventType = event_type::EventType;
 
   namespace {
 
     static_assert(
         std::numeric_limits<double>::has_quiet_NaN,
         "no quiet NaN for double"
     );
     constexpr double NaN = std::numeric_limits<double>::quiet_NaN();
 
     Event::EventData to_EventData(PhaseSpacePoint const & psp){
       Event::EventData result;
       result.incoming=psp.incoming();
       assert(result.incoming.size() == 2);
       result.outgoing=psp.outgoing();
       // technically Event::EventData doesn't have to be sorted,
       // but PhaseSpacePoint should be anyway
       assert(
           std::is_sorted(
               begin(result.outgoing), end(result.outgoing),
               rapidity_less{}
           )
       );
       assert(result.outgoing.size() >= 2);
       result.decays = psp.decays();
       result.parameters.central = {NaN, NaN, psp.weight()};
       return result;
     }
 
   } // namespace anonymous
 
   EventReweighter::EventReweighter(
       LHEF::HEPRUP const & heprup,
       ScaleGenerator scale_gen,
       EventReweighterConfig conf,
       HEJ::RNG & ran
   ):
     EventReweighter{
       HEJ::Beam{
         heprup.EBMUP.first,
         {{
           static_cast<HEJ::ParticleID>(heprup.IDBMUP.first),
           static_cast<HEJ::ParticleID>(heprup.IDBMUP.second)
         }}
       },
       heprup.PDFSUP.first,
       std::move(scale_gen),
       std::move(conf),
       ran
     }
   {
     if(heprup.EBMUP.second != E_beam_){
       throw std::invalid_argument(
           "asymmetric beam: " + std::to_string(E_beam_)
           + " ---> <--- " + std::to_string(heprup.EBMUP.second)
       );
     };
     if(heprup.PDFSUP.second != pdf_.id()){
       throw std::invalid_argument(
           "conflicting PDF ids: " + std::to_string(pdf_.id())
           + " vs. " + std::to_string(heprup.PDFSUP.second)
       );
     }
   }
 
   EventReweighter::EventReweighter(
       Beam beam,
       int pdf_id,
       ScaleGenerator scale_gen,
       EventReweighterConfig conf,
       HEJ::RNG & ran
   ):
     param_{std::move(conf)},
     E_beam_{beam.E},
     pdf_{pdf_id, beam.type.front(), beam.type.back()},
     MEt2_{
       [this](double mu){ return pdf_.Halphas(mu); },
       param_.ME_config
     },
     scale_gen_(std::move(scale_gen)),
     ran_{ran}
   {}
 
   PDF const & EventReweighter::pdf() const{
     return pdf_;
   }
 
   std::vector<Event> EventReweighter::reweight(
       Event const & input_ev, int num_events
   ){
     auto res_events = gen_res_events(input_ev, num_events);
     if(res_events.empty()) return {};
     for(auto & event: res_events) event = scale_gen_(event);
     return rescale(input_ev, std::move(res_events));
   }
 
   std::vector<Event> EventReweighter::gen_res_events(
       Event const & ev,
       int phase_space_points
   ){
     assert(ev.variations().empty());
+    status_.clear();
 
     switch(param_.treat.at(ev.type())){
-    case EventTreatment::discard: return {};
+    case EventTreatment::discard: {
+      status_.emplace_back(StatusCode::discard);
+      return {};
+    }
     case EventTreatment::keep:
-      if(! jets_pass_resummation_cuts(ev)) return {};
-      else return {ev};
+      if(! jets_pass_resummation_cuts(ev)) {
+        status_.emplace_back(StatusCode::failed_resummation_cuts);
+        return {};
+      }
+      else {
+        status_.emplace_back(StatusCode::good);
+        return {ev};
+      }
     default:;
     }
     const double Born_shat = shat(ev);
 
     std::vector<Event> resummation_events;
+    status_.reserve(phase_space_points);
     for(int psp_number = 0; psp_number < phase_space_points; ++psp_number){
       PhaseSpacePoint psp{ev, param_.psp_config, ran_};
-      if(psp.weight() == 0.) continue;
-      if(psp.incoming()[0].E() > E_beam_ || psp.incoming()[1].E() > E_beam_) continue;
+      status_.emplace_back(psp.status());
+      assert(psp.status() != StatusCode::unspecified);
+      if(psp.status() != StatusCode::good) continue;
+      assert(psp.weight() != 0.);
+      if(psp.incoming()[0].E() > E_beam_ || psp.incoming()[1].E() > E_beam_) {
+        status_.back() = StatusCode::too_much_energy;
+        continue;
+      }
 
       resummation_events.emplace_back(
         to_EventData( std::move(psp) ).cluster(
           param_.jet_param.def, param_.jet_param.min_pt
         )
       );
       auto & new_event = resummation_events.back();
       if( new_event.type() != ev.type() )
         throw std::logic_error{"Resummation Event does not match Born event"};
       new_event.generate_colours(ran_);
       assert(new_event.variations().empty());
       new_event.central().mur = ev.central().mur;
       new_event.central().muf = ev.central().muf;
       const double resum_shat = shat(new_event);
       new_event.central().weight *= ev.central().weight*Born_shat*Born_shat/
         (phase_space_points*resum_shat*resum_shat);
     }
     return resummation_events;
   }
 
   std::vector<Event> EventReweighter::rescale(
       Event const & Born_ev,
       std::vector<Event> events
   ) const{
     const double Born_pdf = pdf_factors(Born_ev).central;
     const double Born_ME = tree_matrix_element(Born_ev);
 
     for(auto & cur_event: events){
       const auto pdf = pdf_factors(cur_event);
       assert(pdf.variations.size() == cur_event.variations().size());
       const auto ME = matrix_elements(cur_event);
       assert(ME.variations.size() == cur_event.variations().size());
       cur_event.parameters() *= pdf*ME/(Born_pdf*Born_ME);
     }
     return events;
   };
 
   bool EventReweighter::jets_pass_resummation_cuts(
       Event const & ev
   ) const{
     const auto out_as_PseudoJet = to_PseudoJet(filter_partons(ev.outgoing()));
     fastjet::ClusterSequence cs{out_as_PseudoJet, param_.jet_param.def};
     return cs.inclusive_jets(param_.jet_param.min_pt).size() == ev.jets().size();
   }
 
   Weights EventReweighter::pdf_factors(Event const & ev) const{
     auto const & a = ev.incoming().front();
     auto const & b = ev.incoming().back();
     const double xa = a.p.e()/E_beam_;
     const double xb = b.p.e()/E_beam_;
 
     Weights result;
     std::unordered_map<double, double> known_pdf;
     result.central =
       pdf_.pdfpt(0,xa,ev.central().muf,a.type)*
       pdf_.pdfpt(1,xb,ev.central().muf,b.type);
     known_pdf.emplace(ev.central().muf, result.central);
 
     result.variations.reserve(ev.variations().size());
     for(auto const & ev_param: ev.variations()){
       const double muf = ev_param.muf;
       auto cur_pdf = known_pdf.find(muf);
       if(cur_pdf == known_pdf.end()){
         cur_pdf = known_pdf.emplace(
             muf,
             pdf_.pdfpt(0,xa,muf,a.type)*pdf_.pdfpt(1,xb,muf,b.type)
         ).first;
       }
       result.variations.emplace_back(cur_pdf->second);
     }
     assert(result.variations.size() == ev.variations().size());
     return result;
   }
 
   Weights
   EventReweighter::matrix_elements(Event const & ev) const{
     assert(param_.treat.count(ev.type()) > 0);
     if(param_.treat.find(ev.type())->second == EventTreatment::keep){
       return fixed_order_scale_ME(ev);
     }
 
     return MEt2_(ev);
   }
 
   double EventReweighter::tree_matrix_element(Event const & ev) const{
     assert(ev.variations().empty());
     assert(param_.treat.count(ev.type()) > 0);
     if(param_.treat.find(ev.type())->second == EventTreatment::keep){
       return fixed_order_scale_ME(ev).central;
     }
     return MEt2_.tree(ev).central;
   }
 
   Weights
   EventReweighter::fixed_order_scale_ME(Event const & ev) const{
     int alpha_s_power = 0;
     for(auto const & part: ev.outgoing()){
       if(is_parton(part))
         ++alpha_s_power;
       else if(part.type == pid::Higgs) {
         alpha_s_power += 2;
       }
       // nothing to do for other uncoloured particles
     }
 
     Weights result;
     result.central = pow(pdf_.Halphas(ev.central().mur), alpha_s_power);
     for(auto const & var: ev.variations()){
       result.variations.emplace_back(
           pow(pdf_.Halphas(var.mur), alpha_s_power)
       );
     }
     return result;
   }
 
 } // namespace HEJ
diff --git a/src/HDF5Reader.cc b/src/HDF5Reader.cc
index f359e05..50305f0 100644
--- a/src/HDF5Reader.cc
+++ b/src/HDF5Reader.cc
@@ -1,293 +1,298 @@
+/**
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
+ *  \date      2019
+ *  \copyright GPLv2 or later
+ */
 #include "HEJ/HDF5Reader.hh"
 
 #ifdef HEJ_BUILD_WITH_HDF5
 
 #include <numeric>
 #include <iterator>
 
 #include "highfive/H5File.hpp"
 
 namespace HEJ {
   namespace {
     // buffer size for reader
     // each "reading from disk" reads "chunk_size" many event at once
     constexpr std::size_t chunk_size = 10000;
 
     struct ParticleData {
       std::vector<int> id;
       std::vector<int> status;
       std::vector<int> mother1;
       std::vector<int> mother2;
       std::vector<int> color1;
       std::vector<int> color2;
       std::vector<double> px;
       std::vector<double> py;
       std::vector<double> pz;
       std::vector<double> e;
       std::vector<double> m;
       std::vector<double> lifetime;
       std::vector<double> spin;
     };
 
     struct EventRecords {
       std::vector<int> particle_start;
       std::vector<int> nparticles;
       std::vector<int> pid;
       std::vector<int> weight;
       std::vector<double> scale;
       std::vector<double> fscale;
       std::vector<double> rscale;
       std::vector<double> aqed;
       std::vector<double> aqcd;
       ParticleData particles;
     };
 
 class ConstEventIterator {
    public:
       // iterator traits
       using iterator_category = std::bidirectional_iterator_tag;
       using value_type = LHEF::HEPEUP;
       using difference_type = std::ptrdiff_t;
       using pointer = const LHEF::HEPEUP*;
       using reference = LHEF::HEPEUP const &;
 
       using iterator = ConstEventIterator;
       friend iterator cbegin(EventRecords const & records) noexcept;
       friend iterator cend(EventRecords const & records) noexcept;
 
       iterator& operator++() {
         particle_offset_ += records_.get().nparticles[idx_];
         ++idx_;
         return *this;
       }
       iterator& operator--() {
         --idx_;
         particle_offset_ -= records_.get().nparticles[idx_];
         return *this;
       }
       iterator operator--(int) {
         auto res = *this;
         --(*this);
         return res;
       }
       bool operator==(iterator const & other) const {
         return idx_ == other.idx_;
       }
       bool operator!=(iterator other) const {
         return !(*this == other);
       }
       value_type operator*() const {
         value_type hepeup{};
         auto const & r = records_.get();
         hepeup.NUP        = r.nparticles[idx_];
         hepeup.IDPRUP     = r.pid[idx_];
         hepeup.XWGTUP     = r.weight[idx_];
         hepeup.weights.emplace_back(hepeup.XWGTUP, nullptr);
         hepeup.SCALUP     = r.scale[idx_];
         hepeup.scales.muf = r.fscale[idx_];
         hepeup.scales.mur = r.rscale[idx_];
         hepeup.AQEDUP     = r.aqed[idx_];
         hepeup.AQCDUP     = r.aqcd[idx_];
         const size_t start = particle_offset_;
         const size_t end   = start + hepeup.NUP;
         auto const & p = r.particles;
         hepeup.IDUP    = std::vector<long>(   begin(p.id)+start,       begin(p.id)+end );
         hepeup.ISTUP   = std::vector<int>(    begin(p.status)+start,   begin(p.status)+end );
         hepeup.VTIMUP  = std::vector<double>( begin(p.lifetime)+start, begin(p.lifetime)+end );
         hepeup.SPINUP  = std::vector<double>( begin(p.spin)+start,     begin(p.spin)+end );
         hepeup.MOTHUP.resize(hepeup.NUP);
         hepeup.ICOLUP.resize(hepeup.NUP);
         hepeup.PUP.resize(hepeup.NUP);
         for(size_t i = 0; i < hepeup.MOTHUP.size(); ++i) {
           const size_t idx = start + i;
           assert(idx < end);
           hepeup.MOTHUP[i] = std::make_pair(p.mother1[idx], p.mother2[idx]);
           hepeup.ICOLUP[i] = std::make_pair(p.color1[idx], p.color2[idx]);
           hepeup.PUP[i]    = std::vector<double>{
             p.px[idx], p.py[idx], p.pz[idx], p.e[idx], p.m[idx]
           };
         }
         return hepeup;
       }
 
     private:
       explicit ConstEventIterator(EventRecords const & records):
         records_{records} {}
 
       std::reference_wrapper<const EventRecords> records_;
       size_t idx_ = 0;
       size_t particle_offset_ = 0;
     }; // end ConstEventIterator
 
     ConstEventIterator cbegin(EventRecords const & records) noexcept {
       return ConstEventIterator{records};
     }
 
     ConstEventIterator cend(EventRecords const & records) noexcept {
       auto it =ConstEventIterator{records};
       it.idx_ = records.aqcd.size(); // or size of any other records member
       return it;
     }
 
   } // end anonymous namespace
 
   struct HDF5Reader::HDF5ReaderImpl{
     HighFive::File file;
     std::size_t event_idx;
     std::size_t particle_idx;
     std::size_t nevents;
 
     EventRecords records;
     ConstEventIterator cur_event;
 
     LHEF::HEPRUP heprup;
     LHEF::HEPEUP hepeup;
 
     explicit HDF5ReaderImpl(std::string const & filename):
       file{filename},
       event_idx{0},
       particle_idx{0},
       nevents{
         file.getGroup("event")
         .getDataSet("nparticles") // or any other dataset
         .getSpace().getDimensions().front()
       },
       records{},
       cur_event{cbegin(records)},
       heprup{},
       hepeup{}
     {
       read_heprup();
       read_event_records(chunk_size);
     }
 
     void read_heprup() {
       const auto init = file.getGroup("init");
       init.getDataSet( "PDFgroupA"         ).read(heprup.PDFGUP.first);
       init.getDataSet( "PDFgroupB"         ).read(heprup.PDFGUP.second);
       init.getDataSet( "PDFsetA"           ).read(heprup.PDFSUP.first);
       init.getDataSet( "PDFsetB"           ).read(heprup.PDFSUP.second);
       init.getDataSet( "beamA"             ).read(heprup.IDBMUP.first);
       init.getDataSet( "beamB"             ).read(heprup.IDBMUP.second);
       init.getDataSet( "energyA"           ).read(heprup.EBMUP.first);
       init.getDataSet( "energyB"           ).read(heprup.EBMUP.second);
       init.getDataSet( "numProcesses"      ).read(heprup.NPRUP);
       init.getDataSet( "weightingStrategy" ).read(heprup.IDWTUP);
       const auto proc_info = file.getGroup("procInfo");
       proc_info.getDataSet( "procId"     ).read(heprup.LPRUP);
       proc_info.getDataSet( "xSection"   ).read(heprup.XSECUP);
       proc_info.getDataSet( "error"      ).read(heprup.XERRUP);
       // TODO: is this identification correct?
       proc_info.getDataSet( "unitWeight" ).read(heprup.XMAXUP);
     }
 
     std::size_t read_event_records(std::size_t count) {
       count = std::min(count, nevents-event_idx);
 
       auto events = file.getGroup("event");
       events.getDataSet("nparticles").select({event_idx}, {count}).read(records.nparticles);
       assert(records.nparticles.size() == count);
       events.getDataSet("pid").select(    {event_idx}, {count} ).read( records.pid );
       events.getDataSet("weight").select( {event_idx}, {count} ).read( records.weight );
       events.getDataSet("scale").select(  {event_idx}, {count} ).read( records.scale );
       events.getDataSet("fscale").select( {event_idx}, {count} ).read( records.fscale );
       events.getDataSet("rscale").select( {event_idx}, {count} ).read( records.rscale );
       events.getDataSet("aqed").select(   {event_idx}, {count} ).read( records.aqed );
       events.getDataSet("aqcd").select(   {event_idx}, {count} ).read( records.aqcd );
       const std::size_t particle_count = std::accumulate(
           begin(records.nparticles), end(records.nparticles), 0
       );
       auto pdata = file.getGroup("particle");
       auto & particles = records.particles;
       pdata.getDataSet("id").select(       {particle_idx}, {particle_count} ).read( particles.id );
       pdata.getDataSet("status").select(   {particle_idx}, {particle_count} ).read( particles.status );
       pdata.getDataSet("mother1").select(  {particle_idx}, {particle_count} ).read( particles.mother1 );
       pdata.getDataSet("mother2").select(  {particle_idx}, {particle_count} ).read( particles.mother2 );
       pdata.getDataSet("color1").select(   {particle_idx}, {particle_count} ).read( particles.color1 );
       pdata.getDataSet("color2").select(   {particle_idx}, {particle_count} ).read( particles.color2 );
       pdata.getDataSet("px").select(       {particle_idx}, {particle_count} ).read( particles.px );
       pdata.getDataSet("py").select(       {particle_idx}, {particle_count} ).read( particles.py );
       pdata.getDataSet("pz").select(       {particle_idx}, {particle_count} ).read( particles.pz );
       pdata.getDataSet("e").select(        {particle_idx}, {particle_count} ).read( particles.e );
       pdata.getDataSet("m").select(        {particle_idx}, {particle_count} ).read( particles.m );
       pdata.getDataSet("lifetime").select( {particle_idx}, {particle_count} ).read( particles.lifetime );
       pdata.getDataSet("spin").select(     {particle_idx}, {particle_count} ).read( particles.spin );
 
       event_idx += count;
       particle_idx += particle_count;
       return count;
     }
   };
 
   HDF5Reader::HDF5Reader(std::string const & filename):
     impl_{
           new HDF5Reader::HDF5ReaderImpl{filename},
           HDF5Reader::HDF5ReaderImplDeleter{}
     }
   {}
 
   bool HDF5Reader::read_event() {
     if(impl_->cur_event == cend(impl_->records)) {
       // end of active chunk, read new events from file
       const auto events_read = impl_->read_event_records(chunk_size);
       impl_->cur_event = cbegin(impl_->records);
       if(events_read == 0) return false;
     }
     impl_->hepeup = *impl_->cur_event;
     ++impl_->cur_event;
     return true;
   }
 
   namespace {
     static const std::string nothing = "";
   }
 
   std::string const & HDF5Reader::header() const {
     return nothing;
   }
 
   LHEF::HEPRUP const & HDF5Reader::heprup() const {
     return impl_->heprup;
   }
 
   LHEF::HEPEUP const & HDF5Reader::hepeup() const {
     return impl_->hepeup;
   }
 }
 
 #else // no HDF5 support
 
 namespace HEJ {
   class HDF5Reader::HDF5ReaderImpl{};
 
   HDF5Reader::HDF5Reader(std::string const &){
     throw std::invalid_argument{
           "Failed to create HDF5 reader: "
           "HEJ 2 was built without HDF5 support"
     };
   }
 
   bool HDF5Reader::read_event() {
     throw std::logic_error{"unreachable"};
   }
 
   std::string const & HDF5Reader::header() const {
     throw std::logic_error{"unreachable"};
   }
 
   LHEF::HEPRUP const & HDF5Reader::heprup() const {
     throw std::logic_error{"unreachable"};
   }
 
   LHEF::HEPEUP const & HDF5Reader::hepeup() const {
     throw std::logic_error{"unreachable"};
   }
 }
 
 #endif
 
 namespace HEJ {
   void HDF5Reader::HDF5ReaderImplDeleter::operator()(HDF5ReaderImpl* p) {
     delete p;
   }
 }
diff --git a/src/HepMCInterface.cc b/src/HepMCInterface.cc
index f83ece6..a7a1c19 100644
--- a/src/HepMCInterface.cc
+++ b/src/HepMCInterface.cc
@@ -1,177 +1,177 @@
 /**
- *  \authors   Jeppe Andersen, Tuomas Hapola, Marian Heil, Andreas Maier, Jennifer Smillie
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
  *  \date      2019
  *  \copyright GPLv2 or later
  */
 #include "HEJ/HepMCInterface.hh"
 
 #include "HEJ/exceptions.hh"
 
 #ifdef HEJ_BUILD_WITH_HepMC_VERSION
 
 #include <math.h>
 #include <utility>
 
 #include "HEJ/Event.hh"
 #include "HEJ/Particle.hh"
 
 #include "HepMC/GenCrossSection.h"
 #include "HepMC/GenEvent.h"
 #include "HepMC/GenParticle.h"
 #include "HepMC/GenVertex.h"
 
 namespace HEJ{
 
   namespace {
     HepMC::FourVector to_FourVector(Particle const & sp){
       return {sp.px(), sp.py(), sp.pz(), sp.E()};
     }
     constexpr int status_in = 11;
     constexpr int status_decayed = 2;
     constexpr int status_out = 1;
 
     template<class HepMCClass, typename... Args>
     auto make_ptr(Args&&... args){
       #if HEJ_BUILD_WITH_HepMC_VERSION >= 3
       return HepMC::make_shared<HepMCClass>(std::forward<Args>(args)...);
       #else
       return new HepMCClass(std::forward<Args>(args)...);
       #endif
     }
   } // namespace anonymous
 
   HepMCInterface::HepMCInterface():
     event_count_(0.), tot_weight_(0.), tot_weight2_(0.)
     {}
 
   HepMC::GenCrossSection HepMCInterface::cross_section() const {
     HepMC::GenCrossSection xs;
   #if HEJ_BUILD_WITH_HepMC_VERSION >= 3
     xs.set_cross_section(tot_weight_, sqrt(tot_weight2_), event_count_);
     /// @TODO add number of attempted events
   #else // HepMC 2
     xs.set_cross_section(tot_weight_, sqrt(tot_weight2_));
   #endif
     return xs;
   }
 
   HepMC::GenEvent HepMCInterface::init_kinematics(Event const & event) {
     HepMC::GenEvent out_ev{HepMC::Units::GEV, HepMC::Units::MM};
     auto vx = make_ptr<HepMC::GenVertex>();
     for(auto const & in: event.incoming()){
       vx->add_particle_in(
           make_ptr<HepMC::GenParticle>(
             to_FourVector(in), static_cast<int>(in.type), status_in
           )
       );
     }
     for(size_t i=0; i < event.outgoing().size(); ++i){
       auto const & out = event.outgoing()[i];
       auto particle = make_ptr<HepMC::GenParticle>(
             to_FourVector(out), static_cast<int>(out.type), status_out
           );
       const int status = event.decays().count(i)?status_decayed:status_out;
       particle->set_status(status);
       if( status == status_decayed){
         auto vx_decay = make_ptr<HepMC::GenVertex>();
         vx_decay->add_particle_in(particle);
         for( auto const & out: event.decays().at(i)){
           vx_decay->add_particle_out(
               make_ptr<HepMC::GenParticle>(
                 to_FourVector(out), static_cast<int>(out.type), status_out
               )
           );
         }
         out_ev.add_vertex(vx_decay);
       }
       vx->add_particle_out(particle);
     }
     out_ev.add_vertex(vx);
 
     return out_ev;
   }
 
   void HepMCInterface::set_central(HepMC::GenEvent & out_ev, Event const & event,
     ssize_t const weight_index
   ) {
     EventParameters event_param;
     if(weight_index < 0)
       event_param = event.central();
     else if ( (size_t) weight_index < event.variations().size())
       event_param = event.variations(weight_index);
     else
       throw std::invalid_argument{
          "HepMCInterface tried to access a weight outside of the variation range."
       };
     const double wt = event_param.weight;
 
     tot_weight_ += wt;
     tot_weight2_ += wt * wt;
 
     if(out_ev.weights().size() == 0){
       out_ev.weights().push_back(wt);
     } else { // central always on first
       out_ev.weights()[0] = wt;
     }
 
   #if HEJ_BUILD_WITH_HepMC_VERSION >= 3
     out_ev.set_cross_section(
       HepMC::make_shared<HepMC::GenCrossSection>(cross_section()) );
   #else // HepMC 2
     out_ev.set_cross_section( cross_section() );
     out_ev.set_signal_process_id(event.type()+1);      // "+1": conistent with lhe
     out_ev.set_event_scale(event_param.mur);
   #endif
 
     ++event_count_;
     out_ev.set_event_number(event_count_);
 
     /// @TODO add alphaQCD (need function) and alphaQED
     /// @TODO output pdf (currently not avaiable from event alone)
 
   }
 
   void HepMCInterface::add_variation(HepMC::GenEvent & out_ev,
     std::vector<EventParameters> const & varis
   ) {
     for(auto const & var: varis){
       out_ev.weights().push_back(var.weight);
     }
     /// @TODO add name list for weights
   }
 
   HepMC::GenEvent HepMCInterface::operator()(Event const & event,
       ssize_t const weight_index
   ) {
     HepMC::GenEvent out_ev(init_kinematics(event));
     set_central(out_ev, event, weight_index);
     add_variation(out_ev, event.variations());
     return out_ev;
   }
 
 
 }
 #else // no HepMC => empty class
 namespace HepMC {
   class GenEvent {};
   class GenCrossSection {};
 }
 namespace HEJ{
   HepMCInterface::HepMCInterface(){
       throw std::invalid_argument(
           "Failed to create HepMCInterface: "
           "HEJ 2 was built without HepMC support"
       );
   }
 
   HepMC::GenEvent HepMCInterface::operator()(Event const &, ssize_t)
   {return HepMC::GenEvent();}
   HepMC::GenEvent HepMCInterface::init_kinematics(Event const &)
   {return HepMC::GenEvent();}
   void HepMCInterface::add_variation(HepMC::GenEvent &,
     std::vector<EventParameters> const &){}
   void HepMCInterface::set_central(HepMC::GenEvent &,  Event const &, ssize_t) {}
   HepMC::GenCrossSection HepMCInterface::cross_section() const
   {return HepMC::GenCrossSection();}
 }
 #endif
diff --git a/src/HepMCWriter.cc b/src/HepMCWriter.cc
index e09c538..c23014b 100644
--- a/src/HepMCWriter.cc
+++ b/src/HepMCWriter.cc
@@ -1,148 +1,148 @@
 /**
- *  \authors   Jeppe Andersen, Tuomas Hapola, Marian Heil, Andreas Maier, Jennifer Smillie
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
  *  \date      2019
  *  \copyright GPLv2 or later
  */
 #include "HEJ/HepMCWriter.hh"
 
 #include <cassert>
 
 #include "LHEF/LHEF.h"
 
 #ifdef HEJ_BUILD_WITH_HepMC_VERSION
 
 #if HEJ_BUILD_WITH_HepMC_VERSION >= 3
 #include "HepMC/LHEFAttributes.h"
 #include "HepMC/WriterAscii.h"
 
 #include "HEJ/Version.hh"
 
 #else
 #include "HepMC/IO_GenEvent.h"
 #endif
 
 #include <utility>
 
 #include "HepMC/GenParticle.h"
 #include "HepMC/GenVertex.h"
 
 #include "HEJ/Event.hh"
 #include "HEJ/exceptions.hh"
 #include "HEJ/HepMCInterface.hh"
 
 #if HEJ_BUILD_WITH_HepMC_VERSION >= 3
 namespace {
     void reset_weight_info(LHEF::HEPRUP & heprup){
       heprup.IDWTUP = 2;
       // use placeholders for unknown init block values
       // we can overwrite them after processing all events
       heprup.XSECUP = {0.};
       heprup.XERRUP = {0.};
       heprup.XMAXUP = {0.};
     }
     HepMC::shared_ptr<HepMC::GenRunInfo> init_runinfo(LHEF::HEPRUP && heprup){
       reset_weight_info(heprup);
       HepMC::GenRunInfo runinfo;
 
       auto hepr = HepMC::make_shared<HepMC::HEPRUPAttribute>();
       hepr->heprup = heprup;
       runinfo.add_attribute(std::string("HEPRUP"), hepr);
 
       for (int i = 0, N = hepr->heprup.generators.size(); i < N; ++i ){
         HepMC::GenRunInfo::ToolInfo tool;
         tool.name =  hepr->heprup.generators[i].name;
         tool.version =  hepr->heprup.generators[i].version;
         tool.description =  hepr->heprup.generators[i].contents;
         runinfo.tools().push_back(tool);
       }
       return HepMC::make_shared<HepMC::GenRunInfo>(runinfo);
     }
 
 } // namespace anonymous
 #endif // HepMC 3
 
 namespace HEJ{
 
   struct HepMCWriter::HepMCWriterImpl{
     HepMCInterface hepmc_;
 
     HepMCWriterImpl & operator=(HepMCWriterImpl const & other) = delete;
     HepMCWriterImpl(HepMCWriterImpl const & other) = delete;
     HepMCWriterImpl & operator=(HepMCWriterImpl && other) = delete;
     HepMCWriterImpl(HepMCWriterImpl && other) = delete;
 
   #if HEJ_BUILD_WITH_HepMC_VERSION >= 3
 
     HepMC::WriterAscii writer_;
 
     HepMCWriterImpl(
         std::string const & file, LHEF::HEPRUP && heprup
     ):
       hepmc_(),
       writer_{file, init_runinfo(std::move(heprup))}
     {}
 
     ~HepMCWriterImpl(){
       writer_.close();
     }
 
   #else // HepMC 2
 
     HepMC::IO_GenEvent writer_;
 
     HepMCWriterImpl(
         std::string const & file, LHEF::HEPRUP &&
     ):
       hepmc_(),
       writer_{file}
     {}
 
   #endif
 
     void write(Event const & ev){
       auto out_ev = hepmc_(ev);
     #if HEJ_BUILD_WITH_HepMC_VERSION >= 3
       writer_.write_event(out_ev);
     #else // HepMC 2
       writer_.write_event(&out_ev);
     #endif
     }
   };
 
   void HepMCWriter::HepMCWriterImplDeleter::operator()(HepMCWriterImpl* p) {
     delete p;
   }
 
   HepMCWriter::HepMCWriter(std::string const & file, LHEF::HEPRUP heprup):
     impl_{std::unique_ptr<HepMCWriterImpl, HepMCWriterImplDeleter>{
       new HepMCWriterImpl(file, std::move(heprup))
     }}
   {}
 
   void HepMCWriter::write(Event const & ev){
     impl_->write(ev);
   }
 } // namespace HEJ
 
 #else // no HepMC
 
 namespace HEJ{
 
   class HepMCWriter::HepMCWriterImpl{};
 
   HepMCWriter::HepMCWriter(std::string const &, LHEF::HEPRUP){
       throw std::invalid_argument(
           "Failed to create HepMC writer: "
           "HEJ 2 was built without HepMC support"
       );
   }
 
   void HepMCWriter::write(Event const &){
     assert(false);
   }
 
   void HepMCWriter::HepMCWriterImplDeleter::operator()(HepMCWriterImpl* p) {
     delete p;
   }
 }
 #endif
diff --git a/src/JetSplitter.cc b/src/JetSplitter.cc
index 9d727db..60003ce 100644
--- a/src/JetSplitter.cc
+++ b/src/JetSplitter.cc
@@ -1,178 +1,178 @@
 /**
- *  \authors   Jeppe Andersen, Tuomas Hapola, Marian Heil, Andreas Maier, Jennifer Smillie
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
  *  \date      2019
  *  \copyright GPLv2 or later
  */
 #include "HEJ/JetSplitter.hh"
 
 #include <array>
 #include <assert.h>
 #include <numeric>
 
 #include "fastjet/ClusterSequence.hh"
 #include "fastjet/PseudoJet.hh"
 
 #include "HEJ/Constants.hh"
 #include "HEJ/exceptions.hh"
 
 namespace HEJ {
   namespace{
     constexpr double ccut=HEJ::CMINPT; // min parton pt
 
     template<class Iterator>
       bool same_pt_and_rapidity(
           Iterator begin, Iterator end,
           fastjet::PseudoJet const & jet
     ){
       constexpr double ep = 1e-2;
       const fastjet::PseudoJet reconstructed_jet = std::accumulate(
           begin, end, fastjet::PseudoJet{}
       );
       return
         (std::abs(reconstructed_jet.pt() - jet.pt()) < ep)
         && (std::abs(reconstructed_jet.rapidity() - jet.rapidity()) < ep)
         ;
     }
 
     bool all_in_one_jet(
         std::vector<fastjet::PseudoJet> const & partons,
         fastjet::JetDefinition jet_def, double min_jet_pt
     ){
       fastjet::ClusterSequence ev(partons, jet_def);
       const std::vector<fastjet::PseudoJet> testjet = ev.inclusive_jets(min_jet_pt);
       return testjet.size() == 1u
         && testjet[0].constituents().size() == partons.size();
     }
   }
 
   using SplitResult = JetSplitter::SplitResult;
 
   SplitResult JetSplitter::split(
       fastjet::PseudoJet const & j2split, int ncons
   ) const{
     if(ncons <= 0) {
       throw std::invalid_argument{
         "number of requested jet constituents less than 1"
           };
     }
     double swt = 1.;
 
     std::vector<fastjet::PseudoJet> jcons;
     if(ncons == 1){
       jcons.emplace_back(j2split);
       jcons.back().set_user_index(0);
       return {jcons, swt};
     }
     if(ncons == 2){
       return Split2(j2split);
     }
     const double R_max = R_factor*R_;
     assert(R_max < M_PI);
 
     double pt_remaining = j2split.pt();
     const double phi_jet = j2split.phi();
     const double y_jet = j2split.rapidity();
     for(int i = 0; i < ncons - 1; ++i){
       /**
        * Generate rapidity and azimuthal angle with a distance
        * R = sqrt(delta_y^2 + delta_phi^2) < R_max
        * from the jet centre
        */
       const double R = R_max*ran_.get().flat();
       const double theta = 2*M_PI*ran_.get().flat();
       const double delta_phi = R*cos(theta);
       const double delta_y = R*sin(theta);
 
       /**
        * Generate pt such that the total contribution of all partons
        * along the jet pt axis does not exceed the jet pt
        */
       const double pt_max = pt_remaining/cos(delta_phi);
       assert(pt_max > 0);
       if(pt_max < ccut) return {}; // no pt remaining for this parton
       const double pt = (pt_max - ccut)*ran_.get().flat() + ccut;
       pt_remaining -= pt*cos(delta_phi);
 
       jcons.emplace_back(
           pt*cos(phi_jet + delta_phi), pt*sin(phi_jet + delta_phi),
           pt*sinh(y_jet + delta_y), pt*cosh(y_jet + delta_y)
       );
       jcons.back().set_user_index(i);
       swt *= 2*M_PI*R*R_max*pt*(pt_max - ccut);
     }
 
     const fastjet::PseudoJet p_total = std::accumulate(
         jcons.begin(), jcons.end(), fastjet::PseudoJet{}
     );
 
     // Calculate the pt of the last parton
     const double last_px = j2split.px() - p_total.px();
     const double last_py = j2split.py() - p_total.py();
     const double last_pt = sqrt(last_px*last_px + last_py*last_py);
     if(last_pt < ccut) return {};
 
     // Calculate the rapidity of the last parton using the requirement that the
     // new jet must have the same rapidity as the LO jet.
     const double exp_2y_jet = (j2split.e() + j2split.pz())/(j2split.e() - j2split.pz());
     const double bb = (p_total.e()+p_total.pz()) - exp_2y_jet*(p_total.e()-p_total.pz());
     const double lasty = log((-bb+sqrt(bb*bb+4.*exp_2y_jet*last_pt*last_pt))/(2.*last_pt));
 
     jcons.emplace_back(
         last_px, last_py, last_pt*sinh(lasty), last_pt*cosh(lasty)
     );
     jcons.back().set_user_index(ncons-1);
     assert(same_pt_and_rapidity(begin(jcons), end(jcons), j2split));
 
     // Test that the last parton is not too far away from the jet centre.
     if (jcons.back().delta_R(j2split) > R_max) return {};
 
     if(! all_in_one_jet(jcons, jet_def_, min_jet_pt_)) return {};
 
     return {jcons, swt};
   }
 
   double JetSplitter::sample_distance_2p(double & wt) const{
     static constexpr double x_small = 0.1;
     static constexpr double p_small = 0.4;
 
     const double pR = ran_.get().flat();
     if(pR < p_small){
       wt *= x_small/p_small;
       return x_small/p_small*pR;
     }
     wt *= (1-x_small)/(1-p_small);
     return (1-x_small)/(1-p_small)*(pR-p_small) + x_small;
   }
 
   SplitResult JetSplitter::Split2(fastjet::PseudoJet const & j2split) const{
     static constexpr size_t ncons = 2;
     std::vector<fastjet::PseudoJet> jcons(ncons);
     std::array<double, ncons> R, phi, y, pt;
     double wt = 1;
 
     const double theta = 2*M_PI*ran_.get().flat(); // angle in y-phi plane
     // empiric observation: we are always within the jet radius
     R[0] = sample_distance_2p(wt)*R_;
     R[1] = -sample_distance_2p(wt)*R_;
     for(size_t i = 0; i <= 1; ++i){
       phi[i] = j2split.phi() + R[i]*cos(theta);
       y[i] = j2split.rapidity() + R[i]*sin(theta);
     }
     for(size_t i = 0; i <= 1; ++i){
       pt[i] = (j2split.py() - tan(phi[1-i])*j2split.px())/
         (sin(phi[i]) - tan(phi[1-i])*cos(phi[i]));
       if(pt[i] < ccut) return {};
       jcons[i].reset_PtYPhiM(pt[i], y[i], phi[i]);
       jcons[i].set_user_index(i);
     }
     assert(same_pt_and_rapidity(begin(jcons), end(jcons), j2split));
 
     if(! all_in_one_jet(jcons, jet_def_, min_jet_pt_)) return {};
     wt *= 2*M_PI*pt[0]*R[0]*R_*R_;
     // from transformation of delta(R[1] - ...) to delta(pt[0] - ...)
     const double dphi0 = phi[0] - j2split.phi();
     const double ptJ = j2split.pt();
     const double jacobian = cos(theta)*pt[1]*pt[1]/(ptJ*sin(dphi0));
     return {jcons, jacobian*wt};
   }
 }
diff --git a/src/LesHouchesWriter.cc b/src/LesHouchesWriter.cc
index e9c40cc..54d3a12 100644
--- a/src/LesHouchesWriter.cc
+++ b/src/LesHouchesWriter.cc
@@ -1,95 +1,94 @@
 /**
- *  \authors   Jeppe Andersen, Tuomas Hapola, Marian Heil, Andreas Maier, Jennifer Smillie
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
  *  \date      2019
  *  \copyright GPLv2 or later
  */
-
 #include <cassert>
 #include <utility>
 #include <vector>
 
 #include "HEJ/Event.hh"
 #include "HEJ/event_types.hh"
 #include "HEJ/LesHouchesWriter.hh"
 #include "HEJ/utility.hh"
 
 namespace HEJ{
   namespace{
     template<class T, class... Args>
       std::unique_ptr<T> make_unique(Args&&... a){
       return std::unique_ptr<T>{new T{std::forward<Args>(a)...}};
     }
   }
 
   LesHouchesWriter::LesHouchesWriter(
       std::string const & file, LHEF::HEPRUP heprup
   ):
     out_{file, std::fstream::in | std::fstream::out | std::fstream::trunc},
     writer_{HEJ::make_unique<LHEF::Writer>(out_)}
   {
     if(! out_.is_open()){
       throw std::ios_base::failure("Failed to open " + file);
     };
     writer_->heprup = std::move(heprup);
     // lhe Stardard: IDWTUP (negative => weights = +/-)
     // 1: weighted events, xs = mean(weight), XMAXUP given
     // 2: weighted events, xs = XSECUP, XMAXUP given
     // 3: unweighted events, no additional information given
     // 4: unweighted events, xs = mean(weight), no additional information given
     writer_->heprup.IDWTUP = 2;
 
     // use placeholders for unknown init block values
     // we can overwrite them after processing all events
     writer_->heprup.XSECUP = std::vector<double>(event_type::last_type+1, 0.);
     writer_->heprup.XERRUP = std::vector<double>(event_type::last_type+1, 0.);
     writer_->heprup.XMAXUP = std::vector<double>(event_type::last_type+1, 0.);
     write_init();
   }
 
   void LesHouchesWriter::write(Event const &  ev){
     assert(writer_ && out_.is_open());
 
     const double wt = ev.central().weight;
     writer_->hepeup = HEJ::to_HEPEUP(std::move(ev), &heprup());
     writer_->writeEvent();
     heprup().XSECUP[ev.type()] += wt;
     heprup().XERRUP[ev.type()] += wt*wt;
     if(wt > heprup().XMAXUP[ev.type()]){
       heprup().XMAXUP[ev.type()] = wt;
     }
   }
 
   // this function is called after overwritting the Les Houches init block
   // assert that we have overwritten *exactly* the init block,
   // i.e. we are at the end of the file or an intact event block is next
   void assert_next_event_intact(std::istream & out){
     (void) out; // suppress compiler warnings if not in debug mode
 #ifndef NDEBUG
     std::string line;
     getline(out, line);
     assert(out.eof() || line == "<event>");
 #endif
   }
 
   void LesHouchesWriter::rewrite_init(){
     assert(writer_ && out_.is_open());
 
     // replace placeholder entries
     const auto pos = out_.tellp();
     out_.seekp(0);
     writer_->init();
     assert_next_event_intact(out_);
     out_.seekp(pos);
   }
 
   LesHouchesWriter::~LesHouchesWriter(){
     assert(writer_ && out_.is_open());
 
     for(auto & xs_err: heprup().XERRUP)
     {
       xs_err = sqrt(xs_err);
     }
     rewrite_init();
   }
 
 }
diff --git a/src/MatrixElement.cc b/src/MatrixElement.cc
index 2a7cfbe..5686fd1 100644
--- a/src/MatrixElement.cc
+++ b/src/MatrixElement.cc
@@ -1,1751 +1,1751 @@
 /**
- *  \authors   Jeppe Andersen, Tuomas Hapola, Marian Heil, Andreas Maier, Jennifer Smillie
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
  *  \date      2019
  *  \copyright GPLv2 or later
  */
 #include "HEJ/MatrixElement.hh"
 
 #include <algorithm>
 #include <assert.h>
 #include <limits>
 #include <math.h>
 #include <stddef.h>
 #include <unordered_map>
 #include <utility>
 
 #include "CLHEP/Vector/LorentzVector.h"
 
 #include "fastjet/ClusterSequence.hh"
 
 #include "HEJ/Constants.hh"
 #include "HEJ/currents.hh"
 #include "HEJ/PDG_codes.hh"
 #include "HEJ/event_types.hh"
 #include "HEJ/Event.hh"
 #include "HEJ/exceptions.hh"
 #include "HEJ/Particle.hh"
 #include "HEJ/utility.hh"
 
 namespace HEJ{
   double MatrixElement::omega0(
       double alpha_s, double mur,
       fastjet::PseudoJet const & q_j
   ) const {
     const double lambda = param_.regulator_lambda;
     const double result = - alpha_s*N_C/M_PI*log(q_j.perp2()/(lambda*lambda));
     if(! param_.log_correction) return result;
     // use alpha_s(sqrt(q_j*lambda)), evolved to mur
     return (
         1. + alpha_s/(4.*M_PI)*beta0*log(mur*mur/(q_j.perp()*lambda))
     )*result;
   }
 
   Weights MatrixElement::operator()(
       Event const & event
   ) const {
     return tree(event)*virtual_corrections(event);
   }
 
   Weights MatrixElement::tree(
       Event const & event
   ) const {
     return tree_param(event)*tree_kin(event);
   }
 
   Weights MatrixElement::tree_param(
       Event const & event
   ) const {
     if(! is_HEJ(event.type())) {
       return Weights{0., std::vector<double>(event.variations().size(), 0.)};
     }
     Weights result;
     // only compute once for each renormalisation scale
     std::unordered_map<double, double> known;
     result.central = tree_param(event, event.central().mur);
     known.emplace(event.central().mur, result.central);
     for(auto const & var: event.variations()) {
       const auto ME_it = known.find(var.mur);
       if(ME_it == end(known)) {
         const double wt = tree_param(event, var.mur);
         result.variations.emplace_back(wt);
         known.emplace(var.mur, wt);
       }
       else {
         result.variations.emplace_back(ME_it->second);
       }
     }
     return result;
   }
 
   Weights MatrixElement::virtual_corrections(
       Event const & event
   ) const {
     if(! is_HEJ(event.type())) {
       return Weights{0., std::vector<double>(event.variations().size(), 0.)};
     }
     Weights result;
     // only compute once for each renormalisation scale
     std::unordered_map<double, double> known;
     result.central = virtual_corrections(event, event.central().mur);
     known.emplace(event.central().mur, result.central);
     for(auto const & var: event.variations()) {
       const auto ME_it = known.find(var.mur);
       if(ME_it == end(known)) {
         const double wt = virtual_corrections(event, var.mur);
         result.variations.emplace_back(wt);
         known.emplace(var.mur, wt);
       }
       else {
         result.variations.emplace_back(ME_it->second);
       }
     }
     return result;
   }
 
   double MatrixElement::virtual_corrections_W(
       Event const & event,
       double mur,
       Particle const & WBoson
   ) const{
     auto const & in = event.incoming();
     const auto partons = filter_partons(event.outgoing());
     fastjet::PseudoJet const & pa = in.front().p;
 #ifndef NDEBUG
     fastjet::PseudoJet const & pb = in.back().p;
     double const norm = (in.front().p + in.back().p).E();
 #endif
 
     assert(std::is_sorted(partons.begin(), partons.end(), rapidity_less{}));
     assert(partons.size() >= 2);
     assert(pa.pz() < pb.pz());
 
 
     fastjet::PseudoJet q = pa - partons[0].p;
     size_t first_idx = 0;
     size_t last_idx = partons.size() - 1;
 
     bool wc = true;
     bool wqq = false;
 
     // With extremal qqx or unordered gluon outside the extremal
     // partons then it is not part of the FKL ladder and does not
     // contribute to the virtual corrections. W emitted from the
     // most backward leg must be taken into account in t-channel
     if (event.type() == event_type::FKL) {
       if (in[0].type != partons[0].type ){
         q -= WBoson.p;
         wc = false;
       }
     }
 
     else if (event.type() == event_type::unob) {
       q -= partons[1].p;
       ++first_idx;
       if (in[0].type != partons[1].type ){
         q -= WBoson.p;
         wc = false;
       }
     }
 
     else if (event.type() == event_type::qqxexb) {
       q -= partons[1].p;
       ++first_idx;
       if (abs(partons[0].type) != abs(partons[1].type)){
         q -= WBoson.p;
         wc = false;
       }
     }
 
     if(event.type() == event_type::unof
        || event.type() == event_type::qqxexf){
       --last_idx;
     }
 
     size_t first_idx_qqx = last_idx;
     size_t last_idx_qqx = last_idx;
 
     //if qqxMid event, virtual correction do not occur between
     //qqx pair.
     if(event.type() == event_type::qqxmid){
       const auto backquark = std::find_if(
         begin(partons) + 1, end(partons) - 1 ,
         [](Particle const & s){ return (s.type != pid::gluon); }
       );
       if(backquark == end(partons) || (backquark+1)->type==pid::gluon) return 0;
       if(abs(backquark->type) != abs((backquark+1)->type)) {
         wqq=true;
         wc=false;
       }
       last_idx = std::distance(begin(partons), backquark);
       first_idx_qqx = last_idx+1;
     }
     double exponent = 0;
     const double alpha_s = alpha_s_(mur);
     for(size_t j = first_idx; j < last_idx; ++j){
       exponent += omega0(alpha_s, mur, q)*(
           partons[j+1].rapidity() - partons[j].rapidity()
       );
       q -=partons[j+1].p;
     } // End Loop one
 
     if (last_idx != first_idx_qqx) q -= partons[last_idx+1].p;
     if (wqq)  q -= WBoson.p;
 
     for(size_t j = first_idx_qqx; j < last_idx_qqx; ++j){
       exponent += omega0(alpha_s, mur, q)*(
           partons[j+1].rapidity() - partons[j].rapidity()
       );
       q -= partons[j+1].p;
     }
 
     if (wc) q -= WBoson.p;
 
     assert(
         nearby(q, -1*pb, norm)
         || is_AWZH_boson(partons.back().type)
         || event.type() == event_type::unof
         || event.type() == event_type::qqxexf
     );
 
     return exp(exponent);
   }
 
   double MatrixElement::virtual_corrections(
       Event const & event,
       double mur
   ) const{
     auto const & in = event.incoming();
     auto const & out = event.outgoing();
     fastjet::PseudoJet const & pa = in.front().p;
 #ifndef NDEBUG
     fastjet::PseudoJet const & pb = in.back().p;
     double const norm = (in.front().p + in.back().p).E();
 #endif
 
     const auto AWZH_boson = std::find_if(
         begin(out), end(out),
         [](Particle const & p){ return is_AWZH_boson(p); }
     );
 
     if(AWZH_boson != end(out) && abs(AWZH_boson->type) == pid::Wp){
       return virtual_corrections_W(event, mur, *AWZH_boson);
     }
 
     assert(std::is_sorted(out.begin(), out.end(), rapidity_less{}));
     assert(out.size() >= 2);
     assert(pa.pz() < pb.pz());
 
     fastjet::PseudoJet q = pa - out[0].p;
     size_t first_idx = 0;
     size_t last_idx = out.size() - 1;
 
     // if there is a Higgs boson, extremal qqx or unordered gluon
     // outside the extremal partons then it is not part of the FKL
     // ladder and does not contribute to the virtual corrections
     if((out.front().type == pid::Higgs)
        || event.type() == event_type::unob
        || event.type() == event_type::qqxexb){
       q -= out[1].p;
       ++first_idx;
     }
     if((out.back().type == pid::Higgs)
        || event.type() == event_type::unof
        || event.type() == event_type::qqxexf){
       --last_idx;
     }
 
     size_t first_idx_qqx = last_idx;
     size_t last_idx_qqx = last_idx;
 
     //if qqxMid event, virtual correction do not occur between
     //qqx pair.
     if(event.type() == event_type::qqxmid){
       const auto backquark = std::find_if(
         begin(out) + 1, end(out) - 1 ,
         [](Particle const & s){ return (s.type != pid::gluon && is_parton(s.type)); }
       );
       if(backquark == end(out) || (backquark+1)->type==pid::gluon) return 0;
       last_idx = std::distance(begin(out), backquark);
       first_idx_qqx = last_idx+1;
     }
     double exponent = 0;
     const double alpha_s = alpha_s_(mur);
     for(size_t j = first_idx; j < last_idx; ++j){
       exponent += omega0(alpha_s, mur, q)*(
           out[j+1].rapidity() - out[j].rapidity()
       );
       q -= out[j+1].p;
     }
 
     if (last_idx != first_idx_qqx) q -= out[last_idx+1].p;
 
     for(size_t j = first_idx_qqx; j < last_idx_qqx; ++j){
       exponent += omega0(alpha_s, mur, q)*(
           out[j+1].rapidity() - out[j].rapidity()
       );
       q -= out[j+1].p;
     }
     assert(
         nearby(q, -1*pb, norm)
         || out.back().type == pid::Higgs
         || event.type() == event_type::unof
         || event.type() == event_type::qqxexf
     );
     return exp(exponent);
   }
 } // namespace HEJ
 
 namespace {
   //! Lipatov vertex for partons emitted into extremal jets
   double C2Lipatov(CLHEP::HepLorentzVector qav, CLHEP::HepLorentzVector qbv,
     CLHEP::HepLorentzVector p1, CLHEP::HepLorentzVector p2)
   {
     CLHEP::HepLorentzVector temptrans=-(qav+qbv);
     CLHEP::HepLorentzVector p5=qav-qbv;
     CLHEP::HepLorentzVector CL=temptrans
       + p1*(qav.m2()/p5.dot(p1) + 2.*p5.dot(p2)/p1.dot(p2))
       - p2*(qbv.m2()/p5.dot(p2) + 2.*p5.dot(p1)/p1.dot(p2));
     return -CL.dot(CL);
   }
 
   //! Lipatov vertex with soft subtraction for partons emitted into extremal jets
   double C2Lipatovots(
       CLHEP::HepLorentzVector qav,
       CLHEP::HepLorentzVector qbv,
       CLHEP::HepLorentzVector p1,
       CLHEP::HepLorentzVector p2,
       double lambda
   ) {
     double kperp=(qav-qbv).perp();
     if (kperp>lambda)
       return C2Lipatov(qav, qbv, p1, p2)/(qav.m2()*qbv.m2());
     else {
       double Cls=(C2Lipatov(qav, qbv, p1, p2)/(qav.m2()*qbv.m2()));
       return Cls-4./(kperp*kperp);
     }
   }
 
   //! Lipatov vertex
   double C2Lipatov(CLHEP::HepLorentzVector qav, CLHEP::HepLorentzVector qbv,
     CLHEP::HepLorentzVector pim, CLHEP::HepLorentzVector pip,
     CLHEP::HepLorentzVector pom, CLHEP::HepLorentzVector pop) // B
   {
     CLHEP::HepLorentzVector temptrans=-(qav+qbv);
     CLHEP::HepLorentzVector p5=qav-qbv;
     CLHEP::HepLorentzVector CL=temptrans
       + qav.m2()*(1./p5.dot(pip)*pip + 1./p5.dot(pop)*pop)/2.
       - qbv.m2()*(1./p5.dot(pim)*pim + 1./p5.dot(pom)*pom)/2.
       + ( pip*(p5.dot(pim)/pip.dot(pim) + p5.dot(pom)/pip.dot(pom))
         + pop*(p5.dot(pim)/pop.dot(pim) + p5.dot(pom)/pop.dot(pom))
         - pim*(p5.dot(pip)/pip.dot(pim) + p5.dot(pop)/pop.dot(pim))
         - pom*(p5.dot(pip)/pip.dot(pom) + p5.dot(pop)/pop.dot(pom)) )/2.;
 
     return -CL.dot(CL);
   }
 
   //! Lipatov vertex with soft subtraction
   double C2Lipatovots(
       CLHEP::HepLorentzVector qav,
       CLHEP::HepLorentzVector qbv,
       CLHEP::HepLorentzVector pa,
       CLHEP::HepLorentzVector pb,
       CLHEP::HepLorentzVector p1,
       CLHEP::HepLorentzVector p2,
       double lambda
   ) {
     double kperp=(qav-qbv).perp();
     if (kperp>lambda)
       return C2Lipatov(qav, qbv, pa, pb, p1, p2)/(qav.m2()*qbv.m2());
     else {
       double Cls=(C2Lipatov(qav, qbv, pa, pb, p1, p2)/(qav.m2()*qbv.m2()));
       double temp=Cls-4./(kperp*kperp);
       return temp;
     }
   }
 
   /** Matrix element squared for tree-level current-current scattering
    *  @param aptype          Particle a PDG ID
    *  @param bptype          Particle b PDG ID
    *  @param pn              Particle n Momentum
    *  @param pb              Particle b Momentum
    *  @param p1              Particle 1 Momentum
    *  @param pa              Particle a Momentum
    *  @returns               ME Squared for Tree-Level Current-Current Scattering
    */
   double ME_current(
       int aptype, int bptype,
       CLHEP::HepLorentzVector const & pn,
       CLHEP::HepLorentzVector const & pb,
       CLHEP::HepLorentzVector const & p1,
       CLHEP::HepLorentzVector const & pa
   ){
     if (aptype==21&&bptype==21) {
       return jM2gg(pn,pb,p1,pa);
     } else if (aptype==21&&bptype!=21) {
       if (bptype > 0)
         return jM2qg(pn,pb,p1,pa);
       else
         return jM2qbarg(pn,pb,p1,pa);
     }
     else if (bptype==21&&aptype!=21) { // ----- || -----
       if (aptype > 0)
         return jM2qg(p1,pa,pn,pb);
       else
         return jM2qbarg(p1,pa,pn,pb);
     }
     else { // they are both quark
       if (bptype>0) {
         if (aptype>0)
           return jM2qQ(pn,pb,p1,pa);
         else
           return jM2qQbar(pn,pb,p1,pa);
       }
       else {
         if (aptype>0)
           return jM2qQbar(p1,pa,pn,pb);
         else
           return jM2qbarQbar(pn,pb,p1,pa);
       }
     }
     throw std::logic_error("unknown particle types");
   }
 
   /** Matrix element squared for tree-level current-current scattering With W+Jets
    *  @param aptype          Particle a PDG ID
    *  @param bptype          Particle b PDG ID
    *  @param pn              Particle n Momentum
    *  @param pb              Particle b Momentum
    *  @param p1              Particle 1 Momentum
    *  @param pa              Particle a Momentum
    *  @param wc              Boolean. True->W Emitted from b. Else; emitted from leg a
    *  @returns               ME Squared for Tree-Level Current-Current Scattering
    */
   double ME_W_current(
       int aptype, int bptype,
       CLHEP::HepLorentzVector const & pn,
       CLHEP::HepLorentzVector const & pb,
       CLHEP::HepLorentzVector const & p1,
       CLHEP::HepLorentzVector const & pa,
       CLHEP::HepLorentzVector const & plbar,
       CLHEP::HepLorentzVector const & pl,
       bool const wc
   ){
     // We know it cannot be gg incoming.
     assert(!(aptype==21 && bptype==21));
     if (aptype==21&&bptype!=21) {
       if (bptype > 0)
         return jMWqg(pn,plbar,pl,pb,p1,pa);
       else
         return jMWqbarg(pn,plbar,pl,pb,p1,pa);
     }
     else if (bptype==21&&aptype!=21) { // ----- || -----
       if (aptype > 0)
         return jMWqg(p1,plbar,pl,pa,pn,pb);
       else
         return jMWqbarg(p1,plbar,pl,pa,pn,pb);
     }
     else { // they are both quark
       if (wc==true){ // emission off b, (first argument pbout)
         if (bptype>0) {
           if (aptype>0)
             return jMWqQ(pn,plbar,pl,pb,p1,pa);
           else
             return jMWqQbar(pn,plbar,pl,pb,p1,pa);
         }
         else {
           if (aptype>0)
             return jMWqbarQ(pn,plbar,pl,pb,p1,pa);
           else
             return jMWqbarQbar(pn,plbar,pl,pb,p1,pa);
         }
       }
       else{ // emission off a, (first argument paout)
         if (aptype > 0) {
           if (bptype > 0)
             return jMWqQ(p1,plbar,pl,pa,pn,pb);
           else
             return jMWqQbar(p1,plbar,pl,pa,pn,pb);
         }
         else {  // a is anti-quark
           if (bptype > 0)
             return jMWqbarQ(p1,plbar,pl,pa,pn,pb);
           else
             return jMWqbarQbar(p1,plbar,pl,pa,pn,pb);
         }
 
       }
     }
     throw std::logic_error("unknown particle types");
   }
 
   /** Matrix element squared for backwards uno tree-level current-current scattering With W+Jets
    *  @param aptype          Particle a PDG ID
    *  @param bptype          Particle b PDG ID
    *  @param pn              Particle n Momentum
    *  @param pb              Particle b Momentum
    *  @param p1              Particle 1 Momentum
    *  @param pa              Particle a Momentum
    *  @param pg              Unordered gluon momentum
    *  @param wc              Boolean. True->W Emitted from b. Else; emitted from leg a
    *  @returns               ME Squared for unob Tree-Level Current-Current Scattering
    */
   double ME_W_unob_current(
       int aptype, int bptype,
       CLHEP::HepLorentzVector const & pn,
       CLHEP::HepLorentzVector const & pb,
       CLHEP::HepLorentzVector const & p1,
       CLHEP::HepLorentzVector const & pa,
       CLHEP::HepLorentzVector const & pg,
       CLHEP::HepLorentzVector const & plbar,
       CLHEP::HepLorentzVector const & pl,
       bool const wc
   ){
     // we know they are not both gluons
     if (bptype == 21 && aptype != 21) { // b gluon => W emission off a
       if (aptype > 0)
         return jM2Wunogqg(pg,p1,plbar,pl,pa,pn,pb);
       else
         return jM2Wunogqbarg(pg,p1,plbar,pl,pa,pn,pb);
     }
 
     else { // they are both quark
       if (wc==true) {// emission off b, i.e. b is first current
         if (bptype>0){
           if (aptype>0)
             return junobMWqQg(pn,plbar,pl,pb,p1,pa,pg);
           else
             return junobMWqQbarg(pn,plbar,pl,pb,p1,pa,pg);
         }
         else{
           if (aptype>0)
             return junobMWqbarQg(pn,plbar,pl,pb,p1,pa,pg);
           else
             return junobMWqbarQbarg(pn,plbar,pl,pb,p1,pa,pg);
         }
       }
       else {// wc == false, emission off a, i.e. a is first current
         if (aptype > 0) {
           if (bptype > 0) //qq
             return jM2WunogqQ(pg,p1,plbar,pl,pa,pn,pb);
           else //qqbar
             return jM2WunogqQbar(pg,p1,plbar,pl,pa,pn,pb);
         }
         else {  // a is anti-quark
           if (bptype > 0) //qbarq
             return jM2WunogqbarQ(pg,p1,plbar,pl,pa,pn,pb);
           else //qbarqbar
             return jM2WunogqbarQbar(pg,p1,plbar,pl,pa,pn,pb);
         }
       }
     }
   }
 
   /** Matrix element squared for uno forward tree-level current-current scattering With W+Jets
    *  @param aptype          Particle a PDG ID
    *  @param bptype          Particle b PDG ID
    *  @param pn              Particle n Momentum
    *  @param pb              Particle b Momentum
    *  @param p1              Particle 1 Momentum
    *  @param pa              Particle a Momentum
    *  @param pg              Unordered gluon momentum
    *  @param wc              Boolean. True->W Emitted from b. Else; emitted from leg a
    *  @returns               ME Squared for unof Tree-Level Current-Current Scattering
    */
   double ME_W_unof_current(
                            int aptype, int bptype,
                            CLHEP::HepLorentzVector const & pn,
                            CLHEP::HepLorentzVector const & pb,
                            CLHEP::HepLorentzVector const & p1,
                            CLHEP::HepLorentzVector const & pa,
                            CLHEP::HepLorentzVector const & pg,
                            CLHEP::HepLorentzVector const & plbar,
                            CLHEP::HepLorentzVector const & pl,
                            bool const wc
                            ){
 
     // we know they are not both gluons
     if (aptype==21 && bptype!=21) {//a gluon => W emission off b
       if (bptype > 0)
         return jM2Wunogqg(pg, pn,plbar, pl, pb, p1, pa);
       else
         return jM2Wunogqbarg(pg, pn,plbar, pl, pb, p1, pa);
     }
     else { // they are both quark
       if (wc==true) {// emission off b, i.e. b is first current
         if (bptype>0){
           if (aptype>0)
             return jM2WunogqQ(pg,pn,plbar,pl,pb,p1,pa);
           else
             return jM2WunogqQbar(pg,pn,plbar,pl,pb,p1,pa);
         }
         else{
           if (aptype>0)
             return jM2WunogqbarQ(pg,pn,plbar,pl,pb,p1,pa);
           else
             return jM2WunogqbarQbar(pg,pn,plbar,pl,pb,p1,pa);
         }
       }
       else {// wc == false, emission off a, i.e. a is first current
         if (aptype > 0) {
           if (bptype > 0) //qq
             return junofMWgqQ(pg,pn,pb,p1,plbar,pl,pa);
           else //qqbar
             return junofMWgqQbar(pg,pn,pb,p1,plbar,pl,pa);
         }
         else {  // a is anti-quark
           if (bptype > 0) //qbarq
             return junofMWgqbarQ(pg,pn,pb,p1,plbar,pl,pa);
           else //qbarqbar
             return junofMWgqbarQbar(pg,pn,pb,p1,plbar,pl,pa);
         }
       }
     }
   }
 
   /** \brief Matrix element squared for backward qqx tree-level current-current scattering With W+Jets
    *  @param aptype          Particle a PDG ID
    *  @param bptype          Particle b PDG ID
    *  @param pa              Initial state a Momentum
    *  @param pb              Initial state b Momentum
    *  @param pq              Final state q Momentum
    *  @param pqbar           Final state qbar Momentum
    *  @param pn              Final state n Momentum
    *  @param plbar           Final state anti-lepton momentum
    *  @param pl              Final state lepton momentum
    *  @param wc              Boolean. True->W Emitted from b. Else; emitted from leg a
    *  @returns               ME Squared for qqxb Tree-Level Current-Current Scattering
    */
   double ME_W_qqxb_current(
                            int aptype, int bptype,
                            CLHEP::HepLorentzVector const & pa,
                            CLHEP::HepLorentzVector const & pb,
                            CLHEP::HepLorentzVector const & pq,
                            CLHEP::HepLorentzVector const & pqbar,
                            CLHEP::HepLorentzVector const & pn,
                            CLHEP::HepLorentzVector const & plbar,
                            CLHEP::HepLorentzVector const & pl,
                            bool const wc
                            ){
     // CAM factors for the qqx amps, and qqbar ordering (default, qbar extremal)
     bool swapQuarkAntiquark=false;
     double CFbackward;
     if (pqbar.rapidity() > pq.rapidity()){
       swapQuarkAntiquark=true;
       CFbackward = (0.5*(3.-1./3.)*(pa.minus()/(pq.minus())+(pq.minus())/pa.minus())+1./3.)*3./4.;
     }
     else{
       CFbackward = (0.5*(3.-1./3.)*(pa.minus()/(pqbar.minus())+(pqbar.minus())/pa.minus())+1./3.)*3./4.;
     }
     // With qqbar we could have 2 incoming gluons and W Emission
     if (aptype==21&&bptype==21) {//a gluon, b gluon gg->qqbarWg
       // This will be a wqqx emission as there is no other possible W Emission Site.
       if (swapQuarkAntiquark){
         return jM2Wggtoqqbarg(pa, pqbar, plbar, pl, pq, pn,pb)*CFbackward;}
       else {
         return jM2Wggtoqbarqg(pa, pq, plbar, pl, pqbar, pn,pb)*CFbackward;}
     }
     else if (aptype==21&&bptype!=21 ) {//a gluon => W emission off b leg or qqx
       if (wc!=1){ // W Emitted from backwards qqx
         if (swapQuarkAntiquark){
           return jM2WgQtoqqbarQ(pa, pq, plbar, pl, pqbar, pn, pb)*CFbackward;}
         else{
           return jM2WgQtoqbarqQ(pa, pq, plbar, pl, pqbar, pn, pb)*CFbackward;}
       }
       else {   // W Must be emitted from forwards leg.
         if(bptype > 0){
           if (swapQuarkAntiquark){
             return jM2WgqtoQQqW(pb, pa, pn, pqbar, pq, plbar, pl, false)*CFbackward;}
           else{
             return jM2WgqtoQQqW(pb, pa, pn, pq, pqbar, plbar, pl, false)*CFbackward;}
         } else {
           if (swapQuarkAntiquark){
             return jM2WgqtoQQqW(pb, pa, pn, pqbar, pq, plbar, pl, true)*CFbackward;}
           else{
             return jM2WgqtoQQqW(pb, pa, pn, pq, pqbar, plbar, pl, true)*CFbackward;}
         }
       }
     }
     else{
       throw std::logic_error("Incompatible incoming particle types with qqxb");
     }
   }
 
   /*  \brief Matrix element squared for forward qqx tree-level current-current scattering With W+Jets
    *  @param aptype          Particle a PDG ID
    *  @param bptype          Particle b PDG ID
    *  @param pa              Initial state a Momentum
    *  @param pb              Initial state b Momentum
    *  @param pq              Final state q Momentum
    *  @param pqbar           Final state qbar Momentum
    *  @param p1              Final state 1 Momentum
    *  @param plbar           Final state anti-lepton momentum
    *  @param pl              Final state lepton momentum
    *  @param wc              Boolean. True->W Emitted from b. Else; emitted from leg a
    *  @returns               ME Squared for qqxf Tree-Level Current-Current Scattering
    */
   double ME_W_qqxf_current(
                            int aptype, int bptype,
                            CLHEP::HepLorentzVector const & pa,
                            CLHEP::HepLorentzVector const & pb,
                            CLHEP::HepLorentzVector const & pq,
                            CLHEP::HepLorentzVector const & pqbar,
                            CLHEP::HepLorentzVector const & p1,
                            CLHEP::HepLorentzVector const & plbar,
                            CLHEP::HepLorentzVector const & pl,
                            bool const wc
                            ){
     // CAM factors for the qqx amps, and qqbar ordering (default, qbar extremal)
     bool swapQuarkAntiquark=false;
     double CFforward;
     if (pqbar.rapidity() < pq.rapidity()){
       swapQuarkAntiquark=true;
       CFforward = (0.5*(3.-1./3.)*(pb.plus()/(pq.plus())+(pq.plus())/pb.plus())+1./3.)*3./4.;
     }
     else{
       CFforward = (0.5*(3.-1./3.)*(pb.plus()/(pqbar.plus())+(pqbar.plus())/pb.plus())+1./3.)*3./4.;
     }
 
     // With qqbar we could have 2 incoming gluons and W Emission
     if (aptype==21&&bptype==21) {//a gluon, b gluon gg->qqbarWg
       // This will be a wqqx emission as there is no other possible W Emission Site.
       if (swapQuarkAntiquark){
         return jM2Wggtoqqbarg(pb, pqbar, plbar, pl, pq, p1,pa)*CFforward;}
       else {
         return jM2Wggtoqbarqg(pb, pq, plbar, pl, pqbar, p1,pa)*CFforward;}
     }
 
     else if (bptype==21&&aptype!=21) {// b gluon => W emission off a or qqx
       if (wc==1){ // W Emitted from forwards qqx
         if (swapQuarkAntiquark){
           return jM2WgQtoqbarqQ(pb, pq, plbar,pl, pqbar, p1, pa)*CFforward;}
         else {
           return jM2WgQtoqqbarQ(pb, pq, plbar,pl, pqbar, p1, pa)*CFforward;}
       }
       // W Must be emitted from backwards leg.
       if (aptype > 0){
         if (swapQuarkAntiquark){
           return jM2WgqtoQQqW(pa,pb, p1, pqbar, pq, plbar, pl, false)*CFforward;}
         else{
           return jM2WgqtoQQqW(pa,pb, p1, pq, pqbar, plbar, pl, false)*CFforward;}
       } else
         {
           if (swapQuarkAntiquark){
             return jM2WgqtoQQqW(pa,pb, p1, pqbar, pq, plbar, pl, true)*CFforward;}
         else{
           return jM2WgqtoQQqW(pa,pb, p1, pq, pqbar, plbar, pl, true)*CFforward;}
         }
     }
     else{
       throw std::logic_error("Incompatible incoming particle types with qqxf");
     }
   }
 
   /*  \brief Matrix element squared for central qqx tree-level current-current scattering With W+Jets
    *  @param aptype          Particle a PDG ID
    *  @param bptype          Particle b PDG ID
    *  @param nabove          Number of gluons emitted before central qqxpair
    *  @param nbelow          Number of gluons emitted after central qqxpair
    *  @param pa              Initial state a Momentum
    *  @param pb              Initial state b Momentum\
    *  @param pq              Final state qbar Momentum
    *  @param pqbar           Final state q Momentum
    *  @param partons         Vector of all outgoing partons
    *  @param plbar           Final state anti-lepton momentum
    *  @param pl              Final state lepton momentum
    *  @param wqq             Boolean. True siginfies W boson is emitted from Central qqx
    *  @param wc              Boolean. wc=true signifies w boson emitted from leg b; if wqq=false.
    *  @returns               ME Squared for qqxmid Tree-Level Current-Current Scattering
    */
   double ME_W_qqxmid_current(
                            int aptype, int bptype,
                            int nabove, int nbelow,
                            CLHEP::HepLorentzVector const & pa,
                            CLHEP::HepLorentzVector const & pb,
                            CLHEP::HepLorentzVector const & pq,
                            CLHEP::HepLorentzVector const & pqbar,
                            std::vector<HLV> partons,
                            CLHEP::HepLorentzVector const & plbar,
                            CLHEP::HepLorentzVector const & pl,
                            bool const wqq, bool const wc
                            ){
     // CAM factors for the qqx amps, and qqbar ordering (default, pq backwards)
     bool swapQuarkAntiquark=false;
     if (pqbar.rapidity() < pq.rapidity()){
       swapQuarkAntiquark=true;
     }
     double CFforward = (0.5*(3.-1./3.)*(pb.plus()/(partons[partons.size()-1].plus())+(partons[partons.size()-1].plus())/pb.plus())+1./3.)*3./4.;
     double CFbackward = (0.5*(3.-1./3.)*(pa.minus()/(partons[0].minus())+(partons[0].minus())/pa.minus())+1./3.)*3./4.;
     double wt=1.;
 
     if (aptype==21)  wt*=CFbackward;
     if (bptype==21)  wt*=CFforward;
 
     if (aptype <=0 && bptype <=0){ // Both External AntiQuark
       if (wqq==1){//emission from central qqbar
   return wt*jM2WqqtoqQQq(pa, pb, pl,plbar, partons,true,true, swapQuarkAntiquark, nabove);
       }
       else if (wc==1){//emission from b leg
   return wt*jM2WqqtoqQQqW(pa, pb, pl,plbar, partons, true,true, swapQuarkAntiquark, nabove, nbelow, true);
       }
       else { // emission from a leg
   return wt*jM2WqqtoqQQqW(pa, pb, pl,plbar, partons, true,true, swapQuarkAntiquark, nabove, nbelow, false);
       }
     } // end both antiquark
     else if (aptype<=0){ // a is antiquark
       if (wqq==1){//emission from central qqbar
   return wt*jM2WqqtoqQQq(pa, pb, pl,plbar, partons, false, true, swapQuarkAntiquark, nabove);
       }
       else if (wc==1){//emission from b leg
   return wt*jM2WqqtoqQQqW(pa, pb, pl,plbar, partons,false,true, swapQuarkAntiquark, nabove, nbelow, true);
       }
       else { // emission from a leg
   return wt*jM2WqqtoqQQqW(pa, pb, pl,plbar, partons, false, true, swapQuarkAntiquark, nabove, nbelow, false);
       }
 
     } // end a is antiquark
 
     else if (bptype<=0){ // b is antiquark
       if (wqq==1){//emission from central qqbar
   return wt*jM2WqqtoqQQq(pa, pb, pl,plbar, partons, true, false, swapQuarkAntiquark, nabove);
       }
       else if (wc==1){//emission from b leg
   return wt*jM2WqqtoqQQqW(pa, pb, pl,plbar, partons, true, false, swapQuarkAntiquark, nabove, nbelow, true);
       }
       else { // emission from a leg
   return wt*jM2WqqtoqQQqW(pa, pb, pl,plbar, partons, true, false, swapQuarkAntiquark, nabove, nbelow, false);
       }
 
     } //end b is antiquark
     else{ //Both Quark or gluon
       if (wqq==1){//emission from central qqbar
         return wt*jM2WqqtoqQQq(pa, pb, pl, plbar, partons, false, false, swapQuarkAntiquark, nabove);}
       else if (wc==1){//emission from b leg
   return wt*jM2WqqtoqQQqW(pa, pb, pl,plbar, partons, false, false, swapQuarkAntiquark, nabove, nbelow, true);
       }
       else { // emission from a leg
   return wt*jM2WqqtoqQQqW(pa, pb, pl,plbar, partons, false, false, swapQuarkAntiquark, nabove, nbelow, false);
       }
 
     }
   }
 
   /** \brief Matrix element squared for tree-level current-current scattering with Higgs
    *  @param aptype          Particle a PDG ID
    *  @param bptype          Particle b PDG ID
    *  @param pn              Particle n Momentum
    *  @param pb              Particle b Momentum
    *  @param p1              Particle 1 Momentum
    *  @param pa              Particle a Momentum
    *  @param qH              t-channel momentum before Higgs
    *  @param qHp1            t-channel momentum after Higgs
    *  @returns               ME Squared for Tree-Level Current-Current Scattering with Higgs
    */
   double ME_Higgs_current(
       int aptype, int bptype,
       CLHEP::HepLorentzVector const & pn,
       CLHEP::HepLorentzVector const & pb,
       CLHEP::HepLorentzVector const & p1,
       CLHEP::HepLorentzVector const & pa,
       CLHEP::HepLorentzVector const & qH,  // t-channel momentum before Higgs
       CLHEP::HepLorentzVector const & qHp1, // t-channel momentum after Higgs
       double mt, bool include_bottom, double mb
   ){
     if (aptype==21&&bptype==21) // gg initial state
       return MH2gg(pn,pb,p1,pa,-qHp1,-qH,mt,include_bottom,mb);
     else if (aptype==21&&bptype!=21) {
       if (bptype > 0)
         return MH2qg(pn,pb,p1,pa,-qHp1,-qH,mt,include_bottom,mb)*4./9.;
       else
         return MH2qbarg(pn,pb,p1,pa,-qHp1,-qH,mt,include_bottom,mb)*4./9.;
     }
     else if (bptype==21&&aptype!=21) {
       if (aptype > 0)
         return MH2qg(p1,pa,pn,pb,-qH,-qHp1,mt,include_bottom,mb)*4./9.;
       else
         return MH2qbarg(p1,pa,pn,pb,-qH,-qHp1,mt,include_bottom,mb)*4./9.;
     }
     else { // they are both quark
       if (bptype>0) {
         if (aptype>0)
           return MH2qQ(pn,pb,p1,pa,-qHp1,-qH,mt,include_bottom,mb)*4.*4./(9.*9.);
         else
           return MH2qQbar(pn,pb,p1,pa,-qHp1,-qH,mt,include_bottom,mb)*4.*4./(9.*9.);
       }
       else {
         if (aptype>0)
           return MH2qQbar(p1,pa,pn,pb,-qH,-qHp1,mt,include_bottom,mb)*4.*4./(9.*9.);
         else
           return MH2qbarQbar(pn,pb,p1,pa,-qHp1,-qH,mt,include_bottom,mb)*4.*4./(9.*9.);
       }
     }
     throw std::logic_error("unknown particle types");
   }
 
   /** \brief  Current matrix element squared with Higgs and unordered forward emission
    *  @param aptype          Particle A PDG ID
    *  @param bptype          Particle B PDG ID
    *  @param punof           Unordered Particle Momentum
    *  @param pn              Particle n Momentum
    *  @param pb              Particle b Momentum
    *  @param p1              Particle 1 Momentum
    *  @param pa              Particle a Momentum
    *  @param qH              t-channel momentum before Higgs
    *  @param qHp1            t-channel momentum after Higgs
    *  @returns               ME Squared with Higgs and unordered forward emission
    */
   double ME_Higgs_current_unof(
       int aptype, int bptype,
       CLHEP::HepLorentzVector const & punof,
       CLHEP::HepLorentzVector const & pn,
       CLHEP::HepLorentzVector const & pb,
       CLHEP::HepLorentzVector const & p1,
       CLHEP::HepLorentzVector const & pa,
       CLHEP::HepLorentzVector const & qH,  // t-channel momentum before Higgs
       CLHEP::HepLorentzVector const & qHp1, // t-channel momentum after Higgs
       double mt, bool include_bottom, double mb
   ){
     if (aptype==21&&bptype!=21) {
       if (bptype > 0)
         return jM2unogqHg(punof,pn,pb,p1,pa,-qHp1,-qH,mt,include_bottom,mb);
       else
         return jM2unogqbarHg(punof,pn,pb,p1,pa,-qHp1,-qH,mt,include_bottom,mb);
     }
     else { // they are both quark
       if (bptype>0) {
         if (aptype>0)
           return jM2unogqHQ(punof,pn,pb,p1,pa,-qHp1,-qH,mt,include_bottom,mb);
         else
           return jM2unogqHQbar(punof,pn,pb,p1,pa,-qHp1,-qH,mt,include_bottom,mb);
       }
       else {
         if (aptype>0)
           return jM2unogqbarHQ(punof,pn,pb,p1,pa,-qHp1,-qH,mt,include_bottom,mb);
         else
           return jM2unogqbarHQbar(punof,pn,pb,p1,pa,-qHp1,-qH,mt,include_bottom,mb);
       }
     }
     throw std::logic_error("unknown particle types");
   }
 
   /** \brief Current matrix element squared with Higgs and unordered backward emission
    *  @param aptype          Particle A PDG ID
    *  @param bptype          Particle B PDG ID
    *  @param pn              Particle n Momentum
    *  @param pb              Particle b Momentum
    *  @param punob           Unordered back Particle Momentum
    *  @param p1              Particle 1 Momentum
    *  @param pa              Particle a Momentum
    *  @param qH              t-channel momentum before Higgs
    *  @param qHp1            t-channel momentum after Higgs
    *  @returns               ME Squared with Higgs and unordered backward emission
    */
   double ME_Higgs_current_unob(
       int aptype, int bptype,
       CLHEP::HepLorentzVector const & pn,
       CLHEP::HepLorentzVector const & pb,
       CLHEP::HepLorentzVector const & punob,
       CLHEP::HepLorentzVector const & p1,
       CLHEP::HepLorentzVector const & pa,
       CLHEP::HepLorentzVector const & qH,  // t-channel momentum before Higgs
       CLHEP::HepLorentzVector const & qHp1, // t-channel momentum after Higgs
       double mt, bool include_bottom, double mb
   ){
     if (bptype==21&&aptype!=21) {
       if (aptype > 0)
         return jM2unobgHQg(pn,pb,punob,p1,pa,-qHp1,-qH,mt,include_bottom,mb);
       else
         return jM2unobgHQbarg(pn,pb,punob,p1,pa,-qHp1,-qH,mt,include_bottom,mb);
     }
     else { // they are both quark
       if (aptype>0) {
         if (bptype>0)
           return jM2unobqHQg(pn,pb,punob,p1,pa,-qHp1,-qH,mt,include_bottom,mb);
         else
           return jM2unobqbarHQg(pn,pb,punob,p1,pa,-qHp1,-qH,mt,include_bottom,mb);
       }
       else {
         if (bptype>0)
           return jM2unobqHQbarg(pn,pb,punob,p1,pa,-qHp1,-qH,mt,include_bottom,mb);
         else
           return jM2unobqbarHQbarg(pn,pb,punob,p1,pa,-qHp1,-qH,mt,include_bottom,mb);
       }
     }
     throw std::logic_error("unknown particle types");
   }
 
   CLHEP::HepLorentzVector to_HepLorentzVector(HEJ::Particle const & particle){
     return {particle.p.px(), particle.p.py(), particle.p.pz(), particle.p.E()};
   }
 
   void validate(HEJ::MatrixElementConfig const & config) {
 #ifndef HEJ_BUILD_WITH_QCDLOOP
     if(!config.Higgs_coupling.use_impact_factors) {
       throw std::invalid_argument{
         "Invalid Higgs coupling settings.\n"
         "HEJ without QCDloop support can only use impact factors.\n"
         "Set use_impact_factors to true or recompile HEJ.\n"
        };
     }
 #endif
     if(config.Higgs_coupling.use_impact_factors
       && config.Higgs_coupling.mt != std::numeric_limits<double>::infinity()) {
       throw std::invalid_argument{
         "Conflicting settings: "
           "impact factors may only be used in the infinite top mass limit"
       };
     }
   }
 } // namespace anonymous
 
 namespace HEJ{
   MatrixElement::MatrixElement(
       std::function<double (double)> alpha_s,
       MatrixElementConfig conf
   ):
     alpha_s_{std::move(alpha_s)},
     param_{std::move(conf)}
   {
     validate(param_);
   }
 
   double MatrixElement::tree_kin(
       Event const & ev
   ) const {
     if(! is_HEJ(ev.type())) return 0.;
 
     auto AWZH_boson = std::find_if(
         begin(ev.outgoing()), end(ev.outgoing()),
         [](Particle const & p){return is_AWZH_boson(p);}
     );
 
     if(AWZH_boson == end(ev.outgoing()))
       return tree_kin_jets(ev);
 
     switch(AWZH_boson->type){
     case pid::Higgs:
       return tree_kin_Higgs(ev);
     case pid::Wp:
     case pid::Wm:
       return tree_kin_W(ev);
     // TODO
     case pid::photon:
     case pid::Z:
     default:
       throw not_implemented("Emission of boson of unsupported type");
     }
   }
 
   namespace{
     constexpr int extremal_jet_idx = 1;
     constexpr int no_extremal_jet_idx = 0;
 
     bool treat_as_extremal(Particle const & parton){
       return parton.p.user_index() == extremal_jet_idx;
     }
 
     template<class InputIterator>
       double FKL_ladder_weight(
           InputIterator begin_gluon, InputIterator end_gluon,
           CLHEP::HepLorentzVector const & q0,
           CLHEP::HepLorentzVector const & pa, CLHEP::HepLorentzVector const & pb,
           CLHEP::HepLorentzVector const & p1, CLHEP::HepLorentzVector const & pn,
           double lambda
       ){
       double wt = 1;
       auto qi = q0;
       for(auto gluon_it = begin_gluon; gluon_it != end_gluon; ++gluon_it){
         assert(gluon_it->type == pid::gluon);
         const auto g = to_HepLorentzVector(*gluon_it);
         const auto qip1 = qi - g;
 
         if(treat_as_extremal(*gluon_it)){
           wt *= C2Lipatovots(qip1, qi, pa, pb, lambda)*C_A;
         } else{
           wt *= C2Lipatovots(qip1, qi, pa, pb, p1, pn, lambda)*C_A;
         }
 
         qi = qip1;
       }
       return wt;
     }
 
   }  // namespace anonymous
 
   std::vector<Particle> MatrixElement::tag_extremal_jet_partons(
       Event const & ev
   ) const{
     auto out_partons = filter_partons(ev.outgoing());
     if(out_partons.size() == ev.jets().size()){
       // no additional emissions in extremal jets, don't need to tag anything
       for(auto & parton: out_partons){
         parton.p.set_user_index(no_extremal_jet_idx);
       }
       return out_partons;
     }
     // TODO: avoid reclustering
     fastjet::ClusterSequence cs(to_PseudoJet(out_partons), ev.jet_def());
     const auto jets = sorted_by_rapidity(cs.inclusive_jets(ev.min_jet_pt()));
     assert(jets.size() >= 2);
     auto most_backward = begin(jets);
     auto most_forward = end(jets) - 1;
     // skip jets caused by unordered emission or qqx
     if(ev.type() == event_type::unob || ev.type() == event_type::qqxexb){
       assert(jets.size() >= 3);
       ++most_backward;
     }
     else if(ev.type() == event_type::unof || ev.type() == event_type::qqxexf){
       assert(jets.size() >= 3);
       --most_forward;
     }
     const auto extremal_jet_indices = cs.particle_jet_indices(
         {*most_backward, *most_forward}
     );
     assert(extremal_jet_indices.size() == out_partons.size());
     for(size_t i = 0; i < out_partons.size(); ++i){
       assert(HEJ::is_parton(out_partons[i]));
       const int idx = (extremal_jet_indices[i]>=0)?
         extremal_jet_idx:
         no_extremal_jet_idx;
       out_partons[i].p.set_user_index(idx);
     }
     return out_partons;
   }
 
   double MatrixElement::tree_kin_jets(
       Event const & ev
   ) const {
     auto const & incoming = ev.incoming();
     const auto partons = tag_extremal_jet_partons(ev);
     if(is_uno(ev.type())){
       throw not_implemented("unordered emission not implemented for pure jets");
     }
 
     const auto pa = to_HepLorentzVector(incoming[0]);
     const auto pb = to_HepLorentzVector(incoming[1]);
 
     const auto p1 = to_HepLorentzVector(partons.front());
     const auto pn = to_HepLorentzVector(partons.back());
 
     return ME_current(
         incoming[0].type, incoming[1].type,
         pn, pb, p1, pa
     )/(4.*(N_C*N_C - 1.))*FKL_ladder_weight(
         begin(partons) + 1, end(partons) - 1,
         pa - p1, pa, pb, p1, pn,
         param_.regulator_lambda
     );
   }
 
   namespace{
     double tree_kin_W_FKL(
           int aptype, int bptype, HLV pa, HLV pb,
           std::vector<Particle> const & partons,
           HLV plbar, HLV pl,
           double lambda
     ) {
       auto p1 = to_HepLorentzVector(partons[0]);
       auto pn = to_HepLorentzVector(partons[partons.size() - 1]);
 
       auto begin_ladder = begin(partons) + 1;
       auto end_ladder = end(partons) - 1;
 
       bool wc = true;
       auto q0 = pa - p1;
       if (aptype!=partons[0].type) { //leg a emits w
         wc = false;
         q0 -=pl + plbar;
       }
 
       const double current_factor = ME_W_current(
           aptype, bptype, pn, pb,
           p1, pa, plbar, pl, wc
       );
 
       const double ladder_factor = FKL_ladder_weight(
           begin_ladder, end_ladder,
           q0, pa, pb, p1, pn,
           lambda
       );
       return current_factor*ladder_factor;
     }
 
     double tree_kin_W_unob(
         int aptype, int bptype, HLV pa, HLV pb,
         std::vector<Particle> const & partons,
         HLV plbar, HLV pl,
         double lambda
     ) {
       auto pg = to_HepLorentzVector(partons[0]);
       auto p1 = to_HepLorentzVector(partons[1]);
       auto pn = to_HepLorentzVector(partons[partons.size() - 1]);
 
       auto begin_ladder = begin(partons) + 2;
       auto end_ladder = end(partons) - 1;
 
       bool wc = true;
       auto q0 = pa - p1 -pg;
       if (aptype!=partons[1].type) { //leg a emits w
         wc = false;
         q0 -=pl + plbar;
       }
 
       const double current_factor = ME_W_unob_current(
           aptype, bptype, pn, pb,
           p1, pa, pg, plbar, pl, wc
       );
 
       const double ladder_factor = FKL_ladder_weight(
           begin_ladder, end_ladder,
           q0, pa, pb, p1, pn,
           lambda
       );
       return current_factor*C_A*C_A/(N_C*N_C-1.)*ladder_factor;
     }
 
     double tree_kin_W_unof(
         int aptype, int bptype, HLV pa, HLV pb,
         std::vector<Particle> const & partons,
         HLV plbar, HLV pl,
         double lambda
     ) {
       auto p1 = to_HepLorentzVector(partons[0]);
       auto pn = to_HepLorentzVector(partons[partons.size() - 2]);
       auto pg = to_HepLorentzVector(partons[partons.size() - 1]);
 
       auto begin_ladder = begin(partons) + 1;
       auto end_ladder = end(partons) - 2;
 
       bool wc = true;
       auto q0 = pa - p1;
       if (aptype!=partons[0].type) { //leg a emits w
         wc = false;
         q0 -=pl + plbar;
       }
 
       const double current_factor = ME_W_unof_current(
           aptype, bptype, pn, pb,
           p1, pa, pg, plbar, pl, wc
       );
 
       const double ladder_factor = FKL_ladder_weight(
           begin_ladder, end_ladder,
           q0, pa, pb, p1, pn,
           lambda
       );
       return current_factor*C_A*C_A/(N_C*N_C-1.)*ladder_factor;
     }
 
     double tree_kin_W_qqxb(
         int aptype, int bptype, HLV pa, HLV pb,
         std::vector<Particle> const & partons,
         HLV plbar, HLV pl,
         double lambda
     ) {
       HLV pq,pqbar;
       if(is_quark(partons[0])){
         pq = to_HepLorentzVector(partons[0]);
         pqbar = to_HepLorentzVector(partons[1]);
       }
       else{
         pq = to_HepLorentzVector(partons[1]);
         pqbar = to_HepLorentzVector(partons[0]);
       }
       auto p1 = to_HepLorentzVector(partons[0]);
       auto pn = to_HepLorentzVector(partons[partons.size() - 1]);
 
       auto begin_ladder = begin(partons) + 2;
       auto end_ladder = end(partons) - 1;
 
       bool wc = true;
       auto q0 = pa - pq - pqbar;
       if (partons[1].type!=partons[0].type) { //leg a emits w
         wc = false;
         q0 -=pl + plbar;
       }
 
       const double current_factor = ME_W_qqxb_current(
           aptype, bptype, pa, pb,
           pq, pqbar, pn, plbar, pl, wc
       );
 
       const double ladder_factor = FKL_ladder_weight(
           begin_ladder, end_ladder,
           q0, pa, pb, p1, pn,
           lambda
       );
       return current_factor*C_A*C_A/(N_C*N_C-1.)*ladder_factor;
     }
 
     double tree_kin_W_qqxf(
         int aptype, int bptype, HLV pa, HLV pb,
         std::vector<Particle> const & partons,
         HLV plbar, HLV pl,
         double lambda
     ) {
       HLV pq,pqbar;
       if(is_quark(partons[partons.size() - 1])){
         pq = to_HepLorentzVector(partons[partons.size() - 1]);
         pqbar = to_HepLorentzVector(partons[partons.size() - 2]);
       }
       else{
         pq = to_HepLorentzVector(partons[partons.size() - 2]);
         pqbar = to_HepLorentzVector(partons[partons.size() - 1]);
       }
       auto p1 = to_HepLorentzVector(partons[0]);
       auto pn = to_HepLorentzVector(partons[partons.size() - 1]);
 
       auto begin_ladder = begin(partons) + 1;
       auto end_ladder = end(partons) - 2;
 
       bool wc = true;
       auto q0 = pa - p1;
       if (aptype!=partons[0].type) { //leg a emits w
         wc = false;
         q0 -=pl + plbar;
       }
 
       const double current_factor = ME_W_qqxf_current(
           aptype, bptype, pa, pb,
           pq, pqbar, p1, plbar, pl, wc
       );
 
       const double ladder_factor = FKL_ladder_weight(
           begin_ladder, end_ladder,
           q0, pa, pb, p1, pn,
           lambda
       );
       return current_factor*C_A*C_A/(N_C*N_C-1.)*ladder_factor;
     }
 
     double tree_kin_W_qqxmid(
         int aptype, int bptype, HLV pa, HLV pb,
         std::vector<Particle> const & partons,
         HLV plbar, HLV pl,
         double lambda
     ) {
      HLV pq,pqbar;
       const auto backmidquark = std::find_if(
           begin(partons)+1, end(partons)-1,
           [](Particle const & s){ return s.type != pid::gluon; }
       );
 
       assert(backmidquark!=end(partons)-1);
 
       if (is_quark(backmidquark->type)){
         pq = to_HepLorentzVector(*backmidquark);
         pqbar = to_HepLorentzVector(*(backmidquark+1));
       }
       else {
         pqbar = to_HepLorentzVector(*backmidquark);
         pq = to_HepLorentzVector(*(backmidquark+1));
       }
 
       auto p1 = to_HepLorentzVector(partons[0]);
       auto pn = to_HepLorentzVector(partons[partons.size() - 1]);
 
       auto q0 = pa - p1;
       // t-channel momentum after qqx
       auto qqxt = q0;
 
       bool wc, wqq;
       if (backmidquark->type == -(backmidquark+1)->type){ // Central qqx does not emit
         wqq=false;
         if (aptype==partons[0].type) {
           wc = true;
         }
         else{
           wc = false;
           q0-=pl+plbar;
         }
       }
       else{
         wqq = true;
         wc  = false;
         qqxt-=pl+plbar;
       }
 
       auto begin_ladder = begin(partons) + 1;
       auto end_ladder_1 = (backmidquark);
       auto begin_ladder_2 = (backmidquark+2);
       auto end_ladder = end(partons) - 1;
       for(auto parton_it = begin_ladder; parton_it < begin_ladder_2; ++parton_it){
         qqxt -= to_HepLorentzVector(*parton_it);
       }
 
       int nabove = std::distance(begin_ladder, backmidquark);
       int nbelow = std::distance(begin_ladder_2, end_ladder);
 
       std::vector<HLV> partonsHLV;
       partonsHLV.reserve(partons.size());
       for (size_t i = 0; i != partons.size(); ++i) {
         partonsHLV.push_back(to_HepLorentzVector(partons[i]));
       }
 
       const double current_factor = ME_W_qqxmid_current(
           aptype, bptype, nabove, nbelow, pa, pb,
           pq, pqbar, partonsHLV, plbar, pl, wqq, wc
       );
 
       const double ladder_factor = FKL_ladder_weight(
           begin_ladder, end_ladder_1,
           q0, pa, pb, p1, pn,
           lambda
       )*FKL_ladder_weight(
           begin_ladder_2, end_ladder,
           qqxt, pa, pb, p1, pn,
           lambda
         );
       return current_factor*C_A*C_A/(N_C*N_C-1.)*ladder_factor;
     }
   } // namespace anonymous
 
   double MatrixElement::tree_kin_W(Event const & ev) const {
     using namespace event_type;
     auto const & incoming(ev.incoming());
     auto const & decays(ev.decays());
     HLV plbar, pl;
     for (auto& x: decays) {
       if (x.second.at(0).type < 0){
         plbar = to_HepLorentzVector(x.second.at(0));
         pl = to_HepLorentzVector(x.second.at(1));
       }
       else{
         pl = to_HepLorentzVector(x.second.at(0));
         plbar = to_HepLorentzVector(x.second.at(1));
       }
     }
     const auto pa = to_HepLorentzVector(incoming[0]);
     const auto pb = to_HepLorentzVector(incoming[1]);
 
     const auto partons = tag_extremal_jet_partons(ev);
 
     if(ev.type() == unordered_backward){
       return tree_kin_W_unob(incoming[0].type, incoming[1].type,
                              pa, pb, partons, plbar, pl,
                              param_.regulator_lambda);
     }
     if(ev.type() == unordered_forward){
       return tree_kin_W_unof(incoming[0].type, incoming[1].type,
                              pa, pb, partons, plbar, pl,
                              param_.regulator_lambda);
     }
     if(ev.type() == extremal_qqxb){
       return tree_kin_W_qqxb(incoming[0].type, incoming[1].type,
                              pa, pb, partons, plbar, pl,
                              param_.regulator_lambda);
     }
     if(ev.type() == extremal_qqxf){
       return tree_kin_W_qqxf(incoming[0].type, incoming[1].type,
                              pa, pb, partons, plbar, pl,
                              param_.regulator_lambda);
     }
     if(ev.type() == central_qqx){
       return tree_kin_W_qqxmid(incoming[0].type, incoming[1].type,
              pa, pb, partons, plbar, pl,
              param_.regulator_lambda);
     }
     return tree_kin_W_FKL(incoming[0].type, incoming[1].type,
         pa, pb, partons, plbar, pl,
         param_.regulator_lambda);
   }
 
   double MatrixElement::tree_kin_Higgs(
       Event const & ev
   ) const {
     if(is_uno(ev.type())){
       return tree_kin_Higgs_between(ev);
     }
     if(ev.outgoing().front().type == pid::Higgs){
       return tree_kin_Higgs_first(ev);
     }
     if(ev.outgoing().back().type == pid::Higgs){
       return tree_kin_Higgs_last(ev);
     }
     return tree_kin_Higgs_between(ev);
   }
 
   namespace {
     // Colour acceleration multipliers, for gluons see eq. (7) in arXiv:0910.5113
 #ifdef HEJ_BUILD_WITH_QCDLOOP
     // TODO: code duplication with currents.cc
     double K_g(double p1minus, double paminus) {
       return 1./2.*(p1minus/paminus + paminus/p1minus)*(C_A - 1./C_A) + 1./C_A;
     }
     double K_g(
         CLHEP::HepLorentzVector const & pout,
         CLHEP::HepLorentzVector const & pin
     ) {
       if(pin.z() > 0) return K_g(pout.plus(), pin.plus());
       return K_g(pout.minus(), pin.minus());
     }
     double K(
         ParticleID type,
         CLHEP::HepLorentzVector const & pout,
         CLHEP::HepLorentzVector const & pin
     ) {
       if(type == ParticleID::gluon) return K_g(pout, pin);
       return C_F;
     }
 #endif
     // Colour factor in strict MRK limit
     double K_MRK(ParticleID type) {
       return (type == ParticleID::gluon)?C_A:C_F;
     }
   }
 
   double MatrixElement::MH2_forwardH(
       CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector p1in,
       ParticleID type2,
       CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in,
       CLHEP::HepLorentzVector pH,
       double t1, double t2
   ) const{
     ignore(p2out, p2in);
     const double shat = p1in.invariantMass2(p2in);
     // gluon case
 #ifdef HEJ_BUILD_WITH_QCDLOOP
     if(!param_.Higgs_coupling.use_impact_factors){
       return K(type2, p2out, p2in)*C_A*1./(16*M_PI*M_PI)*t1/t2*MH2gq_outsideH(
           p1out, p1in, p2out, p2in, pH,
           param_.Higgs_coupling.mt, param_.Higgs_coupling.include_bottom,
           param_.Higgs_coupling.mb
       )/(4*(N_C*N_C - 1));
     }
 #endif
     return K_MRK(type2)/C_A*9./2.*shat*shat*(
         C2gHgp(p1in,p1out,pH) + C2gHgm(p1in,p1out,pH)
     )/(t1*t2);
   }
 
   double MatrixElement::tree_kin_Higgs_first(
       Event const & ev
   ) const {
     auto const & incoming = ev.incoming();
     auto const & outgoing = ev.outgoing();
     assert(outgoing.front().type == pid::Higgs);
     if(outgoing[1].type != pid::gluon) {
       assert(incoming.front().type == outgoing[1].type);
       return tree_kin_Higgs_between(ev);
     }
     const auto pH = to_HepLorentzVector(outgoing.front());
     const auto partons = tag_extremal_jet_partons(
         ev
     );
 
     const auto pa = to_HepLorentzVector(incoming[0]);
     const auto pb = to_HepLorentzVector(incoming[1]);
 
     const auto p1 = to_HepLorentzVector(partons.front());
     const auto pn = to_HepLorentzVector(partons.back());
 
     const auto q0 = pa - p1 - pH;
 
     const double t1 = q0.m2();
     const double t2 = (pn - pb).m2();
 
     return MH2_forwardH(
         p1, pa, incoming[1].type, pn, pb, pH,
         t1, t2
     )*FKL_ladder_weight(
         begin(partons) + 1, end(partons) - 1,
         q0, pa, pb, p1, pn,
         param_.regulator_lambda
     );
   }
 
   double MatrixElement::tree_kin_Higgs_last(
       Event const & ev
   ) const {
     auto const & incoming = ev.incoming();
     auto const & outgoing = ev.outgoing();
     assert(outgoing.back().type == pid::Higgs);
     if(outgoing[outgoing.size()-2].type != pid::gluon) {
       assert(incoming.back().type == outgoing[outgoing.size()-2].type);
       return tree_kin_Higgs_between(ev);
     }
     const auto pH = to_HepLorentzVector(outgoing.back());
     const auto partons = tag_extremal_jet_partons(
         ev
     );
 
     const auto pa = to_HepLorentzVector(incoming[0]);
     const auto pb = to_HepLorentzVector(incoming[1]);
 
     auto p1 = to_HepLorentzVector(partons.front());
     const auto pn = to_HepLorentzVector(partons.back());
 
     auto q0 = pa - p1;
 
     const double t1 = q0.m2();
     const double t2 = (pn + pH - pb).m2();
 
     return MH2_forwardH(
         pn, pb, incoming[0].type, p1, pa, pH,
         t2, t1
     )*FKL_ladder_weight(
         begin(partons) + 1, end(partons) - 1,
         q0, pa, pb, p1, pn,
         param_.regulator_lambda
     );
   }
 
 
   double MatrixElement::tree_kin_Higgs_between(
       Event const & ev
   ) const {
     using namespace event_type;
     auto const & incoming = ev.incoming();
     auto const & outgoing = ev.outgoing();
 
     const auto the_Higgs = std::find_if(
         begin(outgoing), end(outgoing),
         [](Particle const & s){ return s.type == pid::Higgs; }
     );
     assert(the_Higgs != end(outgoing));
     const auto pH = to_HepLorentzVector(*the_Higgs);
     const auto partons = tag_extremal_jet_partons(ev);
 
     const auto pa = to_HepLorentzVector(incoming[0]);
     const auto pb = to_HepLorentzVector(incoming[1]);
 
     auto p1 = to_HepLorentzVector(
         partons[(ev.type() == unob)?1:0]
     );
     auto pn = to_HepLorentzVector(
         partons[partons.size() - ((ev.type() == unof)?2:1)]
     );
 
     auto first_after_Higgs = begin(partons) + (the_Higgs-begin(outgoing));
     assert(
         (first_after_Higgs == end(partons) && (
             (ev.type() == unob)
             || partons.back().type != pid::gluon
         ))
         || first_after_Higgs->rapidity() >= the_Higgs->rapidity()
     );
     assert(
         (first_after_Higgs == begin(partons) && (
             (ev.type() == unof)
             || partons.front().type != pid::gluon
         ))
         || (first_after_Higgs-1)->rapidity() <= the_Higgs->rapidity()
     );
     // always treat the Higgs as if it were in between the extremal FKL partons
     if(first_after_Higgs == begin(partons)) ++first_after_Higgs;
     else if(first_after_Higgs == end(partons)) --first_after_Higgs;
 
     // t-channel momentum before Higgs
     auto qH = pa;
     for(auto parton_it = begin(partons); parton_it != first_after_Higgs; ++parton_it){
       qH -= to_HepLorentzVector(*parton_it);
     }
 
     auto q0 = pa - p1;
     auto begin_ladder = begin(partons) + 1;
     auto end_ladder = end(partons) - 1;
 
     double current_factor;
     if(ev.type() == unob){
       current_factor = C_A*C_A/2.*ME_Higgs_current_unob( // 1/2 = "K_uno"
           incoming[0].type, incoming[1].type,
           pn, pb, to_HepLorentzVector(partons.front()), p1, pa, qH, qH - pH,
           param_.Higgs_coupling.mt,
           param_.Higgs_coupling.include_bottom, param_.Higgs_coupling.mb
       );
       const auto p_unob = to_HepLorentzVector(partons.front());
       q0 -= p_unob;
       p1 += p_unob;
       ++begin_ladder;
     }
     else if(ev.type() == unof){
       current_factor = C_A*C_A/2.*ME_Higgs_current_unof( // 1/2 = "K_uno"
           incoming[0].type, incoming[1].type,
           to_HepLorentzVector(partons.back()), pn, pb, p1, pa, qH, qH - pH,
           param_.Higgs_coupling.mt,
           param_.Higgs_coupling.include_bottom, param_.Higgs_coupling.mb
       );
       pn += to_HepLorentzVector(partons.back());
       --end_ladder;
     }
     else{
       current_factor = ME_Higgs_current(
           incoming[0].type, incoming[1].type,
           pn, pb, p1, pa, qH, qH - pH,
           param_.Higgs_coupling.mt,
           param_.Higgs_coupling.include_bottom, param_.Higgs_coupling.mb
       );
     }
 
     const double ladder_factor = FKL_ladder_weight(
         begin_ladder, first_after_Higgs,
         q0, pa, pb, p1, pn,
         param_.regulator_lambda
     )*FKL_ladder_weight(
         first_after_Higgs, end_ladder,
         qH - pH, pa, pb, p1, pn,
         param_.regulator_lambda
     );
     return current_factor*C_A*C_A/(N_C*N_C-1.)*ladder_factor;
   }
 
   double MatrixElement::tree_param_partons(
       double alpha_s, double mur,
       std::vector<Particle> const & partons
   ) const{
     const double gs2 = 4.*M_PI*alpha_s;
     double wt = std::pow(gs2, partons.size());
     if(param_.log_correction){
       // use alpha_s(q_perp), evolved to mur
       assert(partons.size() >= 2);
       for(size_t i = 1; i < partons.size()-1; ++i){
         wt *= 1. + alpha_s/(2.*M_PI)*beta0*log(mur/partons[i].p.perp());
       }
     }
     return wt;
   }
 
   namespace {
     double get_AWZH_coupling(Event const & ev, double alpha_s) {
       const auto AWZH_boson = std::find_if(
           begin(ev.outgoing()), end(ev.outgoing()),
           [](auto const & p){return is_AWZH_boson(p);}
       );
       if(AWZH_boson == end(ev.outgoing())) return 1.;
       switch(AWZH_boson->type){
       case pid::Higgs:
         return alpha_s*alpha_s;
       case pid::Wp:
       case pid::Wm:
         return gw*gw*gw*gw/4.;
         // TODO
       case pid::photon:
       case pid::Z:
       default:
         throw not_implemented("Emission of boson of unsupported type");
       }
     }
   }
 
   double MatrixElement::tree_param(
       Event const & ev,
       double mur
   ) const{
     assert(is_HEJ(ev.type()));
 
     const auto & out = ev.outgoing();
     const double alpha_s = alpha_s_(mur);
     const double AWZH_coupling = get_AWZH_coupling(ev, alpha_s);
     if(ev.type() == event_type::unob || ev.type() == event_type::qqxexb){
       return AWZH_coupling*4.*M_PI*alpha_s*tree_param_partons(
           alpha_s, mur, filter_partons({begin(out) + 1, end(out)})
       );
     }
     if(ev.type() == event_type::unof || ev.type() == event_type::qqxexf){
       return AWZH_coupling*4.*M_PI*alpha_s*tree_param_partons(
           alpha_s, mur, filter_partons({begin(out), end(out) - 1})
       );
     }
     return AWZH_coupling*tree_param_partons(alpha_s, mur, filter_partons(out));
   }
 
 } // namespace HEJ
diff --git a/src/PDF.cc b/src/PDF.cc
index 3a82e18..0792a99 100644
--- a/src/PDF.cc
+++ b/src/PDF.cc
@@ -1,112 +1,112 @@
 /**
- *  \authors   Jeppe Andersen, Tuomas Hapola, Marian Heil, Andreas Maier, Jennifer Smillie
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
  *  \date      2019
  *  \copyright GPLv2 or later
  */
 #include "HEJ/PDF.hh"
 
 #include <iostream>
 #include <stdexcept>
 #include <string>
 
 namespace HEJ{
 
   namespace{
 
     int to_beam(ParticleID id){
       if(std::abs(id) == pid::proton){
         return (id > 0)?1:-1;
       }
       throw std::invalid_argument(
           "unknown beam type: " + std::to_string(id)
       );
     }
 
   }
 
 #if defined LHAPDF_MAJOR_VERSION && LHAPDF_MAJOR_VERSION == 6
 
   PDF::PDF(int id, ParticleID beam1, ParticleID beam2):
     pdf{LHAPDF::mkPDF(id)},
     beamtype{{to_beam(beam1), to_beam(beam2)}}
   {}
 
   double PDF::pdfpt(size_t beam_idx, double x, double q, ParticleID id) const{
     if(!(inRangeQ(q) && inRangeX(x))) return 0.;
 
     if(id == pid::gluon){
       return pdf->xfxQ(21,x,q);
     }
     else if(abs(id) < 7){
       return pdf->xfxQ(id*beamtype[beam_idx],x,q);
     }
     else {
       std::cerr << "particle type unknown: "<< id << std::endl;
       return 0.0;
     }
   }
 
   double PDF::Halphas(double q) const{
     double as = pdf->alphasQ(q);
     if (std::isnan(as) || as > 0.5) {
       as = 0.5;
     }
     return as;
   }
 
   int PDF::id() const{
     return pdf->lhapdfID();
   };
 
   bool PDF::inRangeQ(double q) const{
     return pdf->inRangeQ(q);
   }
 
   bool PDF::inRangeX(double x) const{
     return pdf->inRangeX(x);
   }
 
 #else /* LHAPDF version unknown or older than 6 */
 
   PDF::PDF(std::string LHAPDFName, int LHAPDFsubset, ParticleID beam1, ParticleID beam2):
     beamtype{{to_beam(beam1_type), to_beam(beam2_type)}}
 {
   LHAPDF::initPDFSet(LHAPDFName, LHAPDF::LHGRID, LHAPDFsubset);
 }
 
   double PDF::pdfpt(size_t beam_idx, double x, double q, ParticleID id) const{
 
     if(!(inRangeQ(q) && inRangeX(x))) return 0.;
 
     if (id == pid::gluon){
       return LHAPDF::xfx(x,q,0);
     }
     else if (abs(id) < 7){
       return LHAPDF::xfx(x,q,id*beamtype[beam_idx]);
     }
     else {
       std::cerr << "particle type unknown: "<< id <<std::endl;
       return 0.0;
     }
   }
 
   double PDF::Halphas(double q) const{
     double as = LHAPDF::alphasPDF(q);
     if (isnan(as) || as > 0.5) as = 0.5;
     return as;
   }
 
   bool PDF::inRangeQ(double q) const{
     // here we assume that all members actually have the same range!
     static constexpr int member = 0;
     return (LHAPDF::getQ2min(member) < q*q) && (q*q < LHAPDF::getQ2max(member));
   }
 
   bool PDF::inRangeX(double x) const{
     // here we assume that all members actually have the same range!
     static constexpr int member = 0;
     return (LHAPDF::getXmin(member) < x) && (x < LHAPDF::getXmax(member));
   }
 
 #endif
 }
diff --git a/src/PDG_codes.cc b/src/PDG_codes.cc
index 2d1b02e..c4a8aff 100644
--- a/src/PDG_codes.cc
+++ b/src/PDG_codes.cc
@@ -1,93 +1,93 @@
 /**
- *  \authors   Jeppe Andersen, Tuomas Hapola, Marian Heil, Andreas Maier, Jennifer Smillie
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
  *  \date      2019
  *  \copyright GPLv2 or later
  */
 #include "HEJ/PDG_codes.hh"
 
 #include <map>
 
 #include "HEJ/exceptions.hh"
 
 namespace HEJ{
   ParticleID to_ParticleID(std::string const & name){
     using namespace HEJ::pid;
     static const std::map<std::string, ParticleID> known = {
       {"d", d}, {"down", down}, {"1",static_cast<ParticleID>(1)},
       {"u", u}, {"up", up}, {"2",static_cast<ParticleID>(2)},
       {"s", s}, {"strange", strange}, {"3",static_cast<ParticleID>(3)},
       {"c", c}, {"charm", charm}, {"4",static_cast<ParticleID>(4)},
       {"b", b}, {"bottom", bottom}, {"5",static_cast<ParticleID>(5)},
       {"t", t}, {"top", top}, {"6",static_cast<ParticleID>(6)},
       {"e", e}, {"electron", electron}, {"e-", e}, {"11",static_cast<ParticleID>(11)},
       {"nu_e", nu_e}, {"electron_neutrino", electron_neutrino}, {"12",static_cast<ParticleID>(12)},
       {"mu", mu}, {"muon", muon}, {"mu-", mu}, {"13",static_cast<ParticleID>(13)},
       {"nu_mu", nu_mu}, {"muon_neutrino", muon_neutrino}, {"14",static_cast<ParticleID>(14)},
       {"tau", tau}, {"tau-", tau}, {"15",static_cast<ParticleID>(15)},
       {"nu_tau", nu_tau}, {"tau_neutrino", tau_neutrino}, {"16",static_cast<ParticleID>(16)},
       {"d_bar", d_bar}, {"antidown", antidown}, {"-1",static_cast<ParticleID>(-1)},
       {"u_bar", u_bar}, {"antiup", antiup}, {"-2",static_cast<ParticleID>(-2)},
       {"s_bar", s_bar}, {"antistrange", antistrange}, {"-3",static_cast<ParticleID>(-3)},
       {"c_bar", c_bar}, {"anticharm", anticharm}, {"-4",static_cast<ParticleID>(-4)},
       {"b_bar", b_bar}, {"antibottom", antibottom}, {"-5",static_cast<ParticleID>(-5)},
       {"t_bar", t_bar}, {"antitop", antitop}, {"-6",static_cast<ParticleID>(-6)},
       {"e_bar", e_bar}, {"antielectron", antielectron}, {"positron", positron}, {"e+", e_bar}, {"-11",static_cast<ParticleID>(-11)},
       {"nu_e_bar", nu_e_bar}, {"electron-antineutrino", electron_antineutrino}, {"-12",static_cast<ParticleID>(-12)},
       {"mu_bar", mu_bar}, {"mu+", mu_bar}, {"antimuon", antimuon}, {"-13",static_cast<ParticleID>(-13)},
       {"nu_mu_bar", nu_mu_bar}, {"muon-antineutrino", muon_antineutrino}, {"-14",static_cast<ParticleID>(-14)},
       {"tau_bar", tau_bar}, {"tau+", tau_bar}, {"antitau", antitau}, {"-15",static_cast<ParticleID>(-15)},
       {"nu_tau_bar", nu_tau_bar}, {"tau-antineutrino", tau_antineutrino}, {"-16",static_cast<ParticleID>(-16)},
       {"gluon", gluon}, {"g", g}, {"21",static_cast<ParticleID>(21)},
       {"photon", photon}, {"gamma", gamma}, {"22",static_cast<ParticleID>(22)},
       {"Z", Z}, {"23",static_cast<ParticleID>(23)},
       {"Wp", Wp}, {"W+", Wp}, {"24",static_cast<ParticleID>(24)},
       {"Wm", Wm}, {"W-", Wm}, {"-24",static_cast<ParticleID>(-24)},
       {"h", h}, {"H", h}, {"Higgs", Higgs}, {"higgs", higgs}, {"25",static_cast<ParticleID>(25)},
       {"p", p}, {"proton", proton}, {"p_bar", p_bar}, {"2212",static_cast<ParticleID>(2212)}
     };
     const auto res = known.find(name);
     if(res == known.end()){
       throw std::invalid_argument("Unknown particle " + name);
     }
     return res->second;
   }
 
   std::string name(ParticleID id) {
     using namespace HEJ::pid;
     switch (id) {
     case down: return "down";
     case up: return "up";
     case strange: return "strange";
     case charm: return "charm";
     case bottom: return "bottom";
     case top: return "top";
     case electron: return "electron";
     case muon: return "muon";
     case tau: return "tau";
     case electron_neutrino: return "electron-neutrino";
     case muon_neutrino: return "muon-neutrino";
     case tau_neutrino: return "tau-neutrino";
     case antidown: return "antidown";
     case antiup: return "antiup";
     case antistrange: return "antistrange";
     case anticharm: return "anticharm";
     case antibottom: return "antibottom";
     case antitop: return "antitop";
     case positron: return "positron";
     case antimuon: return "antimuon";
     case antitau: return "antitau";
     case electron_antineutrino: return "electron-antineutrino";
     case muon_antineutrino: return "muon-antineutrino";
     case tau_antineutrino: return "tau-antineutrino";
     case gluon: return "gluon";
     case photon: return "photon";
     case Z: return "Z";
     case Wp: return "W+";
     case Wm: return "W-";
     case Higgs: return "Higgs";
     case proton: return "proton";
     case antiproton: return "antiproton";
     }
     throw std::logic_error{"unreachable"};
   }
 }
diff --git a/src/PhaseSpacePoint.cc b/src/PhaseSpacePoint.cc
index 7660d65..5d0c983 100644
--- a/src/PhaseSpacePoint.cc
+++ b/src/PhaseSpacePoint.cc
@@ -1,683 +1,694 @@
 /**
- *  \authors   Jeppe Andersen, Tuomas Hapola, Marian Heil, Andreas Maier, Jennifer Smillie
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
  *  \date      2019
  *  \copyright GPLv2 or later
  */
 #include "HEJ/PhaseSpacePoint.hh"
 
 #include <algorithm>
 #include <assert.h>
 #include <numeric>
 #include <random>
 
 #include "fastjet/ClusterSequence.hh"
 
 #include "HEJ/Constants.hh"
 #include "HEJ/Event.hh"
 #include "HEJ/JetSplitter.hh"
 #include "HEJ/kinematics.hh"
 #include "HEJ/resummation_jet.hh"
 #include "HEJ/utility.hh"
 #include "HEJ/PDG_codes.hh"
 #include "HEJ/event_types.hh"
 
 namespace HEJ{
   namespace {
     constexpr int max_jet_user_idx = PhaseSpacePoint::ng_max;
 
     bool is_nonjet_parton(fastjet::PseudoJet const & parton){
       assert(parton.user_index() != -1);
       return parton.user_index() > max_jet_user_idx;
     }
 
     bool is_jet_parton(fastjet::PseudoJet const & parton){
       assert(parton.user_index() != -1);
       return parton.user_index() <= max_jet_user_idx;
     }
 
     // user indices for partons with extremal rapidity
     constexpr int qqxb_idx = -7;
     constexpr int qqxf_idx = -6;
     constexpr int unob_idx = -5;
     constexpr int unof_idx = -4;
     constexpr int backward_FKL_idx = -3;
     constexpr int forward_FKL_idx = -2;
   }
 
   namespace {
     double estimate_ng_mean(std::vector<fastjet::PseudoJet> const & Born_jets){
       const double delta_y =
         Born_jets.back().rapidity() - Born_jets.front().rapidity();
       assert(delta_y > 0);
       // Formula derived from fit in arXiv:1805.04446 (see Fig. 2)
       return 0.975052*delta_y;
     }
   }
 
   std::vector<fastjet::PseudoJet> PhaseSpacePoint::cluster_jets(
       std::vector<fastjet::PseudoJet> const & partons
   ) const{
     fastjet::ClusterSequence cs(partons, param_.jet_param.def);
     return sorted_by_rapidity(cs.inclusive_jets(param_.jet_param.min_pt));
   }
 
   bool PhaseSpacePoint::pass_resummation_cuts(
       std::vector<fastjet::PseudoJet> const & jets
   ) const{
     return cluster_jets(jets).size() == jets.size();
   }
 
   int PhaseSpacePoint::sample_ng(std::vector<fastjet::PseudoJet> const & Born_jets){
     const double ng_mean = estimate_ng_mean(Born_jets);
     std::poisson_distribution<int> dist(ng_mean);
     const int ng = dist(ran_.get());
     assert(ng >= 0);
     assert(ng < ng_max);
     weight_ *= std::tgamma(ng + 1)*std::exp(ng_mean)*std::pow(ng_mean, -ng);
     return ng;
   }
 
   void PhaseSpacePoint::copy_AWZH_boson_from(Event const & event){
     auto const & from = event.outgoing();
 
     const auto AWZH_boson = std::find_if(
         begin(from), end(from),
         [](Particle const & p){ return is_AWZH_boson(p); }
     );
     if(AWZH_boson == end(from)) return;
     auto insertion_point = std::lower_bound(
         begin(outgoing_), end(outgoing_), *AWZH_boson, rapidity_less{}
     );
     outgoing_.insert(insertion_point, *AWZH_boson);
 
     // copy decay products
     const int idx = std::distance(begin(from), AWZH_boson);
     assert(idx >= 0);
     const auto decay_it = event.decays().find(idx);
     if(decay_it != end(event.decays())){
       const int new_idx = std::distance(begin(outgoing_), insertion_point);
       assert(new_idx >= 0);
       assert(outgoing_[new_idx].type == AWZH_boson->type);
       decays_.emplace(new_idx, decay_it->second);
     }
 
     assert(std::is_sorted(begin(outgoing_), end(outgoing_), rapidity_less{}));
   }
 
   namespace {
 
     template<class ConstIterator, class Iterator>
     void label_extremal_qqx(
       ConstIterator born_begin, ConstIterator born_end,
       Iterator first_out
     ){
       // find born quarks
       const auto firstquark = std::find_if(
         born_begin, born_end-1,
           [](Particle const & s){ return (is_anyquark(s)); }
       );
       assert(firstquark != born_end-1);
       const auto secondquark = std::find_if(
         firstquark+1, born_end,
           [](Particle const & s){ return (is_anyquark(s)); }
       );
       assert(secondquark != born_end);
       assert( ( is_quark(*firstquark) && is_antiquark(*secondquark) )
             || ( is_antiquark(*firstquark) && is_quark(*secondquark) ));
       assert(first_out->type     == ParticleID::gluon);
       assert((first_out+1)->type == ParticleID::gluon);
 
       // copy type from born
       first_out->type     = firstquark->type;
       (first_out+1)->type = secondquark->type;
     }
   }
 
   void PhaseSpacePoint::label_qqx(Event const & event){
     assert(std::is_sorted(begin(outgoing_), end(outgoing_), rapidity_less{}));
     assert(filter_partons(outgoing_).size() == outgoing_.size());
     if(qqxb_){
       label_extremal_qqx( event.outgoing().cbegin(), event.outgoing().cend(),
         outgoing_.begin()
       );
       return;
     }
     if(qqxf_){ // same as qqxb with reversed order
       label_extremal_qqx( event.outgoing().crbegin(), event.outgoing().crend(),
         outgoing_.rbegin()
       );
       return;
     }
     // central qqx
     std::vector<Particle> const born_parton{filter_partons(event.outgoing())};
     // find born quarks (ignore extremal partons)
     auto const firstquark = std::find_if(
       born_parton.cbegin()+1, born_parton.cend()-2,
         [](Particle const & s){ return (is_anyquark(s)); }
     );
     auto const secondquark = firstquark+1;
     assert(firstquark != born_parton.cend()-2);
     assert( ( is_quark(*firstquark) && is_antiquark(*secondquark) )
           || ( is_antiquark(*firstquark) && is_quark(*secondquark) ));
 
     // find jets at FO corresponding to the quarks
     // technically this isn't necessary for LO
     std::vector<int> const born_indices{ event.particle_jet_indices(event.jets()) };
     int const firstjet_idx = born_indices[
                               std::distance(born_parton.cbegin(), firstquark) ];
     assert(firstjet_idx>0);
     assert(born_indices[ std::distance(born_parton.cbegin(), firstquark) ]
             == firstjet_idx);
     assert( born_indices[ std::distance(born_parton.cbegin(), secondquark) ]
             == firstjet_idx+1);
 
     // find corresponding jets after resummation
     fastjet::ClusterSequence cs{to_PseudoJet(outgoing_), param_.jet_param.def};
     auto const jets = fastjet::sorted_by_rapidity(
                         cs.inclusive_jets( param_.jet_param.min_pt ));
     std::vector<int> const resum_indices{ cs.particle_jet_indices({jets}) };
 
 #ifndef NDEBUG
     // assert that jets are where we expect them to be
     auto const firstjet = event.jets().cbegin() + firstjet_idx;
     assert(nearby_ep( firstjet->rapidity(),
                       jets[firstjet_idx].rapidity(),   1e-2) );
     assert(nearby_ep( (firstjet+1)->rapidity(),
                       jets[firstjet_idx+1].rapidity(), 1e-2) );
 #endif
 
     // find last partons in first (central) jet
     size_t idx_out = 0;
     for(size_t i=resum_indices.size()-2; i>0; --i)
       if(resum_indices[i] == firstjet_idx){
         idx_out = i;
         break;
       }
     assert(idx_out != 0);
 
     // check that no additional emission between jets
     //! @TODO don't even generate such configurations
     if(resum_indices[idx_out+1] != resum_indices[idx_out]+1){
       weight_=0.;
       return;
     }
     outgoing_[idx_out].type   = firstquark->type;
     outgoing_[idx_out+1].type = (firstquark+1)->type;
   }
 
   PhaseSpacePoint::PhaseSpacePoint(
       Event const & ev, PhaseSpacePointConfig conf, HEJ::RNG & ran
   ):
     unob_{ev.type() == event_type::unob},
     unof_{ev.type() == event_type::unof},
     qqxb_{ev.type() == event_type::qqxexb},
     qqxf_{ev.type() == event_type::qqxexf},
     qqxmid_{ev.type() == event_type::qqxmid},
     param_{std::move(conf)},
-    ran_{ran}
+    ran_{ran},
+    status_{unspecified}
   {
     weight_ = 1;
     const auto & Born_jets = ev.jets();
     const int ng = sample_ng(Born_jets);
     weight_ /= std::tgamma(ng + 1);
     const int ng_jets = sample_ng_jets(ng, Born_jets);
     std::vector<fastjet::PseudoJet> out_partons = gen_non_jet(
        ng - ng_jets, CMINPT, param_.jet_param.min_pt
     );
 
     const auto qperp = std::accumulate(
       begin(out_partons), end(out_partons),
       fastjet::PseudoJet{}
     );
     const auto jets = reshuffle(Born_jets, qperp);
-    if(weight_ == 0.) return;
+    if(weight_ == 0.) {
+      status_ = failed_reshuffle;
+      return;
+    }
     if(! pass_resummation_cuts(jets)){
-     weight_ = 0.;
-     return;
+      status_ = failed_resummation_cuts;
+      weight_ = 0.;
+      return;
     }
     std::vector<fastjet::PseudoJet> jet_partons = split(jets, ng_jets);
-    if(weight_ == 0.) return;
+    if(weight_ == 0.) {
+      status_ = StatusCode::failed_split;
+      return;
+    }
     rescale_rapidities(
       out_partons,
       most_backward_FKL(jet_partons).rapidity(),
       most_forward_FKL(jet_partons).rapidity()
     );
     if(! cluster_jets(out_partons).empty()){
       weight_ = 0.;
+      status_ = StatusCode::empty_jets;
       return;
     }
     std::sort(begin(out_partons), end(out_partons), rapidity_less{});
     assert(
       std::is_sorted(begin(jet_partons), end(jet_partons), rapidity_less{})
     );
     const auto first_jet_parton = out_partons.insert(
       end(out_partons), begin(jet_partons), end(jet_partons)
     );
     std::inplace_merge(
       begin(out_partons), first_jet_parton, end(out_partons), rapidity_less{}
     );
 
     if(! jets_ok(Born_jets, out_partons)){
       weight_ = 0.;
+      status_ = StatusCode::wrong_jets;
       return;
     }
     weight_ *= phase_space_normalisation(Born_jets.size(), out_partons.size());
 
     outgoing_.reserve(out_partons.size() + 1); // one slot for possible A, W, Z, H
     for(auto & p: out_partons){
       outgoing_.emplace_back(Particle{pid::gluon, std::move(p), {}});
     }
 
     const auto WEmit = std::find_if(
       begin(ev.outgoing()), end(ev.outgoing()),
       [](Particle const & s){ return abs(s.type) == pid::Wp; }
     );
 
     if (WEmit != end(ev.outgoing())){
       if(!qqxb_)
         outgoing_[unob_].type = filter_partons(ev.outgoing())[unob_].type;
       if(!qqxf_)
         outgoing_.rbegin()[unof_].type = filter_partons(ev.outgoing()).rbegin()[unof_].type;
     }
     else{
       most_backward_FKL(outgoing_).type = ev.incoming().front().type;
       most_forward_FKL(outgoing_).type = ev.incoming().back().type;
     }
 
     if(qqxmid_||qqxb_||qqxf_){
       label_qqx(ev);
       if(weight_ == 0.) return; //!< @TODO optimise s.t. this is not possible
     }
 
     if(! jets_ok(Born_jets, out_partons)){
       weight_ = 0.;
       return;
     }
 
     copy_AWZH_boson_from(ev);
 
     assert(!outgoing_.empty());
 
     reconstruct_incoming(ev.incoming());
+    status_ = StatusCode::good;
   }
 
   std::vector<fastjet::PseudoJet> PhaseSpacePoint::gen_non_jet(
       int count, double ptmin, double ptmax
   ){
     // heuristic parameters for pt sampling
     const double ptpar = 1.3 + count/5.;
     const double temp1 = atan((ptmax - ptmin)/ptpar);
 
     std::vector<fastjet::PseudoJet> partons(count);
     for(size_t i = 0; i < (size_t) count; ++i){
       const double r1 = ran_.get().flat();
       const double pt = ptmin + ptpar*tan(r1*temp1);
       const double temp2 = cos(r1*temp1);
       const double phi = 2*M_PI*ran_.get().flat();
       weight_ *= 2.0*M_PI*pt*ptpar*temp1/(temp2*temp2);
       // we don't know the allowed rapidity span yet,
       // set a random value to be rescaled later on
       const double y = ran_.get().flat();
       partons[i].reset_PtYPhiM(pt, y, phi);
       // Set user index higher than any jet-parton index
       // in order to assert that these are not inside jets
       partons[i].set_user_index(i + 1 + ng_max);
 
       assert(ptmin-1e-5 <= partons[i].pt() && partons[i].pt() <= ptmax+1e-5);
     }
     assert(std::all_of(partons.cbegin(), partons.cend(), is_nonjet_parton));
     return partons;
   }
 
   void PhaseSpacePoint::rescale_rapidities(
       std::vector<fastjet::PseudoJet> & partons,
       double ymin, double ymax
   ){
     constexpr double ep = 1e-7;
     for(auto & parton: partons){
       assert(0 <= parton.rapidity() && parton.rapidity() <= 1);
       const double dy = ymax - ymin - 2*ep;
       const double y = ymin + ep + dy*parton.rapidity();
       parton.reset_momentum_PtYPhiM(parton.pt(), y, parton.phi());
       weight_ *= dy;
       assert(ymin <= parton.rapidity() && parton.rapidity() <= ymax);
     }
   }
 
   namespace {
     template<typename T, typename... Rest>
     auto min(T const & a, T const & b, Rest&&... r) {
       using std::min;
       return min(a, min(b, std::forward<Rest>(r)...));
     }
   }
 
   double PhaseSpacePoint::probability_in_jet(
       std::vector<fastjet::PseudoJet> const & Born_jets
   ) const{
     assert(std::is_sorted(begin(Born_jets), end(Born_jets), rapidity_less{}));
     assert(Born_jets.size() >= 2);
 
     const double dy =
       Born_jets.back().rapidity() - Born_jets.front().rapidity();
     const double R = param_.jet_param.def.R();
     const int njets = Born_jets.size();
     const double p_J_y_large = (njets-1)*R*R/(2.*dy);
     const double p_J_y0 = njets*R/M_PI;
     return min(p_J_y_large, p_J_y0, 1.);
   }
 
   int PhaseSpacePoint::sample_ng_jets(
       int ng, std::vector<fastjet::PseudoJet> const & Born_jets
   ){
     const double p_J = probability_in_jet(Born_jets);
     std::binomial_distribution<> bin_dist(ng, p_J);
     const int ng_J = bin_dist(ran_.get());
     weight_ *= std::pow(p_J, -ng_J)*std::pow(1 - p_J, ng_J - ng);
     return ng_J;
   }
 
   std::vector<fastjet::PseudoJet> PhaseSpacePoint::reshuffle(
       std::vector<fastjet::PseudoJet> const & Born_jets,
       fastjet::PseudoJet const & q
   ){
     if(q == fastjet::PseudoJet{0, 0, 0, 0}) return Born_jets;
     const auto jets = resummation_jet_momenta(Born_jets, q);
     if(jets.empty()){
       weight_ = 0;
       return {};
     }
 
     // additional Jacobian to ensure Born integration over delta gives 1
     weight_ *= resummation_jet_weight(Born_jets, q);
     return jets;
   }
 
   std::vector<int> PhaseSpacePoint::distribute_jet_partons(
       int ng_jets, std::vector<fastjet::PseudoJet> const & jets
   ){
     size_t first_valid_jet = 0;
     size_t num_valid_jets = jets.size();
     const double R_eff = 5./3.*param_.jet_param.def.R();
     // if there is an unordered jet too far away from the FKL jets
     // then extra gluon constituents of the unordered jet would
     // violate the FKL rapidity ordering
     if((unob_||qqxb_) && jets[0].delta_R(jets[1]) > R_eff){
       ++first_valid_jet;
       --num_valid_jets;
     }
     else if((unof_||qqxf_) && jets[jets.size()-1].delta_R(jets[jets.size()-2]) > R_eff){
       --num_valid_jets;
     }
     std::vector<int> np(jets.size(), 1);
     for(int i = 0; i < ng_jets; ++i){
       ++np[first_valid_jet + ran_.get().flat() * num_valid_jets];
     }
     weight_ *= std::pow(num_valid_jets, ng_jets);
     return np;
   }
 
 #ifndef NDEBUG
   namespace{
     bool tagged_FKL_backward(
         std::vector<fastjet::PseudoJet> const & jet_partons
     ){
       return std::find_if(
           begin(jet_partons), end(jet_partons),
           [](fastjet::PseudoJet const & p){
             return p.user_index() == backward_FKL_idx;
           }
       ) != end(jet_partons);
     }
 
     bool tagged_FKL_forward(
         std::vector<fastjet::PseudoJet> const & jet_partons
     ){
       // the most forward FKL parton is most likely near the end of jet_partons;
       // start search from there
       return std::find_if(
           jet_partons.rbegin(), jet_partons.rend(),
           [](fastjet::PseudoJet const & p){
             return p.user_index() == forward_FKL_idx;
           }
       ) != jet_partons.rend();
     }
 
     bool tagged_FKL_extremal(
         std::vector<fastjet::PseudoJet> const & jet_partons
     ){
       return tagged_FKL_backward(jet_partons) && tagged_FKL_forward(jet_partons);
     }
 
   } // namespace anonymous
 #endif
 
   std::vector<fastjet::PseudoJet> PhaseSpacePoint::split(
       std::vector<fastjet::PseudoJet> const & jets,
       int ng_jets
   ){
     return split(jets, distribute_jet_partons(ng_jets, jets));
   }
 
   bool PhaseSpacePoint::pass_extremal_cuts(
       fastjet::PseudoJet const & ext_parton,
       fastjet::PseudoJet const & jet
   ) const{
     if(ext_parton.pt() < param_.min_extparton_pt) return false;
     return (ext_parton - jet).pt()/jet.pt() < param_.max_ext_soft_pt_fraction;
   }
 
   std::vector<fastjet::PseudoJet> PhaseSpacePoint::split(
       std::vector<fastjet::PseudoJet> const & jets,
       std::vector<int> const & np
   ){
     assert(! jets.empty());
     assert(jets.size() == np.size());
     assert(pass_resummation_cuts(jets));
 
     const size_t most_backward_FKL_idx = 0 + unob_ + qqxb_;
     const size_t most_forward_FKL_idx = jets.size() - 1 - unof_ - qqxf_;
     const auto & jet = param_.jet_param;
     const JetSplitter jet_splitter{jet.def, jet.min_pt, ran_};
 
     std::vector<fastjet::PseudoJet> jet_partons;
     // randomly distribute jet gluons among jets
     for(size_t i = 0; i < jets.size(); ++i){
       auto split_res = jet_splitter.split(jets[i], np[i]);
       weight_ *= split_res.weight;
       if(weight_ == 0) return {};
       assert(
           std::all_of(
               begin(split_res.constituents), end(split_res.constituents),
               is_jet_parton
           )
       );
       const auto first_new_parton = jet_partons.insert(
           end(jet_partons),
           begin(split_res.constituents), end(split_res.constituents)
       );
       // mark uno and extremal FKL emissions here so we can check
       // their position once all emissions are generated
       auto extremal = end(jet_partons);
       if (i == most_backward_FKL_idx){ //FKL backward emission
         extremal = std::min_element(
             first_new_parton, end(jet_partons), rapidity_less{}
         );
         extremal->set_user_index(backward_FKL_idx);
       }
       else if(((unob_ || qqxb_) && i == 0)){
         // unordered/qqxb
         extremal = std::min_element(
             first_new_parton, end(jet_partons), rapidity_less{}
         );
         extremal->set_user_index((unob_)?unob_idx:qqxb_idx);
       }
 
       else if (i == most_forward_FKL_idx){
         extremal = std::max_element(
             first_new_parton, end(jet_partons), rapidity_less{}
         );
         extremal->set_user_index(forward_FKL_idx);
       }
       else if(((unof_ || qqxf_) && i == jets.size() - 1)){
         // unordered/qqxf
         extremal = std::max_element(
             first_new_parton, end(jet_partons), rapidity_less{}
         );
         extremal->set_user_index((unof_)?unof_idx:qqxf_idx);
       }
       if(
           extremal != end(jet_partons)
           && !pass_extremal_cuts(*extremal, jets[i])
       ){
         weight_ = 0;
         return {};
       }
     }
     assert(tagged_FKL_extremal(jet_partons));
     std::sort(begin(jet_partons), end(jet_partons), rapidity_less{});
     if(
         !extremal_ok(jet_partons)
         || !split_preserved_jets(jets, jet_partons)
     ){
       weight_ = 0.;
       return {};
     }
     return jet_partons;
   }
 
   bool PhaseSpacePoint::extremal_ok(
       std::vector<fastjet::PseudoJet> const & partons
   ) const{
     assert(std::is_sorted(begin(partons), end(partons), rapidity_less{}));
     if(unob_ && partons.front().user_index() !=  unob_idx) return false;
     if(unof_ && partons.back().user_index() !=  unof_idx) return false;
     if(qqxb_ && partons.front().user_index() !=  qqxb_idx) return false;
     if(qqxf_ && partons.back().user_index() !=  qqxf_idx) return false;
     return
       most_backward_FKL(partons).user_index() == backward_FKL_idx
       && most_forward_FKL(partons).user_index() == forward_FKL_idx;
   }
 
   bool PhaseSpacePoint::split_preserved_jets(
         std::vector<fastjet::PseudoJet> const & jets,
         std::vector<fastjet::PseudoJet> const & jet_partons
   ) const{
     assert(std::is_sorted(begin(jets), end(jets), rapidity_less{}));
 
     const auto split_jets = cluster_jets(jet_partons);
     // this can happen if two overlapping jets
     // are both split into more than one parton
     if(split_jets.size() != jets.size()) return false;
     for(size_t i = 0; i < split_jets.size(); ++i){
       // this can happen if there are two overlapping jets
       // and a parton is assigned to the "wrong" jet
       if(!nearby_ep(jets[i].rapidity(), split_jets[i].rapidity(), 1e-2)){
         return false;
       }
     }
     return true;
   }
 
   template<class Particle>
   Particle const & PhaseSpacePoint::most_backward_FKL(
       std::vector<Particle> const & partons
   ) const{
     return partons[0 + unob_ + qqxb_];
   }
 
   template<class Particle>
   Particle const & PhaseSpacePoint::most_forward_FKL(
       std::vector<Particle> const & partons
   ) const{
     const size_t idx = partons.size() - 1 - unof_ - qqxf_;
     assert(idx < partons.size());
     return partons[idx];
   }
 
   template<class Particle>
   Particle & PhaseSpacePoint::most_backward_FKL(
       std::vector<Particle> & partons
   ) const{
     return partons[0 + unob_ + qqxb_];
   }
 
   template<class Particle>
   Particle & PhaseSpacePoint::most_forward_FKL(
       std::vector<Particle> & partons
   ) const{
     const size_t idx = partons.size() - 1 - unof_ - qqxf_;
     assert(idx < partons.size());
     return partons[idx];
   }
 
   namespace {
     bool contains_idx(
         fastjet::PseudoJet const & jet, fastjet::PseudoJet const & parton
     ){
       auto const & constituents = jet.constituents();
       const int idx = parton.user_index();
       return std::find_if(
           begin(constituents), end(constituents),
           [idx](fastjet::PseudoJet const & con){return con.user_index() == idx;}
       ) != end(constituents);
     }
   }
 
   bool PhaseSpacePoint::jets_ok(
       std::vector<fastjet::PseudoJet> const & Born_jets,
       std::vector<fastjet::PseudoJet> const & partons
   ) const{
     fastjet::ClusterSequence cs(partons, param_.jet_param.def);
     const auto jets = sorted_by_rapidity(cs.inclusive_jets(param_.jet_param.min_pt));
     if(jets.size() != Born_jets.size()) return false;
     int in_jet = 0;
     for(size_t i = 0; i < jets.size(); ++i){
       assert(jets[i].has_constituents());
       for(auto && parton: jets[i].constituents()){
         if(is_nonjet_parton(parton)) return false;
       }
       in_jet += jets[i].constituents().size();
     }
     const int expect_in_jet = std::count_if(
         partons.cbegin(), partons.cend(), is_jet_parton
     );
     if(in_jet != expect_in_jet) return false;
     // note that PseudoJet::contains does not work here
     if(! (
            contains_idx(most_backward_FKL(jets), most_backward_FKL(partons))
            &&  contains_idx(most_forward_FKL(jets), most_forward_FKL(partons))
        )) return false;
     if(unob_ && !contains_idx(jets.front(), partons.front())) return false;
     if(unof_ && !contains_idx(jets.back(), partons.back())) return false;
     for(size_t i = 0; i < jets.size(); ++i){
       assert(nearby_ep(jets[i].rapidity(), Born_jets[i].rapidity(), 1e-2));
     }
     return true;
   }
 
   void PhaseSpacePoint::reconstruct_incoming(
       std::array<Particle, 2> const & Born_incoming
   ){
     std::tie(incoming_[0].p, incoming_[1].p) = incoming_momenta(outgoing_);
     for(size_t i = 0; i < incoming_.size(); ++i){
       incoming_[i].type = Born_incoming[i].type;
     }
     assert(momentum_conserved());
   }
 
   double PhaseSpacePoint::phase_space_normalisation(
       int num_Born_jets, int num_out_partons
   ) const{
     return pow(16*pow(M_PI,3), num_Born_jets - num_out_partons);
   }
 
   bool PhaseSpacePoint::momentum_conserved() const{
     fastjet::PseudoJet diff;
     for(auto const & in: incoming()) diff += in.p;
     const double norm = diff.E();
     for(auto const & out: outgoing()) diff -= out.p;
     return nearby(diff, fastjet::PseudoJet{}, norm);
   }
 
 } //namespace HEJ
diff --git a/src/Ranlux64.cc b/src/Ranlux64.cc
index c870864..ea67bb6 100644
--- a/src/Ranlux64.cc
+++ b/src/Ranlux64.cc
@@ -1,61 +1,61 @@
 /**
- *  \authors   Jeppe Andersen, Tuomas Hapola, Marian Heil, Andreas Maier, Jennifer Smillie
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
  *  \date      2019
  *  \copyright GPLv2 or later
  */
 #include "HEJ/Ranlux64.hh"
 
 #include <cstdio>
 
 namespace HEJ {
   namespace {
     //! create Ranlux64Engine with state read from the given file
     CLHEP::Ranlux64Engine make_Ranlux64Engine(std::string const & seed_file) {
       CLHEP::Ranlux64Engine result;
       result.restoreStatus(seed_file.c_str());
       return result;
     }
 
     CLHEP::Ranlux64Engine make_Ranlux64Engine() {
       /*
        * some (all?) of the Ranlux64Engine constructors leave fields
        * uninitialised, invoking undefined behaviour. This can be
        * circumvented by restoring the state from a file
        */
       static const std::string state =
         "9876\n"
         "0.91280703978419097666\n"
         "0.41606065829518357191\n"
         "0.99156342622341142601\n"
         "0.030922955274050423213\n"
         "0.16206278421638486975\n"
         "0.76151768001958330956\n"
         "0.43765760066092695979\n"
         "0.42904698253748563275\n"
         "0.11476317525663759511\n"
         "0.026620053590963976831\n"
         "0.65953715764414511114\n"
         "0.30136722624439826745\n"
         "3.5527136788005009294e-15 4\n"
         "1 202\n";
       const std::string file = std::tmpnam(nullptr);
       {
         std::ofstream out{file};
         out << state;
       }
       auto result = make_Ranlux64Engine(file);
       std::remove(file.c_str());
       return result;
     }
   }
 
   Ranlux64::Ranlux64(): ran_{make_Ranlux64Engine()} {}
 
   Ranlux64::Ranlux64(std::string const & seed_file):
     ran_{make_Ranlux64Engine(seed_file)}
   {}
 
   double Ranlux64::flat() {
     return ran_.flat();
   }
 }
diff --git a/src/RivetAnalysis.cc b/src/RivetAnalysis.cc
index 7db9b8b..cb07fd9 100644
--- a/src/RivetAnalysis.cc
+++ b/src/RivetAnalysis.cc
@@ -1,143 +1,143 @@
 /**
- *  \authors   Jeppe Andersen, Tuomas Hapola, Marian Heil, Andreas Maier, Jennifer Smillie
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
  *  \date      2019
  *  \copyright GPLv2 or later
  */
 #include "HEJ/RivetAnalysis.hh"
 
 #ifdef HEJ_BUILD_WITH_RIVET
 
 #include <ostream>
 #include <stddef.h>
 
 #include "yaml-cpp/yaml.h"
 
 #include "Rivet/AnalysisHandler.hh"
 
 #include "HepMC/GenEvent.h"
 
 #include "HEJ/Event.hh"
 #include "HEJ/exceptions.hh"
 
 #endif
 
 namespace HEJ{
   std::unique_ptr<Analysis> RivetAnalysis::create(YAML::Node const & config){
     return std::unique_ptr<Analysis>{new RivetAnalysis{config}};
   }
 }
 
 #ifdef HEJ_BUILD_WITH_RIVET
 
 namespace HEJ {
 
   RivetAnalysis::RivetAnalysis(YAML::Node const & config):
     output_name_{config["output"].as<std::string>()},
     first_event_(true)
   {
     // read in analyses
     const auto & name_node = config["rivet"];
     switch(name_node.Type()){
     case YAML::NodeType::Scalar:
       analyses_names_.push_back(name_node.as<std::string>());
       break;
     case YAML::NodeType::Sequence:
       for(YAML::const_iterator it = name_node.begin(); it != name_node.end(); ++it){
         analyses_names_.push_back(it->as<std::string>());
       }
       break;
     default:
       throw std::invalid_argument{
         "No Analysis was provided to rivet. "
         "Either give an analysis or deactivate rivet."
       };
     }
   }
 
   namespace {
     void replace(
         std::string & str,
         char to_replace, std::string const & replacement
     ) {
       for(
           auto pos = str.find(to_replace);
           pos != str.npos;
           pos = str.find(to_replace, pos)
       ) {
         str.replace(pos, 1, replacement);
         pos += replacement.size();
       }
     }
 
     // remove "special" characters from scale name
     // so that we can more easily use it as part of a file name
     std::string sanitise_scalename(std::string scalename) {
       replace(scalename, '/', "_over_");
       replace(scalename, '*', "_times_");
       return scalename;
     }
   }
 
   void RivetAnalysis::init(Event const & event){
     rivet_runs_.push_back(
       {std::make_unique<Rivet::AnalysisHandler>(), "", HepMCInterface()}
     );
     rivet_runs_.back().handler->addAnalyses(analyses_names_);
     if( !event.variations().empty() ){
       rivet_runs_.reserve(event.variations().size()+1);
       for(auto const & vari : event.variations()){
         std::ostringstream name;
         name << ".Scale" << sanitise_scalename(vari.description->scale_name)
              << "_MuR" << vari.description->mur_factor
              << "_MuF" << vari.description->muf_factor;
         rivet_runs_.push_back(
           {std::make_unique<Rivet::AnalysisHandler>(), name.str(), HepMCInterface()}
         );
         rivet_runs_.back().handler->addAnalyses(analyses_names_);
       }
     }
   }
 
   void RivetAnalysis::fill(Event const & event, Event const &){
     if(first_event_){
       first_event_=false;
       init(event);
     }
     HepMC::GenEvent hepmc_kin(rivet_runs_[0].hepmc.init_kinematics(event));
     for(size_t i = 0; i < rivet_runs_.size(); ++i){
       auto & run = rivet_runs_[i];
       run.hepmc.set_central(hepmc_kin, event, i-1); // -1: first = central
       run.handler->analyze(hepmc_kin);
     }
   }
 
   void RivetAnalysis::finalise(){
     for(auto const & run: rivet_runs_){
       run.handler->finalize();
       run.handler->writeData(output_name_+run.name+std::string(".yoda"));
     }
   }
 } // namespace HEJ
 
 #else // no rivet => create empty analysis
 
 namespace Rivet {
   class AnalysisHandler {};
 }
 
 namespace HEJ {
 
   RivetAnalysis::RivetAnalysis(YAML::Node const &)
   {
     throw std::invalid_argument(
         "Failed to create RivetAnalysis: "
         "HEJ 2 was built without rivet support"
     );
   }
 
   void RivetAnalysis::init(Event const &){}
   void RivetAnalysis::fill(Event const &, Event const &){}
   void RivetAnalysis::finalise(){}
 } // namespace HEJ
 
 #endif
diff --git a/src/ScaleFunction.cc b/src/ScaleFunction.cc
index 5fc7d7e..04eb410 100644
--- a/src/ScaleFunction.cc
+++ b/src/ScaleFunction.cc
@@ -1,171 +1,171 @@
 /**
- *  \authors   Jeppe Andersen, Tuomas Hapola, Marian Heil, Andreas Maier, Jennifer Smillie
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
  *  \date      2019
  *  \copyright GPLv2 or later
  */
 #include "HEJ/ScaleFunction.hh"
 
 #include <cassert>
 
 #include "HEJ/Event.hh"
 #include "HEJ/exceptions.hh"
 
 namespace HEJ{
 
   double H_T(Event const & ev){
     double result = 0.;
     for(size_t i = 0; i < ev.outgoing().size(); ++i){
       auto const decay_products = ev.decays().find(i);
       if(decay_products == end(ev.decays())){
         result += ev.outgoing()[i].perp();
       }
       else{
         for(auto const & particle: decay_products->second){
           result += particle.perp();
         }
       }
     }
     return result;
   }
 
   double max_jet_pt(Event const & ev) {
     return sorted_by_pt(ev.jets()).front().pt();
   }
 
   double jet_invariant_mass(Event const & ev) {
     fastjet::PseudoJet sum;
     for(const auto & jet: ev.jets()) sum+=jet;
     return sum.m();
   }
 
   double m_j1j2(Event const & ev) {
     const auto jets = sorted_by_pt(ev.jets());
     assert(jets.size() >= 2);
     return (jets[0] + jets[1]).m();
   }
 
   ScaleFunction operator*(double factor, ScaleFunction base_scale) {
     base_scale.name_.insert(0, std::to_string(factor) + '*');
     auto new_fun =
       [factor,fun{std::move(base_scale.fun_)}](HEJ::Event const & ev) {
       return factor*fun(ev);
     };
     base_scale.fun_ = std::move(new_fun);
     return base_scale;
   }
 
   ScaleFunction operator*(ScaleFunction factor, ScaleFunction base_scale) {
     base_scale.name_.insert(0, factor.name_ + '*');
     auto new_fun =
       [fun1{std::move(factor.fun_)}, fun2{std::move(base_scale.fun_)}, name{base_scale.name_}]
       (HEJ::Event const & ev) {
         return fun1(ev)*fun2(ev);
     };
     base_scale.fun_ = std::move(new_fun);
     return base_scale;
   }
 
   ScaleFunction operator/(ScaleFunction base_scale, double denom) {
     base_scale.name_.append('/' + std::to_string(denom));
     auto new_fun =
       [denom,fun{std::move(base_scale.fun_)}](HEJ::Event const & ev) {
       return fun(ev)/denom;
     };
     base_scale.fun_ = std::move(new_fun);
     return base_scale;
   }
 
   ScaleFunction operator/(ScaleFunction base_scale, ScaleFunction denom) {
     base_scale.name_.append('/' + denom.name_);
     auto new_fun =
       [fun2{std::move(denom.fun_)}, fun1{std::move(base_scale.fun_)}]
       (HEJ::Event const & ev) {
         return fun1(ev)/fun2(ev);
     };
     base_scale.fun_ = std::move(new_fun);
     return base_scale;
   }
 
   // TODO: significant logic duplication with operator()
   void ScaleGenerator::gen_descriptions() {
     if(scales_.empty()) {
       throw std::logic_error{"Need at least one scale"};
     }
     descriptions_.emplace_back(
         std::make_shared<ParameterDescription>(scales_.front().name(), 1., 1.)
     );
 
     for(auto & base_scale: scales_){
       const auto base_name = base_scale.name();
       descriptions_.emplace_back(
           std::make_shared<ParameterDescription>(base_name, 1., 1.)
       );
       //multiplicative scale variation
       for(double mur_factor: scale_factors_){
         for(double muf_factor: scale_factors_){
           if(muf_factor == 1. && mur_factor == 1.) continue;
           if(
               mur_factor/muf_factor < 1/max_scale_ratio_
               || mur_factor/muf_factor > max_scale_ratio_
           ) continue;
           descriptions_.emplace_back(
               std::make_shared<ParameterDescription>(
                   base_name, mur_factor, muf_factor
               )
           );
         }
       }
     }
   }
 
   Event ScaleGenerator::operator()(Event ev) const {
     if(! ev.variations().empty()) {
       throw std::invalid_argument{"Input event already has scale variation"};
     }
     assert(!scales_.empty());
     assert(!descriptions_.empty());
 
     size_t descr_idx = 0;
     const double mu_central = (scales_.front())(ev);
     ev.central().mur = mu_central;
     ev.central().muf = mu_central;
     assert(descr_idx < descriptions_.size());
     assert(descriptions_[descr_idx]);
     ev.central().description = descriptions_[descr_idx++];
 
     // check if we are doing scale variation at all
     if(scales_.size() == 1 && scale_factors_.empty()) return ev;
 
     for(auto & base_scale: scales_){
       const double mu_base = base_scale(ev);
       assert(descr_idx < descriptions_.size());
       assert(descriptions_[descr_idx]);
       ev.variations().emplace_back(
           EventParameters{
             mu_base, mu_base, ev.central().weight, descriptions_[descr_idx++]
           }
       );
       //multiplicative scale variation
       for(double mur_factor: scale_factors_){
         const double mur = mu_base*mur_factor;
         for(double muf_factor: scale_factors_){
           if(muf_factor == 1. && mur_factor == 1.) continue;
           const double muf = mu_base*muf_factor;
           if(
               mur/muf < 1/max_scale_ratio_
               || mur/muf > max_scale_ratio_
           ) continue;
           assert(descr_idx < descriptions_.size());
           assert(descriptions_[descr_idx]);
           ev.variations().emplace_back(
               EventParameters{
                 mur, muf, ev.central().weight, descriptions_[descr_idx++]
               }
           );
         }
       }
     };
     return ev;
   }
 
 }
diff --git a/src/Tensor.cc b/src/Tensor.cc
index 0fba3bb..af7e882 100644
--- a/src/Tensor.cc
+++ b/src/Tensor.cc
@@ -1,790 +1,795 @@
+/**
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
+ *  \date      2019
+ *  \copyright GPLv2 or later
+ */
 #include "HEJ/currents.hh"
 #include "HEJ/Tensor.hh"
 
 #include <array>
 
 #include <iostream>
 
 namespace{
 // Tensor Template definitions
   short int sigma_index5[1024];
   short int sigma_index3[64];
   std::valarray<COM> permfactor5;
   std::valarray<COM> permfactor3;
   short int helfactor5[2][1024];
   short int helfactor3[2][64];
 
   // 2D sigma matrices
   const COM sigma0[2][2] = { {1.,0.},          {0.,          1.} };
   const COM sigma1[2][2] = { {0.,1.},          {1.,          0.} };
   const COM sigma2[2][2] = { {0,-1.*COM(0,1)}, {1.*COM(0,1), 0.} };
   const COM sigma3[2][2] = { {1.,0.},          {0.,         -1.} };
 
   Tensor<1,4> Sigma(int i, int j, bool hel){
     Tensor<1,4> newT;
     if(hel){
       newT.components[0]=sigma0[i][j];
       newT.components[1]=sigma1[i][j];
       newT.components[2]=sigma2[i][j];
       newT.components[3]=sigma3[i][j];
     } else {
       newT.components[0]= sigma0[i][j];
       newT.components[1]=-sigma1[i][j];
       newT.components[2]=-sigma2[i][j];
       newT.components[3]=-sigma3[i][j];
     }
     return newT;
   }
 
   // map from a list of 5 tensor lorentz indices onto a single index 0<=i<1024
   // in 4 dimensional spacetime
 
   int tensor2listindex(std::array<int,5> indexlist){
     int mu=indexlist[0];
     int nu=indexlist[1];
     int sigma=indexlist[2];
     int tau=indexlist[3];
     int rho=indexlist[4];
     int myindex;
 
     myindex = 256*mu+64*nu+16*sigma+4*tau+rho;
 
     if(myindex<0||myindex>1023){
       std::cerr<<"bad index in tensor2listindex "<<std::endl;
       return 1024;
     }
     return myindex;
   }
 
   // map from a list of 3 tensor lorentz indices onto a single index 0<=i<64 in
   // 4 dimensional spacetime
   int tensor2listindex(std::array<int,3> indexlist){
     int mu=indexlist[0];
     int nu=indexlist[1];
     int sigma=indexlist[2];
     int myindex;
 
     myindex = 16*mu+4*nu+sigma;
 
     if(myindex<0||myindex>64){
       std::cerr<<"bad index in tensor2listindex "<<std::endl;
       return 64;
     }
     return myindex;
   }
 
   // generate all unique perms of vectors {a,a,a,a,b}, return in perms
   // set_permfactor is a bool which encodes the anticommutation relations of the
   // sigma matrices namely if we have sigma0, set_permfactor= false because it
   // commutes with all others otherwise we need to assign a minus sign to odd
   // permutations, set in permfactor
   // note, inital perm is always even
   void perms41(int same4, int diff, std::vector<std::array<int,5>> & perms){
     bool set_permfactor(true);
     if(same4==0||diff==0)
       set_permfactor=false;
 
     for(int diffpos=0;diffpos<5;diffpos++){
       std::array<int,5> tempvec={same4,same4,same4,same4,same4};
       tempvec[diffpos]=diff;
       perms.push_back(tempvec);
       if(set_permfactor){
         if(diffpos%2==1)
           permfactor5[tensor2listindex(tempvec)]=-1.;
       }
     }
   }
 
   // generate all unique perms of vectors {a,a,a,b,b}, return in perms
   // note, inital perm is always even
   void perms32(int same3, int diff, std::vector<std::array<int,5>> & perms){
     bool set_permfactor(true);
     if(same3==0||diff==0)
       set_permfactor=false;
 
     for(int diffpos1=0;diffpos1<5;diffpos1++){
       for(int diffpos2=diffpos1+1;diffpos2<5;diffpos2++){
         std::array<int,5> tempvec={same3,same3,same3,same3,same3};
         tempvec[diffpos1]=diff;
         tempvec[diffpos2]=diff;
         perms.push_back(tempvec);
         if(set_permfactor){
           if((diffpos2-diffpos1)%2==0)
             permfactor5[tensor2listindex(tempvec)]=-1.;
         }
       }
     }
   }
 
   // generate all unique perms of vectors {a,a,a,b,c}, return in perms
   // we have two bools since there are three distinct type of sigma matrices to
   // permute, so need to test if the 3xrepeated = sigma0 or if one of the
   // singles is sigma0
   // if diff1/diff2 are distinct, flipping them results in a change of perm,
   // otherwise it'll be symmetric under flip -> encode this in set_permfactor2
   // as long as diff2!=0 can ensure inital perm is even
   // if diff2=0 then it is not possible to ensure inital perm even -> encode in
   // bool signflip
   void perms311(int same3, int diff1, int diff2,
       std::vector<std::array<int,5>> & perms
   ){
     bool set_permfactor2(true);
     bool same0(false);
     bool diff0(false);
     bool signflip(false); // if true, inital perm is odd
 
     if(same3==0) // is the repeated matrix sigma0?
       same0 = true;
     else if(diff2==0){ // is one of the single matrices sigma0
       diff0=true;
       if((diff1%3-same3)!=-1)
         signflip=true;
       } else if(diff1==0){
         std::cerr<<"Note, only first and last argument may be zero"<<std::endl;
         return;
     }
 
     // possible outcomes: tt, ft, tf
 
     for(int diffpos1=0;diffpos1<5;diffpos1++){
       for(int diffpos2=diffpos1+1;diffpos2<5;diffpos2++){
         std::array<int,5> tempvec={same3,same3,same3,same3,same3};
         tempvec[diffpos1]=diff1;
         tempvec[diffpos2]=diff2;
         perms.push_back(tempvec);
 
         if(!same0 && !diff0){
         // full antisymmetric under exchange of same3,diff1,diff2
           if((diffpos2-diffpos1)%2==0){
             permfactor5[tensor2listindex(tempvec)]=-1.*COM(0,1); // odd perm
             // if this perm is odd, swapping diff1<->diff2 automatically even
             set_permfactor2=false;
           } else {
             permfactor5[tensor2listindex(tempvec)]=COM(0,1); // even perm
             // if this perm is even, swapping diff1<->diff2 automatically odd
             set_permfactor2=true;
           }
         } else if(diff0){// one of the single matrices is sigma0
           if(signflip){ // initial config is odd!
             if(diffpos1%2==1){
               permfactor5[tensor2listindex(tempvec)]=COM(0,1); // even perm
               // initally symmetric under diff1<->diff2 =>
               // if this perm is even, automatically even for first diffpos2
               set_permfactor2=false;
             } else {
               permfactor5[tensor2listindex(tempvec)]=-1.*COM(0,1); // odd perm
               // initally symmetric under diff1<->diff2 =>
               // if this perm is odd, automatically odd for first diffpos2
               set_permfactor2=true;
             }
           } else {// diff0=true, initial config is even
             if(diffpos1%2==1){
               permfactor5[tensor2listindex(tempvec)]=-1.*COM(0,1); // odd perm
               // initally symmetric under diff1<->diff2 =>
               // if this perm is odd, automatically odd for first diffpos2
               set_permfactor2=true;
             } else {
               permfactor5[tensor2listindex(tempvec)]=COM(0,1); // even perm
               // initally symmetric under diff1<->diff2 =>
               // if this perm is even, automatically even for first diffpos2
               set_permfactor2=false;
             }
           }
           if((diffpos2-diffpos1-1)%2==1)
             set_permfactor2=!set_permfactor2; // change to account for diffpos2
         } else if(same0){
           // the repeated matrix is sigma0
           // => only relative positions of diff1, diff2 matter.
           // always even before flip  because diff1pos<diff2pos
           permfactor5[tensor2listindex(tempvec)]=COM(0,1);
           // if this perm is odd, swapping diff1<->diff2 automatically odd
           set_permfactor2=true;
         }
 
         tempvec[diffpos1]=diff2;
         tempvec[diffpos2]=diff1;
         perms.push_back(tempvec);
 
         if(set_permfactor2)
           permfactor5[tensor2listindex(tempvec)]=-1.*COM(0,1);
         else
           permfactor5[tensor2listindex(tempvec)]=COM(0,1);
 
       }
     }
   } // end perms311
 
   // generate all unique perms of vectors {a,a,b,b,c}, return in perms
   void perms221(int same2a, int same2b, int diff,
       std::vector<std::array<int,5>> & perms
   ){
     bool set_permfactor1(true);
     bool set_permfactor2(true);
     if(same2b==0){
       std::cerr<<"second entry in perms221() shouldn't be zero" <<std::endl;
       return;
     } else if(same2a==0)
       set_permfactor1=false;
     else if(diff==0)
       set_permfactor2=false;
 
     for(int samepos=0;samepos<5;samepos++){
       int permcount = 0;
       for(int samepos2=samepos+1;samepos2<5;samepos2++){
         for(int diffpos=0;diffpos<5;diffpos++){
           if(diffpos==samepos||diffpos==samepos2) continue;
 
           std::array<int,5> tempvec={same2a,same2a,same2a,same2a,same2a};
           tempvec[samepos]=same2b;
           tempvec[samepos2]=same2b;
           tempvec[diffpos]=diff;
           perms.push_back(tempvec);
 
           if(set_permfactor1){
             if(set_permfactor2){// full anti-symmetry
               if(permcount%2==1)
           permfactor5[tensor2listindex(tempvec)]=-1.;
             } else { // diff is sigma0
               if( ((samepos2-samepos-1)%3>0)
                   && (abs(abs(samepos2-diffpos)-abs(samepos-diffpos))%3>0) )
                 permfactor5[tensor2listindex(tempvec)]=-1.;
             }
           } else { // same2a is sigma0
             if((diffpos>samepos) && (diffpos<samepos2))
               permfactor5[tensor2listindex(tempvec)]=-1.;
           }
           permcount++;
         }
       }
     }
   }
 
   // generate all unique perms of vectors {a,a,b,b,c}, return in perms
   // there must be a sigma zero if we have 4 different matrices in string
   // bool is true if sigma0 is the repeated matrix
   void perms2111(int same2, int diff1,int diff2,int diff3,
       std::vector<std::array<int,5>> & perms
   ){
     bool twozero(false);
     if(same2==0)
       twozero=true;
     else if (diff1!=0){
       std::cerr<<"One of first or second argurments must be a zero"<<std::endl;
       return;
     } else if(diff2==0|| diff3==0){
       std::cerr<<"Only first and second arguments may be a zero."<<std::endl;
       return;
     }
 
     int permcount = 0;
 
     for(int diffpos1=0;diffpos1<5;diffpos1++){
       for(int diffpos2=0;diffpos2<5;diffpos2++){
         if(diffpos2==diffpos1) continue;
         for(int diffpos3=0;diffpos3<5;diffpos3++){
           if(diffpos3==diffpos2||diffpos3==diffpos1) continue;
 
           std::array<int,5> tempvec={same2,same2,same2,same2,same2};
           tempvec[diffpos1]=diff1;
           tempvec[diffpos2]=diff2;
           tempvec[diffpos3]=diff3;
           perms.push_back(tempvec);
 
           if(twozero){// don't care about exact positions of singles, just order
             if(diffpos2>diffpos3 && diffpos3>diffpos1)
               permfactor5[tensor2listindex(tempvec)]=-1.*COM(0,1);// odd
             else if(diffpos1>diffpos2 && diffpos2>diffpos3)
               permfactor5[tensor2listindex(tempvec)]=-1.*COM(0,1);// odd
             else if(diffpos3>diffpos1 && diffpos1>diffpos2)
               permfactor5[tensor2listindex(tempvec)]=-1.*COM(0,1);// odd
             else
               permfactor5[tensor2listindex(tempvec)]=COM(0,1);// evwn
           } else {
             if(permcount%2==1)
               permfactor5[tensor2listindex(tempvec)]=-1.*COM(0,1);
             else
               permfactor5[tensor2listindex(tempvec)]=COM(0,1);
           }
           permcount++;
         }
       }
     }
   }
 
   void perms21(int same, int diff, std::vector<std::array<int,3>> & perms){
 
     bool set_permfactor(true);
     if(same==0||diff==0)
       set_permfactor=false;
 
     for(int diffpos=0; diffpos<3;diffpos++){
       std::array<int,3> tempvec={same,same,same};
       tempvec[diffpos]=diff;
       perms.push_back(tempvec);
       if(set_permfactor && diffpos==1)
         permfactor3[tensor2listindex(tempvec)]=-1.;
     }
   }
 
   void perms111(int diff1, int diff2, int diff3,
     std::vector<std::array<int,3>> & perms
   ){
     bool sig_zero(false);
     if(diff1==0)
       sig_zero=true;
     else if(diff2==0||diff3==0){
       std::cerr<<"Only first argument may be a zero."<<std::endl;
       return;
     }
     int permcount=0;
     for(int pos1=0;pos1<3;pos1++){
       for(int pos2=pos1+1;pos2<3;pos2++){
         std::array<int,3> tempvec={diff1,diff1,diff1};
         tempvec[pos1]=diff2;
         tempvec[pos2]=diff3;
         perms.push_back(tempvec);
         if(sig_zero){
           permfactor3[tensor2listindex(tempvec)]=1.*COM(0,1); // even
         } else if(permcount%2==1){
           permfactor3[tensor2listindex(tempvec)]=-1.*COM(0,1); // odd
         } else {
           permfactor3[tensor2listindex(tempvec)]=1.*COM(0,1); // even
         }
 
         tempvec[pos1]=diff3;
         tempvec[pos2]=diff2;
         perms.push_back(tempvec);
 
         if(sig_zero){
           permfactor3[tensor2listindex(tempvec)]=-1.*COM(0,1); // odd
         } else if(permcount%2==1){
           permfactor3[tensor2listindex(tempvec)]=1.*COM(0,1); // even
         } else {
           permfactor3[tensor2listindex(tempvec)]=-1.*COM(0,1); // odd
         }
         permcount++;
       }
     }
   }
 
   void SpinorO(CLHEP::HepLorentzVector p, bool hel, COM *sp){
     // sp is pointer to COM sp[2]
     COM pplus = p.e() +p.z();
     COM pminus = p.e() -p.z();
     COM sqpp= sqrt(pplus);
     COM sqpm= sqrt(pminus);
     COM pperp = p.x() + COM(0,1)*p.y();
 
     // if hel=+
     if(hel){
       sp[0] = sqpp;
       sp[1] = sqpm*pperp/abs(pperp);
     } else {
       sp[0] = sqpm*conj(pperp)/abs(pperp);
       sp[1] = -sqpp;
     }
   }
 
   void SpinorIp(COM sqpp, bool hel, COM *sp){
     // if hel=+
     if(hel){
       sp[0] = sqpp;
       sp[1] = 0.;
     } else {
       sp[0] = 0.;
       sp[1] = -sqpp;
     }
   }
 
   void SpinorIm(COM sqpm, bool hel, COM *sp){
     // if hel=+
     if(hel){
       sp[0] = 0;
       sp[1] = -sqpm;
     } else {
       sp[0] = -sqpm;
       sp[1] = 0.;
     }
   }
 
   void Spinor(CLHEP::HepLorentzVector p, bool hel, COM *sp){
     COM pplus = p.e() +p.z();
     COM pminus = p.e() -p.z();
 
     // If incoming along +ve z
     if (pminus==0.){
       COM sqpp= sqrt(pplus);
       SpinorIp(sqpp,hel,sp);
     }
     // If incoming along -ve z
     else if(pplus==0.){
       COM sqpm= sqrt(pminus);
       SpinorIm(sqpm,hel,sp);
     }
     // Outgoing
     else {
       SpinorO(p,hel,sp);
     }
   }
 } // anonymous namespace
 
 Tensor<2,4> Metric(){
   Tensor<2,4> g(0.);
   g.Set(0,0, 1.);
   g.Set(1,1, -1.);
   g.Set(2,2, -1.);
   g.Set(3,3, -1.);
   return g;
 }
 
 // <1|mu|2>
 Tensor<1,4> TCurrent(CLHEP::HepLorentzVector p1, bool h1,
                      CLHEP::HepLorentzVector p2, bool h2
 ){
   COM sp1[2];
   COM sp2[2];
   Tensor<1,4> newT(0.);
 
   CLHEP::HepLorentzVector p1new{ p1.e()<0?-p1:p1 };
   CLHEP::HepLorentzVector p2new{ p2.e()<0?-p2:p2 };
 
   if(h1!=h2){
     return newT;
   }
 
   Spinor(p1new, h1, sp1);
   Spinor(p2new, h2, sp2);
 
   for(int i=0;i<2;i++){
     for(int j=0; j<2; j++){
       newT+=(Sigma(i,j,h2)*sp2[j])*conj(sp1[i]);
     }
   }
   return newT;
 }
 // <1|mu nu sigma|2>
 Tensor<3,4> T3Current(CLHEP::HepLorentzVector p1, bool h1,
   CLHEP::HepLorentzVector p2, bool h2
 ){
 
   COM sp1[2];
   COM sp2[2];
 
   Tensor<3,4> newT(0.);
 
   CLHEP::HepLorentzVector p1new{ p1.e()<0?-p1:p1 };
   CLHEP::HepLorentzVector p2new{ p2.e()<0?-p2:p2 };
 
   if(h1!=h2){
     return newT;
   }
 
   Spinor(p1new, h1, sp1);
   Spinor(p2new, h2, sp2);
 
   COM current[4];
 
   for(int i=0; i<2;i++){
     for(int j=0; j<2;j++){
       current[0]+=conj(sp1[i])*sigma0[i][j]*sp2[j];
       current[1]+=conj(sp1[i])*sigma1[i][j]*sp2[j];
       current[2]+=conj(sp1[i])*sigma2[i][j]*sp2[j];
       current[3]+=conj(sp1[i])*sigma3[i][j]*sp2[j];
     }
   }
 
   for( int itensor=0; itensor<newT.len(); itensor++ ){
     double hfact = double( helfactor3[h2][itensor] );
     newT.components[itensor] = current[sigma_index3[itensor]] * hfact
                               * permfactor3[itensor];
   }
 
   return newT;
 }
 
 // <1|mu nu sigma tau rho|2>
 Tensor<5,4> T5Current(CLHEP::HepLorentzVector p1, bool h1,
                       CLHEP::HepLorentzVector p2, bool h2
 ){
 
   COM sp1[2];
   COM sp2[2];
 
   Tensor<5,4> newT(0.);
 
   CLHEP::HepLorentzVector p1new{ p1.e()<0?-p1:p1 };
   CLHEP::HepLorentzVector p2new{ p2.e()<0?-p2:p2 };
 
   if(h1!=h2){
     return newT;
   }
 
   Spinor(p1new, h1, sp1);
   Spinor(p2new, h2, sp2);
 
   COM current[4];
 
   for(int i=0; i<2;i++){
     for(int j=0; j<2;j++){
       current[0]+=conj(sp1[i])*sigma0[i][j]*sp2[j];
       current[1]+=conj(sp1[i])*sigma1[i][j]*sp2[j];
       current[2]+=conj(sp1[i])*sigma2[i][j]*sp2[j];
       current[3]+=conj(sp1[i])*sigma3[i][j]*sp2[j];
     }
   }
 
   for(int itensor=0;itensor<newT.len();itensor++){
     double hfact = double(helfactor5[h2][itensor]);
     newT.components[itensor] = current[sigma_index5[itensor]] * hfact
                               * permfactor5[itensor];
   }
 
   return newT;
 }
 
 Tensor<1,4> Construct1Tensor(CCurrent j){
   Tensor<1,4> newT;
   newT.components={j.c0,j.c1,j.c2,j.c3};
   return newT;
 }
 
 Tensor<1,4> Construct1Tensor(CLHEP::HepLorentzVector p){
   Tensor<1,4> newT;
   newT.components={p.e(),p.x(),p.y(),p.z()};
   return newT;
 }
 
 Tensor<1,4> eps(CLHEP::HepLorentzVector k, CLHEP::HepLorentzVector ref, bool pol){
 
   Tensor<1,4> polvec;
   COM spk[2];
   COM spr[2];
   COM denom;
 
   CLHEP::HepLorentzVector knew{ k.e()<0?-k:k };
 
   Spinor(knew, pol, spk);
   Spinor(ref, !pol, spr);
   denom  = pow(-1.,pol)*sqrt(2)*(conj(spr[0])*spk[0] + conj(spr[1])*spk[1]);
   polvec = TCurrent(ref,!pol,knew,!pol)/denom;
 
   return polvec;
 }
 
 // slow function! - but only need to evaluate once.
 bool init_sigma_index(){
 
   // initialize permfactor(3) to list of ones (change to minus one for each odd
   // permutation and multiply by i for all permutations in perms2111, perms311,
   // perms111)
   permfactor5.resize(1024,1.);
   permfactor3.resize(64,1.);
 
   // first set sigma_index (5)
   std::vector<std::array<int,5>> sigma0indices;
   std::vector<std::array<int,5>> sigma1indices;
   std::vector<std::array<int,5>> sigma2indices;
   std::vector<std::array<int,5>> sigma3indices;
 
   // need to generate all possible permuations of {i,j,k,l,m}
   // where each index can be {0,1,2,3,4}
   // 1024 possibilities
 
   // perms with 5 same
   sigma0indices.push_back({0,0,0,0,0});
   sigma1indices.push_back({1,1,1,1,1});
   sigma2indices.push_back({2,2,2,2,2});
   sigma3indices.push_back({3,3,3,3,3});
 
   // perms with 4 same
   perms41(1,0,sigma0indices);
   perms41(2,0,sigma0indices);
   perms41(3,0,sigma0indices);
   perms41(0,1,sigma1indices);
   perms41(2,1,sigma1indices);
   perms41(3,1,sigma1indices);
   perms41(0,2,sigma2indices);
   perms41(1,2,sigma2indices);
   perms41(3,2,sigma2indices);
   perms41(0,3,sigma3indices);
   perms41(1,3,sigma3indices);
   perms41(2,3,sigma3indices);
 
   // perms with 3 same, 2 same
   perms32(0,1,sigma0indices);
   perms32(0,2,sigma0indices);
   perms32(0,3,sigma0indices);
   perms32(1,0,sigma1indices);
   perms32(1,2,sigma1indices);
   perms32(1,3,sigma1indices);
   perms32(2,0,sigma2indices);
   perms32(2,1,sigma2indices);
   perms32(2,3,sigma2indices);
   perms32(3,0,sigma3indices);
   perms32(3,1,sigma3indices);
   perms32(3,2,sigma3indices);
 
   // perms with 3 same, 2 different
   perms311(1,2,3,sigma0indices);
   perms311(2,3,1,sigma0indices);
   perms311(3,1,2,sigma0indices);
   perms311(0,2,3,sigma1indices);
   perms311(2,3,0,sigma1indices);
   perms311(3,2,0,sigma1indices);
   perms311(0,3,1,sigma2indices);
   perms311(1,3,0,sigma2indices);
   perms311(3,1,0,sigma2indices);
   perms311(0,1,2,sigma3indices);
   perms311(1,2,0,sigma3indices);
   perms311(2,1,0,sigma3indices);
 
   perms221(1,2,0,sigma0indices);
   perms221(1,3,0,sigma0indices);
   perms221(2,3,0,sigma0indices);
   perms221(0,2,1,sigma1indices);
   perms221(0,3,1,sigma1indices);
   perms221(2,3,1,sigma1indices);
   perms221(0,1,2,sigma2indices);
   perms221(0,3,2,sigma2indices);
   perms221(1,3,2,sigma2indices);
   perms221(0,1,3,sigma3indices);
   perms221(0,2,3,sigma3indices);
   perms221(1,2,3,sigma3indices);
 
   perms2111(0,1,2,3,sigma0indices);
   perms2111(1,0,2,3,sigma1indices);
   perms2111(2,0,3,1,sigma2indices);
   perms2111(3,0,1,2,sigma3indices);
 
   if(sigma0indices.size()!=256){
     std::cerr<<"sigma_index not set: ";
     std::cerr<<"sigma0indices has "<< sigma0indices.size() << " components" << std::endl;
     return false;
   } else if(sigma1indices.size()!=256){
     std::cerr<<"sigma_index not set: ";
     std::cerr<<"sigma1indices has "<< sigma0indices.size() << " components" << std::endl;
     return false;
   } else if(sigma2indices.size()!=256){
     std::cerr<<"sigma_index not set: ";
     std::cerr<<"sigma2indices has "<< sigma0indices.size() << " components" << std::endl;
     return false;
   } else if(sigma3indices.size()!=256){
     std::cerr<<"sigma_index not set: ";
     std::cerr<<"sigma3indices has "<< sigma0indices.size() << " components" << std::endl;
     return false;
   }
 
   for(int i=0;i<256;i++){
     // map each unique set of tensor indices to its position in a list
     int index0 = tensor2listindex(sigma0indices.at(i));
     int index1 = tensor2listindex(sigma1indices.at(i));
     int index2 = tensor2listindex(sigma2indices.at(i));
     int index3 = tensor2listindex(sigma3indices.at(i));
     sigma_index5[index0]=0;
     sigma_index5[index1]=1;
     sigma_index5[index2]=2;
     sigma_index5[index3]=3;
 
     short int sign[4]={1,-1,-1,-1};
     // plus->true->1
     helfactor5[1][index0] = sign[sigma0indices.at(i)[1]]
                           * sign[sigma0indices.at(i)[3]];
     helfactor5[1][index1] = sign[sigma1indices.at(i)[1]]
                           * sign[sigma1indices.at(i)[3]];
     helfactor5[1][index2] = sign[sigma2indices.at(i)[1]]
                           * sign[sigma2indices.at(i)[3]];
     helfactor5[1][index3] = sign[sigma3indices.at(i)[1]]
                           * sign[sigma3indices.at(i)[3]];
     // minus->false->0
     helfactor5[0][index0] = sign[sigma0indices.at(i)[0]]
                           * sign[sigma0indices.at(i)[2]]
                           * sign[sigma0indices.at(i)[4]];
     helfactor5[0][index1] = sign[sigma1indices.at(i)[0]]
                           * sign[sigma1indices.at(i)[2]]
                           * sign[sigma1indices.at(i)[4]];
     helfactor5[0][index2] = sign[sigma2indices.at(i)[0]]
                           * sign[sigma2indices.at(i)[2]]
                           * sign[sigma2indices.at(i)[4]];
     helfactor5[0][index3] = sign[sigma3indices.at(i)[0]]
                           * sign[sigma3indices.at(i)[2]]
                           * sign[sigma3indices.at(i)[4]];
   }
 
   // now set sigma_index3
   std::vector<std::array<int,3>> sigma0indices3;
   std::vector<std::array<int,3>> sigma1indices3;
   std::vector<std::array<int,3>> sigma2indices3;
   std::vector<std::array<int,3>> sigma3indices3;
 
   // perms with 3 same
   sigma0indices3.push_back({0,0,0});
   sigma1indices3.push_back({1,1,1});
   sigma2indices3.push_back({2,2,2});
   sigma3indices3.push_back({3,3,3});
 
   // 2 same
   perms21(1,0,sigma0indices3);
   perms21(2,0,sigma0indices3);
   perms21(3,0,sigma0indices3);
   perms21(0,1,sigma1indices3);
   perms21(2,1,sigma1indices3);
   perms21(3,1,sigma1indices3);
   perms21(0,2,sigma2indices3);
   perms21(1,2,sigma2indices3);
   perms21(3,2,sigma2indices3);
   perms21(0,3,sigma3indices3);
   perms21(1,3,sigma3indices3);
   perms21(2,3,sigma3indices3);
 
   // none same
   perms111(1,2,3,sigma0indices3);
   perms111(0,2,3,sigma1indices3);
   perms111(0,3,1,sigma2indices3);
   perms111(0,1,2,sigma3indices3);
 
   if(sigma0indices3.size()!=16){
     std::cerr<<"sigma_index3 not set: ";
     std::cerr<<"sigma0indices3 has "<< sigma0indices3.size() << " components" << std::endl;
     return false;
   } else if(sigma1indices3.size()!=16){
     std::cerr<<"sigma_index3 not set: ";
     std::cerr<<"sigma1indices3 has "<< sigma0indices3.size() << " components" << std::endl;
     return false;
   } else if(sigma2indices3.size()!=16){
     std::cerr<<"sigma_index3 not set: ";
     std::cerr<<"sigma2indices3 has "<< sigma0indices3.size() << " components" << std::endl;
     return false;
   } else if(sigma3indices3.size()!=16){
     std::cerr<<"sigma_index3 not set: ";
     std::cerr<<"sigma3indices3 has "<< sigma0indices3.size() << " components" << std::endl;
     return false;
   }
 
   for(int i=0;i<16;i++){
     int index0 = tensor2listindex(sigma0indices3.at(i));
     int index1 = tensor2listindex(sigma1indices3.at(i));
     int index2 = tensor2listindex(sigma2indices3.at(i));
     int index3 = tensor2listindex(sigma3indices3.at(i));
     sigma_index3[index0]=0;
     sigma_index3[index1]=1;
     sigma_index3[index2]=2;
     sigma_index3[index3]=3;
 
     short int sign[4]={1,-1,-1,-1};
     // plus->true->1
     helfactor3[1][index0] = sign[sigma0indices3.at(i)[1]];
     helfactor3[1][index1] = sign[sigma1indices3.at(i)[1]];
     helfactor3[1][index2] = sign[sigma2indices3.at(i)[1]];
     helfactor3[1][index3] = sign[sigma3indices3.at(i)[1]];
     // minus->false->0
     helfactor3[0][index0] = sign[sigma0indices3.at(i)[0]]
                           * sign[sigma0indices3.at(i)[2]];
     helfactor3[0][index1] = sign[sigma1indices3.at(i)[0]]
                           * sign[sigma1indices3.at(i)[2]];
     helfactor3[0][index2] = sign[sigma2indices3.at(i)[0]]
                           * sign[sigma2indices3.at(i)[2]];
     helfactor3[0][index3] = sign[sigma3indices3.at(i)[0]]
                           * sign[sigma3indices3.at(i)[2]];
   }
   return true;
 } // end init_sigma_index
diff --git a/src/Wjets.cc b/src/Wjets.cc
index cf5ae44..ddd3d38 100644
--- a/src/Wjets.cc
+++ b/src/Wjets.cc
@@ -1,2067 +1,2072 @@
+/**
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
+ *  \date      2019
+ *  \copyright GPLv2 or later
+ */
 #include "HEJ/currents.hh"
 #include "HEJ/utility.hh"
 #include "HEJ/Tensor.hh"
 #include "HEJ/Constants.hh"
 
 #include <array>
 
 #include <iostream>
 
 
 
 namespace { // Helper Functions
   // FKL W Helper Functions
   void jW (CLHEP::HepLorentzVector pout, bool helout, CLHEP::HepLorentzVector pe, bool hele, CLHEP::HepLorentzVector pnu, bool helnu, CLHEP::HepLorentzVector pin, bool helin, current cur)
   {
     // NOTA BENE: Conventions for W+ --> e+ nu, so that nu is lepton(6), e is anti-lepton(5)
     // Need to swap e and nu for events with W- --> e- nubar!
     if (helin==helout && hele==helnu) {
       CLHEP::HepLorentzVector qa=pout+pe+pnu;
       CLHEP::HepLorentzVector qb=pin-pe-pnu;
       double ta(qa.m2()),tb(qb.m2());
 
       current t65,vout,vin,temp2,temp3,temp5;
       joo(pnu,helnu,pe,hele,t65);
       vout[0]=pout.e();
       vout[1]=pout.x();
       vout[2]=pout.y();
       vout[3]=pout.z();
       vin[0]=pin.e();
       vin[1]=pin.x();
       vin[2]=pin.y();
       vin[3]=pin.z();
 
       COM brac615=cdot(t65,vout);
       COM brac645=cdot(t65,vin);
 
       // prod1565 and prod6465 are zero for Ws (not Zs)!!
       // noalias(temp)=prod(trans(CurrentOutOut(pout,helout,pnu,helout)),metric);
       joo(pout,helout,pnu,helout,temp2);
       // noalias(temp2)=prod(temp,ctemp);
       COM prod1665=cdot(temp2,t65);
       // noalias(temp)=prod(trans(Current(pe,helin,pin,helin)),metric);
       // noalias(temp2)=prod(temp,ctemp);
       joi(pe,helin,pin,helin,temp3);
       COM prod5465=cdot(temp3,t65);
       // noalias(temp)=prod(trans(Current(pnu,helin,pin,helin)),metric);
       // noalias(temp2)=prod(temp,ctemp);
 
       joo(pout,helout,pe,helout,temp2);
       joi(pnu,helnu,pin,helin,temp3);
       joi(pout,helout,pin,helin,temp5);
 
       current term1,term2,term3,sum;
       cmult(2.*brac615/ta+2.*brac645/tb,temp5,term1);
       cmult(prod1665/ta,temp3,term2);
       cmult(-prod5465/tb,temp2,term3);
 
      //    cur=((2.*brac615*Current(pout,helout,pin,helin)+prod1565*Current(pe,helin,pin,helin)+prod1665*Current(pnu,helin,pin,helin))/ta + (2.*brac645*Current(pout,helout,pin,helin)-prod5465*CurrentOutOut(pout,helout,pe,helout)-prod6465*CurrentOutOut(pout,helout,pnu,helout))/tb);
       //    cur=((2.*brac615*temp5+prod1565*temp3+prod1665*temp4)/ta + (2.*brac645*temp5-prod5465*temp1-prod6465*temp2)/tb);
       cadd(term1,term2,term3,sum);
       //    std::cout<<"sum: ("<<sum[0]<<","<<sum[1]<<","<<sum[2]<<","<<sum[3]<<")\n";
       cur[0]=sum[0];
       cur[1]=sum[1];
       cur[2]=sum[2];
       cur[3]=sum[3];
     }
   }
 
   void jWbar (CLHEP::HepLorentzVector pout, bool helout, CLHEP::HepLorentzVector pe, bool hele, CLHEP::HepLorentzVector pnu, bool helnu, CLHEP::HepLorentzVector pin, bool helin, current cur)
   {
     // NOTA BENE: Conventions for W+ --> e+ nu, so that nu is lepton(6), e is anti-lepton(5)
     // Need to swap e and nu for events with W- --> e- nubar!
     if (helin==helout && hele==helnu) {
       CLHEP::HepLorentzVector qa=pout+pe+pnu;
       CLHEP::HepLorentzVector qb=pin-pe-pnu;
       double ta(qa.m2()),tb(qb.m2());
 
       current t65,vout,vin,temp2,temp3,temp5;
       joo(pnu,helnu,pe,hele,t65);
       vout[0]=pout.e();
       vout[1]=pout.x();
       vout[2]=pout.y();
       vout[3]=pout.z();
       vin[0]=pin.e();
       vin[1]=pin.x();
       vin[2]=pin.y();
       vin[3]=pin.z();
 
       COM brac615=cdot(t65,vout);
       COM brac645=cdot(t65,vin);
 
       // prod1565 and prod6465 are zero for Ws (not Zs)!!
       joo(pe,helout,pout,helout,temp2);  //  temp2 is <5|alpha|1>
       COM prod5165=cdot(temp2,t65);
       jio(pin,helin,pnu,helin,temp3);      // temp3 is <4|alpha|6>
       COM prod4665=cdot(temp3,t65);
 
       joo(pnu,helout,pout,helout,temp2);  // temp2 is now <6|mu|1>
       jio(pin,helin,pe,helin,temp3);        // temp3 is now <4|mu|5>
       jio(pin,helin,pout,helout,temp5);     //  temp5 is <4|mu|1>
 
       current term1,term2,term3,sum;
       cmult(-2.*brac615/ta-2.*brac645/tb,temp5,term1);
       cmult(-prod5165/ta,temp3,term2);
       cmult(prod4665/tb,temp2,term3);
 
      //    cur=((2.*brac615*Current(pout,helout,pin,helin)+prod1565*Current(pe,helin,pin,helin)+prod1665*Current(pnu,helin,pin,helin))/ta + (2.*brac645*Current(pout,helout,pin,helin)-prod5465*CurrentOutOut(pout,helout,pe,helout)-prod6465*CurrentOutOut(pout,helout,pnu,helout))/tb);
       //    cur=((2.*brac615*temp5+prod1565*temp3+prod1665*temp4)/ta + (2.*brac645*temp5-prod5465*temp1-prod6465*temp2)/tb);
       cadd(term1,term2,term3,sum);
       //    std::cout<<"term1: ("<<temp5[0]<<"  "<<temp5[1]<<"  "<<temp5[2]<<"  "<<temp5[3]<<")"<<std::endl;
       //    std::cout<<"sum: ("<<sum[0]<<","<<sum[1]<<","<<sum[2]<<","<<sum[3]<<")\n";
       cur[0]=sum[0];
       cur[1]=sum[1];
       cur[2]=sum[2];
       cur[3]=sum[3];
     }
   }
 
   double WProp (const CLHEP::HepLorentzVector & plbar, const CLHEP::HepLorentzVector & pl){
     COM propW = COM(0.,-1.)/((pl+plbar).m2() -HEJ::MW*HEJ::MW + COM(0.,1.)*HEJ::MW*HEJ::GammaW);
     double PropFactor=(propW*conj(propW)).real();
     return PropFactor;
   }
 
 CCurrent jW (CLHEP::HepLorentzVector pout, bool helout, CLHEP::HepLorentzVector pe, bool hele, CLHEP::HepLorentzVector pnu, bool helnu, CLHEP::HepLorentzVector pin, bool helin)
 {
 
   COM cur[4];
 
   cur[0]=0.;
   cur[1]=0.;
   cur[2]=0.;
   cur[3]=0.;
   CCurrent sum(0.,0.,0.,0.);
 
   // NOTA BENE: Conventions for W+ --> e+ nu, so that nu is lepton(6), e is anti-lepton(5)
   // Need to swap e and nu for events with W- --> e- nubar!
   if (helin==helout && hele==helnu) {
     CLHEP::HepLorentzVector qa=pout+pe+pnu;
     CLHEP::HepLorentzVector qb=pin-pe-pnu;
     double ta(qa.m2()),tb(qb.m2());
 
     CCurrent temp2,temp3,temp5;
     CCurrent t65 = joo(pnu,helnu,pe,hele);
     CCurrent vout(pout.e(),pout.x(),pout.y(),pout.z());
     CCurrent vin(pin.e(),pin.x(),pin.y(),pin.z());
 
     COM brac615=t65.dot(vout);
     COM brac645=t65.dot(vin);
 
 
     // prod1565 and prod6465 are zero for Ws (not Zs)!!
     temp2 = joo(pout,helout,pnu,helout);
     COM prod1665=temp2.dot(t65);
     temp3 = joi(pe,helin,pin,helin);
     COM prod5465=temp3.dot(t65);
 
     temp2=joo(pout,helout,pe,helout);
     temp3=joi(pnu,helnu,pin,helin);
     temp5=joi(pout,helout,pin,helin);
 
     CCurrent term1,term2,term3;
     term1=(2.*brac615/ta+2.*brac645/tb)*temp5;
     term2=(prod1665/ta)*temp3;
     term3=(-prod5465/tb)*temp2;
 
     sum=term1+term2+term3;
   }
 
   return sum;
 }
 
 CCurrent jWbar (CLHEP::HepLorentzVector pout, bool helout, CLHEP::HepLorentzVector pe, bool hele, CLHEP::HepLorentzVector pnu, bool helnu, CLHEP::HepLorentzVector pin, bool helin)
 {
 
     COM cur[4];
 
     cur[0]=0.;
     cur[1]=0.;
     cur[2]=0.;
     cur[3]=0.;
     CCurrent sum(0.,0.,0.,0.);
 
   // NOTA BENE: Conventions for W+ --> e+ nu, so that nu is lepton(6), e is anti-lepton(5)
   // Need to swap e and nu for events with W- --> e- nubar!
   if (helin==helout && hele==helnu) {
     CLHEP::HepLorentzVector qa=pout+pe+pnu;
     CLHEP::HepLorentzVector qb=pin-pe-pnu;
     double ta(qa.m2()),tb(qb.m2());
 
     CCurrent temp2,temp3,temp5;
     CCurrent t65 = joo(pnu,helnu,pe,hele);
     CCurrent vout(pout.e(),pout.x(),pout.y(),pout.z());
     CCurrent vin(pin.e(),pin.x(),pin.y(),pin.z());
 
     COM brac615=t65.dot(vout);
     COM brac645=t65.dot(vin);
 
     // prod1565 and prod6465 are zero for Ws (not Zs)!!
     temp2 = joo(pe,helout,pout,helout);  //  temp2 is <5|alpha|1>
     COM prod5165=temp2.dot(t65);
     temp3 = jio(pin,helin,pnu,helin);      // temp3 is <4|alpha|6>
     COM prod4665=temp3.dot(t65);
 
     temp2=joo(pnu,helout,pout,helout);  // temp2 is now <6|mu|1>
     temp3=jio(pin,helin,pe,helin);        // temp3 is now <4|mu|5>
     temp5=jio(pin,helin,pout,helout);     //  temp5 is <4|mu|1>
 
     CCurrent term1,term2,term3;
     term1 =(-2.*brac615/ta-2.*brac645/tb)*temp5;
     term2 =(-prod5165/ta)*temp3;
     term3 =(prod4665/tb)*temp2;
 
     sum = term1 + term2 + term3;
   }
 
   return sum;
 }
 
 
   // Extremal quark current with W emission. Using Tensor class rather than CCurrent
   Tensor <1,4> jW(HLV pin, HLV pout, HLV plbar, HLV pl, bool aqline){
     // Build the external quark line W Emmision
     Tensor<1,4> ABCurr = TCurrent(pl, false, plbar, false);
     Tensor<1,4> Tp4W = Construct1Tensor((pout+pl+plbar));//p4+pw
     Tensor<1,4> TpbW = Construct1Tensor((pin-pl-plbar));//pb-pw
 
     Tensor<3,4> J4bBlank;
     if (aqline){
       J4bBlank = T3Current(pin,false,pout,false);
     }
     else{
       J4bBlank = T3Current(pout,false,pin,false);
     }
     double t4AB = (pout+pl+plbar).m2();
     double tbAB = (pin-pl-plbar).m2();
 
     Tensor<2,4> J4b1 = (J4bBlank.contract(Tp4W,2))/t4AB;
     Tensor<2,4> J4b2 = (J4bBlank.contract(TpbW,2))/tbAB;
 
     Tensor<2,4> T4bmMom(0.);
 
     if (aqline){
       for(int mu=0; mu<4;mu++){
         for(int nu=0;nu<4;nu++){
           T4bmMom.Set(mu,nu, (J4b1.at(nu,mu) + J4b2.at(mu,nu))*(COM(0,-1)));
         }
       }
     }
     else{
       for(int mu=0; mu<4;mu++){
         for(int nu=0;nu<4;nu++){
           T4bmMom.Set(nu,mu, (J4b1.at(nu,mu) + J4b2.at(mu,nu))*(COM(0,1)));
         }
       }
     }
     Tensor<1,4> T4bm = T4bmMom.contract(ABCurr,1);
 
     return T4bm;
   }
 
 
 
   // Relevant W+Jets Unordered Contribution Helper Functions
   // W+Jets Uno
   double jM2Wuno(CLHEP::HepLorentzVector pg, CLHEP::HepLorentzVector p1,CLHEP::HepLorentzVector plbar, CLHEP::HepLorentzVector pl, CLHEP::HepLorentzVector pa, bool h1, CLHEP::HepLorentzVector p2, CLHEP::HepLorentzVector pb, bool h2, bool pol)
   {
     static bool is_sigma_index_set(false);
 
     if(!is_sigma_index_set){
       //std::cout<<"Setting sigma_index...." << std::endl;
       if(init_sigma_index())
   is_sigma_index_set = true;
       else
   return 0.;
     }
 
     CLHEP::HepLorentzVector pW = pl+plbar;
     CLHEP::HepLorentzVector q1g=pa-pW-p1-pg;
     CLHEP::HepLorentzVector q1 = pa-p1-pW;
     CLHEP::HepLorentzVector q2 = p2-pb;
 
     const double taW  = (pa-pW).m2();
     const double taW1 = (pa-pW-p1).m2();
     const double tb2  = (pb-p2).m2();
     const double tb2g = (pb-p2-pg).m2();
     const double s1W  = (p1+pW).m2();
     const double s1gW = (p1+pW+pg).m2();
     const double s1g  = (p1+pg).m2();
     const double tag  = (pa-pg).m2();
     const double taWg = (pa-pW-pg).m2();
 
     //use p1 as ref vec in pol tensor
     Tensor<1,4> epsg = eps(pg,p2,pol);
     Tensor<1,4> epsW = TCurrent(pl,false,plbar,false);
     Tensor<1,4> j2b = TCurrent(p2,h2,pb,h2);
 
     Tensor<1,4> Tq1q2 = Construct1Tensor((q1+q2)/taW1 + (pb/pb.dot(pg)
       + p2/p2.dot(pg)) * tb2/(2*tb2g));
     Tensor<1,4> Tq1g = Construct1Tensor((-pg-q1))/taW1;
     Tensor<1,4> Tq2g = Construct1Tensor((pg-q2));
     Tensor<1,4> TqaW = Construct1Tensor((pa-pW));//pa-pw
     Tensor<1,4> Tqag = Construct1Tensor((pa-pg));
     Tensor<1,4> TqaWg = Construct1Tensor((pa-pg-pW));
     Tensor<1,4> Tp1g = Construct1Tensor((p1+pg));
     Tensor<1,4> Tp1W = Construct1Tensor((p1+pW));//p1+pw
     Tensor<1,4> Tp1gW = Construct1Tensor((p1+pg+pW));//p1+pw+pg
 
     Tensor<2,4> g=Metric();
 
     Tensor<3,4> J31a = T3Current(p1, h1, pa, h1);
     Tensor<2,4> J2_qaW =J31a.contract(TqaW/taW, 2);
     Tensor<2,4> J2_p1W =J31a.contract(Tp1W/s1W, 2);
     Tensor<3,4> L1a =J2_qaW.leftprod(Tq1q2);
     Tensor<3,4> L1b =J2_p1W.leftprod(Tq1q2);
     Tensor<3,4> L2a = J2_qaW.leftprod(Tq1g);
     Tensor<3,4> L2b = J2_p1W.leftprod(Tq1g);
     Tensor<3,4> L3 = (g.rightprod(J2_qaW.contract(Tq2g,1)+J2_p1W.contract(Tq2g,2)))/taW1;
     Tensor<3,4> L(0.);
 
     Tensor<5,4> J51a = T5Current(p1, h1, pa, h1);
 
     Tensor<4,4> J_qaW = J51a.contract(TqaW,4);
     Tensor<4,4> J_qag = J51a.contract(Tqag,4);
     Tensor<4,4> J_p1gW = J51a.contract(Tp1gW,4);
 
     Tensor<3,4> U1a = J_qaW.contract(Tp1g,2);
     Tensor<3,4> U1b = J_p1gW.contract(Tp1g,2);
     Tensor<3,4> U1c = J_p1gW.contract(Tp1W,2);
     Tensor<3,4> U1(0.);
 
     Tensor<3,4> U2a = J_qaW.contract(TqaWg,2);
     Tensor<3,4> U2b = J_qag.contract(TqaWg,2);
     Tensor<3,4> U2c = J_qag.contract(Tp1W,2);
     Tensor<3,4> U2(0.);
 
     for(int nu=0; nu<4;nu++){
       for(int mu=0;mu<4;mu++){
         for(int rho=0;rho<4;rho++){
           L.Set(nu, mu, rho, L1a.at(nu,mu,rho) + L1b.at(nu,rho,mu)
             + L2a.at(mu,nu,rho) + L2b.at(mu,rho,nu) + L3.at(mu,nu,rho));
           U1.Set(nu, mu, rho, U1a.at(nu, mu, rho) / (s1g*taW)
             + U1b.at(nu,rho,mu) / (s1g*s1gW) + U1c.at(rho,nu,mu) / (s1W*s1gW));
           U2.Set(nu,mu,rho,U2a.at(mu,nu,rho) / (taWg*taW)
             + U2b.at(mu,rho,nu) / (taWg*tag) + U2c.at(rho,mu,nu) / (s1W*tag));
         }
       }
     }
 
     COM X = ((((U1-L).contract(epsW,3)).contract(j2b,2)).contract(epsg,1)).at(0);
     COM Y = ((((U2+L).contract(epsW,3)).contract(j2b,2)).contract(epsg,1)).at(0);
 
     double amp = HEJ::C_A*HEJ::C_F*HEJ::C_F/2.*(norm(X)+norm(Y)) - HEJ::C_F/2.*(X*conj(Y)).real();
 
     double t1 = q1g.m2();
     double t2 = q2.m2();
 
     double WPropfact = WProp(plbar, pl);
 
     //Divide by WProp
     amp*=WPropfact;
 
     //Divide by t-channels
     amp/=(t1*t2);
 
     //Average over initial states
     amp/=(4.*HEJ::C_A*HEJ::C_A);
 
     return amp;
   }
 
 
 
   // Relevant Wqqx Helper Functions.
   //g->qxqlxl (Calculates gluon to qqx Current. See JV_\mu in WSubleading Notes)
   Tensor <1,4> gtqqxW(CLHEP::HepLorentzVector pq,CLHEP::HepLorentzVector pqbar,CLHEP::HepLorentzVector pl,CLHEP::HepLorentzVector plbar){
 
     double s2AB=(pl+plbar+pq).m2();
     double s3AB=(pl+plbar+pqbar).m2();
 
     Tensor<1,4> Tpq = Construct1Tensor(pq);
     Tensor<1,4> Tpqbar = Construct1Tensor(pqbar);
     Tensor<1,4> TAB = Construct1Tensor(pl+plbar);
 
     // Define llx current.
     Tensor<1,4> ABCur = TCurrent(pl, false, plbar, false);
 
     //blank 3 Gamma Current
     Tensor<3,4> JV23 = T3Current(pq,false,pqbar,false);
 
     // Components of g->qqW before W Contraction
     Tensor<2,4> JV1 = JV23.contract((Tpq + TAB),2)/(s2AB);
     Tensor<2,4> JV2 = JV23.contract((Tpqbar + TAB),2)/(s3AB);
 
     // g->qqW Current. Note Minus between terms due to momentum flow.
     // Also note: (-I)^2 from W vert. (I) from Quark prop.
     Tensor<1,4> JVCur = (JV1.contract(ABCur,1) - JV2.contract(ABCur,2))*COM(0.,-1.);
 
     return JVCur;
   }
 
   // Helper Functions Calculate the Crossed Contribution
   Tensor <2,4> MCrossW(CLHEP::HepLorentzVector pa,CLHEP::HepLorentzVector p1,CLHEP::HepLorentzVector pb,CLHEP::HepLorentzVector p4, CLHEP::HepLorentzVector pq,CLHEP::HepLorentzVector pqbar,CLHEP::HepLorentzVector pl,CLHEP::HepLorentzVector plbar, std::vector<HLV> partons, int nabove){
 
     // Useful propagator factors
     double s2AB=(pl+plbar+pq).m2();
     double s3AB=(pl+plbar+pqbar).m2();
 
     CLHEP::HepLorentzVector q1, q3;
     q1=pa;
     for(int i=0; i<nabove+1;i++){
       q1=q1-partons.at(i);
     }
     q3 = q1 - pq - pqbar - pl - plbar;
 
     double tcro1=(q3+pq).m2();
     double tcro2=(q1-pqbar).m2();
 
     Tensor<1,4> Tp1 = Construct1Tensor(p1);
     Tensor<1,4> Tp4 = Construct1Tensor(p4);
     Tensor<1,4> Tpa = Construct1Tensor(pa);
     Tensor<1,4> Tpb = Construct1Tensor(pb);
     Tensor<1,4> Tpq = Construct1Tensor(pq);
     Tensor<1,4> Tpqbar = Construct1Tensor(pqbar);
     Tensor<1,4> TAB = Construct1Tensor(pl+plbar);
     Tensor<1,4> Tq1 = Construct1Tensor(q1);
     Tensor<1,4> Tq3 = Construct1Tensor(q3);
     Tensor<2,4> g=Metric();
 
     // Define llx current.
     Tensor<1,4> ABCur = TCurrent(pl, false, plbar,false);
 
     //Blank 5 gamma Current
     Tensor<5,4> J523 = T5Current(pq,false,pqbar,false);
 
     // 4 gamma currents (with 1 contraction already).
     Tensor<4,4> J_q3q = J523.contract((Tq3+Tpq),2);
     Tensor<4,4> J_2AB = J523.contract((Tpq+TAB),2);
 
     // Components of Crossed Vertex Contribution
     Tensor<3,4> Xcro1 = J_q3q.contract((Tpqbar + TAB),3);
     Tensor<3,4> Xcro2 = J_q3q.contract((Tq1-Tpqbar),3);
     Tensor<3,4> Xcro3 = J_2AB.contract((Tq1-Tpqbar),3);
 
     // Term Denominators Taken Care of at this stage
     Tensor<2,4> Xcro1Cont = Xcro1.contract(ABCur,3)/(tcro1*s3AB);
     Tensor<2,4> Xcro2Cont = Xcro2.contract(ABCur,2)/(tcro1*tcro2);
     Tensor<2,4> Xcro3Cont = Xcro3.contract(ABCur,1)/(s2AB*tcro2);
 
     //Initialise the Crossed Vertex Object
     Tensor<2,4> Xcro(0.);
 
     for(int mu=0; mu<4;mu++){
       for(int nu=0;nu<4;nu++){
         Xcro.Set(mu,nu, -(-Xcro1Cont.at(nu,mu)+Xcro2Cont.at(nu,mu)+Xcro3Cont.at(nu,mu)));
       }
     }
 
 
     return Xcro;
   }
 
   // Helper Functions Calculate the Uncrossed Contribution
   Tensor <2,4> MUncrossW(CLHEP::HepLorentzVector pa, CLHEP::HepLorentzVector p1, CLHEP::HepLorentzVector pb, CLHEP::HepLorentzVector p4, CLHEP::HepLorentzVector pq,CLHEP::HepLorentzVector pqbar,CLHEP::HepLorentzVector pl,CLHEP::HepLorentzVector plbar, std::vector<HLV> partons, int nabove){
 
     double s2AB=(pl+plbar+pq).m2();
     double s3AB=(pl+plbar+pqbar).m2();
 
     CLHEP::HepLorentzVector q1, q3;
     q1=pa;
     for(int i=0; i<nabove+1;i++){
       q1=q1-partons.at(i);
     }
     q3 = q1 - pl - plbar - pq - pqbar;
     double tunc1 = (q1-pq).m2();
     double tunc2 = (q3+pqbar).m2();
 
     Tensor<1,4> Tp1 = Construct1Tensor(p1);
     Tensor<1,4> Tp4 = Construct1Tensor(p4);
     Tensor<1,4> Tpa = Construct1Tensor(pa);
     Tensor<1,4> Tpb = Construct1Tensor(pb);
     Tensor<1,4> Tpq = Construct1Tensor(pq);
     Tensor<1,4> Tpqbar = Construct1Tensor(pqbar);
     Tensor<1,4> TAB = Construct1Tensor(pl+plbar);
     Tensor<1,4> Tq1 = Construct1Tensor(q1);
     Tensor<1,4> Tq3 = Construct1Tensor(q3);
     Tensor<2,4> g=Metric();
 
 
     // Define llx current.
     Tensor<1,4> ABCur = TCurrent(pl, false, plbar, false);
 
     //Blank 5 gamma Current
     Tensor<5,4> J523 = T5Current(pq,false,pqbar,false);
 
     // 4 gamma currents (with 1 contraction already).
     Tensor<4,4> J_2AB = J523.contract((Tpq+TAB),2);
     Tensor<4,4> J_q1q = J523.contract((Tq1-Tpq),2);
 
     // 2 Contractions taken care of.
     Tensor<3,4> Xunc1 = J_2AB.contract((Tq3+Tpqbar),3);
     Tensor<3,4> Xunc2 = J_q1q.contract((Tq3+Tpqbar),3);
     Tensor<3,4> Xunc3 = J_q1q.contract((Tpqbar+TAB),3);
 
     // Term Denominators Taken Care of at this stage
     Tensor<2,4> Xunc1Cont = Xunc1.contract(ABCur,1)/(s2AB*tunc2);
     Tensor<2,4> Xunc2Cont = Xunc2.contract(ABCur,2)/(tunc1*tunc2);
     Tensor<2,4> Xunc3Cont = Xunc3.contract(ABCur,3)/(tunc1*s3AB);
 
     //Initialise the Uncrossed Vertex Object
     Tensor<2,4> Xunc(0.);
 
     for(int mu=0; mu<4;mu++){
       for(int nu=0;nu<4;nu++){
         Xunc.Set(mu,nu,-(- Xunc1Cont.at(mu,nu)+Xunc2Cont.at(mu,nu) +Xunc3Cont.at(mu,nu)));
       }
     }
 
     return Xunc;
   }
 
 
   // Helper Functions Calculate the g->qqxW (Eikonal) Contributions
   Tensor <2,4> MSymW(CLHEP::HepLorentzVector pa,CLHEP::HepLorentzVector p1,CLHEP::HepLorentzVector pb,CLHEP::HepLorentzVector p4, CLHEP::HepLorentzVector pq,CLHEP::HepLorentzVector pqbar,CLHEP::HepLorentzVector pl,CLHEP::HepLorentzVector plbar, std::vector<HLV> partons, int nabove){
 
     double sa2=(pa+pq).m2();
     double s12=(p1+pq).m2();
     double sa3=(pa+pqbar).m2();
     double s13=(p1+pqbar).m2();
     double saA=(pa+pl).m2();
     double s1A=(p1+pl).m2();
     double saB=(pa+plbar).m2();
     double s1B=(p1+plbar).m2();
     double sb2=(pb+pq).m2();
     double s42=(p4+pq).m2();
     double sb3=(pb+pqbar).m2();
     double s43=(p4+pqbar).m2();
     double sbA=(pb+pl).m2();
     double s4A=(p4+pl).m2();
     double sbB=(pb+plbar).m2();
     double s4B=(p4+plbar).m2();
     double s23AB=(pl+plbar+pq+pqbar).m2();
 
     CLHEP::HepLorentzVector q1,q3;
     q1=pa;
     for(int i=0;i<nabove+1;i++){
       q1-=partons.at(i);
     }
     q3=q1-pq-pqbar-plbar-pl;
     double t1 = (q1).m2();
     double t3 = (q3).m2();
 
     //Define Tensors to be used
     Tensor<1,4> Tp1 = Construct1Tensor(p1);
     Tensor<1,4> Tp4 = Construct1Tensor(p4);
     Tensor<1,4> Tpa = Construct1Tensor(pa);
     Tensor<1,4> Tpb = Construct1Tensor(pb);
     Tensor<1,4> Tpq = Construct1Tensor(pq);
     Tensor<1,4> Tpqbar = Construct1Tensor(pqbar);
     Tensor<1,4> TAB = Construct1Tensor(pl+plbar);
     Tensor<1,4> Tq1 = Construct1Tensor(q1);
     Tensor<1,4> Tq3 = Construct1Tensor(q3);
     Tensor<2,4> g=Metric();
 
     // g->qqW Current (Factors of sqrt2 dealt with in this function.)
     Tensor<1,4> JV = gtqqxW(pq,pqbar,pl,plbar);
 
     // 1a gluon emisson Contribution
     Tensor<3,4> X1a = g.rightprod(Tp1*(t1/(s12+s13+s1A+s1B)) + Tpa*(t1/(sa2+sa3+saA+saB)));
     Tensor<2,4> X1aCont = X1a.contract(JV,3);
 
     //4b gluon emission Contribution
     Tensor<3,4> X4b = g.rightprod(Tp4*(t3/(s42+s43+s4A+s4B)) + Tpb*(t3/(sb2+sb3+sbA+sbB)));
     Tensor<2,4> X4bCont = X4b.contract(JV,3);
 
     //Set up each term of 3G diagram.
     Tensor<3,4> X3g1 = g.leftprod(Tq1+Tpq+Tpqbar+TAB);
     Tensor<3,4> X3g2 = g.leftprod(Tq3-Tpq-Tpqbar-TAB);
     Tensor<3,4> X3g3 = g.leftprod((Tq1+Tq3));
 
     // Note the contraction of indices changes term by term
     Tensor<2,4> X3g1Cont = X3g1.contract(JV,3);
     Tensor<2,4> X3g2Cont = X3g2.contract(JV,2);
     Tensor<2,4> X3g3Cont = X3g3.contract(JV,1);
 
     // XSym is an amalgamation of x1a, X4b and X3g. Makes sense from a colour factor point of view.
     Tensor<2,4>Xsym(0.);
 
     for(int mu=0; mu<4;mu++){
       for(int nu=0;nu<4;nu++){
   Xsym.Set(mu,nu, (X3g1Cont.at(nu,mu) + X3g2Cont.at(mu,nu) - X3g3Cont.at(nu,mu))
      + (X1aCont.at(mu,nu) - X4bCont.at(mu,nu)) );
       }
     }
     return Xsym/s23AB;
   }
 
   Tensor <2,4> MCross(CLHEP::HepLorentzVector pa, CLHEP::HepLorentzVector pq,CLHEP::HepLorentzVector pqbar, std::vector<HLV> partons, bool hq, int nabove){
 
     CLHEP::HepLorentzVector q1;
     q1=pa;
     for(int i=0;i<nabove+1;i++){
       q1-=partons.at(i);
     }
 
     double t2=(q1-pqbar).m2();
 
     Tensor<1,4> Tq1 = Construct1Tensor(q1-pqbar);
 
     //Blank 3 gamma Current
     Tensor<3,4> J323 = T3Current(pq,hq,pqbar,hq);
 
     // 2 gamma current (with 1 contraction already).
     Tensor<2,4> XCroCont = J323.contract((Tq1),2)/(t2);
 
     //Initialise the Crossed Vertex
     Tensor<2,4> Xcro(0.);
 
     for(int mu=0; mu<4;mu++){
       for(int nu=0;nu<4;nu++){
         Xcro.Set(mu,nu, (XCroCont.at(nu,mu)));
       }
     }
 
     return Xcro;
   }
 
 
   // Helper Functions Calculate the Uncrossed Contribution
   Tensor <2,4> MUncross(CLHEP::HepLorentzVector pa, CLHEP::HepLorentzVector pq,CLHEP::HepLorentzVector pqbar, std::vector<HLV> partons, bool hq, int nabove){
 
     CLHEP::HepLorentzVector q1;
     q1=pa;
     for(int i=0;i<nabove+1;i++){
       q1-=partons.at(i);
     }
     double t2 = (q1-pq).m2();
 
     Tensor<1,4> Tq1 = Construct1Tensor(q1-pq);
 
     //Blank 3 gamma Current
     Tensor<3,4> J323 = T3Current(pq,hq,pqbar,hq);
 
     // 2 gamma currents (with 1 contraction already).
     Tensor<2,4> XUncCont = J323.contract((Tq1),2)/t2;
 
     //Initialise the Uncrossed Vertex
     Tensor<2,4> Xunc(0.);
 
     for(int mu=0; mu<4;mu++){
       for(int nu=0;nu<4;nu++){
   Xunc.Set(mu,nu,-(XUncCont.at(mu,nu)));
       }
     }
 
     return Xunc;
   }
 
 
   // Helper Functions Calculate the Eikonal Contributions
   Tensor <2,4> MSym(CLHEP::HepLorentzVector pa,CLHEP::HepLorentzVector p1,CLHEP::HepLorentzVector pb,CLHEP::HepLorentzVector p4, CLHEP::HepLorentzVector pq,CLHEP::HepLorentzVector pqbar, std::vector<HLV> partons, bool hq, int nabove){
 
     CLHEP::HepLorentzVector q1, q3;
     q1=pa;
     for(int i=0;i<nabove+1;i++){
       q1-=partons.at(i);
     }
     q3 = q1-pq-pqbar;
     double t1 = (q1).m2();
     double t3 = (q3).m2();
 
     double s23 = (pq+pqbar).m2();
     double sa2 = (pa+pq).m2();
     double sa3 = (pa+pqbar).m2();
     double s12 = (p1+pq).m2();
     double s13 = (p1+pqbar).m2();
     double sb2 = (pb+pq).m2();
     double sb3 = (pb+pqbar).m2();
     double s42 = (p4+pq).m2();
     double s43 = (p4+pqbar).m2();
 
     //Define Tensors to be used
     Tensor<1,4> Tp1 = Construct1Tensor(p1);
     Tensor<1,4> Tp4 = Construct1Tensor(p4);
     Tensor<1,4> Tpa = Construct1Tensor(pa);
     Tensor<1,4> Tpb = Construct1Tensor(pb);
     Tensor<1,4> Tpq = Construct1Tensor(pq);
     Tensor<1,4> Tpqbar = Construct1Tensor(pqbar);
     Tensor<1,4> Tq1 = Construct1Tensor(q1);
     Tensor<1,4> Tq3 = Construct1Tensor(q3);
     Tensor<2,4> g=Metric();
 
     Tensor<1,4> qqxCur = TCurrent(pq, hq, pqbar, hq);
 
     // // 1a gluon emisson Contribution
     Tensor<3,4> X1a = g.rightprod(Tp1*(t1/(s12+s13))+Tpa*(t1/(sa2+sa3)));
     Tensor<2,4> X1aCont = X1a.contract(qqxCur,3);
 
     // //4b gluon emission Contribution
     Tensor<3,4> X4b = g.rightprod(Tp4*(t3/(s42+s43)) + Tpb*(t3/(sb2+sb3)));
     Tensor<2,4> X4bCont = X4b.contract(qqxCur,3);
 
     // New Formulation Corresponding to New Analytics
     Tensor<3,4> X3g1 = g.leftprod(Tq1+Tpq+Tpqbar);
     Tensor<3,4> X3g2 = g.leftprod(Tq3-Tpq-Tpqbar);
     Tensor<3,4> X3g3 = g.leftprod((Tq1+Tq3));
 
     // Note the contraction of indices changes term by term
     Tensor<2,4> X3g1Cont = X3g1.contract(qqxCur,3);
     Tensor<2,4> X3g2Cont = X3g2.contract(qqxCur,2);
     Tensor<2,4> X3g3Cont = X3g3.contract(qqxCur,1);
 
     Tensor<2,4>Xsym(0.);
 
     for(int mu=0; mu<4;mu++){
       for(int nu=0;nu<4;nu++){
         Xsym.Set(mu, nu, COM(0,1) * ( (X3g1Cont.at(nu,mu) + X3g2Cont.at(mu,nu)
           - X3g3Cont.at(nu,mu)) + (X1aCont.at(mu,nu) - X4bCont.at(mu,nu)) ) );
       }
     }
     return Xsym/s23;
   }
 } // Anonymous Namespace helper functions
 
 // W+Jets FKL Contributions
 double jMWqQ (CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector pe, CLHEP::HepLorentzVector pnu,CLHEP::HepLorentzVector p1in, CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in)
 // Calculates the square of the current contractions for qQ->qenuQ scattering
 // p1: quark (with W emittance)
 // p2: Quark
 {
   current mj1m,mj2p,mj2m;
   CLHEP::HepLorentzVector q1=p1in-p1out-pe-pnu;
   CLHEP::HepLorentzVector q2=-(p2in-p2out);
 
   jW(p1out,false,pe,false,pnu,false,p1in,false,mj1m);
   joi(p2out,true,p2in,true,mj2p);
   joi(p2out,false,p2in,false,mj2m);
 
   COM Mmp=cdot(mj1m,mj2p);
 
   // mj1m.mj2m
   COM Mmm=cdot(mj1m,mj2m);
 
   // sum of spinor strings ||^2
   double a2Mmp=abs2(Mmp);
   double a2Mmm=abs2(Mmm);
 
   double WPropfact = WProp(pe, pnu);
 
   // Division by colour and Helicity average (Nc2-1)(4)
   // Multiply by Cf^2
   return HEJ::C_F*HEJ::C_F*WPropfact*(a2Mmp+a2Mmm)/(q1.m2()*q2.m2()*(HEJ::N_C*HEJ::N_C - 1)*4);
 
 }
 
 double jMWqQbar (CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector pe, CLHEP::HepLorentzVector pnu,CLHEP::HepLorentzVector p1in, CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in)
 // Calculates the square of the current contractions for qQ->qenuQ scattering
 // p1: quark (with W emittance)
 // p2: Quark
 {
   current mj1m,mj2p,mj2m;
   CLHEP::HepLorentzVector q1=p1in-p1out-pe-pnu;
   CLHEP::HepLorentzVector q2=-(p2in-p2out);
 
   jW(p1out,false,pe,false,pnu,false,p1in,false,mj1m);
   jio(p2in,true,p2out,true,mj2p);
   jio(p2in,false,p2out,false,mj2m);
 
   COM Mmp=cdot(mj1m,mj2p);
 
   // mj1m.mj2m
   COM Mmm=cdot(mj1m,mj2m);
 
   // sum of spinor strings ||^2
   double a2Mmp=abs2(Mmp);
   double a2Mmm=abs2(Mmm);
 
   double WPropfact = WProp(pe, pnu);
 
   // Division by colour and Helicity average (Nc2-1)(4)
   // Multiply by Cf^2
   return HEJ::C_F*HEJ::C_F*WPropfact*(a2Mmp+a2Mmm)/(q1.m2()*q2.m2()*(HEJ::N_C*HEJ::N_C - 1)*4);
 
 }
 
 double jMWqbarQ (CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector pe, CLHEP::HepLorentzVector pnu,CLHEP::HepLorentzVector p1in, CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in)
 // Calculates the square of the current contractions for qQ->qenuQ scattering
 // p1: quark (with W emittance)
 // p2: Quark
 {
   current mj1m,mj2p,mj2m;
   CLHEP::HepLorentzVector q1=p1in-p1out-pe-pnu;
   CLHEP::HepLorentzVector q2=-(p2in-p2out);
 
   jWbar(p1out,false,pe,false,pnu,false,p1in,false,mj1m);
   joi(p2out,true,p2in,true,mj2p);
   joi(p2out,false,p2in,false,mj2m);
 
   COM Mmp=cdot(mj1m,mj2p);
 
   // mj1m.mj2m
   COM Mmm=cdot(mj1m,mj2m);
 
   // sum of spinor strings ||^2
   double a2Mmp=abs2(Mmp);
   double a2Mmm=abs2(Mmm);
 
   double WPropfact = WProp(pe, pnu);
 
   // Division by colour and Helicity average (Nc2-1)(4)
   // Multiply by Cf^2
   return HEJ::C_F*HEJ::C_F*WPropfact*(a2Mmp+a2Mmm)/(q1.m2()*q2.m2()*(HEJ::N_C*HEJ::N_C - 1)*4);
 
 }
 
 double jMWqbarQbar (CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector pe, CLHEP::HepLorentzVector pnu,CLHEP::HepLorentzVector p1in, CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in)
 // Calculates the square of the current contractions for qQ->qenuQ scattering
 // p1: quark (with W emittance)
 // p2: Quark
 {
   current mj1m,mj2p,mj2m;
   CLHEP::HepLorentzVector q1=p1in-p1out-pe-pnu;
   CLHEP::HepLorentzVector q2=-(p2in-p2out);
 
   jWbar(p1out,false,pe,false,pnu,false,p1in,false,mj1m);
   jio(p2in,true,p2out,true,mj2p);
   jio(p2in,false,p2out,false,mj2m);
 
   COM Mmp=cdot(mj1m,mj2p);
 
   // mj1m.mj2m
   COM Mmm=cdot(mj1m,mj2m);
 
   // sum of spinor strings ||^2
   double a2Mmp=abs2(Mmp);
   double a2Mmm=abs2(Mmm);
 
   double WPropfact = WProp(pe, pnu);
 
   // Division by colour and Helicity average (Nc2-1)(4)
   // Multiply by Cf^2
   return HEJ::C_F*HEJ::C_F*WPropfact*(a2Mmp+a2Mmm)/(q1.m2()*q2.m2()*(HEJ::N_C*HEJ::N_C - 1)*4);
 
 }
 
 double jMWqg (CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector pe, CLHEP::HepLorentzVector pnu,CLHEP::HepLorentzVector p1in, CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in)
 // Calculates the square of the current contractions for qg->qenug scattering
 // p1: quark
 // p2: gluon
 {
   CLHEP::HepLorentzVector q1=p1in-p1out-pe-pnu;
   CLHEP::HepLorentzVector q2=-(p2in-p2out);
   current mj1m,mj2p,mj2m;
 
   jW(p1out,false,pe,false,pnu,false,p1in,false,mj1m);
 
   joi(p2out,true,p2in,true,mj2p);
   joi(p2out,false,p2in,false,mj2m);
 
   // mj1m.mj2p
   COM Mmp=cdot(mj1m,mj2p);
 
   // mj1m.mj2m
   COM Mmm=cdot(mj1m,mj2m);
 
   const double K = K_g(p2out, p2in);
 
   // sum of spinor strings ||^2
   double a2Mmp=abs2(Mmp);
   double a2Mmm=abs2(Mmm);
   double sst = K/HEJ::C_A*(a2Mmp+a2Mmm);
 
   double WPropfact = WProp(pe, pnu);
 
   // Division by colour and Helicity average (Nc2-1)(4)
   // Multiply by Cf*Ca=4
   return HEJ::C_F*HEJ::C_A*WPropfact*sst/(q1.m2()*q2.m2()*(HEJ::N_C*HEJ::N_C - 1)*4);
 
 }
 
 double jMWqbarg (CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector pe, CLHEP::HepLorentzVector pnu,CLHEP::HepLorentzVector p1in, CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in)
 // Calculates the square of the current contractions for qg->qenug scattering
 // p1: quark
 // p2: gluon
 {
   CLHEP::HepLorentzVector q1=p1in-p1out-pe-pnu;
   CLHEP::HepLorentzVector q2=-(p2in-p2out);
   current mj1m,mj2p,mj2m;
 
   jWbar(p1out,false,pe,false,pnu,false,p1in,false,mj1m);
 
   joi(p2out,true,p2in,true,mj2p);
   joi(p2out,false,p2in,false,mj2m);
 
   // mj1m.mj2p
   COM Mmp=cdot(mj1m,mj2p);
 
   // mj1m.mj2m
   COM Mmm=cdot(mj1m,mj2m);
 
   const double K = K_g(p2out, p2in);
 
   // sum of spinor strings ||^2
   double a2Mmp=abs2(Mmp);
   double a2Mmm=abs2(Mmm);
   double sst = K/HEJ::C_A*(a2Mmp+a2Mmm);
 
   double WPropfact = WProp(pe, pnu);
 
   // Division by colour and Helicity average (Nc2-1)(4)
   // Multiply by Cf*Ca=4
   return HEJ::C_F*HEJ::C_A*WPropfact*sst/(q1.m2()*q2.m2()*(HEJ::N_C*HEJ::N_C - 1)*4);
 
 }
 
 
 // W+Jets Unordered Contributions
 //qQ->qQWg_unob
 double junobMWqQg (CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector pe, CLHEP::HepLorentzVector pnu,CLHEP::HepLorentzVector p1in, CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in, CLHEP::HepLorentzVector pg)
 // Calculates the square of the current contractions for qQ->qenuQ scattering
 // p1: quark (with W emittance)
 // p2: Quark
 {
   CCurrent mj1m,mj2p,mj2m;
   CLHEP::HepLorentzVector q1=p1in-p1out-pe-pnu;
   CLHEP::HepLorentzVector q2=-(p2in-p2out-pg);
   CLHEP::HepLorentzVector q3=-(p2in-p2out);
 
   mj1m=jW(p1out,false,pe,false,pnu,false,p1in,false);
   mj2p=joi(p2out,true,p2in,true);
   mj2m=joi(p2out,false,p2in,false);
 
 
   // Dot products of these which occur again and again
   COM MWmp=mj1m.dot(mj2p);  // And now for the Higgs ones
   COM MWmm=mj1m.dot(mj2m);
 
   CCurrent jgbm,jgbp,j2gm,j2gp;
   j2gp=joo(p2out,true,pg,true);
   j2gm=joo(p2out,false,pg,false);
   jgbp=joi(pg,true,p2in,true);
   jgbm=joi(pg,false,p2in,false);
 
   CCurrent qsum(q2+q3);
 
   CCurrent Lmp,Lmm,Lpp,Lpm,U1mp,U1mm,U1pp,U1pm,U2mp,U2mm,U2pp,U2pm,p1o(p1out),p1i(p1in);
   CCurrent p2o(p2out);
   CCurrent p2i(p2in);
 
 
   Lmm=((-1.)*qsum*(MWmm) + (-2.*mj1m.dot(pg))*mj2m+2.*mj2m.dot(pg)*mj1m+(p1o/pg.dot(p1out) + p1i/pg.dot(p1in))*(q2.m2()*MWmm/2.))/q3.m2();
   Lmp=((-1.)*qsum*(MWmp) + (-2.*mj1m.dot(pg))*mj2p+2.*mj2p.dot(pg)*mj1m+(p1o/pg.dot(p1out) + p1i/pg.dot(p1in))*(q2.m2()*MWmp/2.))/q3.m2();
 
   U1mm=(jgbm.dot(mj1m)*j2gm+2.*p2o*MWmm)/(p2out+pg).m2();
   U1mp=(jgbp.dot(mj1m)*j2gp+2.*p2o*MWmp)/(p2out+pg).m2();
 
   U2mm=((-1.)*j2gm.dot(mj1m)*jgbm+2.*p2i*MWmm)/(p2in-pg).m2();
   U2mp=((-1.)*j2gp.dot(mj1m)*jgbp+2.*p2i*MWmp)/(p2in-pg).m2();
 
 
   double amm,amp;
 
   amm=HEJ::C_F*(2.*vre(Lmm-U1mm,Lmm+U2mm))+2.*HEJ::C_F*HEJ::C_F/3.*vabs2(U1mm+U2mm);
   amp=HEJ::C_F*(2.*vre(Lmp-U1mp,Lmp+U2mp))+2.*HEJ::C_F*HEJ::C_F/3.*vabs2(U1mp+U2mp);
 
   double ampsq=-(amm+amp);
   //Divide by WProp
   double WPropfact = WProp(pe, pnu);
   ampsq*=WPropfact;
 
 
   // Now add the t-channels
   double th=q2.m2()*q1.m2();
   ampsq/=th;
   ampsq/=16.;
 
 
   return ampsq;
 
 
 }
 
 //qQbar->qQbarWg_unob
 double junobMWqQbarg (CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector pe, CLHEP::HepLorentzVector pnu,CLHEP::HepLorentzVector p1in, CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in, CLHEP::HepLorentzVector pg)
 // Calculates the square of the current contractions for qQ->qenuQ scattering
 // p1: quark (with W emittance)
 // p2: Quark
 {
   CCurrent mj1m,mj2p,mj2m;
   CLHEP::HepLorentzVector q1=p1in-p1out-pe-pnu;
   CLHEP::HepLorentzVector q2=-(p2in-p2out-pg);
   CLHEP::HepLorentzVector q3=-(p2in-p2out);
 
   mj1m=jW(p1out,false,pe,false,pnu,false,p1in,false);
   mj2p=jio(p2in,true,p2out,true);
   mj2m=jio(p2in,false,p2out,false);
 
 
   // Dot products of these which occur again and again
   COM MWmp=mj1m.dot(mj2p);  // And now for the Higgs ones
   COM MWmm=mj1m.dot(mj2m);
 
   CCurrent jgbm,jgbp,j2gm,j2gp;
   j2gp=joo(pg,true,p2out,true);
   j2gm=joo(pg,false,p2out,false);
   jgbp=jio(p2in,true,pg,true);
   jgbm=jio(p2in,false,pg,false);
 
   CCurrent qsum(q2+q3);
 
   CCurrent Lmp,Lmm,Lpp,Lpm,U1mp,U1mm,U1pp,U1pm,U2mp,U2mm,U2pp,U2pm,p1o(p1out),p1i(p1in);
   CCurrent p2o(p2out);
   CCurrent p2i(p2in);
 
 
   Lmm=((-1.)*qsum*(MWmm) + (-2.*mj1m.dot(pg))*mj2m+2.*mj2m.dot(pg)*mj1m+(p1o/pg.dot(p1out) + p1i/pg.dot(p1in))*(q2.m2()*MWmm/2.))/q3.m2();
   Lmp=((-1.)*qsum*(MWmp) + (-2.*mj1m.dot(pg))*mj2p+2.*mj2p.dot(pg)*mj1m+(p1o/pg.dot(p1out) + p1i/pg.dot(p1in))*(q2.m2()*MWmp/2.))/q3.m2();
 
   U1mm=(jgbm.dot(mj1m)*j2gm+2.*p2o*MWmm)/(p2out+pg).m2();
   U1mp=(jgbp.dot(mj1m)*j2gp+2.*p2o*MWmp)/(p2out+pg).m2();
 
   U2mm=((-1.)*j2gm.dot(mj1m)*jgbm+2.*p2i*MWmm)/(p2in-pg).m2();
   U2mp=((-1.)*j2gp.dot(mj1m)*jgbp+2.*p2i*MWmp)/(p2in-pg).m2();
 
   double amm,amp;
 
   amm=HEJ::C_F*(2.*vre(Lmm-U1mm,Lmm+U2mm))+2.*HEJ::C_F*HEJ::C_F/3.*vabs2(U1mm+U2mm);
   amp=HEJ::C_F*(2.*vre(Lmp-U1mp,Lmp+U2mp))+2.*HEJ::C_F*HEJ::C_F/3.*vabs2(U1mp+U2mp);
 
   double ampsq=-(amm+amp);
   //Divide by WProp
   double WPropfact = WProp(pe, pnu);
   ampsq*=WPropfact;
 
   // Now add the t-channels
   double th=q2.m2()*q1.m2();
   ampsq/=th;
   ampsq/=16.;
 
 
   return ampsq;
 
 
 }
 
 //qbarQ->qbarQWg_unob
 double junobMWqbarQg (CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector pe, CLHEP::HepLorentzVector pnu,CLHEP::HepLorentzVector p1in, CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in, CLHEP::HepLorentzVector pg)
 // Calculates the square of the current contractions for qQ->qenuQ scattering
 // p1: quark (with W emittance)
 // p2: Quark
 {
   CCurrent mj1m,mj2p,mj2m;
   CLHEP::HepLorentzVector q1=p1in-p1out-pe-pnu;
   CLHEP::HepLorentzVector q2=-(p2in-p2out-pg);
   CLHEP::HepLorentzVector q3=-(p2in-p2out);
 
   mj1m=jWbar(p1out,false,pe,false,pnu,false,p1in,false);
   mj2p=joi(p2out,true,p2in,true);
   mj2m=joi(p2out,false,p2in,false);
 
 
   // Dot products of these which occur again and again
   COM MWmp=mj1m.dot(mj2p);  // And now for the Higgs ones
   COM MWmm=mj1m.dot(mj2m);
 
   CCurrent jgbm,jgbp,j2gm,j2gp;
   j2gp=joo(p2out,true,pg,true);
   j2gm=joo(p2out,false,pg,false);
   jgbp=joi(pg,true,p2in,true);
   jgbm=joi(pg,false,p2in,false);
 
   CCurrent qsum(q2+q3);
 
   CCurrent Lmp,Lmm,Lpp,Lpm,U1mp,U1mm,U1pp,U1pm,U2mp,U2mm,U2pp,U2pm,p1o(p1out),p1i(p1in);
   CCurrent p2o(p2out);
   CCurrent p2i(p2in);
 
 
   Lmm=((-1.)*qsum*(MWmm) + (-2.*mj1m.dot(pg))*mj2m+2.*mj2m.dot(pg)*mj1m+(p1o/pg.dot(p1out) + p1i/pg.dot(p1in))*(q2.m2()*MWmm/2.))/q3.m2();
   Lmp=((-1.)*qsum*(MWmp) + (-2.*mj1m.dot(pg))*mj2p+2.*mj2p.dot(pg)*mj1m+(p1o/pg.dot(p1out) + p1i/pg.dot(p1in))*(q2.m2()*MWmp/2.))/q3.m2();
 
   U1mm=(jgbm.dot(mj1m)*j2gm+2.*p2o*MWmm)/(p2out+pg).m2();
   U1mp=(jgbp.dot(mj1m)*j2gp+2.*p2o*MWmp)/(p2out+pg).m2();
 
   U2mm=((-1.)*j2gm.dot(mj1m)*jgbm+2.*p2i*MWmm)/(p2in-pg).m2();
   U2mp=((-1.)*j2gp.dot(mj1m)*jgbp+2.*p2i*MWmp)/(p2in-pg).m2();
 
 
   double amm,amp;
 
   amm=HEJ::C_F*(2.*vre(Lmm-U1mm,Lmm+U2mm))+2.*HEJ::C_F*HEJ::C_F/3.*vabs2(U1mm+U2mm);
   amp=HEJ::C_F*(2.*vre(Lmp-U1mp,Lmp+U2mp))+2.*HEJ::C_F*HEJ::C_F/3.*vabs2(U1mp+U2mp);
 
   double ampsq=-(amm+amp);
   //Divide by WProp
   double WPropfact = WProp(pe, pnu);
   ampsq*=WPropfact;
 
   // Now add the t-channels
   double th=q2.m2()*q1.m2();
   ampsq/=th;
   ampsq/=16.;
 
   return ampsq;
 
 
 }
 
 //qbarQbar->qbarQbarWg_unob
 double junobMWqbarQbarg (CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector pe, CLHEP::HepLorentzVector pnu,CLHEP::HepLorentzVector p1in, CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in, CLHEP::HepLorentzVector pg)
 // Calculates the square of the current contractions for qQ->qenuQ scattering
 // p1: quark (with W emittance)
 // p2: Quark
 {
   CCurrent mj1m,mj2p,mj2m;
   CLHEP::HepLorentzVector q1=p1in-p1out-pe-pnu;
   CLHEP::HepLorentzVector q2=-(p2in-p2out-pg);
   CLHEP::HepLorentzVector q3=-(p2in-p2out);
 
   mj1m=jWbar(p1out,false,pe,false,pnu,false,p1in,false);
   mj2p=jio(p2in,true,p2out,true);
   mj2m=jio(p2in,false,p2out,false);
 
 
   // Dot products of these which occur again and again
   COM MWmp=mj1m.dot(mj2p);  // And now for the Higgs ones
   COM MWmm=mj1m.dot(mj2m);
 
   CCurrent jgbm,jgbp,j2gm,j2gp;
   j2gp=joo(pg,true,p2out,true);
   j2gm=joo(pg,false,p2out,false);
   jgbp=jio(p2in,true,pg,true);
   jgbm=jio(p2in,false,pg,false);
 
   CCurrent qsum(q2+q3);
 
   CCurrent Lmp,Lmm,Lpp,Lpm,U1mp,U1mm,U1pp,U1pm,U2mp,U2mm,U2pp,U2pm,p1o(p1out),p1i(p1in);
   CCurrent p2o(p2out);
   CCurrent p2i(p2in);
 
 
   Lmm=((-1.)*qsum*(MWmm) + (-2.*mj1m.dot(pg))*mj2m+2.*mj2m.dot(pg)*mj1m+(p1o/pg.dot(p1out) + p1i/pg.dot(p1in))*(q2.m2()*MWmm/2.))/q3.m2();
   Lmp=((-1.)*qsum*(MWmp) + (-2.*mj1m.dot(pg))*mj2p+2.*mj2p.dot(pg)*mj1m+(p1o/pg.dot(p1out) + p1i/pg.dot(p1in))*(q2.m2()*MWmp/2.))/q3.m2();
 
   U1mm=(jgbm.dot(mj1m)*j2gm+2.*p2o*MWmm)/(p2out+pg).m2();
   U1mp=(jgbp.dot(mj1m)*j2gp+2.*p2o*MWmp)/(p2out+pg).m2();
 
   U2mm=((-1.)*j2gm.dot(mj1m)*jgbm+2.*p2i*MWmm)/(p2in-pg).m2();
   U2mp=((-1.)*j2gp.dot(mj1m)*jgbp+2.*p2i*MWmp)/(p2in-pg).m2();
 
 
   double amm,amp;
 
   amm=HEJ::C_F*(2.*vre(Lmm-U1mm,Lmm+U2mm))+2.*HEJ::C_F*HEJ::C_F/3.*vabs2(U1mm+U2mm);
   amp=HEJ::C_F*(2.*vre(Lmp-U1mp,Lmp+U2mp))+2.*HEJ::C_F*HEJ::C_F/3.*vabs2(U1mp+U2mp);
 
   double ampsq=-(amm+amp);
   //Divide by WProp
   double WPropfact = WProp(pe, pnu);
   ampsq*=WPropfact;
 
   // Now add the t-channels
   double th=q2.m2()*q1.m2();
   ampsq/=th;
   ampsq/=16.;
 
   return ampsq;
 
 
 }
 
 ////////////////////////////////////////////////////////////////////
 //qQ->qQWg_unof
 double junofMWgqQ (CLHEP::HepLorentzVector pg,CLHEP::HepLorentzVector p1out,CLHEP::HepLorentzVector p1in, CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector pe, CLHEP::HepLorentzVector pnu, CLHEP::HepLorentzVector p2in)
 // Calculates the square of the current contractions for qQ->qenuQ scattering
 // p1: quark (with W emittance)
 // p2: Quark
 {
   CCurrent mj2m,mj1p,mj1m;
   CLHEP::HepLorentzVector q1=p1in-p1out;
   CLHEP::HepLorentzVector qg=p1in-p1out-pg;
   CLHEP::HepLorentzVector q2=-(p2in-p2out-pe-pnu);
 
   mj2m=jW(p2out,false,pe,false,pnu,false,p2in,false);
   mj1p=joi(p1out,true,p1in,true);
   mj1m=joi(p1out,false,p1in,false);
 
 
   // Dot products of these which occur again and again
   COM MWpm=mj1p.dot(mj2m);  // And now for the Higgs ones
   COM MWmm=mj1m.dot(mj2m);
 
   CCurrent jgam,jgap,j2gm,j2gp;
   j2gp=joo(p1out,true,pg,true);
   j2gm=joo(p1out,false,pg,false);
   jgap=joi(pg,true,p1in,true);
   jgam=joi(pg,false,p1in,false);
 
   CCurrent qsum(q1+qg);
 
   CCurrent Lmp,Lmm,Lpp,Lpm,U1mp,U1mm,U1pp,U1pm,U2mp,U2mm,U2pp,U2pm,p2o(p2out),p2i(p2in);
   CCurrent p1o(p1out);
   CCurrent p1i(p1in);
 
   Lmm=(qsum*(MWmm) + (-2.*mj2m.dot(pg))*mj1m+2.*mj1m.dot(pg)*mj2m+(p2o/pg.dot(p2out) + p2i/pg.dot(p2in))*(qg.m2()*MWmm/2.))/q1.m2();
   Lpm=(qsum*(MWpm) + (-2.*mj2m.dot(pg))*mj1p+2.*mj1p.dot(pg)*mj2m+(p2o/pg.dot(p2out) + p2i/pg.dot(p2in))*(qg.m2()*MWpm/2.))/q1.m2();
 
 
   U1mm=(jgam.dot(mj2m)*j2gm+2.*p1o*MWmm)/(p1out+pg).m2();
   U1pm=(jgap.dot(mj2m)*j2gp+2.*p1o*MWpm)/(p1out+pg).m2();
 
   U2mm=((-1.)*j2gm.dot(mj2m)*jgam+2.*p1i*MWmm)/(p1in-pg).m2();
   U2pm=((-1.)*j2gp.dot(mj2m)*jgap+2.*p1i*MWpm)/(p1in-pg).m2();
 
   double amm,apm;
   amm=HEJ::C_F*(2.*vre(Lmm-U1mm,Lmm+U2mm))+2.*HEJ::C_F*HEJ::C_F/3.*vabs2(U1mm+U2mm);
   apm=HEJ::C_F*(2.*vre(Lpm-U1pm,Lpm+U2pm))+2.*HEJ::C_F*HEJ::C_F/3.*vabs2(U1pm+U2pm);
 
   double ampsq=-(apm+amm);
   //Divide by WProp
   double WPropfact = WProp(pe, pnu);
   ampsq*=WPropfact;
 
   // Now add the t-channels
   double th=q2.m2()*qg.m2();
   ampsq/=th;
   ampsq/=16.;
 
   return ampsq;
 
 
 }
 
 //qQbar->qQbarWg_unof
 double junofMWgqQbar (CLHEP::HepLorentzVector pg,CLHEP::HepLorentzVector p1out,CLHEP::HepLorentzVector p1in, CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector pe, CLHEP::HepLorentzVector pnu, CLHEP::HepLorentzVector p2in)
 // Calculates the square of the current contractions for qQ->qenuQ scattering
 // p1: quark (with W emittance)
 // p2: Quark
 {
   CCurrent mj2m,mj1p,mj1m;
   CLHEP::HepLorentzVector q1=p1in-p1out;
   CLHEP::HepLorentzVector qg=p1in-p1out-pg;
   CLHEP::HepLorentzVector q2=-(p2in-p2out-pe-pnu);
 
   mj2m=jWbar(p2out,false,pe,false,pnu,false,p2in,false);
   mj1p=joi(p1out,true,p1in,true);
   mj1m=joi(p1out,false,p1in,false);
 
 
   // Dot products of these which occur again and again
   COM MWpm=mj1p.dot(mj2m);  // And now for the Higgs ones
   COM MWmm=mj1m.dot(mj2m);
 
   CCurrent jgam,jgap,j2gm,j2gp;
   j2gp=joo(p1out,true,pg,true);
   j2gm=joo(p1out,false,pg,false);
   jgap=joi(pg,true,p1in,true);
   jgam=joi(pg,false,p1in,false);
 
   CCurrent qsum(q1+qg);
 
   CCurrent Lmp,Lmm,Lpp,Lpm,U1mp,U1mm,U1pp,U1pm,U2mp,U2mm,U2pp,U2pm,p2o(p2out),p2i(p2in);
   CCurrent p1o(p1out);
   CCurrent p1i(p1in);
 
   Lmm=(qsum*(MWmm) + (-2.*mj2m.dot(pg))*mj1m+2.*mj1m.dot(pg)*mj2m+(p2o/pg.dot(p2out) + p2i/pg.dot(p2in))*(qg.m2()*MWmm/2.))/q1.m2();
   Lpm=(qsum*(MWpm) + (-2.*mj2m.dot(pg))*mj1p+2.*mj1p.dot(pg)*mj2m+(p2o/pg.dot(p2out) + p2i/pg.dot(p2in))*(qg.m2()*MWpm/2.))/q1.m2();
 
 
   U1mm=(jgam.dot(mj2m)*j2gm+2.*p1o*MWmm)/(p1out+pg).m2();
   U1pm=(jgap.dot(mj2m)*j2gp+2.*p1o*MWpm)/(p1out+pg).m2();
 
   U2mm=((-1.)*j2gm.dot(mj2m)*jgam+2.*p1i*MWmm)/(p1in-pg).m2();
   U2pm=((-1.)*j2gp.dot(mj2m)*jgap+2.*p1i*MWpm)/(p1in-pg).m2();
 
   double amm,apm;
 
   amm=HEJ::C_F*(2.*vre(Lmm-U1mm,Lmm+U2mm))+2.*HEJ::C_F*HEJ::C_F/3.*vabs2(U1mm+U2mm);
   apm=HEJ::C_F*(2.*vre(Lpm-U1pm,Lpm+U2pm))+2.*HEJ::C_F*HEJ::C_F/3.*vabs2(U1pm+U2pm);
 
   double ampsq=-(apm+amm);
   //Divide by WProp
   double WPropfact = WProp(pe, pnu);
   ampsq*=WPropfact;
 
   // Now add the t-channels
   double th=q2.m2()*qg.m2();
   ampsq/=th;
   ampsq/=16.;
 
   return ampsq;
 }
 
 //qbarQ->qbarQWg_unof
 double junofMWgqbarQ (CLHEP::HepLorentzVector pg,CLHEP::HepLorentzVector p1out,CLHEP::HepLorentzVector p1in, CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector pe, CLHEP::HepLorentzVector pnu, CLHEP::HepLorentzVector p2in)
 // Calculates the square of the current contractions for qQ->qenuQ scattering
 // p1: quark (with W emittance)
 // p2: Quark
 {
   CCurrent mj2m,mj1p,mj1m;
   CLHEP::HepLorentzVector q1=p1in-p1out;
   CLHEP::HepLorentzVector qg=p1in-p1out-pg;
   CLHEP::HepLorentzVector q2=-(p2in-p2out-pe-pnu);
 
   mj2m=jW(p2out,false,pe,false,pnu,false,p2in,false);
   mj1p=jio(p1in,true,p1out,true);
   mj1m=jio(p1in,false,p1out,false);
 
 
   // Dot products of these which occur again and again
   COM MWpm=mj1p.dot(mj2m);  // And now for the Higgs ones
   COM MWmm=mj1m.dot(mj2m);
 
   CCurrent jgam,jgap,j2gm,j2gp;
   j2gp=joo(pg,true,p1out,true);
   j2gm=joo(pg,false,p1out,false);
   jgap=jio(p1in,true,pg,true);
   jgam=jio(p1in,false,pg,false);
 
   CCurrent qsum(q1+qg);
 
   CCurrent Lmp,Lmm,Lpp,Lpm,U1mp,U1mm,U1pp,U1pm,U2mp,U2mm,U2pp,U2pm,p2o(p2out),p2i(p2in);
   CCurrent p1o(p1out);
   CCurrent p1i(p1in);
 
   Lmm=(qsum*(MWmm) + (-2.*mj2m.dot(pg))*mj1m+2.*mj1m.dot(pg)*mj2m+(p2o/pg.dot(p2out) + p2i/pg.dot(p2in))*(qg.m2()*MWmm/2.))/q1.m2();
   Lpm=(qsum*(MWpm) + (-2.*mj2m.dot(pg))*mj1p+2.*mj1p.dot(pg)*mj2m+(p2o/pg.dot(p2out) + p2i/pg.dot(p2in))*(qg.m2()*MWpm/2.))/q1.m2();
 
 
   U1mm=(jgam.dot(mj2m)*j2gm+2.*p1o*MWmm)/(p1out+pg).m2();
   U1pm=(jgap.dot(mj2m)*j2gp+2.*p1o*MWpm)/(p1out+pg).m2();
 
   U2mm=((-1.)*j2gm.dot(mj2m)*jgam+2.*p1i*MWmm)/(p1in-pg).m2();
   U2pm=((-1.)*j2gp.dot(mj2m)*jgap+2.*p1i*MWpm)/(p1in-pg).m2();
 
   double amm,apm;
   amm=HEJ::C_F*(2.*vre(Lmm-U1mm,Lmm+U2mm))+2.*HEJ::C_F*HEJ::C_F/3.*vabs2(U1mm+U2mm);
   apm=HEJ::C_F*(2.*vre(Lpm-U1pm,Lpm+U2pm))+2.*HEJ::C_F*HEJ::C_F/3.*vabs2(U1pm+U2pm);
 
   double ampsq=-(apm+amm);
   //Divide by WProp
   double WPropfact = WProp(pe, pnu);
   ampsq*=WPropfact;
 
   // Now add the t-channels
   double th=q2.m2()*qg.m2();
   ampsq/=th;
   ampsq/=16.;
 
   return ampsq;
 
 
 }
 
 //qbarQbar->qbarQbarWg_unof
 double junofMWgqbarQbar (CLHEP::HepLorentzVector pg,CLHEP::HepLorentzVector p1out,CLHEP::HepLorentzVector p1in, CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector pe, CLHEP::HepLorentzVector pnu, CLHEP::HepLorentzVector p2in)
 // Calculates the square of the current contractions for qQ->qenuQ scattering
 // p1: quark (with W emittance)
 // p2: Quark
 {
   CCurrent mj2m,mj1p,mj1m;
   CLHEP::HepLorentzVector q1=p1in-p1out;
   CLHEP::HepLorentzVector qg=p1in-p1out-pg;
   CLHEP::HepLorentzVector q2=-(p2in-p2out-pe-pnu);
 
   mj2m=jWbar(p2out,false,pe,false,pnu,false,p2in,false);
   mj1p=jio(p1in,true,p1out,true);
   mj1m=jio(p1in,false,p1out,false);
 
 
   // Dot products of these which occur again and again
   COM MWpm=mj1p.dot(mj2m);  // And now for the Higgs ones
   COM MWmm=mj1m.dot(mj2m);
 
   CCurrent jgam,jgap,j2gm,j2gp;
   j2gp=joo(pg,true,p1out,true);
   j2gm=joo(pg,false,p1out,false);
   jgap=jio(p1in,true,pg,true);
   jgam=jio(p1in,false,pg,false);
 
   CCurrent qsum(q1+qg);
 
   CCurrent Lmp,Lmm,Lpp,Lpm,U1mp,U1mm,U1pp,U1pm,U2mp,U2mm,U2pp,U2pm,p2o(p2out),p2i(p2in);
   CCurrent p1o(p1out);
   CCurrent p1i(p1in);
 
   Lmm=(qsum*(MWmm) + (-2.*mj2m.dot(pg))*mj1m+2.*mj1m.dot(pg)*mj2m+(p2o/pg.dot(p2out) + p2i/pg.dot(p2in))*(qg.m2()*MWmm/2.))/q1.m2();
   Lpm=(qsum*(MWpm) + (-2.*mj2m.dot(pg))*mj1p+2.*mj1p.dot(pg)*mj2m+(p2o/pg.dot(p2out) + p2i/pg.dot(p2in))*(qg.m2()*MWpm/2.))/q1.m2();
 
   U1mm=(jgam.dot(mj2m)*j2gm+2.*p1o*MWmm)/(p1out+pg).m2();
   U1pm=(jgap.dot(mj2m)*j2gp+2.*p1o*MWpm)/(p1out+pg).m2();
 
   U2mm=((-1.)*j2gm.dot(mj2m)*jgam+2.*p1i*MWmm)/(p1in-pg).m2();
   U2pm=((-1.)*j2gp.dot(mj2m)*jgap+2.*p1i*MWpm)/(p1in-pg).m2();
 
   double amm,apm;
 
   amm=HEJ::C_F*(2.*vre(Lmm-U1mm,Lmm+U2mm))+2.*HEJ::C_F*HEJ::C_F/3.*vabs2(U1mm+U2mm);
   apm=HEJ::C_F*(2.*vre(Lpm-U1pm,Lpm+U2pm))+2.*HEJ::C_F*HEJ::C_F/3.*vabs2(U1pm+U2pm);
 
 
 
   double ampsq=-(apm+amm);
   //Divide by WProp
   double WPropfact = WProp(pe, pnu);
   ampsq*=WPropfact;
 
 
   // Now add the t-channels
   double th=q2.m2()*qg.m2();
   ampsq/=th;
   ampsq/=16.;
   return ampsq;
 
 
 }
 
 
 
 ///TODO make this comment more visible
 /// Naming scheme jM2-Wuno-g-({q/qbar}{Q/Qbar/g})
 ///TODO Spit naming for more complicated functions?
 /// e.g. jM2WqqtoqQQq -> jM2_Wqq_to_qQQq
 double jM2WunogqQ(CLHEP::HepLorentzVector pg, CLHEP::HepLorentzVector p1out,CLHEP::HepLorentzVector plbar,CLHEP::HepLorentzVector pl, CLHEP::HepLorentzVector p1in, CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in)
 {
   //COM temp;
   double ME2mpp=0.;
   double ME2mpm=0.;
   double ME2mmp=0.;
   double ME2mmm=0.;
   double ME2;
 
   ME2mpp = jM2Wuno(pg, p1out,plbar,pl,p1in,false,p2out,p2in,true,true);
   ME2mpm = jM2Wuno(pg, p1out,plbar,pl,p1in,false,p2out,p2in,true,false);
   ME2mmp = jM2Wuno(pg, p1out,plbar,pl,p1in,false,p2out,p2in,false,true);
   ME2mmm = jM2Wuno(pg, p1out,plbar,pl,p1in,false,p2out,p2in,false,false);
 
   //Helicity sum
   ME2 = ME2mpp + ME2mpm + ME2mmp + ME2mmm;
 
   return ME2;
 
 }
 
 //same as function above but actually obtaining the antiquark line by crossing symmetry, where p1out and p1in are expected to be negative.
 //should give same result as jM2WunogqbarQ below (verified)
 double jM2WunogqQ_crossqQ(CLHEP::HepLorentzVector pg, CLHEP::HepLorentzVector p1out,CLHEP::HepLorentzVector plbar,CLHEP::HepLorentzVector pl, CLHEP::HepLorentzVector p1in, CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in)
 {
   //COM temp;
   double ME2mpp=0.;
   double ME2mpm=0.;
   double ME2mmp=0.;
   double ME2mmm=0.;
   double ME2;
 
   ME2mpp = jM2Wuno(pg, p1out,plbar,pl,p1in,false,p2out,p2in,true,true);
   ME2mpm = jM2Wuno(pg, p1out,plbar,pl,p1in,false,p2out,p2in,true,false);
   ME2mmp = jM2Wuno(pg, p1out,plbar,pl,p1in,false,p2out,p2in,false,true);
   ME2mmm = jM2Wuno(pg, p1out,plbar,pl,p1in,false,p2out,p2in,false,false);
 
   //Helicity sum
   ME2 = ME2mpp + ME2mpm + ME2mmp + ME2mmm;
 
   return ME2;
 
 }
 
 double jM2WunogqQbar(CLHEP::HepLorentzVector pg, CLHEP::HepLorentzVector p1out,CLHEP::HepLorentzVector plbar,CLHEP::HepLorentzVector pl, CLHEP::HepLorentzVector p1in, CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in)
 {
   //COM temp;
   double ME2mpp=0.;
   double ME2mpm=0.;
   double ME2mmp=0.;
   double ME2mmm=0.;
   double ME2;
 
   ME2mpp = jM2Wuno(pg, p1out,plbar,pl,p1in,false,p2out,p2in,true,true);
   ME2mpm = jM2Wuno(pg, p1out,plbar,pl,p1in,false,p2out,p2in,true,false);
   ME2mmp = jM2Wuno(pg, p1out,plbar,pl,p1in,false,p2out,p2in,false,true);
   ME2mmm = jM2Wuno(pg, p1out,plbar,pl,p1in,false,p2out,p2in,false,false);
 
   //Helicity sum
   ME2 = ME2mpp + ME2mpm + ME2mmp + ME2mmm;
 
   return ME2;
 
 }
 
 double jM2Wunogqg(CLHEP::HepLorentzVector pg, CLHEP::HepLorentzVector p1out,CLHEP::HepLorentzVector plbar,CLHEP::HepLorentzVector pl, CLHEP::HepLorentzVector p1in, CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in)
 {
   //COM temp;
   double ME2mpp=0.;
   double ME2mpm=0.;
   double ME2mmp=0.;
   double ME2mmm=0.;
   double ME2;
 
   ME2mpp = jM2Wuno(pg, p1out,plbar,pl,p1in,false,p2out,p2in,true,true);
   ME2mpm = jM2Wuno(pg, p1out,plbar,pl,p1in,false,p2out,p2in,true,false);
   ME2mmp = jM2Wuno(pg, p1out,plbar,pl,p1in,false,p2out,p2in,false,true);
   ME2mmm = jM2Wuno(pg, p1out,plbar,pl,p1in,false,p2out,p2in,false,false);
 
   //Helicity sum
   ME2 = ME2mpp + ME2mpm + ME2mmp + ME2mmm;
 
   double ratio; // p2-/pb- in the notes
   if (p2in.pz()>0.) // if the gluon is the positive
     ratio=p2out.plus()/p2in.plus();
   else // the gluon is the negative
     ratio=p2out.minus()/p2in.minus();
 
   double cam = ( (HEJ::C_A - 1/HEJ::C_A)*(ratio + 1./ratio)/2. + 1/HEJ::C_A)/HEJ::C_F;
   ME2*=cam;
 
   return ME2;
 
 }
 
 double jM2WunogqbarQ(CLHEP::HepLorentzVector pg, CLHEP::HepLorentzVector p1out,CLHEP::HepLorentzVector plbar,CLHEP::HepLorentzVector pl, CLHEP::HepLorentzVector p1in, CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in)
 {
   //COM temp;
   double ME2mpp=0.;
   double ME2mpm=0.;
   double ME2mmp=0.;
   double ME2mmm=0.;
   double ME2;
 
   ME2mpp = jM2Wuno(pg, p1out,plbar,pl,p1in,true,p2out,p2in,true,true);
   ME2mpm = jM2Wuno(pg, p1out,plbar,pl,p1in,true,p2out,p2in,true,false);
   ME2mmp = jM2Wuno(pg, p1out,plbar,pl,p1in,true,p2out,p2in,false,true);
   ME2mmm = jM2Wuno(pg, p1out,plbar,pl,p1in,true,p2out,p2in,false,false);
 
   //Helicity sum
   ME2 = ME2mpp + ME2mpm + ME2mmp + ME2mmm;
 
   return ME2;
 
 }
 
 double jM2WunogqbarQbar(CLHEP::HepLorentzVector pg, CLHEP::HepLorentzVector p1out,CLHEP::HepLorentzVector plbar,CLHEP::HepLorentzVector pl, CLHEP::HepLorentzVector p1in, CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in)
 {
   //COM temp;
   double ME2mpp=0.;
   double ME2mpm=0.;
   double ME2mmp=0.;
   double ME2mmm=0.;
   double ME2;
 
   ME2mpp = jM2Wuno(pg, p1out,plbar,pl,p1in,true,p2out,p2in,true,true);
   ME2mpm = jM2Wuno(pg, p1out,plbar,pl,p1in,true,p2out,p2in,true,false);
   ME2mmp = jM2Wuno(pg, p1out,plbar,pl,p1in,true,p2out,p2in,false,true);
   ME2mmm = jM2Wuno(pg, p1out,plbar,pl,p1in,true,p2out,p2in,false,false);
 
   //Helicity sum
   ME2 = ME2mpp + ME2mpm + ME2mmp + ME2mmm;
 
   return ME2;
 
 }
 
 double jM2Wunogqbarg(CLHEP::HepLorentzVector pg, CLHEP::HepLorentzVector p1out,CLHEP::HepLorentzVector plbar,CLHEP::HepLorentzVector pl, CLHEP::HepLorentzVector p1in, CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in)
 {
   //COM temp;
   double ME2mpp=0.;
   double ME2mpm=0.;
   double ME2mmp=0.;
   double ME2mmm=0.;
   double ME2;
 
   ME2mpp = jM2Wuno(pg, p1out,plbar,pl,p1in,true,p2out,p2in,true,true);
   ME2mpm = jM2Wuno(pg, p1out,plbar,pl,p1in,true,p2out,p2in,true,false);
   ME2mmp = jM2Wuno(pg, p1out,plbar,pl,p1in,true,p2out,p2in,false,true);
   ME2mmm = jM2Wuno(pg, p1out,plbar,pl,p1in,true,p2out,p2in,false,false);
 
   //Helicity sum
   ME2 = ME2mpp + ME2mpm + ME2mmp + ME2mmm;
 
   double ratio; // p2-/pb- in the notes
   if (p2in.pz()>0.) // if the gluon is the positive
     ratio=p2out.plus()/p2in.plus();
   else // the gluon is the negative
     ratio=p2out.minus()/p2in.minus();
 
   double cam = ( (HEJ::C_A - 1/HEJ::C_A)*(ratio + 1./ratio)/2. + 1/HEJ::C_A)/HEJ::C_F;
   ME2*=cam;
 
   return ME2;
 
 }
 
 
 // W+Jets qqxExtremal
 // W+Jets qqxExtremal Currents - wqq emission
 double jM2WgQtoqbarqQ(CLHEP::HepLorentzVector pgin, CLHEP::HepLorentzVector pqout,CLHEP::HepLorentzVector plbar,CLHEP::HepLorentzVector pl, CLHEP::HepLorentzVector pqbarout, CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in)
 {
   //COM temp;
   double ME2mpp=0.;
   double ME2mpm=0.;
   double ME2mmp=0.;
   double ME2mmm=0.;
   double ME2;
 
   ME2mpp = jM2Wuno(-pgin, pqout,plbar,pl,-pqbarout,false,p2out,p2in,true,true);
   ME2mpm = jM2Wuno(-pgin, pqout,plbar,pl,-pqbarout,false,p2out,p2in,true,false);
   ME2mmp = jM2Wuno(-pgin, pqout,plbar,pl,-pqbarout,false,p2out,p2in,false,true);
   ME2mmm = jM2Wuno(-pgin, pqout,plbar,pl,-pqbarout,false,p2out,p2in,false,false);
 
   //Helicity sum
   ME2 = ME2mpp + ME2mpm + ME2mmp + ME2mmm;
 
   //Correct colour averaging
   ME2*=(3.0/8.0);
 
   return ME2;
 
 }
 
 double jM2WgQtoqqbarQ(CLHEP::HepLorentzVector pgin, CLHEP::HepLorentzVector pqbarout,CLHEP::HepLorentzVector plbar,CLHEP::HepLorentzVector pl, CLHEP::HepLorentzVector pqout, CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in){
 
   //COM temp;
   double ME2mpp=0.;
   double ME2mpm=0.;
   double ME2mmp=0.;
   double ME2mmm=0.;
   double ME2;
 
   ME2mpp = jM2Wuno(-pgin, pqbarout,plbar,pl,-pqout,true,p2out,p2in,true,true);
   ME2mpm = jM2Wuno(-pgin, pqbarout,plbar,pl,-pqout,true,p2out,p2in,true,false);
   ME2mmp = jM2Wuno(-pgin, pqbarout,plbar,pl,-pqout,true,p2out,p2in,false,true);
   ME2mmm = jM2Wuno(-pgin, pqbarout,plbar,pl,-pqout,true,p2out,p2in,false,false);
 
   //Helicity sum
   ME2 = ME2mpp + ME2mpm + ME2mmp + ME2mmm;
 
   //Correct colour averaging
   ME2*=(3.0/8.0);
 
   return ME2;
 
 }
 
 
 double jM2Wggtoqbarqg(CLHEP::HepLorentzVector pgin, CLHEP::HepLorentzVector pqout,CLHEP::HepLorentzVector plbar,CLHEP::HepLorentzVector pl, CLHEP::HepLorentzVector pqbarout, CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in)
 {
   //COM temp;
   double ME2mpp=0.;
   double ME2mpm=0.;
   double ME2mmp=0.;
   double ME2mmm=0.;
   double ME2;
 
   ME2mpp = jM2Wuno(-pgin, pqout,plbar,pl,-pqbarout,false,p2out,p2in,true,true);
   ME2mpm = jM2Wuno(-pgin, pqout,plbar,pl,-pqbarout,false,p2out,p2in,true,false);
   ME2mmp = jM2Wuno(-pgin, pqout,plbar,pl,-pqbarout,false,p2out,p2in,false,true);
   ME2mmm = jM2Wuno(-pgin, pqout,plbar,pl,-pqbarout,false,p2out,p2in,false,false);
 
   //Helicity sum
   ME2 = ME2mpp + ME2mpm + ME2mmp + ME2mmm;
 
   double ratio; // p2-/pb- in the notes
   if (p2in.pz()>0.) // if the gluon is the positive
     ratio=p2out.plus()/p2in.plus();
   else // the gluon is the negative
     ratio=p2out.minus()/p2in.minus();
 
   double cam = ( (HEJ::C_A - 1/HEJ::C_A)*(ratio + 1./ratio)/2. + 1/HEJ::C_A)/HEJ::C_F;
   ME2*=cam;
 
   //Correct colour averaging
   ME2*=(3.0/8.0);
 
   return ME2;
 }
 
 double jM2Wggtoqqbarg(CLHEP::HepLorentzVector pgin, CLHEP::HepLorentzVector pqbarout,CLHEP::HepLorentzVector plbar,CLHEP::HepLorentzVector pl, CLHEP::HepLorentzVector pqout, CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in){
 
   //COM temp;
   double ME2mpp=0.;
   double ME2mpm=0.;
   double ME2mmp=0.;
   double ME2mmm=0.;
   double ME2;
 
   ME2mpp = jM2Wuno(-pgin, pqbarout,plbar,pl,-pqout,true,p2out,p2in,true,true);
   ME2mpm = jM2Wuno(-pgin, pqbarout,plbar,pl,-pqout,true,p2out,p2in,true,false);
   ME2mmp = jM2Wuno(-pgin, pqbarout,plbar,pl,-pqout,true,p2out,p2in,false,true);
   ME2mmm = jM2Wuno(-pgin, pqbarout,plbar,pl,-pqout,true,p2out,p2in,false,false);
 
   //Helicity sum
   ME2 = ME2mpp + ME2mpm + ME2mmp + ME2mmm;
 
   double ratio; // p2-/pb- in the notes
   if (p2in.pz()>0.) // if the gluon is the positive
     ratio=p2out.plus()/p2in.plus();
   else // the gluon is the negative
     ratio=p2out.minus()/p2in.minus();
 
   double cam = ( (HEJ::C_A - 1/HEJ::C_A)*(ratio + 1./ratio)/2. + 1/HEJ::C_A)/HEJ::C_F;
   ME2*=cam;
 
   //Correct colour averaging
   ME2*=(3.0/8.0);
 
   return ME2;
 
 }
 
 
 namespace {
 //Function to calculate Term 1 in Equation 3.23 in James Cockburn's Thesis.
   Tensor<1,4> qggm1(CLHEP::HepLorentzVector pb, CLHEP::HepLorentzVector p2, CLHEP::HepLorentzVector p3, bool hel2, bool helg, CLHEP::HepLorentzVector refmom){
 
     double t1 = (p3-pb)*(p3-pb);
     Tensor<1,4> Tp3 = Construct1Tensor((p3));//p3
     Tensor<1,4> Tpb = Construct1Tensor((pb));//pb
     // Gauge choice in polarisation tensor. (see JC's Thesis)
     Tensor<1,4> epsg = eps(pb, refmom, helg);
     Tensor<3,4> qqCurBlank = T3Current(p2,hel2,p3,hel2);
     Tensor<2,4> qqCur = qqCurBlank.contract(Tp3-Tpb,2);
     Tensor<1,4> gqqCur = qqCur.contract(epsg,2)/t1;
 
     return gqqCur*(-1);
 }
 
 //Function to calculate Term 2 in Equation 3.23 in James Cockburn's Thesis.
   Tensor<1,4> qggm2(CLHEP::HepLorentzVector pb, CLHEP::HepLorentzVector p2, CLHEP::HepLorentzVector p3, bool hel2, bool helg, CLHEP::HepLorentzVector refmom){
 
     double t1 = (p2-pb)*(p2-pb);
     Tensor<1,4> Tp2 = Construct1Tensor((p2));//p2
     Tensor<1,4> Tpb = Construct1Tensor((pb));//pb
     // Gauge choice in polarisation tensor. (see JC's Thesis)
     Tensor<1,4> epsg = eps(pb,refmom, helg);
     Tensor<3,4> qqCurBlank = T3Current(p2,hel2,p3,hel2);
     Tensor<2,4> qqCur = qqCurBlank.contract(Tp2-Tpb,2);
     Tensor<1,4> gqqCur = qqCur.contract(epsg,1)/t1;
 
     return gqqCur;
 }
 
 //Function to calculate Term 3 in Equation 3.23 in James Cockburn's Thesis.
   Tensor<1,4> qggm3(CLHEP::HepLorentzVector pb, CLHEP::HepLorentzVector p2, CLHEP::HepLorentzVector p3, bool hel2, bool helg, CLHEP::HepLorentzVector refmom){
 
     double s23 = (p2+p3)*(p2+p3);
     Tensor<1,4> Tp2 = Construct1Tensor((p2));//p2
     Tensor<1,4> Tp3 = Construct1Tensor((p3));//p3
     Tensor<1,4> Tpb = Construct1Tensor((pb));//pb
     // Gauge choice in polarisation tensor. (see JC's Thesis)
     Tensor<1,4> epsg = eps(pb, refmom, helg);
     Tensor<2,4> g=Metric();
     Tensor<3,4> qqCurBlank1 = g.leftprod(Tp2+Tp3)/s23;
     Tensor<3,4> qqCurBlank2 = g.leftprod(Tpb)/s23;
     Tensor<1,4> Cur23 = TCurrent(p2,hel2, p3,hel2);
 
     Tensor<2,4> qqCur1 = qqCurBlank1.contract(Cur23,3);
     Tensor<2,4> qqCur2 = qqCurBlank2.contract(Cur23,3);
     Tensor<2,4> qqCur3 = qqCurBlank2.contract(Cur23,1);
 
     Tensor<1,4> gqqCur = (qqCur1.contract(epsg,1)
                           - qqCur2.contract(epsg,2)
                           + qqCur3.contract(epsg,1))*2*COM(0,1);
     return gqqCur;
 }
 }
 
 // no wqq emission
 double jM2WgqtoQQqW(CLHEP::HepLorentzVector pa, CLHEP::HepLorentzVector pb, CLHEP::HepLorentzVector p1,  CLHEP::HepLorentzVector p2, CLHEP::HepLorentzVector p3,CLHEP::HepLorentzVector plbar,CLHEP::HepLorentzVector pl, bool aqlinepa){
 
   static bool is_sigma_index_set(false);
   if(!is_sigma_index_set){
     if(init_sigma_index())
       is_sigma_index_set = true;
     else
       return 0.;}
 
   // 2 independent helicity choices (complex conjugation related).
   Tensor<1,4> TMmmm1 = qggm1(pb,p2,p3,false,false, pa);
   Tensor<1,4> TMmmm2 = qggm2(pb,p2,p3,false,false, pa);
   Tensor<1,4> TMmmm3 = qggm3(pb,p2,p3,false,false, pa);
   Tensor<1,4> TMpmm1 = qggm1(pb,p2,p3,false,true, pa);
   Tensor<1,4> TMpmm2 = qggm2(pb,p2,p3,false,true, pa);
   Tensor<1,4> TMpmm3 = qggm3(pb,p2,p3,false,true, pa);
 
   // Build the external quark line W Emmision
   Tensor<1,4> cur1a = jW(pa,p1,plbar,pl, aqlinepa);
 
   //Contract with the qqxCurrent.
   COM Mmmm1 = TMmmm1.contract(cur1a,1).at(0);
   COM Mmmm2 = TMmmm2.contract(cur1a,1).at(0);
   COM Mmmm3 = TMmmm3.contract(cur1a,1).at(0);
   COM Mpmm1 = TMpmm1.contract(cur1a,1).at(0);
   COM Mpmm2 = TMpmm2.contract(cur1a,1).at(0);
   COM Mpmm3 = TMpmm3.contract(cur1a,1).at(0);
 
   //Colour factors:
   COM cm1m1,cm2m2,cm3m3,cm1m2,cm1m3,cm2m3;
   cm1m1=8./3.;
   cm2m2=8./3.;
   cm3m3=6.;
   cm1m2 =-1./3.;
   cm1m3 = -3.*COM(0.,1.);
   cm2m3 = 3.*COM(0.,1.);
 
   //Sqaure and sum for each helicity config:
   double Mmmm = real(cm1m1*pow(abs(Mmmm1),2)+cm2m2*pow(abs(Mmmm2),2)+cm3m3*pow(abs(Mmmm3),2)+2.*real(cm1m2*Mmmm1*conj(Mmmm2))+2.*real(cm1m3*Mmmm1*conj(Mmmm3))+2.*real(cm2m3*Mmmm2*conj(Mmmm3)));
   double Mpmm = real(cm1m1*pow(abs(Mpmm1),2)+cm2m2*pow(abs(Mpmm2),2)+cm3m3*pow(abs(Mpmm3),2)+2.*real(cm1m2*Mpmm1*conj(Mpmm2))+2.*real(cm1m3*Mpmm1*conj(Mpmm3))+2.*real(cm2m3*Mpmm2*conj(Mpmm3)));
 
   // Divide by WProp
   double WPropfact = WProp(plbar, pl);
 
   return (2*WPropfact*(Mmmm+Mpmm)/24./4.)/(pa-p1-pl-plbar).m2()/(p2+p3-pb).m2();
 }
 
 
 // W+Jets qqxCentral
 double jM2WqqtoqQQq(CLHEP::HepLorentzVector pa, CLHEP::HepLorentzVector pb,CLHEP::HepLorentzVector pl, CLHEP::HepLorentzVector plbar, std::vector<HLV> partons, bool aqlinepa, bool aqlinepb, bool qqxmarker, int nabove)
 {
 
   static bool is_sigma_index_set(false);
   if(!is_sigma_index_set){
     if(init_sigma_index())
       is_sigma_index_set = true;
     else
       return 0.;}
 
   HLV pq, pqbar, p1, p4;
   if (qqxmarker){
     pqbar = partons[nabove+1];
     pq = partons[nabove+2];}
   else{
     pq = partons[nabove+1];
     pqbar = partons[nabove+2];}
 
   p1 = partons.front();
   p4 = partons.back();
 
   Tensor<1,4> T1am, T4bm, T1ap, T4bp;
   if(!(aqlinepa)){
     T1ap = TCurrent(p1, true, pa, true);
     T1am = TCurrent(p1, false, pa, false);}
   else if(aqlinepa){
     T1ap = TCurrent(pa, true, p1, true);
     T1am = TCurrent(pa, false, p1, false);}
   if(!(aqlinepb)){
     T4bp = TCurrent(p4, true, pb, true);
     T4bm = TCurrent(p4, false, pb, false);}
   else if(aqlinepb){
     T4bp = TCurrent(pb, true, p4, true);
     T4bm = TCurrent(pb, false, p4, false);}
 
   // Calculate the 3 separate contributions to the effective vertex
   Tensor<2,4> Xunc = MUncrossW(pa, p1, pb, p4, pq, pqbar, pl, plbar, partons, nabove);
   Tensor<2,4> Xcro = MCrossW(  pa, p1, pb, p4, pq, pqbar, pl, plbar, partons, nabove);
   Tensor<2,4> Xsym = MSymW(    pa, p1, pb, p4, pq, pqbar, pl, plbar, partons, nabove);
 
   // 4 Different Helicity Choices (Differs from Pure Jet Case, where there is also the choice in qqbar helicity.
   // (- - hel choice)
   COM M_mmUnc = (((Xunc).contract(T1am,1)).contract(T4bm,1)).at(0);
   COM M_mmCro = (((Xcro).contract(T1am,1)).contract(T4bm,1)).at(0);
   COM M_mmSym = (((Xsym).contract(T1am,1)).contract(T4bm,1)).at(0);
   // (- + hel choice)
   COM M_mpUnc = (((Xunc).contract(T1am,1)).contract(T4bp,1)).at(0);
   COM M_mpCro = (((Xcro).contract(T1am,1)).contract(T4bp,1)).at(0);
   COM M_mpSym = (((Xsym).contract(T1am,1)).contract(T4bp,1)).at(0);
   // (+ - hel choice)
   COM M_pmUnc = (((Xunc).contract(T1ap,1)).contract(T4bm,1)).at(0);
   COM M_pmCro = (((Xcro).contract(T1ap,1)).contract(T4bm,1)).at(0);
   COM M_pmSym = (((Xsym).contract(T1ap,1)).contract(T4bm,1)).at(0);
   // (+ + hel choice)
   COM M_ppUnc = (((Xunc).contract(T1ap,1)).contract(T4bp,1)).at(0);
   COM M_ppCro = (((Xcro).contract(T1ap,1)).contract(T4bp,1)).at(0);
   COM M_ppSym = (((Xsym).contract(T1ap,1)).contract(T4bp,1)).at(0);
 
   //Colour factors:
   COM cmsms,cmumu,cmcmc,cmsmu,cmsmc,cmumc;
   cmsms=3.;
   cmumu=4./3.;
   cmcmc=4./3.;
   cmsmu =3./2.*COM(0.,1.);
   cmsmc = -3./2.*COM(0.,1.);
   cmumc = -1./6.;
 
   // Work Out Interference in each case of helicity:
   double amp_mm = real(cmsms*pow(abs(M_mmSym),2)
              +cmumu*pow(abs(M_mmUnc),2)
              +cmcmc*pow(abs(M_mmCro),2)
              +2.*real(cmsmu*M_mmSym*conj(M_mmUnc))
              +2.*real(cmsmc*M_mmSym*conj(M_mmCro))
              +2.*real(cmumc*M_mmUnc*conj(M_mmCro)));
 
   double amp_mp = real(cmsms*pow(abs(M_mpSym),2)
              +cmumu*pow(abs(M_mpUnc),2)
              +cmcmc*pow(abs(M_mpCro),2)
              +2.*real(cmsmu*M_mpSym*conj(M_mpUnc))
              +2.*real(cmsmc*M_mpSym*conj(M_mpCro))
              +2.*real(cmumc*M_mpUnc*conj(M_mpCro)));
 
   double amp_pm = real(cmsms*pow(abs(M_pmSym),2)
              +cmumu*pow(abs(M_pmUnc),2)
              +cmcmc*pow(abs(M_pmCro),2)
              +2.*real(cmsmu*M_pmSym*conj(M_pmUnc))
              +2.*real(cmsmc*M_pmSym*conj(M_pmCro))
              +2.*real(cmumc*M_pmUnc*conj(M_pmCro)));
 
   double amp_pp = real(cmsms*pow(abs(M_ppSym),2)
              +cmumu*pow(abs(M_ppUnc),2)
              +cmcmc*pow(abs(M_ppCro),2)
              +2.*real(cmsmu*M_ppSym*conj(M_ppUnc))
              +2.*real(cmsmc*M_ppSym*conj(M_ppCro))
              +2.*real(cmumc*M_ppUnc*conj(M_ppCro)));
 
   double amp=((amp_mm+amp_mp+amp_pm+amp_pp)/(9.*4.));
 
 
 
   CLHEP::HepLorentzVector q1,q3;
   q1=pa;
   for(int i=0;i<nabove+1;i++){
     q1-=partons.at(i);
   }
   q3 = q1 - pq - pqbar - pl - plbar;
 
   double t1 = (q1).m2();
   double t3 = (q3).m2();
 
   //Divide by t-channels
   amp/=(t1*t1*t3*t3);
 
   //Divide by WProp
   double WPropfact = WProp(plbar, pl);
   amp*=WPropfact;
 
 
   return amp;
 }
 
 // no wqq emission
 double jM2WqqtoqQQqW(CLHEP::HepLorentzVector pa, CLHEP::HepLorentzVector pb,CLHEP::HepLorentzVector pl,CLHEP::HepLorentzVector plbar, std::vector<CLHEP::HepLorentzVector> partons, bool aqlinepa, bool aqlinepb, bool qqxmarker, int nabove, int nbelow, bool forwards){
 
   static bool is_sigma_index_set(false);
   if(!is_sigma_index_set){
     if(init_sigma_index())
       is_sigma_index_set = true;
     else
       return 0.;
   }
 
   if (!forwards){ //If Emission from Leg a instead, flip process.
     HLV dummymom = pa;
     bool dummybool= aqlinepa;
     int dummyint = nabove;
     pa = pb;
     pb = dummymom;
     std::reverse(partons.begin(),partons.end());
     qqxmarker = !(qqxmarker);
     aqlinepa = aqlinepb;
     aqlinepb = dummybool;
     nabove = nbelow;
     nbelow = dummyint;
   }
 
   HLV pq, pqbar, p1,p4;
   if (qqxmarker){
     pqbar = partons[nabove+1];
     pq = partons[nabove+2];}
   else{
     pq = partons[nabove+1];
     pqbar = partons[nabove+2];}
 
   p1 = partons.front();
   p4 = partons.back();
 
   Tensor<1,4> T1am(0.), T1ap(0.);
   if(!(aqlinepa)){
     T1ap = TCurrent(p1, true, pa, true);
     T1am = TCurrent(p1, false, pa, false);}
   else if(aqlinepa){
     T1ap = TCurrent(pa, true, p1, true);
     T1am = TCurrent(pa, false, p1, false);}
 
   Tensor <1,4> T4bm = jW(pb, p4, plbar, pl, aqlinepb);
 
   // Calculate the 3 separate contributions to the effective vertex
   Tensor<2,4> Xunc_m = MUncross(pa, pq, pqbar,partons, false, nabove);
   Tensor<2,4> Xcro_m = MCross(  pa, pq, pqbar,partons, false, nabove);
   Tensor<2,4> Xsym_m = MSym(    pa, p1, pb, p4, pq, pqbar, partons, false, nabove);
 
   Tensor<2,4> Xunc_p = MUncross(pa, pq, pqbar,partons, true, nabove);
   Tensor<2,4> Xcro_p = MCross(  pa, pq, pqbar,partons, true, nabove);
   Tensor<2,4> Xsym_p = MSym(    pa, p1, pb, p4, pq, pqbar, partons, true, nabove);
 
 
   // (- - hel choice)
   COM M_mmUnc = (((Xunc_m).contract(T1am,1)).contract(T4bm,1)).at(0);
   COM M_mmCro = (((Xcro_m).contract(T1am,1)).contract(T4bm,1)).at(0);
   COM M_mmSym = (((Xsym_m).contract(T1am,1)).contract(T4bm,1)).at(0);
   // (- + hel choice)
   COM M_mpUnc = (((Xunc_p).contract(T1am,1)).contract(T4bm,1)).at(0);
   COM M_mpCro = (((Xcro_p).contract(T1am,1)).contract(T4bm,1)).at(0);
   COM M_mpSym = (((Xsym_p).contract(T1am,1)).contract(T4bm,1)).at(0);
   // (+ - hel choice)
   COM M_pmUnc = (((Xunc_m).contract(T1ap,1)).contract(T4bm,1)).at(0);
   COM M_pmCro = (((Xcro_m).contract(T1ap,1)).contract(T4bm,1)).at(0);
   COM M_pmSym = (((Xsym_m).contract(T1ap,1)).contract(T4bm,1)).at(0);
   // (+ + hel choice)
   COM M_ppUnc = (((Xunc_p).contract(T1ap,1)).contract(T4bm,1)).at(0);
   COM M_ppCro = (((Xcro_p).contract(T1ap,1)).contract(T4bm,1)).at(0);
   COM M_ppSym = (((Xsym_p).contract(T1ap,1)).contract(T4bm,1)).at(0);
 
   //Colour factors:
   COM cmsms,cmumu,cmcmc,cmsmu,cmsmc,cmumc;
   cmsms=3.;
   cmumu=4./3.;
   cmcmc=4./3.;
   cmsmu =3./2.*COM(0.,1.);
   cmsmc = -3./2.*COM(0.,1.);
   cmumc = -1./6.;
 
   // Work Out Interference in each case of helicity:
   double amp_mm = real(cmsms*pow(abs(M_mmSym),2)
              +cmumu*pow(abs(M_mmUnc),2)
              +cmcmc*pow(abs(M_mmCro),2)
              +2.*real(cmsmu*M_mmSym*conj(M_mmUnc))
              +2.*real(cmsmc*M_mmSym*conj(M_mmCro))
              +2.*real(cmumc*M_mmUnc*conj(M_mmCro)));
 
   double amp_mp = real(cmsms*pow(abs(M_mpSym),2)
              +cmumu*pow(abs(M_mpUnc),2)
              +cmcmc*pow(abs(M_mpCro),2)
              +2.*real(cmsmu*M_mpSym*conj(M_mpUnc))
              +2.*real(cmsmc*M_mpSym*conj(M_mpCro))
              +2.*real(cmumc*M_mpUnc*conj(M_mpCro)));
 
   double amp_pm = real(cmsms*pow(abs(M_pmSym),2)
              +cmumu*pow(abs(M_pmUnc),2)
              +cmcmc*pow(abs(M_pmCro),2)
              +2.*real(cmsmu*M_pmSym*conj(M_pmUnc))
              +2.*real(cmsmc*M_pmSym*conj(M_pmCro))
              +2.*real(cmumc*M_pmUnc*conj(M_pmCro)));
 
   double amp_pp = real(cmsms*pow(abs(M_ppSym),2)
              +cmumu*pow(abs(M_ppUnc),2)
              +cmcmc*pow(abs(M_ppCro),2)
              +2.*real(cmsmu*M_ppSym*conj(M_ppUnc))
              +2.*real(cmsmc*M_ppSym*conj(M_ppCro))
              +2.*real(cmumc*M_ppUnc*conj(M_ppCro)));
 
   double amp=((amp_mm+amp_mp+amp_pm+amp_pp)/(9.*4.));
 
   CLHEP::HepLorentzVector q1,q3;
   q1=pa;
   for(int i=0;i<nabove+1;i++){
     q1-=partons.at(i);
   }
   q3 = q1 - pq - pqbar;
 
   double t1 = (q1).m2();
   double t3 = (q3).m2();
 
   //Divide by t-channels
   amp/=(t1*t1*t3*t3);
 
   //Divide by WProp
   double WPropfact = WProp(plbar, pl);
   amp*=WPropfact;
 
   return amp;
 }
diff --git a/src/YAMLreader.cc b/src/YAMLreader.cc
index 1d51bd4..d19c1f8 100644
--- a/src/YAMLreader.cc
+++ b/src/YAMLreader.cc
@@ -1,475 +1,475 @@
 /**
- *  \authors   Jeppe Andersen, Tuomas Hapola, Marian Heil, Andreas Maier, Jennifer Smillie
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
  *  \date      2019
  *  \copyright GPLv2 or later
  */
 #include "HEJ/YAMLreader.hh"
 
 #include <algorithm>
 #include <iostream>
 #include <limits>
 #include <map>
 #include <string>
 #include <unordered_map>
 #include <vector>
 
 #include <dlfcn.h>
 
 #include "HEJ/ScaleFunction.hh"
 #include "HEJ/event_types.hh"
 #include "HEJ/output_formats.hh"
 #include "HEJ/Constants.hh"
 
 namespace HEJ{
   class Event;
 
   namespace{
     //! Get YAML tree of supported options
     /**
      * The configuration file is checked against this tree of options
      * in assert_all_options_known.
      */
     YAML::Node const & get_supported_options(){
       const static YAML::Node supported = [](){
         YAML::Node supported;
         static const auto opts = {
           "trials", "min extparton pt", "max ext soft pt fraction",
           "FKL", "unordered", "qqx", "non-HEJ",
           "scales", "scale factors", "max scale ratio", "import scales",
           "log correction", "event output", "analysis", "regulator parameter"
         };
         // add subnodes to "supported" - the assigned value is irrelevant
         for(auto && opt: opts) supported[opt] = "";
         for(auto && jet_opt: {"min pt", "algorithm", "R"}){
           supported["resummation jets"][jet_opt] = "";
           supported["fixed order jets"][jet_opt] = "";
         }
         for(auto && opt: {"mt", "use impact factors", "include bottom", "mb"}){
           supported["Higgs coupling"][opt] = "";
         }
         for(auto && opt: {"name", "seed"}){
           supported["random generator"][opt] = "";
         }
         return supported;
       }();
       return supported;
     }
 
     fastjet::JetAlgorithm to_JetAlgorithm(std::string const & algo){
       using namespace fastjet;
       static const std::map<std::string, fastjet::JetAlgorithm> known = {
         {"kt", kt_algorithm},
         {"cambridge", cambridge_algorithm},
         {"antikt", antikt_algorithm},
         {"cambridge for passive", cambridge_for_passive_algorithm},
         {"plugin", plugin_algorithm}
       };
       const auto res = known.find(algo);
       if(res == known.end()){
         throw std::invalid_argument("Unknown jet algorithm " + algo);
       }
       return res->second;
     }
 
     EventTreatment to_EventTreatment(std::string const & name){
       static const std::map<std::string, EventTreatment> known = {
         {"reweight", EventTreatment::reweight},
         {"keep", EventTreatment::keep},
         {"discard", EventTreatment::discard}
       };
       const auto res = known.find(name);
       if(res == known.end()){
         throw std::invalid_argument("Unknown event treatment " + name);
       }
       return res->second;
     }
 
   } // namespace anonymous
 
   namespace detail{
     void set_from_yaml(fastjet::JetAlgorithm & setting, YAML::Node const & yaml){
       setting = to_JetAlgorithm(yaml.as<std::string>());
     }
 
     void set_from_yaml(EventTreatment & setting, YAML::Node const & yaml){
       setting = to_EventTreatment(yaml.as<std::string>());
     }
 
     void set_from_yaml(ParticleID & setting, YAML::Node const & yaml){
       setting = to_ParticleID(yaml.as<std::string>());
     }
   } // namespace detail
 
   JetParameters get_jet_parameters(
       YAML::Node const & node,
       std::string const & entry
   ){
     assert(node);
     JetParameters result;
     fastjet::JetAlgorithm jet_algo = fastjet::antikt_algorithm;
     double R;
     set_from_yaml_if_defined(jet_algo, node, entry, "algorithm");
     set_from_yaml(R, node, entry, "R");
     result.def = fastjet::JetDefinition{jet_algo, R};
     set_from_yaml(result.min_pt, node, entry, "min pt");
     return result;
   }
 
   RNGConfig to_RNGConfig(
       YAML::Node const & node,
       std::string const & entry
   ){
     assert(node);
     RNGConfig result;
     set_from_yaml(result.name, node, entry, "name");
     set_from_yaml_if_defined(result.seed, node, entry, "seed");
     return result;
   }
 
   HiggsCouplingSettings get_Higgs_coupling(
       YAML::Node const & node,
       std::string const & entry
   ){
     assert(node);
     static constexpr double mt_max = 2e4;
 #ifndef HEJ_BUILD_WITH_QCDLOOP
     if(node[entry]){
       throw std::invalid_argument{
         "Higgs coupling settings require building HEJ 2 "
           "with QCDloop support"
           };
     }
 #endif
     HiggsCouplingSettings settings;
     set_from_yaml_if_defined(settings.mt, node, entry, "mt");
     set_from_yaml_if_defined(settings.mb, node, entry, "mb");
     set_from_yaml_if_defined(settings.include_bottom, node, entry, "include bottom");
     set_from_yaml_if_defined(settings.use_impact_factors, node, entry, "use impact factors");
     if(settings.use_impact_factors){
       if(settings.mt != std::numeric_limits<double>::infinity()){
         throw std::invalid_argument{
           "Conflicting settings: "
             "impact factors may only be used in the infinite top mass limit"
             };
       }
     }
     else{
       // huge values of the top mass are numerically unstable
       settings.mt = std::min(settings.mt, mt_max);
     }
     return settings;
   }
 
   FileFormat to_FileFormat(std::string const & name){
     static const std::map<std::string, FileFormat> known = {
       {"Les Houches", FileFormat::Les_Houches},
       {"HepMC", FileFormat::HepMC}
     };
     const auto res = known.find(name);
     if(res == known.end()){
       throw std::invalid_argument("Unknown file format " + name);
     }
     return res->second;
   }
 
   std::string extract_suffix(std::string const & filename){
     size_t separator = filename.rfind('.');
     if(separator == filename.npos) return {};
     return filename.substr(separator + 1);
   }
 
   FileFormat format_from_suffix(std::string const & filename){
     const std::string suffix = extract_suffix(filename);
     if(suffix == "lhe") return FileFormat::Les_Houches;
     if(suffix == "hepmc") return FileFormat::HepMC;
     throw std::invalid_argument{
       "Can't determine format for output file " + filename
     };
   }
 
   void assert_all_options_known(
       YAML::Node const & conf, YAML::Node const & supported
   ){
     if(!conf.IsMap()) return;
     if(!supported.IsMap()) throw invalid_type{"must not have sub-entries"};
     for(auto const & entry: conf){
       const auto name = entry.first.as<std::string>();
       if(! supported[name]) throw unknown_option{name};
       /* check sub-options, e.g. 'resummation jets: min pt'
        * we don't check analysis sub-options
        * those depend on the analysis being used and should be checked there
        * similar for "import scales"
        */
       if(name != "analysis" && name != "import scales"){
         try{
           assert_all_options_known(conf[name], supported[name]);
         }
         catch(unknown_option const & ex){
           throw unknown_option{name + ": " + ex.what()};
         }
         catch(invalid_type const & ex){
           throw invalid_type{name + ": " + ex.what()};
         }
       }
     }
   }
 
 } // namespace HEJ
 
 namespace YAML {
 
   Node convert<HEJ::OutputFile>::encode(HEJ::OutputFile const & outfile) {
     Node node;
     node[to_string(outfile.format)] = outfile.name;
     return node;
   };
 
   bool convert<HEJ::OutputFile>::decode(Node const & node, HEJ::OutputFile & out) {
     switch(node.Type()){
     case NodeType::Map: {
       YAML::const_iterator it = node.begin();
       out.format = HEJ::to_FileFormat(it->first.as<std::string>());
       out.name = it->second.as<std::string>();
       return true;
     }
     case NodeType::Scalar:
       out.name = node.as<std::string>();
       out.format = HEJ::format_from_suffix(out.name);
       return true;
     default:
       return false;
     }
   }
 } // namespace YAML
 
 namespace HEJ{
 
   namespace detail{
     void set_from_yaml(OutputFile & setting, YAML::Node const & yaml){
       setting = yaml.as<OutputFile>();
     }
   }
 
   namespace{
     void update_fixed_order_jet_parameters(
         JetParameters & fixed_order_jets, YAML::Node const & yaml
     ){
       if(!yaml["fixed order jets"]) return;
       set_from_yaml_if_defined(
           fixed_order_jets.min_pt, yaml, "fixed order jets", "min pt"
       );
       fastjet::JetAlgorithm algo = fixed_order_jets.def.jet_algorithm();
       set_from_yaml_if_defined(algo, yaml, "fixed order jets", "algorithm");
       double R = fixed_order_jets.def.R();
       set_from_yaml_if_defined(R, yaml, "fixed order jets", "R");
       fixed_order_jets.def = fastjet::JetDefinition{algo, R};
     }
 
     // like std::stod, but throw if not the whole string can be converted
     double to_double(std::string const & str){
       std::size_t pos;
       const double result = std::stod(str, &pos);
       if(pos < str.size()){
         throw std::invalid_argument(str + " is not a valid double value");
       }
       return result;
     }
 
     using EventScale = double (*)(Event const &);
 
     void import_scale_functions(
         std::string const & file,
         std::vector<std::string> const & scale_names,
         std::unordered_map<std::string, EventScale> & known
     ) {
       auto handle = dlopen(file.c_str(), RTLD_NOW);
       char * error = dlerror();
       if(error != nullptr) throw std::runtime_error{error};
 
       for(auto const & scale: scale_names) {
         void * sym = dlsym(handle, scale.c_str());
         error = dlerror();
         if(error != nullptr) throw std::runtime_error{error};
         known.emplace(scale, reinterpret_cast<EventScale>(sym));
       }
     }
 
     auto get_scale_map(
         YAML::Node const & yaml
     ) {
       std::unordered_map<std::string, EventScale> scale_map;
       scale_map.emplace("H_T", H_T);
       scale_map.emplace("max jet pperp", max_jet_pt);
       scale_map.emplace("jet invariant mass", jet_invariant_mass);
       scale_map.emplace("m_j1j2", m_j1j2);
       if(yaml["import scales"]) {
         if(! yaml["import scales"].IsMap()) {
           throw invalid_type{"Entry 'import scales' is not a map"};
         }
         for(auto const & import: yaml["import scales"]) {
           const auto file = import.first.as<std::string>();
           const auto scale_names =
             import.second.IsSequence()
             ?import.second.as<std::vector<std::string>>()
             :std::vector<std::string>{import.second.as<std::string>()};
           import_scale_functions(file, scale_names, scale_map);
         }
       }
       return scale_map;
     }
 
     // simple (as in non-composite) scale functions
     /**
      * An example for a simple scale function would be H_T,
      * H_T/2 is then composite (take H_T and then divide by 2)
      */
     ScaleFunction parse_simple_ScaleFunction(
         std::string const & scale_fun,
         std::unordered_map<std::string, EventScale> const & known
     ) {
       assert(
           scale_fun.empty() ||
           (!std::isspace(scale_fun.front()) && !std::isspace(scale_fun.back()))
       );
       const auto it = known.find(scale_fun);
       if(it != end(known)) return {it->first, it->second};
       try{
         const double scale = to_double(scale_fun);
         return {scale_fun, FixedScale{scale}};
       } catch(std::invalid_argument const &){}
       throw std::invalid_argument{"Unknown scale choice: " + scale_fun};
     }
 
     std::string trim_front(std::string const & str){
       const auto new_begin = std::find_if(
           begin(str), end(str), [](char c){ return ! std::isspace(c); }
       );
       return std::string(new_begin, end(str));
     }
 
     std::string trim_back(std::string str){
       size_t pos = str.size() - 1;
       // use guaranteed wrap-around behaviour to check whether we have
       // traversed the whole string
       for(; pos < str.size() && std::isspace(str[pos]); --pos) {}
       str.resize(pos + 1); // note that pos + 1 can be 0
       return str;
     }
 
     ScaleFunction parse_ScaleFunction(
         std::string const & scale_fun,
         std::unordered_map<std::string, EventScale> const & known
     ){
       assert(
           scale_fun.empty() ||
           (!std::isspace(scale_fun.front()) && !std::isspace(scale_fun.back()))
       );
       // parse from right to left => a/b/c gives (a/b)/c
       const size_t delim = scale_fun.find_last_of("*/");
       if(delim == scale_fun.npos){
         return parse_simple_ScaleFunction(scale_fun, known);
       }
       const std::string first = trim_back(std::string{scale_fun, 0, delim});
       const std::string second = trim_front(std::string{scale_fun, delim+1});
       if(scale_fun[delim] == '/'){
         return parse_ScaleFunction(first, known)
           / parse_ScaleFunction(second, known);
       }
       else{
         assert(scale_fun[delim] == '*');
         return parse_ScaleFunction(first, known)
           * parse_ScaleFunction(second, known);
       }
     }
 
     EventTreatMap get_event_treatment(
         YAML::Node const & yaml
     ){
       using namespace event_type;
       EventTreatMap treat {
         {no_2_jets, EventTreatment::discard},
         {bad_final_state, EventTreatment::discard},
         {FKL, EventTreatment::reweight},
         {unob, EventTreatment::keep},
         {unof, EventTreatment::keep},
         {qqxexb, EventTreatment::keep},
         {qqxexf, EventTreatment::keep},
         {qqxmid, EventTreatment::keep},
         {nonHEJ, EventTreatment::keep}
       };
       set_from_yaml(treat.at(FKL), yaml, "FKL");
       set_from_yaml(treat.at(unob), yaml, "unordered");
       treat.at(unof) = treat.at(unob);
       set_from_yaml(treat.at(qqxexb), yaml, "qqx");
       set_from_yaml(treat.at(qqxexf), yaml, "qqx");
       set_from_yaml(treat.at(qqxmid), yaml, "qqx");
       set_from_yaml(treat.at(nonHEJ), yaml, "non-HEJ");
       if(treat[nonHEJ] == EventTreatment::reweight){
         throw std::invalid_argument{"Cannot reweight non-HEJ events"};
       }
       return treat;
     }
 
 
     Config to_Config(YAML::Node const & yaml){
       try{
         assert_all_options_known(yaml, get_supported_options());
       }
       catch(unknown_option const & ex){
         throw unknown_option{std::string{"Unknown option '"} + ex.what() + "'"};
       }
 
       Config config;
       config.resummation_jets = get_jet_parameters(yaml, "resummation jets");
       config.fixed_order_jets = config.resummation_jets;
       update_fixed_order_jet_parameters(config.fixed_order_jets, yaml);
       set_from_yaml(config.min_extparton_pt, yaml, "min extparton pt");
       // Sets the standard value, then changes this if defined
       config.regulator_lambda=CLAMBDA;
       set_from_yaml_if_defined(config.regulator_lambda, yaml, "regulator parameter");
       config.max_ext_soft_pt_fraction = std::numeric_limits<double>::infinity();
       set_from_yaml_if_defined(
           config.max_ext_soft_pt_fraction, yaml, "max ext soft pt fraction"
       );
       set_from_yaml(config.trials, yaml, "trials");
       set_from_yaml(config.log_correction, yaml, "log correction");
       config.treat = get_event_treatment(yaml);
       set_from_yaml_if_defined(config.output, yaml, "event output");
       config.rng = to_RNGConfig(yaml, "random generator");
       set_from_yaml_if_defined(config.analysis_parameters, yaml, "analysis");
       config.scales = to_ScaleConfig(yaml);
       config.Higgs_coupling = get_Higgs_coupling(yaml, "Higgs coupling");
       return config;
     }
 
   } // namespace anonymous
 
   ScaleConfig to_ScaleConfig(YAML::Node const & yaml){
     ScaleConfig config;
     auto scale_funs = get_scale_map(yaml);
     std::vector<std::string> scales;
     set_from_yaml(scales, yaml, "scales");
     config.base.reserve(scales.size());
     std::transform(
         begin(scales), end(scales), std::back_inserter(config.base),
         [scale_funs](auto const & entry){
           return parse_ScaleFunction(entry, scale_funs);
         }
     );
     set_from_yaml_if_defined(config.factors, yaml, "scale factors");
     config.max_ratio = std::numeric_limits<double>::infinity();
     set_from_yaml_if_defined(config.max_ratio, yaml, "max scale ratio");
     return config;
   }
 
   Config load_config(std::string const & config_file){
     try{
       return to_Config(YAML::LoadFile(config_file));
     }
     catch(...){
       std::cerr << "Error reading " << config_file << ":\n  ";
       throw;
     }
   }
 
 } // namespace HEJ
diff --git a/src/bin/HEJ.cc b/src/bin/HEJ.cc
index 0338a92..ed90104 100644
--- a/src/bin/HEJ.cc
+++ b/src/bin/HEJ.cc
@@ -1,191 +1,211 @@
 /**
- *  \authors   Jeppe Andersen, Tuomas Hapola, Marian Heil, Andreas Maier, Jennifer Smillie
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
  *  \date      2019
  *  \copyright GPLv2 or later
  */
 #include <array>
 #include <chrono>
 #include <iostream>
 #include <limits>
 #include <memory>
 #include <numeric>
 
 #include "yaml-cpp/yaml.h"
 
 #include "fastjet/ClusterSequence.hh"
 
 #include "HEJ/CombinedEventWriter.hh"
 #include "HEJ/config.hh"
 #include "HEJ/CrossSectionAccumulator.hh"
 #include "HEJ/Event.hh"
 #include "HEJ/EventReader.hh"
 #include "HEJ/EventReweighter.hh"
 #include "HEJ/get_analysis.hh"
 #include "HEJ/make_RNG.hh"
 #include "HEJ/ProgressBar.hh"
 #include "HEJ/stream.hh"
 #include "HEJ/Version.hh"
 #include "HEJ/YAMLreader.hh"
 
 int event_number(std::string const & record){
   size_t start = record.rfind("Number of Events");
   start = record.find_first_of("123456789", start);
   if(start == std::string::npos) {
     throw std::invalid_argument("no event number record found");
   }
   const size_t end = record.find_first_not_of("0123456789", start);
   return std::stoi(record.substr(start, end - start));
 }
 
 HEJ::Config load_config(char const * filename){
   try{
     return HEJ::load_config(filename);
   }
   catch(std::exception const & exc){
     std::cerr << "Error: " << exc.what() << '\n';
     std::exit(EXIT_FAILURE);
   }
 }
 
 std::unique_ptr<HEJ::Analysis> get_analysis(
     YAML::Node const & parameters
 ){
   try{
     return HEJ::get_analysis(parameters);
   }
   catch(std::exception const & exc){
     std::cerr << "Failed to load analysis: " << exc.what() << '\n';
     std::exit(EXIT_FAILURE);
   }
 }
 
 // unique_ptr is a workaround:
 // HEJ::optional is a better fit, but gives spurious errors with g++ 7.3.0
 std::unique_ptr<HEJ::ProgressBar<double>> make_progress_bar(
     std::vector<double> const & xs
 ) {
   if(xs.empty()) return {};
   const double Born_xs = std::accumulate(begin(xs), end(xs), 0.);
   return std::make_unique<HEJ::ProgressBar<double>>(std::cout, Born_xs);
 }
 
 std::string time_to_string(const time_t time){
   char s[30];
   struct tm * p = localtime(&time);
   strftime(s, 30, "%a %b %d %Y %H:%M:%S", p);
   return s;
 }
 
 int main(int argn, char** argv) {
   using clock = std::chrono::system_clock;
 
   if (argn < 3) {
     std::cerr << "\n# Usage:\n."<< argv[0] <<" config_file input_file\n\n";
     return EXIT_FAILURE;
   }
 
   const auto start_time = clock::now();
   {
     std::cout << "Starting " << HEJ::Version::package_name_full()
       << ", revision " << HEJ::Version::revision() << " ("
       << time_to_string(clock::to_time_t(start_time)) << ")" << std::endl;
   }
   fastjet::ClusterSequence::print_banner();
 
   // read configuration
   const HEJ::Config config = load_config(argv[1]);
   auto reader = HEJ::make_reader(argv[2]);
   assert(reader);
 
   std::unique_ptr<HEJ::Analysis> analysis = get_analysis(
       config.analysis_parameters
   );
   assert(analysis != nullptr);
 
   auto heprup = reader->heprup();
   heprup.generators.emplace_back(LHEF::XMLTag{});
   heprup.generators.back().name = HEJ::Version::package_name();
   heprup.generators.back().version = HEJ::Version::String();
   HEJ::CombinedEventWriter writer{config.output, std::move(heprup)};
 
   double global_reweight = 1.;
   int max_events = std::numeric_limits<int>::max();
   if(argn > 3){
     max_events = std::stoi(argv[3]);
     const int input_events = event_number(reader->header());
     global_reweight = input_events/static_cast<double>(max_events);
     std::cout << "Processing " << max_events
               << " out of " << input_events << " events\n";
   }
   HEJ::ScaleGenerator scale_gen{
     config.scales.base,
     config.scales.factors,
     config.scales.max_ratio
   };
   auto ran = HEJ::make_RNG(config.rng.name, config.rng.seed);
   assert(ran != nullptr);
   HEJ::EventReweighter hej{
     reader->heprup(),
     std::move(scale_gen),
     to_EventReweighterConfig(config),
     *ran
   };
 
+  // status infos & eye candy
   int nevent = 0;
   std::array<int, HEJ::event_type::last_type + 1>
     nevent_type{0}, nfailed_type{0};
   auto progress = make_progress_bar(reader->heprup().XSECUP);
   HEJ::CrossSectionAccumulator xs;
-  // Loop over the events in the inputfile
+  std::map<HEJ::StatusCode, int> status_counter;
+  size_t total_trials = 0;
+
+  // Loop over the events in the input file
   while(reader->read_event()){
     // reweight events so that the total cross section is conserved
     auto hepeup = reader->hepeup();
     hepeup.setWeight(0, global_reweight * hepeup.weight());
 
     if(nevent == max_events) break;
     ++nevent;
 
     HEJ::Event::EventData event_data{hepeup};
     event_data.reconstruct_intermediate();
 
     // calculate HEJ weight
     HEJ::Event FO_event{
       std::move(event_data).cluster(
         config.fixed_order_jets.def, config.fixed_order_jets.min_pt
       )
     };
     auto resummed_events = hej.reweight(FO_event, config.trials);
+    for(auto const & s: hej.status())
+      ++status_counter[s];
+    total_trials+=hej.status().size();
     ++nevent_type[FO_event.type()];
 
     if(resummed_events.empty()) ++nfailed_type[FO_event.type()];
 
     for(auto const & ev: resummed_events){
       //TODO: move pass_cuts to after phase space point generation
       if(analysis->pass_cuts(ev, FO_event)){
         analysis->fill(ev, FO_event);
         writer.write(ev);
         xs.fill(ev);
       }
     }
     if(progress) progress->increment(FO_event.central().weight);
   } // main event loop
   std::cout << '\n';
   analysis->finalise();
 
   using namespace  HEJ::event_type;
   std::cout<< "Events processed: " << nevent << '\n';
   for(size_t ev_type = first_type; ev_type <= last_type; ++ev_type){
     std::cout << '\t' << names[ev_type] << ": " << nevent_type[ev_type]
               << ", failed to reconstruct " << nfailed_type[ev_type]
               << '\n';
   }
 
   std::cout << '\n' << xs << '\n';
 
+  std::cout << "Generation statistic: "
+    << status_counter[HEJ::StatusCode::good] << "/" << total_trials
+    << " trials successful.\n";
+  for(auto && entry: status_counter){
+    const double fraction = static_cast<double>(entry.second)/total_trials;
+    const int percent = std::round(100*fraction);
+    std::cout << std::left << std::setw(16) << (to_string(entry.first) + ":")
+              << " [";
+    for(int i = 0; i < percent/2; ++i) std::cout << '#';
+    for(int i = percent/2; i < 50; ++i) std::cout << ' ';
+    std::cout << "] " << percent << "%\n";
+  }
+
   std::chrono::duration<double> run_time = (clock::now() - start_time);
-  std::cout << "Finished " << HEJ::Version::package_name() << " at "
+  std::cout << "\nFinished " << HEJ::Version::package_name() << " at "
     << time_to_string(clock::to_time_t(clock::now()))
     << "\n=> Runtime: " << run_time.count() << " sec ("
     << nevent/run_time.count() << " Events/sec).\n";
 
 }
diff --git a/src/currents.cc b/src/currents.cc
index de692eb..26542a9 100644
--- a/src/currents.cc
+++ b/src/currents.cc
@@ -1,3210 +1,3198 @@
-//////////////////////////////////////////////////
-//////////////////////////////////////////////////
-// This source code is Copyright (2012) of      //
-//  Jeppe R. Andersen and Jennifer M. Smillie   //
-// and is distributed under the                 //
-// Gnu Public License version 2                 //
-// http://www.gnu.org/licenses/gpl-2.0.html     //
-// You are allowed to distribute and alter the  //
-// source under the conditions of the GPLv2     //
-// as long as this copyright notice             //
-// is unaltered and distributed with the source //
-// Any use should comply with the               //
-//             MCNET GUIDELINES                 //
-//    for Event Generator Authors and Users     //
-// as distributed with this source code         //
-//////////////////////////////////////////////////
-//////////////////////////////////////////////////
+/**
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
+ *  \date      2019
+ *  \copyright GPLv2 or later
+ */
 #include "HEJ/currents.hh"
 
 #include <iostream>
 #include <limits>
 #include <utility>
 #include <vector>
 
 #ifdef HEJ_BUILD_WITH_QCDLOOP
 #include "qcdloop/qcdloop.h"
 #endif
 
 #include "HEJ/Constants.hh"
 #include "HEJ/exceptions.hh"
 #include "HEJ/PDG_codes.hh"
 
 const COM looprwfactor = (COM(0.,1.)*M_PI*M_PI)/pow((2.*M_PI),4);
 constexpr double infinity = std::numeric_limits<double>::infinity();
 
 namespace {
   // Loop integrals
   #ifdef HEJ_BUILD_WITH_QCDLOOP
 
   COM B0DD(CLHEP::HepLorentzVector q, double mq)
   {
     static std::vector<std::complex<double>> result(3);
     static auto ql_B0 = [](){
       ql::Bubble<std::complex<double>,double,double> ql_B0;
       ql_B0.setCacheSize(100);
       return ql_B0;
     }();
     static std::vector<double> masses(2);
     static std::vector<double> momenta(1);
     for(auto & m: masses) m = mq*mq;
     momenta.front() = q.m2();
     ql_B0.integral(result, 1, masses, momenta);
     return result[0];
   }
   COM C0DD(CLHEP::HepLorentzVector q1, CLHEP::HepLorentzVector q2, double mq)
   {
     static std::vector<std::complex<double>> result(3);
     static auto ql_C0 = [](){
       ql::Triangle<std::complex<double>,double,double> ql_C0;
       ql_C0.setCacheSize(100);
       return ql_C0;
     }();
     static std::vector<double> masses(3);
     static std::vector<double> momenta(3);
     for(auto & m: masses) m = mq*mq;
     momenta[0] = q1.m2();
     momenta[1] = q2.m2();
     momenta[2] = (q1+q2).m2();
     ql_C0.integral(result, 1, masses, momenta);
     return result[0];
   }
   COM D0DD(CLHEP::HepLorentzVector q1,CLHEP::HepLorentzVector q2, CLHEP::HepLorentzVector q3, double mq)
   {
     static std::vector<std::complex<double>> result(3);
     static auto ql_D0 = [](){
       ql::Box<std::complex<double>,double,double> ql_D0;
       ql_D0.setCacheSize(100);
       return ql_D0;
     }();
     static std::vector<double> masses(4);
     static std::vector<double> momenta(6);
     for(auto & m: masses) m = mq*mq;
     momenta[0] = q1.m2();
     momenta[1] = q2.m2();
     momenta[2] = q3.m2();
     momenta[3] = (q1+q2+q3).m2();
     momenta[4] = (q1+q2).m2();
     momenta[5] = (q2+q3).m2();
     ql_D0.integral(result, 1, masses, momenta);
     return result[0];
   }
 
   COM A1(CLHEP::HepLorentzVector q1, CLHEP::HepLorentzVector q2, double mt)
   // As given in Eq. (B.2) of VDD
   {
     double q12,q22,Q2;
     CLHEP::HepLorentzVector Q;
     double Delta3,mt2;
     COM ans(COM(0.,0.));
 
     q12=q1.m2();
     q22=q2.m2();
     Q=-q1-q2; // Define all momenta ingoing as in appendix of VDD
     Q2=Q.m2();
 
     Delta3=q12*q12+q22*q22+Q2*Q2-2*q12*q22-2*q12*Q2-2*q22*Q2;
     if (mt < 0.)
       std::cerr<<"Problem in A1! mt = "<<mt<<std::endl;
     mt2=mt*mt;
 
     ans=looprwfactor*COM(0,-1)*C0DD(q1,q2,mt)*( 4.*mt2/Delta3*(Q2-q12-q22)
         -1.-4.*q12*q22/Delta3-12.*q12*q22*Q2/Delta3/Delta3*(q12+q22-Q2) )
       - looprwfactor*COM(0,-1)*( B0DD(q2,mt)-B0DD(Q,mt) )
         * ( 2.*q22/Delta3+12.*q12*q22/Delta3/Delta3*(q22-q12+Q2) )
       - looprwfactor*COM(0,-1)*( B0DD(q1,mt)-B0DD(Q,mt) )
         * ( 2.*q12/Delta3+12.*q12*q22/Delta3/Delta3*(q12-q22+Q2) )
       - 2./Delta3/16/M_PI/M_PI*(q12+q22-Q2);
 
     return ans;
 
   }
 
 
   COM A2(CLHEP::HepLorentzVector q1, CLHEP::HepLorentzVector q2, double mt)
   // As given in Eq. (B.2) of VDD, but with high energy limit
   // of invariants taken.
   {
     double q12,q22,Q2;
     CLHEP::HepLorentzVector Q;
     double Delta3,mt2;
     COM ans(COM(0.,0.));
 
     if (mt < 0.)
       std::cerr<<"Problem in A2! mt = "<<mt<<std::endl;
     mt2=mt*mt;
 
     q12=q1.m2();
     q22=q2.m2();
     Q=-q1-q2; // Define all momenta ingoing as in appendix of VDD
     Q2=Q.m2();
 
     Delta3=q12*q12+q22*q22+Q2*Q2-2*q12*q22-2*q12*Q2-2*q22*Q2;
     ans=looprwfactor*COM(0,-1)*C0DD(q1,q2,mt)*( 2.*mt2+1./2.*(q12+q22-Q2)
         +2.*q12*q22*Q2/Delta3 )
       +looprwfactor*COM(0,-1)*(B0DD(q2,mt)-B0DD(Q,mt))
         *q22*(q22-q12-Q2)/Delta3
       +looprwfactor*COM(0,-1)*(B0DD(q1,mt)-B0DD(Q,mt))
         *q12*(q12-q22-Q2)/Delta3+1./16/M_PI/M_PI;
 
     return ans;
   }
 
 #else // no QCDloop
 
   COM A1(CLHEP::HepLorentzVector, CLHEP::HepLorentzVector, double) {
     throw std::logic_error{"A1 called without QCDloop support"};
   }
 
   COM A2(CLHEP::HepLorentzVector, CLHEP::HepLorentzVector, double) {
     throw std::logic_error{"A2 called without QCDloop support"};
   }
 
 #endif
 
   void to_current(const CLHEP::HepLorentzVector & q, current & ret){
     ret[0]=q.e();
     ret[1]=q.x();
     ret[2]=q.y();
     ret[3]=q.z();
   }
 
   constexpr double C_A = 3.;
   constexpr double C_F = 4./3.;
   // using ParticleID = HEJ::pid::ParticleID;
 
 
 } // namespace anonymous
 
   // Colour acceleration multiplier for gluons see eq. (7) in arXiv:0910.5113
   // @TODO: this is not a current and should be moved somewhere else
   double K_g(double p1minus, double paminus) {
     return 1./2.*(p1minus/paminus + paminus/p1minus)*(HEJ::C_A - 1./HEJ::C_A) + 1./HEJ::C_A;
   }
   double K_g(
       CLHEP::HepLorentzVector const & pout,
       CLHEP::HepLorentzVector const & pin
   ) {
     if(pin.z() > 0) return K_g(pout.plus(), pin.plus());
     return K_g(pout.minus(), pin.minus());
   }
 
 
 CCurrent CCurrent::operator+(const CCurrent& other)
 {
     COM result_c0=c0 + other.c0;
     COM result_c1=c1 + other.c1;
     COM result_c2=c2 + other.c2;
     COM result_c3=c3 + other.c3;
 
     return CCurrent(result_c0,result_c1,result_c2,result_c3);
 }
 
 CCurrent CCurrent::operator-(const CCurrent& other)
 {
     COM result_c0=c0 - other.c0;
     COM result_c1=c1 - other.c1;
     COM result_c2=c2 - other.c2;
     COM result_c3=c3 - other.c3;
 
     return CCurrent(result_c0,result_c1,result_c2,result_c3);
 }
 
 CCurrent CCurrent::operator*(const double x)
 {
     COM result_c0=x*CCurrent::c0;
     COM result_c1=x*CCurrent::c1;
     COM result_c2=x*CCurrent::c2;
     COM result_c3=x*CCurrent::c3;
 
     return CCurrent(result_c0,result_c1,result_c2,result_c3);
 }
 
 CCurrent CCurrent::operator/(const double x)
 {
     COM result_c0=CCurrent::c0/x;
     COM result_c1=CCurrent::c1/x;
     COM result_c2=CCurrent::c2/x;
     COM result_c3=CCurrent::c3/x;
 
     return CCurrent(result_c0,result_c1,result_c2,result_c3);
 }
 
 CCurrent CCurrent::operator*(const COM x)
 {
     COM result_c0=x*CCurrent::c0;
     COM result_c1=x*CCurrent::c1;
     COM result_c2=x*CCurrent::c2;
     COM result_c3=x*CCurrent::c3;
 
     return CCurrent(result_c0,result_c1,result_c2,result_c3);
 }
 
 CCurrent CCurrent::operator/(const COM x)
 {
     COM result_c0=(CCurrent::c0)/x;
     COM result_c1=(CCurrent::c1)/x;
     COM result_c2=(CCurrent::c2)/x;
     COM result_c3=(CCurrent::c3)/x;
 
     return CCurrent(result_c0,result_c1,result_c2,result_c3);
 }
 
 std::ostream& operator <<(std::ostream& os, const CCurrent& cur)
 {
     os << "("<<cur.c0<< " ; "<<cur.c1<<" , "<<cur.c2<<" , "<<cur.c3<<")";
     return os;
 }
 
 CCurrent operator * ( double x, CCurrent& m)
 {
     return m*x;
 }
 
 CCurrent operator * ( COM x, CCurrent& m)
 {
     return m*x;
 }
 
 CCurrent operator / ( double x, CCurrent& m)
 {
     return m/x;
 }
 
 CCurrent operator / ( COM x, CCurrent& m)
 {
     return m/x;
 }
 
 COM CCurrent::dot(CLHEP::HepLorentzVector p1)
 {
     //  Current goes (E,px,py,pz)
     //  std::cout<<"current = ("<<c0<<","<<c1<<","<<c2<<","<<c3<<")\n";
     //  Vector goes (px,py,pz,E)
     //  std::cout<<"vector = ("<<p1[0]<<","<<p1[1]<<","<<p1[2]<<","<<p1[3]<<")\n";
     return p1[3]*c0-p1[0]*c1-p1[1]*c2-p1[2]*c3;
 }
 
 COM CCurrent::dot(CCurrent p1)
 {
     return p1.c0*c0-p1.c1*c1-p1.c2*c2-p1.c3*c3;
 }
 
 //Current Functions
 
 // Current for <outgoing state | mu | incoming state>
 /// @TODO always use this instead of "j"
 /// @TODO isn't this jio with flipt helicities?
 void joi(HLV pout, bool helout, HLV pin, bool helin, current &cur) {
   cur[0]=0.;
   cur[1]=0.;
   cur[2]=0.;
   cur[3]=0.;
 
   const double sqpop = sqrt(pout.plus());
   const double sqpom = sqrt(pout.minus());
   const COM poperp = pout.x() + COM(0, 1) * pout.y();
 
   if (helout != helin) {
     throw std::invalid_argument{"Non-matching helicities"};
   } else if (helout == false) { // negative helicity
     if (pin.plus() > pin.minus()) { // if forward
       const double sqpip = sqrt(pin.plus());
       cur[0] = sqpop * sqpip;
       cur[1] = sqpom * sqpip * poperp / abs(poperp);
       cur[2] = -COM(0,1) * cur[1];
       cur[3] = cur[0];
     } else { // if backward
       const double sqpim = sqrt(pin.minus());
       cur[0] = -sqpom * sqpim * poperp / abs(poperp);
       cur[1] = -sqpim * sqpop;
       cur[2] = COM(0,1) * cur[1];
       cur[3] = -cur[0];
     }
   } else { // positive helicity
     if (pin.plus() > pin.minus()) { // if forward
       const double sqpip = sqrt(pin.plus());
       cur[0] = sqpop * sqpip;
       cur[1] = sqpom * sqpip * conj(poperp) / abs(poperp);
       cur[2] = COM(0,1) * cur[1];
       cur[3] = cur[0];
     } else { // if backward
       const double sqpim = sqrt(pin.minus());
       cur[0] = -sqpom * sqpim * conj(poperp) / abs(poperp);
       cur[1] = -sqpim * sqpop;
       cur[2] = -COM(0,1) * cur[1];
       cur[3] = -cur[0];
     }
   }
 }
 
 CCurrent joi (HLV pout, bool helout, HLV pin, bool helin)
 {
   current cur;
   joi(pout, helout, pin, helin, cur);
   return CCurrent(cur[0],cur[1],cur[2],cur[3]);
 }
 
 /// @TODO remove this
 void j (HLV pout, bool helout, HLV pin, bool helin,current &cur) {
   joi(pout, helout, pin, helin, cur);
 }
 
 /// @TODO remove this
 CCurrent j (HLV pout, bool helout, HLV pin, bool helin)
 {
     return joi(pout, helout, pin, helin);
 }
 
 
 // Current for <incoming state | mu | outgoing state>
 void jio(HLV pin, bool helin, HLV pout, bool helout, current &cur) {
 
   cur[0] = 0.0;
   cur[1] = 0.0;
   cur[2] = 0.0;
   cur[3] = 0.0;
   const double sqpop = sqrt(pout.plus());
   const double sqpom = sqrt(pout.minus());
   const COM poperp = pout.x() + COM(0, 1) * pout.y();
 
 
   if (helout != helin) {
     throw std::invalid_argument{"Non-matching helicities"};
   } else if (helout == false) { // negative helicity
     if (pin.plus() > pin.minus()) { // if forward
       const double sqpip = sqrt(pin.plus());
       cur[0] = sqpop * sqpip;
       cur[1] = sqpom * sqpip * conj(poperp) / abs(poperp);
       cur[2] = COM(0,1) * cur[1];
       cur[3] = cur[0];
     } else { // if backward
       const double sqpim = sqrt(pin.minus());
       cur[0] = -sqpom * sqpim * conj(poperp) / abs(poperp);
       cur[1] = -sqpim * sqpop;
       cur[2] = -COM(0,1) * cur[1];
       cur[3] = -cur[0];
     }
   } else { // positive helicity
     if (pin.plus() > pin.minus()) { // if forward
       const double sqpip = sqrt(pin.plus());
       cur[0] = sqpop * sqpip;
       cur[1] = sqpom * sqpip * poperp / abs(poperp);
       cur[2] = -COM(0,1) * cur[1];
       cur[3] = cur[0];
     } else { // if backward
       const double sqpim = sqrt(pin.minus());
       cur[0] = -sqpom * sqpim * poperp / abs(poperp);
       cur[1] = -sqpim * sqpop;
       cur[2] = COM(0,1) * cur[1];
       cur[3] = -cur[0];
     }
   }
 }
 
 CCurrent jio (HLV pin, bool helin, HLV pout, bool helout)
 {
     current cur;
     jio(pin, helin, pout, helout, cur);
     return CCurrent(cur[0],cur[1],cur[2],cur[3]);
 }
 
 
 // Current for <outgoing state | mu | outgoing state>
 void joo(HLV pi, bool heli, HLV pj, bool helj, current &cur) {
 
   // Zero our current
   cur[0] = 0.0;
   cur[1] = 0.0;
   cur[2] = 0.0;
   cur[3] = 0.0;
   if (heli!=helj) {
     throw std::invalid_argument{"Non-matching helicities"};
   } else if ( heli == true ) { // If positive helicity swap momenta
     std::swap(pi,pj);
   }
 
   const double sqpjp = sqrt(pj.plus());
   const double sqpjm = sqrt(pj.minus());
   const double sqpip = sqrt(pi.plus());
   const double sqpim = sqrt(pi.minus());
 
   const COM piperp = pi.x() + COM(0,1) * pi.y();
   const COM pjperp = pj.x() + COM(0,1) * pj.y();
   const COM phasei = piperp / abs(piperp);
   const COM phasej = pjperp / abs(pjperp);
 
   cur[0] = sqpim * sqpjm * phasei * conj(phasej) + sqpip * sqpjp;
   cur[1] = sqpim * sqpjp * phasei + sqpip * sqpjm * conj(phasej);
   cur[2] = -COM(0, 1) * (sqpim * sqpjp * phasei - sqpip * sqpjm * conj(phasej));
   cur[3] = -sqpim * sqpjm * phasei * conj(phasej) + sqpip * sqpjp;
 }
 
 CCurrent joo (HLV pi, bool heli, HLV pj, bool helj)
 {
   current cur;
   joo(pi, heli, pj, helj, cur);
   return CCurrent(cur[0],cur[1],cur[2],cur[3]);
 }
 
 namespace {
   /// @TODO unused function
   // double jM2 (CLHEP::HepLorentzVector p1out, bool hel1out, CLHEP::HepLorentzVector p1in, bool hel1in, CLHEP::HepLorentzVector p2out, bool hel2out, CLHEP::HepLorentzVector p2in, bool hel2in)
   // {
   //   CLHEP::HepLorentzVector q1=p1in-p1out;
   //   CLHEP::HepLorentzVector q2=-(p2in-p2out);
   //   current C1,C2;
   //   j (p1out,hel1out,p1in,hel1in, C1);
   //   j (p2out,hel2out,p2in,hel2in, C2);
 
   //   std::cout << "# From Currents, C1 : ("<<C1[0]<<","<<C1[1]<<","<<C1[2]<<","<<C1[3]<<"\n";
   //   std::cout << "# From Currents, C2 : ("<<C2[0]<<","<<C2[1]<<","<<C2[2]<<","<<C2[3]<<"\n";
 
   //   COM M=cdot(C1,C2);
 
   //   return (M*conj(M)).real()/(q1.m2()*q2.m2());
   // }
 
 } // namespace anonymous
 
 double jM2qQ (CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector p1in, CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in)
 {
   //  std::cerr<<"Current: "<<p1out<<"  "<<p1in<<"  "<<p2out<<"  "<<p2in<<std::endl;
 
   CLHEP::HepLorentzVector q1=p1in-p1out;
   CLHEP::HepLorentzVector q2=-(p2in-p2out);
   //  std::cerr<<"Current:  "<<q1.m2()<<"  "<<q2.m2()<<std::endl;
   current mj1m,mj1p,mj2m,mj2p;
   joi(p1out,true,p1in,true,mj1p);
   joi(p1out,false,p1in,false,mj1m);
   joi(p2out,true,p2in,true,mj2p);
   joi(p2out,false,p2in,false,mj2m);
 
   COM Mmp=cdot(mj1m,mj2p);
   COM Mmm=cdot(mj1m,mj2m);
   COM Mpp=cdot(mj1p,mj2p);
   COM Mpm=cdot(mj1p,mj2m);
 
   double sst=abs2(Mmm)+abs2(Mmp)+abs2(Mpp)+abs2(Mpm);
 
   // Multiply by Cf^2
   return HEJ::C_F*HEJ::C_F*(sst)/(q1.m2()*q2.m2());
 }
 
 double jM2qQbar (CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector p1in, CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in)
 {
   CLHEP::HepLorentzVector q1=p1in-p1out;
   CLHEP::HepLorentzVector q2=-(p2in-p2out);
   current mj1m,mj1p,mj2m,mj2p;
   joi(p1out,true,p1in,true,mj1p);
   joi(p1out,false,p1in,false,mj1m);
   jio(p2in,true,p2out,true,mj2p);
   jio(p2in,false,p2out,false,mj2m);
 
   COM Mmp=cdot(mj1m,mj2p);
   COM Mmm=cdot(mj1m,mj2m);
   COM Mpp=cdot(mj1p,mj2p);
   COM Mpm=cdot(mj1p,mj2m);
 
   double sumsq=abs2(Mmm)+abs2(Mmp)+abs2(Mpp)+abs2(Mpm);
 
   // Multiply by Cf^2
   return C_F*C_F*(sumsq)/(q1.m2()*q2.m2());
 }
 
 double jM2qbarQbar (CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector p1in, CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in)
 {
   CLHEP::HepLorentzVector q1=p1in-p1out;
   CLHEP::HepLorentzVector q2=-(p2in-p2out);
   current mj1m,mj1p,mj2m,mj2p;
   jio(p1in,true,p1out,true,mj1p);
   jio(p1in,false,p1out,false,mj1m);
   jio(p2in,true,p2out,true,mj2p);
   jio(p2in,false,p2out,false,mj2m);
 
   COM Mmp=cdot(mj1m,mj2p);
   COM Mmm=cdot(mj1m,mj2m);
   COM Mpp=cdot(mj1p,mj2p);
   COM Mpm=cdot(mj1p,mj2m);
 
   double sumsq=abs2(Mmm)+abs2(Mmp)+abs2(Mpp)+abs2(Mpm);
 
   // Multiply by Cf^2
   return C_F*C_F*(sumsq)/(q1.m2()*q2.m2());
 }
 
 double jM2qg (CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector p1in, CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in)
 // Calculates the square of the current contractions for qg scattering
 // p1: quark
 // p2: gluon
 {
   CLHEP::HepLorentzVector q1=p1in-p1out;
   CLHEP::HepLorentzVector q2=-(p2in-p2out);
 
   current mj1m,mj1p,mj2m,mj2p;
   joi(p1out,true,p1in,true,mj1p);
   joi(p1out,false,p1in,false,mj1m);
   joi(p2out,true,p2in,true,mj2p);
   joi(p2out,false,p2in,false,mj2m);
 
   COM Mmp=cdot(mj1m,mj2p);
   COM Mmm=cdot(mj1m,mj2m);
   COM Mpp=cdot(mj1p,mj2p);
   COM Mpm=cdot(mj1p,mj2m);
 
   const double K = K_g(p2out, p2in);
 
   // sum of spinor strings ||^2
   double a2Mmp=abs2(Mmp);
   double a2Mmm=abs2(Mmm);
   double a2Mpp=abs2(Mpp);
   double a2Mpm=abs2(Mpm);
   double sst = K/C_A*(a2Mpp+a2Mpm+a2Mmp+a2Mmm);
 
   // Cf*Ca=4
   return C_F*C_A*sst/(q1.m2()*q2.m2());
 
 }
 
 double jM2qbarg (CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector p1in, CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in)
 // Calculates the square of the current contractions for qg scattering
 // p1: quark
 // p2: gluon
 {
   CLHEP::HepLorentzVector q1=p1in-p1out;
   CLHEP::HepLorentzVector q2=-(p2in-p2out);
 
   current mj1m,mj1p,mj2m,mj2p;
   jio(p1in,true,p1out,true,mj1p);
   jio(p1in,false,p1out,false,mj1m);
   joi(p2out,true,p2in,true,mj2p);
   joi(p2out,false,p2in,false,mj2m);
 
   COM Mmp=cdot(mj1m,mj2p);
   COM Mmm=cdot(mj1m,mj2m);
   COM Mpp=cdot(mj1p,mj2p);
   COM Mpm=cdot(mj1p,mj2m);
 
   const double K = K_g(p2out, p2in);
 
   // sum of spinor strings ||^2
   double a2Mmp=abs2(Mmp);
   double a2Mmm=abs2(Mmm);
   double a2Mpp=abs2(Mpp);
   double a2Mpm=abs2(Mpm);
   double sst = K/C_A*(a2Mpp+a2Mpm+a2Mmp+a2Mmm);
 
   // Cf*Ca=4
   return C_F*C_A*sst/(q1.m2()*q2.m2());
 
 }
 
 double jM2gg (CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector p1in, CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in)
 // Calculates the square of the current contractions for gg scattering
 // p1: gluon
 // p2: gluon
 {
   CLHEP::HepLorentzVector q1=p1in-p1out;
   CLHEP::HepLorentzVector q2=-(p2in-p2out);
 
   current mj1m,mj1p,mj2m,mj2p;
   joi(p1out,true,p1in,true,mj1p);
   joi(p1out,false,p1in,false,mj1m);
   joi(p2out,true,p2in,true,mj2p);
   joi(p2out,false,p2in,false,mj2m);
 
   COM Mmp=cdot(mj1m,mj2p);
   COM Mmm=cdot(mj1m,mj2m);
   COM Mpp=cdot(mj1p,mj2p);
   COM Mpm=cdot(mj1p,mj2m);
 
   const double K_g1 = K_g(p1out, p1in);
   const double K_g2 = K_g(p2out, p2in);
 
   // sum of spinor strings ||^2
   double a2Mmp=abs2(Mmp);
   double a2Mmm=abs2(Mmm);
   double a2Mpp=abs2(Mpp);
   double a2Mpm=abs2(Mpm);
   double sst = K_g1/C_A*K_g2/C_A*(a2Mpp+a2Mpm+a2Mmp+a2Mmm);
   // Ca*Ca=9
   return C_A*C_A*sst/(q1.m2()*q2.m2());
 }
 
 namespace {
   /**
    * @brief Higgs vertex contracted with current @param C1 and @param C2
    */
   COM cHdot(const current & C1, const current & C2, const current & q1,
             const current & q2, double mt, bool incBot, double mb)
   {
     if (mt == infinity) {
       return (cdot(C1,C2)*cdot(q1,q2)-cdot(C1,q2)*cdot(C2,q1))/(6*M_PI*HEJ::vev);
     }
     else {
       CLHEP::HepLorentzVector vq1,vq2;
       vq1.set(q1[1].real(),q1[2].real(),q1[3].real(),q1[0].real());
       vq2.set(q2[1].real(),q2[2].real(),q2[3].real(),q2[0].real());
       // first minus sign obtained because of q1-difference to VDD
       // std::cout<<"A1 : " << A1(-vq1,vq2)<<std::endl;
       // std::cout<<"A2 : " << A2(-vq1,vq2)<<std::endl;
       if(!(incBot))
         // Factor is because 4 mt^2 g^2/HEJ::vev A1 -> 16 pi mt^2/HEJ::vev alphas,
         // and we divide by a factor 4 at the amp sqaured level later
         // which I absorb here (i.e. I divide by 2)
         /// @TODO move factor 1/2 from S to |ME|^2 => consistent with general notation
         return 8.*M_PI*mt*mt/HEJ::vev*(-cdot(C1,q2)*cdot(C2,q1)*A1(-vq1,vq2,mt)-cdot(C1,C2)*A2(-vq1,vq2,mt));
       else
         return 8.*M_PI*mt*mt/HEJ::vev*(-cdot(C1,q2)*cdot(C2,q1)*A1(-vq1,vq2,mt)-cdot(C1,C2)*A2(-vq1,vq2,mt))
              + 8.*M_PI*mb*mb/HEJ::vev*(-cdot(C1,q2)*cdot(C2,q1)*A1(-vq1,vq2,mb)-cdot(C1,C2)*A2(-vq1,vq2,mb));
     }
   }
 } // namespace anonymous
 
 double MH2qQ (CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector p1in,
               CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in,
               CLHEP::HepLorentzVector q1, CLHEP::HepLorentzVector q2,
               double mt, bool incBot, double mb)
 {
 //   CLHEP::HepLorentzVector q1=p1in-p1out;
 //   CLHEP::HepLorentzVector q2=-(p2in-p2out);
   current j1p,j1m,j2p,j2m, q1v, q2v;
 
   joi (p1out,true,p1in,true,j1p);
   joi (p1out,false,p1in,false,j1m);
 
   joi (p2out,true,p2in,true,j2p);
   joi (p2out,false,p2in,false,j2m);
 
   to_current(q1, q1v);
   to_current(q2, q2v);
 
   COM Mmp=cHdot(j1m,j2p,q1v,q2v,mt, incBot, mb);
   COM Mmm=cHdot(j1m,j2m,q1v,q2v,mt, incBot, mb);
   COM Mpp=cHdot(j1p,j2p,q1v,q2v,mt, incBot, mb);
   COM Mpm=cHdot(j1p,j2m,q1v,q2v,mt, incBot, mb);
 
   double sst=abs2(Mmp)+abs2(Mmm)+abs2(Mpp)+abs2(Mpm);
   // return (4./3.)*(4./3.)*sst/((p1in-p1out).m2()*(p2in-p2out).m2()*q1.m2()*q2.m2());
   return sst/((p1in-p1out).m2()*(p2in-p2out).m2()*q1.m2()*q2.m2());
 }
 
 double MH2qQbar (CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector p1in, CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in, CLHEP::HepLorentzVector q1, CLHEP::HepLorentzVector q2, double mt, bool incBot, double mb)
 {
 //   CLHEP::HepLorentzVector q1=p1in-p1out;
 //   CLHEP::HepLorentzVector q2=-(p2in-p2out);
   current j1p,j1m,j2p,j2m,q1v,q2v;
 
   joi (p1out,true,p1in,true,j1p);
   joi (p1out,false,p1in,false,j1m);
 
   jio (p2in,true,p2out,true,j2p);
   jio (p2in,false,p2out,false,j2m);
 
   to_current(q1, q1v);
   to_current(q2, q2v);
 
   COM Mmp=cHdot(j1m,j2p,q1v,q2v,mt, incBot, mb);
   COM Mmm=cHdot(j1m,j2m,q1v,q2v,mt, incBot, mb);
   COM Mpp=cHdot(j1p,j2p,q1v,q2v,mt, incBot, mb);
   COM Mpm=cHdot(j1p,j2m,q1v,q2v,mt, incBot, mb);
 
   double sst=abs2(Mmp)+abs2(Mmm)+abs2(Mpp)+abs2(Mpm);
   // return (4./3.)*(4./3.)*sst/((p1in-p1out).m2()*(p2in-p2out).m2()*q1.m2()*q2.m2());
   return sst/((p1in-p1out).m2()*(p2in-p2out).m2()*q1.m2()*q2.m2());
 }
 
 double MH2qbarQ (CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector p1in, CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in, CLHEP::HepLorentzVector q1, CLHEP::HepLorentzVector q2, double mt, bool incBot, double mb)
 {
 //   CLHEP::HepLorentzVector q1=p1in-p1out;
 //   CLHEP::HepLorentzVector q2=-(p2in-p2out);
   current j1p,j1m,j2p,j2m,q1v,q2v;
 
   jio (p1in,true,p1out,true,j1p);
   jio (p1in,false,p1out,false,j1m);
 
   joi (p2out,true,p2in,true,j2p);
   joi (p2out,false,p2in,false,j2m);
 
   to_current(q1, q1v);
   to_current(q2, q2v);
 
   COM Mmp=cHdot(j1m,j2p,q1v,q2v,mt, incBot, mb);
   COM Mmm=cHdot(j1m,j2m,q1v,q2v,mt, incBot, mb);
   COM Mpp=cHdot(j1p,j2p,q1v,q2v,mt, incBot, mb);
   COM Mpm=cHdot(j1p,j2m,q1v,q2v,mt, incBot, mb);
 
   double sst=abs2(Mmp)+abs2(Mmm)+abs2(Mpp)+abs2(Mpm);
   // return (4./3.)*(4./3.)*sst/((p1in-p1out).m2()*(p2in-p2out).m2()*q1.m2()*q2.m2());
   return sst/((p1in-p1out).m2()*(p2in-p2out).m2()*q1.m2()*q2.m2());
 }
 
 double MH2qbarQbar (CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector p1in, CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in, CLHEP::HepLorentzVector q1, CLHEP::HepLorentzVector q2, double mt, bool incBot, double mb)
 {
 //   CLHEP::HepLorentzVector q1=p1in-p1out;
 //   CLHEP::HepLorentzVector q2=-(p2in-p2out);
   current j1p,j1m,j2p,j2m,q1v,q2v;
 
   jio (p1in,true,p1out,true,j1p);
   jio (p1in,false,p1out,false,j1m);
 
   jio (p2in,true,p2out,true,j2p);
   jio (p2in,false,p2out,false,j2m);
 
   to_current(q1, q1v);
   to_current(q2, q2v);
 
   COM Mmp=cHdot(j1m,j2p,q1v,q2v,mt, incBot, mb);
   COM Mmm=cHdot(j1m,j2m,q1v,q2v,mt, incBot, mb);
   COM Mpp=cHdot(j1p,j2p,q1v,q2v,mt, incBot, mb);
   COM Mpm=cHdot(j1p,j2m,q1v,q2v,mt, incBot, mb);
 
   double sst=abs2(Mmp)+abs2(Mmm)+abs2(Mpp)+abs2(Mpm);
   // return (4./3.)*(4./3.)*sst/((p1in-p1out).m2()*(p2in-p2out).m2()*q1.m2()*q2.m2());
   return sst/((p1in-p1out).m2()*(p2in-p2out).m2()*q1.m2()*q2.m2());
 }
 
 double MH2qg (CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector p1in, CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in, CLHEP::HepLorentzVector q1, CLHEP::HepLorentzVector q2, double mt, bool incBot, double mb)
 // q~p1 g~p2 (i.e. ALWAYS p1 for quark, p2 for gluon)
 // should be called with q1 meant to be contracted with p2 in first part of vertex
 // (i.e. if g is backward, q1 is forward)
 {
   current j1p,j1m,j2p,j2m,q1v,q2v;
 
   joi (p1out,true,p1in,true,j1p);
   joi (p1out,false,p1in,false,j1m);
 
   joi (p2out,true,p2in,true,j2p);
   joi (p2out,false,p2in,false,j2m);
 
   to_current(q1, q1v);
   to_current(q2, q2v);
 
   // First, calculate the non-flipping amplitudes:
 
   COM Mpp=cHdot(j1p,j2p,q1v,q2v,mt, incBot, mb);
   COM Mpm=cHdot(j1p,j2m,q1v,q2v,mt, incBot, mb);
   COM Mmp=cHdot(j1m,j2p,q1v,q2v,mt, incBot, mb);
   COM Mmm=cHdot(j1m,j2m,q1v,q2v,mt, incBot, mb);
 
   //cout << "Bits in MH2qg: " << Mpp << " " << Mpm << " " << Mmp << " " << Mmm << endl;
 
   const double K = K_g(p2out, p2in);
 
   double sst=K/C_A*(abs2(Mmp)+abs2(Mmm)+abs2(Mpp)+abs2(Mpm));
 
   // Cf*Ca=4
   // return 4.*sst/((p1in-p1out).m2()*(p2in-p2out).m2()*q1.m2()*q2.m2());
   return sst/((p1in-p1out).m2()*(p2in-p2out).m2()*q1.m2()*q2.m2());
 }
 
 double MH2qbarg (CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector p1in, CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in, CLHEP::HepLorentzVector q1, CLHEP::HepLorentzVector q2, double mt, bool incBot, double mb)
 // qbar~p1 g~p2 (i.e. ALWAYS p1 for anti-quark, p2 for gluon)
 // should be called with q1 meant to be contracted with p2 in first part of vertex
 // (i.e. if g is backward, q1 is forward)
 {
   current j1p,j1m,j2p,j2m,q1v,q2v;
 
   jio (p1in,true,p1out,true,j1p);
   jio (p1in,false,p1out,false,j1m);
 
   joi (p2out,true,p2in,true,j2p);
   joi (p2out,false,p2in,false,j2m);
 
   to_current(q1, q1v);
   to_current(q2, q2v);
 
   // First, calculate the non-flipping amplitudes:
 
   COM amp,amm,apm,app;
   app=cHdot(j1p,j2p,q1v,q2v,mt, incBot, mb);
   apm=cHdot(j1p,j2m,q1v,q2v,mt, incBot, mb);
   amp=cHdot(j1m,j2p,q1v,q2v,mt, incBot, mb);
   amm=cHdot(j1m,j2m,q1v,q2v,mt, incBot, mb);
 
   double MH2sum = abs2(app)+abs2(amm)+abs2(apm)+abs2(amp);
 
   const double K = K_g(p2out, p2in);
   MH2sum*=K/C_A;
 
   // Cf*Ca=4
   // return 4.*MH2sum/((p1in-p1out).m2()*(p2in-p2out).m2()*q1.m2()*q2.m2());
   return MH2sum/((p1in-p1out).m2()*(p2in-p2out).m2()*q1.m2()*q2.m2());
 }
 
 double MH2gg (CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector p1in, CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in, CLHEP::HepLorentzVector q1, CLHEP::HepLorentzVector q2, double mt, bool incBot, double mb)
 // g~p1 g~p2
 // should be called with q1 meant to be contracted with p2 in first part of vertex
 // (i.e. if g is backward, q1 is forward)
 {
   current j1p,j1m,j2p,j2m,q1v,q2v;
 
   joi (p1out,true,p1in,true,j1p);
   joi (p1out,false,p1in,false,j1m);
 
   joi (p2out,true,p2in,true,j2p);
   joi (p2out,false,p2in,false,j2m);
 
   to_current(q1, q1v);
   to_current(q2, q2v);
 
   // First, calculate the non-flipping amplitudes:
 
   COM amp,amm,apm,app;
   app=cHdot(j1p,j2p,q1v,q2v,mt, incBot, mb);
   apm=cHdot(j1p,j2m,q1v,q2v,mt, incBot, mb);
   amp=cHdot(j1m,j2p,q1v,q2v,mt, incBot, mb);
   amm=cHdot(j1m,j2m,q1v,q2v,mt, incBot, mb);
 
   double MH2sum = abs2(app)+abs2(amm)+abs2(apm)+abs2(amp);
 
   const double K_g1 = K_g(p1out, p1in);
   const double K_g2 = K_g(p2out, p2in);
 
   MH2sum*=K_g1/C_A*K_g2/C_A;
 
   // Ca*Ca=9
   // return 9.*MH2sum/((p1in-p1out).m2()*(p2in-p2out).m2()*q1.m2()*q2.m2());
   return MH2sum/((p1in-p1out).m2()*(p2in-p2out).m2()*q1.m2()*q2.m2());
 }
 
 // // Z's stuff
 // void jZ(HLV pin, HLV pout, HLV pem, HLV pep, bool HelPartons, bool HelLeptons, current cur) {
 
 //  // Init current to zero
 //  cur[0] = 0.0;
 //  cur[1] = 0.0;
 //  cur[2] = 0.0;
 //  cur[3] = 0.0;
 
 //  // Temporary variables
 //  COM temp;
 //  current Term_1, Term_2, Term_3, Term_4, J_temp, TempCur1, TempCur2;
 
 //  // Momentum of virtual gluons aroun weak boson emission site
 //  HLV qa = pout + pep + pem;
 //  HLV qb = pin  - pep - pem;
 
 //  double ta = qa.m2();
 //  double tb = qb.m2();
 
 //  // Out-Out currents:
 //  current Em_Ep, Out_Em, Out_Ep;
 
 //  // Other currents:
 //  current Out_In, Em_In, Ep_In;
 
 //  joi(pout, HelPartons, pin, HelPartons, Out_In);
 //  joi(pem,  HelLeptons, pin, HelPartons, Em_In);
 //  joi(pep,  HelLeptons, pin, HelPartons, Ep_In);
 
 //  joo(pem,  HelLeptons, pep, HelLeptons, Em_Ep);
 //  joo(pout, HelPartons, pem, HelLeptons, Out_Em);
 //  joo(pout, HelPartons, pep, HelLeptons, Out_Ep);
 
 //  if (HelLeptons == HelPartons) {
 
 //    temp = 2.0 * cdot(pout, Em_Ep);
 //    cmult(temp / ta, Out_In, Term_1);
 
 //    temp = cdot(Out_Em, Em_Ep);
 //    cmult(temp / ta , Em_In, Term_2);
 
 //    temp = 2.0 * cdot(pin, Em_Ep);
 //    cmult(temp / tb, Out_In, Term_3);
 
 //    temp = -cdot(Ep_In, Em_Ep);
 //    cmult(temp / tb, Out_Ep, Term_4);
 
 //    cadd(Term_1, Term_2, Term_3, Term_4, J_temp);
 
 //    cur[0] = J_temp[0];
 //    cur[1] = J_temp[1];
 //    cur[2] = J_temp[2];
 //    cur[3] = J_temp[3];
 //  }
 
 //  else {
 //    if (HelPartons == true) {
 //      temp = 2.0 * cdot(pout, Em_Ep);
 //      cmult(temp / ta, Out_In, Term_1);
 
 //      joo(pout, true, pep, true, TempCur1);
 //      joi(pep,  true, pin, true, TempCur2);
 
 //      temp = cdot(TempCur1, Em_Ep);
 //      cmult(temp / ta , TempCur2, Term_2);
 
 //      temp = 2.0 * cdot(pin, Em_Ep);
 //      cmult(temp / tb, Out_In, Term_3);
 
 //      joo(pout, true, pem, true, TempCur1);
 //      joi(pem,  true, pin, true, TempCur2);
 
 //      temp = -cdot(TempCur2, Em_Ep);
 //      cmult(temp / tb, TempCur1, Term_4);
 
 //      cadd(Term_1, Term_2, Term_3, Term_4, J_temp);
 
 //      cur[0] = J_temp[0];
 //      cur[1] = J_temp[1];
 //      cur[2] = J_temp[2];
 //      cur[3] = J_temp[3];
 //    }
 
 //    else {
 //      temp = 2.0 * cdot(pout, Em_Ep);
 //      cmult(temp / ta, Out_In, Term_1);
 
 //      joo(pout, false, pep, false, TempCur1);
 //      joi(pep,  false, pin, false, TempCur2);
 
 //      temp = cdot(TempCur1, Em_Ep);
 //      cmult(temp / ta, TempCur2, Term_2);
 
 //      temp = 2.0 * cdot(pin, Em_Ep);
 //      cmult(temp / tb, Out_In, Term_3);
 
 //      joo(pout, false, pem, false, TempCur1);
 //      joi(pem,  false, pin, false, TempCur2);
 
 //      temp = -cdot(TempCur2, Em_Ep);
 //      cmult(temp / tb, TempCur1, Term_4);
 
 //      cadd(Term_1, Term_2, Term_3, Term_4, J_temp);
 
 //      cur[0] = J_temp[0];
 //      cur[1] = J_temp[1];
 //      cur[2] = J_temp[2];
 //      cur[3] = J_temp[3];
 //    }
 
 //  }
 // }
 
 // void jZbar(HLV pin, HLV pout, HLV pem, HLV pep, bool HelPartons, bool HelLeptons, current cur) {
 
 //  // Init current to zero
 //  cur[0] = 0.0;
 //  cur[1] = 0.0;
 //  cur[2] = 0.0;
 //  cur[3] = 0.0;
 
 //  // Temporary variables
 //  COM temp;
 //  current Term_1, Term_2, Term_3, Term_4, J_temp, TempCur1, TempCur2;
 
 //  // Transfered 4-momenta
 //  HLV qa = pout + pep + pem;
 //  HLV qb = pin  - pep - pem;
 
 //  // The square of the transfered 4-momenta
 //  double ta = qa.m2();
 //  double tb = qb.m2();
 
 //  // Out-Out currents:
 //  current Em_Ep, Em_Out, Ep_Out;
 
 //  // In-Out currents:
 //  current In_Out, In_Em, In_Ep;
 
 //  // Safe to use the currents since helicity structure is ok
 //  if (HelPartons == HelLeptons) {
 //    jio(pin, HelPartons, pout, HelPartons, In_Out);
 //    joo(pem, HelLeptons, pep,  HelLeptons, Em_Ep);
 //    jio(pin, HelPartons, pem,  HelLeptons, In_Em);
 //    jio(pin, HelPartons, pep,  HelLeptons, In_Ep);
 //    joo(pem, HelLeptons, pout, HelPartons, Em_Out);
 //    joo(pep, HelLeptons, pout, HelPartons, Ep_Out);
 //  }
 
 //  else {
 //    jio(pin, HelPartons, pout, HelPartons, In_Out);
 //    joo(pem, HelLeptons, pep,  HelLeptons, Em_Ep);
 
 //    In_Em[0] = 0.0;
 //    In_Em[1] = 0.0;
 //    In_Em[2] = 0.0;
 //    In_Em[3] = 0.0;
 
 //    In_Ep[0] = 0.0;
 //    In_Ep[1] = 0.0;
 //    In_Ep[2] = 0.0;
 //    In_Ep[3] = 0.0;
 
 //    Em_Out[0] = 0.0;
 //    Em_Out[1] = 0.0;
 //    Em_Out[2] = 0.0;
 //    Em_Out[3] = 0.0;
 
 //    Ep_Out[0] = 0.0;
 //    Ep_Out[1] = 0.0;
 //    Ep_Out[2] = 0.0;
 //    Ep_Out[3] = 0.0;
 //  }
 
 //  if (HelLeptons == HelPartons) {
 
 //    temp = 2.0 * cdot(pout, Em_Ep);
 //    cmult(temp / ta, In_Out, Term_1);
 
 //    temp = cdot(Ep_Out, Em_Ep);
 //    cmult(temp / ta, In_Ep, Term_2);
 
 //    temp = 2.0 * cdot(pin, Em_Ep);
 //    cmult(temp / tb, In_Out, Term_3);
 
 //    temp = - cdot(In_Em, Em_Ep);
 //    cmult(temp / tb, Em_Out, Term_4);
 
 //    cadd(Term_1, Term_2, Term_3, Term_4, J_temp);
 
 //    cur[0] = J_temp[0];
 //    cur[1] = J_temp[1];
 //    cur[2] = J_temp[2];
 //    cur[3] = J_temp[3];
 //  }
 
 //  else {
 //    if (HelPartons == true) {
 
 //      temp = 2.0 * cdot(pout, Em_Ep);
 //      cmult(temp / ta, In_Out, Term_1);
 
 //      joo(pem, true, pout, true, TempCur1);
 //      jio(pin, true, pem,  true, TempCur2);
 
 //      temp = cdot(TempCur1, Em_Ep);
 //      cmult(temp / ta , TempCur2, Term_2);
 
 //      temp = 2.0 * cdot(pin, Em_Ep);
 //      cmult(temp / tb, In_Out, Term_3);
 
 //      joo(pep, true, pout, true, TempCur1);
 //      jio(pin, true, pep,  true, TempCur2);
 
 //      temp = - cdot(TempCur2, Em_Ep);
 //      cmult(temp / tb, TempCur1, Term_4);
 
 //      cadd(Term_1, Term_2, Term_3, Term_4, J_temp);
 
 //      cur[0] = J_temp[0];
 //      cur[1] = J_temp[1];
 //      cur[2] = J_temp[2];
 //      cur[3] = J_temp[3];
 //    }
 
 //    else {
 
 //      temp = 2.0 * cdot(pout, Em_Ep);
 //      cmult(temp / ta, In_Out, Term_1);
 
 //      joo(pem, false, pout, false, TempCur1);
 //      jio(pin, false, pem,  false, TempCur2);
 
 //      temp = cdot(TempCur1, Em_Ep);
 //      cmult(temp / ta , TempCur2, Term_2);
 
 //      temp = 2.0 * cdot(pin, Em_Ep);
 //      cmult(temp / tb, In_Out, Term_3);
 
 //      joo(pep, false, pout, false, TempCur1);
 //      jio(pin, false, pep,  false, TempCur2);
 
 //      temp = - cdot(TempCur2, Em_Ep);
 //      cmult(temp / tb, TempCur1, Term_4);
 
 //      cadd(Term_1, Term_2, Term_3, Term_4, J_temp);
 
 //      cur[0] = J_temp[0];
 //      cur[1] = J_temp[1];
 //      cur[2] = J_temp[2];
 //      cur[3] = J_temp[3];
 //    }
 //  }
 // }
 
 // // Progagators
 // COM PZ(double s) {
 
 //  double MZ, GammaZ;
 
 //  MZ       = 9.118800e+01;  // Mass of the mediating gauge boson
 //  GammaZ   = 2.441404e+00;  // Z peak width
 
 //  // Return Z Prop value
 //  return 1.0 / (s - MZ * MZ + COM(0.0, 1.0) * GammaZ * MZ);
 // }
 
 // COM PG(double s) {
 //  return 1.0 / s;
 // }
 
 // // Non-gluonic with pa emitting
 // std::vector <double> jMZqQ (HLV pa, HLV pb, HLV p1, HLV p2, HLV pep, HLV pem, std::vector <double> VProducts, std::vector < std::vector <double> > Virtuals, int aptype, int bptype, bool UseVirtuals, bool BottomLineEmit) {
 
 //  std::vector <double> ScaledWeights;
 
 //  double Sum;
 
 //  // Propagator factors
 //  COM PZs = PZ((pep + pem).m2());
 //  COM PGs = PG((pep + pem).m2());
 
 
 //  // Emitting current initialisation
 //  current j1pptop, j1pmtop; // Emission from top line
 //  current j1ppbot, j1pmbot; // Emission from bottom line
 
 //  // Non-emitting current initialisation
 //  current j2ptop, j2mtop;   // Emission from top line
 //  current j2pbot, j2mbot;   // Emission from bottom line
 
 //  // Currents for top emission
 //  // Upper current calculations
 //  // if a is a quark
 //  if (aptype > 0) {
 //    jZ(pa, p1, pem, pep, true,  true,  j1pptop);
 //    jZ(pa, p1, pem, pep, true,  false, j1pmtop);
 //  }
 //  // if a is an antiquark
 //  else {
 //    jZbar(pa, p1, pem, pep, true,  true,  j1pptop);
 //    jZbar(pa, p1, pem, pep, true,  false, j1pmtop);
 //  }
 
 //  // Lower current calculations
 //  // if b is a quark
 //  if (bptype > 0) {
 //    joi(p2, true,  pb, true,  j2ptop);
 //    joi(p2, false, pb, false, j2mtop);
 //  }
 //  // if b is an antiquark
 //  else {
 //    jio(pb, true,  p2, true,  j2ptop);
 //    jio(pb, false, p2, false, j2mtop);
 //  }
 
 //  // Currents for bottom emission
 //  // Lower current calculations
 //  if (bptype > 0) {
 //    jZ(pb, p2, pem, pep, true,  true,  j1ppbot);
 //    jZ(pb, p2, pem, pep, true,  false, j1pmbot);
 //  }
 //  else {
 //    jZbar(pb, p2, pem, pep, true,  true,  j1ppbot);
 //    jZbar(pb, p2, pem, pep, true,  false, j1pmbot);
 //  }
 
 //  // Upper current calculations
 //  if (aptype > 0) {
 //    joi(p1, true,  pa, true,  j2pbot);
 //    joi(p1, false, pa, false, j2mbot);
 //  }
 //  else {
 //    jio(pa, true,  p1, true,  j2pbot);
 //    jio(pa, false, p1, false, j2mbot);
 //  }
 
 //  COM Coeff[2][8];
 
 //  if (!Interference) {
 
 //    double ZCharge_a_P = Zq(aptype, true);
 //    double ZCharge_a_M = Zq(aptype, false);
 //    double ZCharge_b_P = Zq(bptype, true);
 //    double ZCharge_b_M = Zq(bptype, false);
 
 //    if (BottomLineEmit) {
 //      // Emission from top-line quark (pa/p1 line)
 //      Coeff[0][0] = (ZCharge_a_P * Zep * PZs * RWeak + Gq(aptype) * PGs) *      cdot(j1pptop, j2ptop);
 //      Coeff[0][1] = (ZCharge_a_P * Zep * PZs * RWeak + Gq(aptype) * PGs) *      cdot(j1pptop, j2mtop);
 //      Coeff[0][2] = (ZCharge_a_P * Zem * PZs * RWeak + Gq(aptype) * PGs) *      cdot(j1pmtop, j2ptop);
 //      Coeff[0][3] = (ZCharge_a_P * Zem * PZs * RWeak + Gq(aptype) * PGs) *      cdot(j1pmtop, j2mtop);
 //      Coeff[0][4] = (ZCharge_a_M * Zem * PZs * RWeak + Gq(aptype) * PGs) * conj(cdot(j1pptop, j2ptop));
 //      Coeff[0][5] = (ZCharge_a_M * Zem * PZs * RWeak + Gq(aptype) * PGs) * conj(cdot(j1pptop, j2mtop));
 //      Coeff[0][6] = (ZCharge_a_M * Zep * PZs * RWeak + Gq(aptype) * PGs) * conj(cdot(j1pmtop, j2ptop));
 //      Coeff[0][7] = (ZCharge_a_M * Zep * PZs * RWeak + Gq(aptype) * PGs) * conj(cdot(j1pmtop, j2mtop));
 //    }
 
 //    else {
 //      // Emission from bottom-line quark (pb/p2 line)
 //      Coeff[1][0] = (ZCharge_b_P * Zep * PZs * RWeak + Gq(bptype) * PGs) *      cdot(j1ppbot, j2pbot);
 //      Coeff[1][7] = (ZCharge_b_P * Zep * PZs * RWeak + Gq(bptype) * PGs) *      cdot(j1ppbot, j2mbot);
 //      Coeff[1][2] = (ZCharge_b_P * Zem * PZs * RWeak + Gq(bptype) * PGs) *      cdot(j1pmbot, j2pbot);
 //      Coeff[1][5] = (ZCharge_b_P * Zem * PZs * RWeak + Gq(bptype) * PGs) *      cdot(j1pmbot, j2mbot);
 //      Coeff[1][4] = (ZCharge_b_M * Zem * PZs * RWeak + Gq(bptype) * PGs) * conj(cdot(j1ppbot, j2pbot));
 //      Coeff[1][3] = (ZCharge_b_M * Zem * PZs * RWeak + Gq(bptype) * PGs) * conj(cdot(j1ppbot, j2mbot));
 //      Coeff[1][6] = (ZCharge_b_M * Zep * PZs * RWeak + Gq(bptype) * PGs) * conj(cdot(j1pmbot, j2pbot));
 //      Coeff[1][1] = (ZCharge_b_M * Zep * PZs * RWeak + Gq(bptype) * PGs) * conj(cdot(j1pmbot, j2mbot));
 //    }
 //  }
 
 //  // Else calculate all the possiblities
 //  else {
 
 //    double ZCharge_a_P = Zq(aptype, true);
 //    double ZCharge_a_M = Zq(aptype, false);
 //    double ZCharge_b_P = Zq(bptype, true);
 //    double ZCharge_b_M = Zq(bptype, false);
 
 //    // Emission from top-line quark (pa/p1 line)
 //    Coeff[0][0] = (ZCharge_a_P * Zep * PZs * RWeak + Gq(aptype) * PGs) *      cdot(j1pptop, j2ptop);
 //    Coeff[0][1] = (ZCharge_a_P * Zep * PZs * RWeak + Gq(aptype) * PGs) *      cdot(j1pptop, j2mtop);
 //    Coeff[0][2] = (ZCharge_a_P * Zem * PZs * RWeak + Gq(aptype) * PGs) *      cdot(j1pmtop, j2ptop);
 //    Coeff[0][3] = (ZCharge_a_P * Zem * PZs * RWeak + Gq(aptype) * PGs) *      cdot(j1pmtop, j2mtop);
 //    Coeff[0][4] = (ZCharge_a_M * Zem * PZs * RWeak + Gq(aptype) * PGs) * conj(cdot(j1pptop, j2ptop));
 //    Coeff[0][5] = (ZCharge_a_M * Zem * PZs * RWeak + Gq(aptype) * PGs) * conj(cdot(j1pptop, j2mtop));
 //    Coeff[0][6] = (ZCharge_a_M * Zep * PZs * RWeak + Gq(aptype) * PGs) * conj(cdot(j1pmtop, j2ptop));
 //    Coeff[0][7] = (ZCharge_a_M * Zep * PZs * RWeak + Gq(aptype) * PGs) * conj(cdot(j1pmtop, j2mtop));
 
 //    // Emission from bottom-line quark (pb/p2 line)
 //    Coeff[1][0] = (ZCharge_b_P * Zep * PZs * RWeak + Gq(bptype) * PGs) *      cdot(j1ppbot, j2pbot);
 //    Coeff[1][7] = (ZCharge_b_P * Zep * PZs * RWeak + Gq(bptype) * PGs) *      cdot(j1ppbot, j2mbot);
 //    Coeff[1][2] = (ZCharge_b_P * Zem * PZs * RWeak + Gq(bptype) * PGs) *      cdot(j1pmbot, j2pbot);
 //    Coeff[1][5] = (ZCharge_b_P * Zem * PZs * RWeak + Gq(bptype) * PGs) *      cdot(j1pmbot, j2mbot);
 //    Coeff[1][4] = (ZCharge_b_M * Zem * PZs * RWeak + Gq(bptype) * PGs) * conj(cdot(j1ppbot, j2pbot));
 //    Coeff[1][3] = (ZCharge_b_M * Zem * PZs * RWeak + Gq(bptype) * PGs) * conj(cdot(j1ppbot, j2mbot));
 //    Coeff[1][6] = (ZCharge_b_M * Zep * PZs * RWeak + Gq(bptype) * PGs) * conj(cdot(j1pmbot, j2pbot));
 //    Coeff[1][1] = (ZCharge_b_M * Zep * PZs * RWeak + Gq(bptype) * PGs) * conj(cdot(j1pmbot, j2mbot));
 //  }
 
 //  // Find the numbers of scales
 //  int ScaleCount;
 //  #if calcscaleunc
 //    ScaleCount = 20;
 //  #else
 //    ScaleCount = 1;
 //  #endif
 
 //  // For each scale...
 //  for (int j = 0; j < ScaleCount; j++) {
 
 //    Sum = 0.0;
 
 //    // If we want to compare back to the W's code only emit from one quark and only couple to left handed particles
 //    // virtuals arent here since they are calculated and included in weight() call.
 //    if (!Interference) {
 
 //      if (BottomLineEmit) for (int i = 0; i < 8; i++) Sum += abs2(Coeff[1][i]) * VProducts.at(1);
 //      else                for (int i = 0; i < 8; i++) Sum += abs2(Coeff[0][i]) * VProducts.at(0);
 //    }
 
 //    // Else work out the full interference
 //    else {
 
 //      // For the full calculation...
 //      if (UseVirtuals) {
 //        for (int i = 0; i < 8; i++) {
 //          Sum += abs2(Coeff[0][i])  * VProducts.at(0) * Virtuals.at(j).at(0)
 //               + abs2(Coeff[1][i])  * VProducts.at(1) * Virtuals.at(j).at(1)
 //                     + 2.0 * real(Coeff[0][i] * conj(Coeff[1][i])) * VProducts.at(2) * Virtuals.at(j).at(2);
 //        }
 //      }
 
 //      // For the tree level calculation...
 //      else {
 //        for (int i = 0; i < 8; i++) {
 //          Sum += abs2(Coeff[0][i])  * VProducts.at(0)
 //               + abs2(Coeff[1][i])  * VProducts.at(1)
 //        + 2.0 * real(Coeff[0][i] * conj(Coeff[1][i])) * VProducts.at(2);
 //        }
 //      }
 //    }
 
 //    // Add this to the vector to be returned with the other factors of C_A and the helicity sum/average factors.
 //    ScaledWeights.push_back(Sum / 18.0);
 //  }
 
 //  // Return all the scale values
 //  return ScaledWeights;
 // }
 
 // // Semi-gluonic with pa emitting
 // std::vector <double> jMZqg (HLV pa, HLV pb, HLV p1, HLV p2, HLV pep, HLV pem, std::vector <double> VProducts, std::vector < std::vector <double> > Virtuals, int aptype, int bptype, bool UseVirtuals, bool BottomLineEmit) {
 
 //  COM Coeff[8];
 
 //  double Sum;
 
 //  std::vector <double> ScaledWeights;
 
 //  COM PZs = PZ((pep + pem).m2());
 //  COM PGs = PG((pep + pem).m2());
 
 //  // Emitting current initialisation - Emission from top line
 //  current j1pptop, j1pmtop;
 
 //  // Non-emitting current initialisation - Emission from top line
 //  current j2ptop, j2mtop;
 
 //  // Currents for top emission
 //  // Upper current calculations
 //  if (aptype > 0) {
 //    jZ   (pa, p1, pem, pep, true,  true,  j1pptop);
 //    jZ   (pa, p1, pem, pep, true,  false, j1pmtop);
 //  }
 //  else {
 //    jZbar(pa, p1, pem, pep, true,  true,  j1pptop);
 //    jZbar(pa, p1, pem, pep, true,  false, j1pmtop);
 //  }
 
 //  // Lower current calculations
 //  joi(p2, true,  pb, true,  j2ptop);
 //  joi(p2, false, pb, false, j2mtop);
 
 //  // Calculate all the possiblities
 //  double ZCharge_a_P = Zq(aptype, true);
 //  double ZCharge_a_M = Zq(aptype, false);
 
 //  // Emission from top-line quark (pa/p1 line)
 //  Coeff[0] = (ZCharge_a_P * Zep * PZs * RWeak + Gq(aptype) * PGs) *      cdot(j1pptop, j2ptop);
 //  Coeff[1] = (ZCharge_a_P * Zep * PZs * RWeak + Gq(aptype) * PGs) *      cdot(j1pptop, j2mtop);
 //  Coeff[2] = (ZCharge_a_P * Zem * PZs * RWeak + Gq(aptype) * PGs) *      cdot(j1pmtop, j2ptop);
 //  Coeff[3] = (ZCharge_a_P * Zem * PZs * RWeak + Gq(aptype) * PGs) *      cdot(j1pmtop, j2mtop);
 //  Coeff[4] = (ZCharge_a_M * Zem * PZs * RWeak + Gq(aptype) * PGs) * conj(cdot(j1pptop, j2ptop));
 //  Coeff[5] = (ZCharge_a_M * Zem * PZs * RWeak + Gq(aptype) * PGs) * conj(cdot(j1pptop, j2mtop));
 //  Coeff[6] = (ZCharge_a_M * Zep * PZs * RWeak + Gq(aptype) * PGs) * conj(cdot(j1pmtop, j2ptop));
 //  Coeff[7] = (ZCharge_a_M * Zep * PZs * RWeak + Gq(aptype) * PGs) * conj(cdot(j1pmtop, j2mtop));
 
 //  // Calculate gluon colour accelerated factor
 //  double CAMFactor, z;
 
 //  // If b is a forward moving gluon define z (C.F. multiple jets papers)
 //  if (pb.pz() > 0) z = p2.plus()  / pb.plus();
 //  else             z = p2.minus() / pb.minus();
 
 //  CAMFactor = (1.0 - 1.0 / 9.0) / 2.0 * (z + 1.0 / z) + 1.0 / 9.0;
 
 //  // Find the numbers of scales
 //  int ScaleCount;
 //  #if calcscaleunc
 //    ScaleCount = 20;
 //  #else
 //    ScaleCount = 1;
 //  #endif
 
 //  // For each scale...
 //  for (int j = 0; j < ScaleCount; j++) {
 
 //    Sum = 0.0;
 
 //    // If we dont want the interference
 //    if (!Interference) for (int i = 0; i < 8; i++) Sum += abs2(Coeff[i]) * VProducts.at(0);
 
 //    // Else work out the full interference
 //    else {
 //      if (UseVirtuals) {
 //        for (int i = 0; i < 8; i++) Sum += abs2(Coeff[i]) * VProducts.at(0) * Virtuals.at(j).at(0);
 //      }
 //      else {
 //        for (int i = 0; i < 8; i++) Sum += abs2(Coeff[i]) * VProducts.at(0);
 //      }
 //    }
 
 //    // Add this to the vector to be returned with the other factors of C_A, the colour accelerated factor and the helicity sum/average factors.: (4/3)*3/32
 //    ScaledWeights.push_back(CAMFactor * Sum / 8.0);
 //  }
 
 //  return ScaledWeights;
 // }
 
 // // Electroweak Charge Functions
 // double Zq (int PID, bool Helcitiy) {
 
 //  double temp;
 
 //  // Positive Spin
 //  if (Helcitiy == true) {
 //    if (PID ==   1 || PID ==  3 || PID ==  5) temp = (+ 1.0 * stw2 / 3.0) / ctw;
 //    if (PID ==   2 || PID ==  4)              temp = (- 2.0 * stw2 / 3.0) / ctw;
 //    if (PID ==  -1 || PID == -3 || PID == -5) temp = (- 1.0 * stw2 / 3.0) / ctw;
 //    if (PID ==  -2 || PID == -4)              temp = (+ 2.0 * stw2 / 3.0) / ctw;
 
 //    // If electron or positron
 //    if (PID ==  7 || PID == -7) temp = Zep;
 //  }
 
 //  // Negative Spin
 //  else {
 //    if (PID ==  1 || PID ==  3 || PID ==  5) temp = (-0.5 + 1.0 * stw2 / 3.0) / ctw;
 //    if (PID ==  2 || PID ==  4)              temp = ( 0.5 - 2.0 * stw2 / 3.0) / ctw;
 //    if (PID == -1 || PID == -3 || PID == -5) temp = ( 0.5 - 1.0 * stw2 / 3.0) / ctw;
 //    if (PID == -2 || PID == -4)              temp = (-0.5 + 2.0 * stw2 / 3.0) / ctw;
 
 //    // If electron or positron
 //    if (PID ==  7 || PID == -7) temp = Zem;
 
 //  }
 
 //  return temp;
 // }
 
 // double Gq (int PID) {
 
 //  if (!VirtualPhoton) return 0.0;
 
 //  if (PID == -1) return  1.0 * ee / 3.0;
 //  if (PID == -2) return -2.0 * ee / 3.0;
 //  if (PID == -3) return  1.0 * ee / 3.0;
 //  if (PID == -4) return -2.0 * ee / 3.0;
 //  if (PID == -5) return  1.0 * ee / 3.0;
 //  if (PID ==  1) return -1.0 * ee / 3.0;
 //  if (PID ==  2) return  2.0 * ee / 3.0;
 //  if (PID ==  3) return -1.0 * ee / 3.0;
 //  if (PID ==  4) return  2.0 * ee / 3.0;
 //  if (PID ==  5) return -1.0 * ee / 3.0;
 
 //  std::cout << "ERROR! No Electroweak Charge Found at line " << __LINE__ << "..." << std::endl;
 //  return 0.0;
 // }
 
 
 namespace {
 
   //@{
   /// @brief Higgs vertex contracted with one current
 
   CCurrent jH (CLHEP::HepLorentzVector pout, bool helout, CLHEP::HepLorentzVector pin,
               bool helin, CLHEP::HepLorentzVector q1, CLHEP::HepLorentzVector q2,
               double mt, bool incBot, double mb)
   {
 
       CCurrent j2 = joi(pout,helout,pin,helin);
       CCurrent jq2(q2.e(),q2.px(),q2.py(),q2.pz());
 
       if(mt == infinity)
         return ((q1.dot(q2))*j2 - j2.dot(q1)*jq2)/(3*M_PI*HEJ::vev);
       else
       {
         if(incBot)
           return (-16.*M_PI*mb*mb/HEJ::vev*j2.dot(q1)*jq2*A1(-q1,q2,mb)-16.*M_PI*mb*mb/HEJ::vev*j2*A2(-q1,q2,mb))
                + (-16.*M_PI*mt*mt/HEJ::vev*j2.dot(q1)*jq2*A1(-q1,q2,mt)-16.*M_PI*mt*mt/HEJ::vev*j2*A2(-q1,q2,mt));
         else
           return (-16.*M_PI*mt*mt/HEJ::vev*j2.dot(q1)*jq2*A1(-q1,q2,mt)-16.*M_PI*mt*mt/HEJ::vev*j2*A2(-q1,q2,mt));
       }
   }
 
   CCurrent jioH (CLHEP::HepLorentzVector pin, bool helin, CLHEP::HepLorentzVector pout,
                 bool helout, CLHEP::HepLorentzVector q1, CLHEP::HepLorentzVector q2,
                 double mt, bool incBot, double mb)
   {
 
       CCurrent j2 = jio(pin,helin,pout,helout);
       CCurrent jq2(q2.e(),q2.px(),q2.py(),q2.pz());
 
       if(mt == infinity)
         return ((q1.dot(q2))*j2 - j2.dot(q1)*jq2)/(3*M_PI*HEJ::vev);
       else
       {
         if(incBot)
           return (-16.*M_PI*mb*mb/HEJ::vev*j2.dot(q1)*jq2*A1(-q1,q2,mb)-16.*M_PI*mb*mb/HEJ::vev*j2*A2(-q1,q2,mb))
                + (-16.*M_PI*mt*mt/HEJ::vev*j2.dot(q1)*jq2*A1(-q1,q2,mt)-16.*M_PI*mt*mt/HEJ::vev*j2*A2(-q1,q2,mt));
         else
           return (-16.*M_PI*mt*mt/HEJ::vev*j2.dot(q1)*jq2*A1(-q1,q2,mt)-16.*M_PI*mt*mt/HEJ::vev*j2*A2(-q1,q2,mt));
       }
   }
 
   CCurrent jHtop (CLHEP::HepLorentzVector pout, bool helout, CLHEP::HepLorentzVector pin,
                   bool helin, CLHEP::HepLorentzVector q1, CLHEP::HepLorentzVector q2,
                   double mt, bool incBot, double mb)
   {
 
       CCurrent j1 = joi(pout,helout,pin,helin);
       CCurrent jq1(q1.e(),q1.px(),q1.py(),q1.pz());
 
       if(mt == infinity)
         return ((q1.dot(q2))*j1 - j1.dot(q2)*jq1)/(3*M_PI*HEJ::vev);
       else
       {
         if(incBot)
           return (-16.*M_PI*mb*mb/HEJ::vev*j1.dot(q2)*jq1*A1(-q1,q2,mb)-16.*M_PI*mb*mb/HEJ::vev*j1*A2(-q1,q2,mb))
                + (-16.*M_PI*mt*mt/HEJ::vev*j1.dot(q2)*jq1*A1(-q1,q2,mt)-16.*M_PI*mt*mt/HEJ::vev*j1*A2(-q1,q2,mt));
         else
           return (-16.*M_PI*mt*mt/HEJ::vev*j1.dot(q2)*jq1*A1(-q1,q2,mt)-16.*M_PI*mt*mt/HEJ::vev*j1*A2(-q1,q2,mt));
       }
   }
 
 
   CCurrent jioHtop (CLHEP::HepLorentzVector pin, bool helin, CLHEP::HepLorentzVector pout,
                     bool helout, CLHEP::HepLorentzVector q1, CLHEP::HepLorentzVector q2,
                     double mt, bool incBot, double mb)
   {
 
       CCurrent j1 = jio(pin,helin,pout,helout);
       CCurrent jq1(q1.e(),q1.px(),q1.py(),q1.pz());
 
       if(mt == infinity)
         return ((q1.dot(q2))*j1 - j1.dot(q2)*jq1)/(3*M_PI*HEJ::vev);
       else
       {
         if(incBot)
           return (-16.*M_PI*mb*mb/HEJ::vev*j1.dot(q2)*jq1*A1(-q1,q2,mb)-16.*M_PI*mb*mb/HEJ::vev*j1*A2(-q1,q2,mb))
                + (-16.*M_PI*mt*mt/HEJ::vev*j1.dot(q2)*jq1*A1(-q1,q2,mt)-16.*M_PI*mt*mt/HEJ::vev*j1*A2(-q1,q2,mt));
         else
           return (-16.*M_PI*mt*mt/HEJ::vev*j1.dot(q2)*jq1*A1(-q1,q2,mt)-16.*M_PI*mt*mt/HEJ::vev*j1*A2(-q1,q2,mt));
       }
   }
   //@}
 } // namespace anonymous
 
 double jM2unogqHQ (CLHEP::HepLorentzVector pg, CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector p1in, CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in, CLHEP::HepLorentzVector qH1, CLHEP::HepLorentzVector qH2, double mt, bool incBot, double mb)
 {
     //  This construction is taking rapidity order: pg > p1out >> p2out
     //  std::cerr<<"This Uno Current: "<<p1out<<"  "<<p1in<<"  "<<p2out<<"  "<<p2in<<"  "<<pg<<std::endl;
 
     CLHEP::HepLorentzVector q1=p1in-p1out;  // Top End
     CLHEP::HepLorentzVector q2=-(p2in-p2out);   // Bottom End
     CLHEP::HepLorentzVector qg=p1in-p1out-pg;  // Extra bit post-gluon
 
     //  std::cerr<<"Current:  "<<q1.m2()<<"  "<<q2.m2()<<std::endl;
     CCurrent mj1m,mj1p,mj2m,mj2p,mjH2m,mjH2p;
     mj1p=joi(p1out,true,p1in,true);
     mj1m=joi(p1out,false,p1in,false);
     mjH2p=jH(p2out,true,p2in,true,qH1,qH2, mt, incBot, mb);
     mjH2m=jH(p2out,false,p2in,false,qH1,qH2, mt, incBot, mb);
 
     // Dot products of these which occur again and again
     COM MHmp=mj1m.dot(mjH2p);  // And now for the Higgs ones
     COM MHmm=mj1m.dot(mjH2m);
     COM MHpp=mj1p.dot(mjH2p);
     COM MHpm=mj1p.dot(mjH2m);
 
     //  std::cout<< p1out.rapidity() << "  " << p2out.rapidity()<< "  " << qH1 << "  " << qH2 << "\n" <<MHmm << "  " << MHmp << "  "  << MHpm << "  " << MHpp << std::endl;
 
     // Currents with pg
     CCurrent jgam,jgap,j2gm,j2gp;
     j2gp=joo(p1out,true,pg,true);
     j2gm=joo(p1out,false,pg,false);
     jgap=joi(pg,true,p1in,true);
     jgam=joi(pg,false,p1in,false);
 
     CCurrent qsum(q1+qg);
 
     CCurrent Lmp,Lmm,Lpp,Lpm,U1mp,U1mm,U1pp,U1pm,U2mp,U2mm,U2pp,U2pm,p2o(p2out),p2i(p2in);
     CCurrent p1o(p1out);
     CCurrent p1i(p1in);
 
     Lmm=(qsum*(MHmm) + (-2.*mjH2m.dot(pg))*mj1m+2.*mj1m.dot(pg)*mjH2m+(p2o/pg.dot(p2out) + p2i/pg.dot(p2in))*(qg.m2()*MHmm/2.))/q1.m2();
     Lmp=(qsum*(MHmp) + (-2.*mjH2p.dot(pg))*mj1m+2.*mj1m.dot(pg)*mjH2p+(p2o/pg.dot(p2out) + p2i/pg.dot(p2in))*(qg.m2()*MHmp/2.))/q1.m2();
     Lpm=(qsum*(MHpm) + (-2.*mjH2m.dot(pg))*mj1p+2.*mj1p.dot(pg)*mjH2m+(p2o/pg.dot(p2out) + p2i/pg.dot(p2in))*(qg.m2()*MHpm/2.))/q1.m2();
     Lpp=(qsum*(MHpp) + (-2.*mjH2p.dot(pg))*mj1p+2.*mj1p.dot(pg)*mjH2p+(p2o/pg.dot(p2out) + p2i/pg.dot(p2in))*(qg.m2()*MHpp/2.))/q1.m2();
 
     U1mm=(jgam.dot(mjH2m)*j2gm+2.*p1o*MHmm)/(p1out+pg).m2();
     U1mp=(jgam.dot(mjH2p)*j2gm+2.*p1o*MHmp)/(p1out+pg).m2();
     U1pm=(jgap.dot(mjH2m)*j2gp+2.*p1o*MHpm)/(p1out+pg).m2();
     U1pp=(jgap.dot(mjH2p)*j2gp+2.*p1o*MHpp)/(p1out+pg).m2();
     U2mm=((-1.)*j2gm.dot(mjH2m)*jgam+2.*p1i*MHmm)/(p1in-pg).m2();
     U2mp=((-1.)*j2gm.dot(mjH2p)*jgam+2.*p1i*MHmp)/(p1in-pg).m2();
     U2pm=((-1.)*j2gp.dot(mjH2m)*jgap+2.*p1i*MHpm)/(p1in-pg).m2();
     U2pp=((-1.)*j2gp.dot(mjH2p)*jgap+2.*p1i*MHpp)/(p1in-pg).m2();
 
     const double cf=HEJ::C_F;
     double amm,amp,apm,app;
 
     amm=cf*(2.*vre(Lmm-U1mm,Lmm+U2mm))+2.*cf*cf/3.*vabs2(U1mm+U2mm);
     amp=cf*(2.*vre(Lmp-U1mp,Lmp+U2mp))+2.*cf*cf/3.*vabs2(U1mp+U2mp);
     apm=cf*(2.*vre(Lpm-U1pm,Lpm+U2pm))+2.*cf*cf/3.*vabs2(U1pm+U2pm);
     app=cf*(2.*vre(Lpp-U1pp,Lpp+U2pp))+2.*cf*cf/3.*vabs2(U1pp+U2pp);
     double ampsq=-(amm+amp+apm+app)/(q2.m2()*qH2.m2());
 
   // if ((vabs2(Lmm-U1mm+U2mm)+vabs2(Lmm)-vabs2(U1mm)-vabs2(U2mm))/(2*vre(Lmm-U1mm,Lmm+U2mm)) > 1.0000001)
   //   std::cout << " Big Problem!! " << vabs2(Lmm-U1mm+U2mm)+vabs2(Lmm)-vabs2(U1mm)-vabs2(U2mm) << "  " << 2*vre(Lmm-U1mm,Lmm+U2mm) << std::endl;
   // if ((vabs2(Lmm-U1mm+U2mm)+vabs2(Lmm)-vabs2(U1mm)-vabs2(U2mm))/(2*vre(Lmm-U1mm,Lmm+U2mm)) < 0.9999999)
   //   std::cout << " Big Problem!! " << vabs2(Lmm-U1mm+U2mm)+vabs2(Lmm)-vabs2(U1mm)-vabs2(U2mm) << "  " << 2*vre(Lmm-U1mm,Lmm+U2mm) << std::endl;
 
     // Now add the t-channels for the Higgs
     double th=qH1.m2()*qg.m2();
     ampsq/=th;
     ampsq/=16.;
     ampsq*=HEJ::C_F*HEJ::C_F/HEJ::C_A/HEJ::C_A;  // Factor of (Cf/Ca) for each quark to match MH2qQ.
     //Higgs coupling is included in Hjets.C
 
     return ampsq;
 }
 
 double jM2unogqbarHQ (CLHEP::HepLorentzVector pg, CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector p1in, CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in, CLHEP::HepLorentzVector qH1, CLHEP::HepLorentzVector qH2, double mt, bool incBot, double mb)
 {
     //  This construction is taking rapidity order: pg > p1out >> p2out
     //  std::cerr<<"This Uno Current: "<<p1out<<"  "<<p1in<<"  "<<p2out<<"  "<<p2in<<"  "<<pg<<std::endl;
 
     CLHEP::HepLorentzVector q1=p1in-p1out;  // Top End
     CLHEP::HepLorentzVector q2=-(p2in-p2out);   // Bottom End
     CLHEP::HepLorentzVector qg=p1in-p1out-pg;  // Extra bit post-gluon
 
     //  std::cerr<<"Current:  "<<q1.m2()<<"  "<<q2.m2()<<std::endl;
     CCurrent mj1m,mj1p,mj2m,mj2p,mjH2m,mjH2p;
     mj1p=jio(p1in,true,p1out,true);
     mj1m=jio(p1in,false,p1out,false);
     mjH2p=jH(p2out,true,p2in,true,qH1,qH2, mt, incBot, mb);
     mjH2m=jH(p2out,false,p2in,false,qH1,qH2, mt, incBot, mb);
 
     // Dot products of these which occur again and again
     COM MHmp=mj1m.dot(mjH2p);  // And now for the Higgs ones
     COM MHmm=mj1m.dot(mjH2m);
     COM MHpp=mj1p.dot(mjH2p);
     COM MHpm=mj1p.dot(mjH2m);
 
     // Currents with pg
     CCurrent jgam,jgap,j2gm,j2gp;
     j2gp=joo(pg,true,p1out,true);
     j2gm=joo(pg,false,p1out,false);
     jgap=jio(p1in,true,pg,true);
     jgam=jio(p1in,false,pg,false);
 
     CCurrent qsum(q1+qg);
 
     CCurrent Lmp,Lmm,Lpp,Lpm,U1mp,U1mm,U1pp,U1pm,U2mp,U2mm,U2pp,U2pm,p2o(p2out),p2i(p2in);
     CCurrent p1o(p1out);
     CCurrent p1i(p1in);
 
     Lmm=(qsum*(MHmm) + (-2.*mjH2m.dot(pg))*mj1m+2.*mj1m.dot(pg)*mjH2m+(p2o/pg.dot(p2out) + p2i/pg.dot(p2in))*(qg.m2()*MHmm/2.))/q1.m2();
     Lmp=(qsum*(MHmp) + (-2.*mjH2p.dot(pg))*mj1m+2.*mj1m.dot(pg)*mjH2p+(p2o/pg.dot(p2out) + p2i/pg.dot(p2in))*(qg.m2()*MHmp/2.))/q1.m2();
     Lpm=(qsum*(MHpm) + (-2.*mjH2m.dot(pg))*mj1p+2.*mj1p.dot(pg)*mjH2m+(p2o/pg.dot(p2out) + p2i/pg.dot(p2in))*(qg.m2()*MHpm/2.))/q1.m2();
     Lpp=(qsum*(MHpp) + (-2.*mjH2p.dot(pg))*mj1p+2.*mj1p.dot(pg)*mjH2p+(p2o/pg.dot(p2out) + p2i/pg.dot(p2in))*(qg.m2()*MHpp/2.))/q1.m2();
 
     U1mm=(jgam.dot(mjH2m)*j2gm+2.*p1o*MHmm)/(p1out+pg).m2();
     U1mp=(jgam.dot(mjH2p)*j2gm+2.*p1o*MHmp)/(p1out+pg).m2();
     U1pm=(jgap.dot(mjH2m)*j2gp+2.*p1o*MHpm)/(p1out+pg).m2();
     U1pp=(jgap.dot(mjH2p)*j2gp+2.*p1o*MHpp)/(p1out+pg).m2();
     U2mm=((-1.)*j2gm.dot(mjH2m)*jgam+2.*p1i*MHmm)/(p1in-pg).m2();
     U2mp=((-1.)*j2gm.dot(mjH2p)*jgam+2.*p1i*MHmp)/(p1in-pg).m2();
     U2pm=((-1.)*j2gp.dot(mjH2m)*jgap+2.*p1i*MHpm)/(p1in-pg).m2();
     U2pp=((-1.)*j2gp.dot(mjH2p)*jgap+2.*p1i*MHpp)/(p1in-pg).m2();
 
     const double cf=HEJ::C_F;
     double amm,amp,apm,app;
 
     amm=cf*(2.*vre(Lmm-U1mm,Lmm+U2mm))+2.*cf*cf/3.*vabs2(U1mm+U2mm);
     amp=cf*(2.*vre(Lmp-U1mp,Lmp+U2mp))+2.*cf*cf/3.*vabs2(U1mp+U2mp);
     apm=cf*(2.*vre(Lpm-U1pm,Lpm+U2pm))+2.*cf*cf/3.*vabs2(U1pm+U2pm);
     app=cf*(2.*vre(Lpp-U1pp,Lpp+U2pp))+2.*cf*cf/3.*vabs2(U1pp+U2pp);
     double ampsq=-(amm+amp+apm+app)/(q2.m2()*qH2.m2());
 
   // if ((vabs2(Lmm-U1mm+U2mm)+vabs2(Lmm)-vabs2(U1mm)-vabs2(U2mm))/(2*vre(Lmm-U1mm,Lmm+U2mm)) > 1.0000001)
   //   std::cout << " Big Problem!! " << vabs2(Lmm-U1mm+U2mm)+vabs2(Lmm)-vabs2(U1mm)-vabs2(U2mm) << "  " << 2*vre(Lmm-U1mm,Lmm+U2mm) << std::endl;
   // if ((vabs2(Lmm-U1mm+U2mm)+vabs2(Lmm)-vabs2(U1mm)-vabs2(U2mm))/(2*vre(Lmm-U1mm,Lmm+U2mm)) < 0.9999999)
   //   std::cout << " Big Problem!! " << vabs2(Lmm-U1mm+U2mm)+vabs2(Lmm)-vabs2(U1mm)-vabs2(U2mm) << "  " << 2*vre(Lmm-U1mm,Lmm+U2mm) << std::endl;
 
     // Now add the t-channels for the Higgs
     double th=qH1.m2()*qg.m2();
     ampsq/=th;
     ampsq/=16.;
     ampsq*=4.*4./(9.*9.);  // Factor of (Cf/Ca) for each quark to match MH2qQ.
     //Higgs coupling is included in Hjets.C
 
     return ampsq;
 }
 
 double jM2unogqHQbar (CLHEP::HepLorentzVector pg, CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector p1in, CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in, CLHEP::HepLorentzVector qH1, CLHEP::HepLorentzVector qH2, double mt, bool incBot, double mb)
 {
     //  This construction is taking rapidity order: pg > p1out >> p2out
     //  std::cerr<<"This Uno Current: "<<p1out<<"  "<<p1in<<"  "<<p2out<<"  "<<p2in<<"  "<<pg<<std::endl;
 
     CLHEP::HepLorentzVector q1=p1in-p1out;  // Top End
     CLHEP::HepLorentzVector q2=-(p2in-p2out);   // Bottom End
     CLHEP::HepLorentzVector qg=p1in-p1out-pg;  // Extra bit post-gluon
 
     //  std::cerr<<"Current:  "<<q1.m2()<<"  "<<q2.m2()<<std::endl;
     CCurrent mj1m,mj1p,mj2m,mj2p,mjH2m,mjH2p;
     mj1p=joi(p1out,true,p1in,true);
     mj1m=joi(p1out,false,p1in,false);
     mjH2p=jioH(p2in,true,p2out,true,qH1,qH2, mt, incBot, mb);
     mjH2m=jioH(p2in,false,p2out,false,qH1,qH2, mt, incBot, mb);
 
     // Dot products of these which occur again and again
     COM MHmp=mj1m.dot(mjH2p);  // And now for the Higgs ones
     COM MHmm=mj1m.dot(mjH2m);
     COM MHpp=mj1p.dot(mjH2p);
     COM MHpm=mj1p.dot(mjH2m);
 
     // Currents with pg
     CCurrent jgam,jgap,j2gm,j2gp;
     j2gp=joo(p1out,true,pg,true);
     j2gm=joo(p1out,false,pg,false);
     jgap=joi(pg,true,p1in,true);
     jgam=joi(pg,false,p1in,false);
 
     CCurrent qsum(q1+qg);
 
     CCurrent Lmp,Lmm,Lpp,Lpm,U1mp,U1mm,U1pp,U1pm,U2mp,U2mm,U2pp,U2pm,p2o(p2out),p2i(p2in);
     CCurrent p1o(p1out);
     CCurrent p1i(p1in);
 
     Lmm=(qsum*(MHmm) + (-2.*mjH2m.dot(pg))*mj1m+2.*mj1m.dot(pg)*mjH2m+(p2o/pg.dot(p2out) + p2i/pg.dot(p2in))*(qg.m2()*MHmm/2.))/q1.m2();
     Lmp=(qsum*(MHmp) + (-2.*mjH2p.dot(pg))*mj1m+2.*mj1m.dot(pg)*mjH2p+(p2o/pg.dot(p2out) + p2i/pg.dot(p2in))*(qg.m2()*MHmp/2.))/q1.m2();
     Lpm=(qsum*(MHpm) + (-2.*mjH2m.dot(pg))*mj1p+2.*mj1p.dot(pg)*mjH2m+(p2o/pg.dot(p2out) + p2i/pg.dot(p2in))*(qg.m2()*MHpm/2.))/q1.m2();
     Lpp=(qsum*(MHpp) + (-2.*mjH2p.dot(pg))*mj1p+2.*mj1p.dot(pg)*mjH2p+(p2o/pg.dot(p2out) + p2i/pg.dot(p2in))*(qg.m2()*MHpp/2.))/q1.m2();
 
     U1mm=(jgam.dot(mjH2m)*j2gm+2.*p1o*MHmm)/(p1out+pg).m2();
     U1mp=(jgam.dot(mjH2p)*j2gm+2.*p1o*MHmp)/(p1out+pg).m2();
     U1pm=(jgap.dot(mjH2m)*j2gp+2.*p1o*MHpm)/(p1out+pg).m2();
     U1pp=(jgap.dot(mjH2p)*j2gp+2.*p1o*MHpp)/(p1out+pg).m2();
     U2mm=((-1.)*j2gm.dot(mjH2m)*jgam+2.*p1i*MHmm)/(p1in-pg).m2();
     U2mp=((-1.)*j2gm.dot(mjH2p)*jgam+2.*p1i*MHmp)/(p1in-pg).m2();
     U2pm=((-1.)*j2gp.dot(mjH2m)*jgap+2.*p1i*MHpm)/(p1in-pg).m2();
     U2pp=((-1.)*j2gp.dot(mjH2p)*jgap+2.*p1i*MHpp)/(p1in-pg).m2();
 
     const double cf=HEJ::C_F;
     double amm,amp,apm,app;
 
     amm=cf*(2.*vre(Lmm-U1mm,Lmm+U2mm))+2.*cf*cf/3.*vabs2(U1mm+U2mm);
     amp=cf*(2.*vre(Lmp-U1mp,Lmp+U2mp))+2.*cf*cf/3.*vabs2(U1mp+U2mp);
     apm=cf*(2.*vre(Lpm-U1pm,Lpm+U2pm))+2.*cf*cf/3.*vabs2(U1pm+U2pm);
     app=cf*(2.*vre(Lpp-U1pp,Lpp+U2pp))+2.*cf*cf/3.*vabs2(U1pp+U2pp);
     double ampsq=-(amm+amp+apm+app)/(q2.m2()*qH2.m2());
 
   // if ((vabs2(Lmm-U1mm+U2mm)+vabs2(Lmm)-vabs2(U1mm)-vabs2(U2mm))/(2*vre(Lmm-U1mm,Lmm+U2mm)) > 1.0000001)
   //   std::cout << " Big Problem!! " << vabs2(Lmm-U1mm+U2mm)+vabs2(Lmm)-vabs2(U1mm)-vabs2(U2mm) << "  " << 2*vre(Lmm-U1mm,Lmm+U2mm) << std::endl;
   // if ((vabs2(Lmm-U1mm+U2mm)+vabs2(Lmm)-vabs2(U1mm)-vabs2(U2mm))/(2*vre(Lmm-U1mm,Lmm+U2mm)) < 0.9999999)
   //   std::cout << " Big Problem!! " << vabs2(Lmm-U1mm+U2mm)+vabs2(Lmm)-vabs2(U1mm)-vabs2(U2mm) << "  " << 2*vre(Lmm-U1mm,Lmm+U2mm) << std::endl;
 
     // Now add the t-channels for the Higgs
     double th=qH1.m2()*qg.m2();
     ampsq/=th;
     ampsq/=16.;
     ampsq*=4.*4./(9.*9.);  // Factor of (Cf/Ca) for each quark to match MH2qQ.
     //Higgs coupling is included in Hjets.C
 
     return ampsq;
 }
 
 double jM2unogqbarHQbar (CLHEP::HepLorentzVector pg, CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector p1in, CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in, CLHEP::HepLorentzVector qH1, CLHEP::HepLorentzVector qH2, double mt, bool incBot, double mb)
 {
     //  This construction is taking rapidity order: pg > p1out >> p2out
     //  std::cerr<<"This Uno Current: "<<p1out<<"  "<<p1in<<"  "<<p2out<<"  "<<p2in<<"  "<<pg<<std::endl;
 
     CLHEP::HepLorentzVector q1=p1in-p1out;  // Top End
     CLHEP::HepLorentzVector q2=-(p2in-p2out);   // Bottom End
     CLHEP::HepLorentzVector qg=p1in-p1out-pg;  // Extra bit post-gluon
 
     //  std::cerr<<"Current:  "<<q1.m2()<<"  "<<q2.m2()<<std::endl;
     CCurrent mj1m,mj1p,mj2m,mj2p,mjH2m,mjH2p;
     mj1p=jio(p1in,true,p1out,true);
     mj1m=jio(p1in,false,p1out,false);
     mjH2p=jioH(p2in,true,p2out,true,qH1,qH2, mt, incBot, mb);
     mjH2m=jioH(p2in,false,p2out,false,qH1,qH2, mt, incBot, mb);
 
     // Dot products of these which occur again and again
     COM MHmp=mj1m.dot(mjH2p);  // And now for the Higgs ones
     COM MHmm=mj1m.dot(mjH2m);
     COM MHpp=mj1p.dot(mjH2p);
     COM MHpm=mj1p.dot(mjH2m);
 
     // Currents with pg
     CCurrent jgam,jgap,j2gm,j2gp;
     j2gp=joo(pg,true,p1out,true);
     j2gm=joo(pg,false,p1out,false);
     jgap=jio(p1in,true,pg,true);
     jgam=jio(p1in,false,pg,false);
 
     CCurrent qsum(q1+qg);
 
     CCurrent Lmp,Lmm,Lpp,Lpm,U1mp,U1mm,U1pp,U1pm,U2mp,U2mm,U2pp,U2pm,p2o(p2out),p2i(p2in);
     CCurrent p1o(p1out);
     CCurrent p1i(p1in);
 
     Lmm=(qsum*(MHmm) + (-2.*mjH2m.dot(pg))*mj1m+2.*mj1m.dot(pg)*mjH2m+(p2o/pg.dot(p2out) + p2i/pg.dot(p2in))*(qg.m2()*MHmm/2.))/q1.m2();
     Lmp=(qsum*(MHmp) + (-2.*mjH2p.dot(pg))*mj1m+2.*mj1m.dot(pg)*mjH2p+(p2o/pg.dot(p2out) + p2i/pg.dot(p2in))*(qg.m2()*MHmp/2.))/q1.m2();
     Lpm=(qsum*(MHpm) + (-2.*mjH2m.dot(pg))*mj1p+2.*mj1p.dot(pg)*mjH2m+(p2o/pg.dot(p2out) + p2i/pg.dot(p2in))*(qg.m2()*MHpm/2.))/q1.m2();
     Lpp=(qsum*(MHpp) + (-2.*mjH2p.dot(pg))*mj1p+2.*mj1p.dot(pg)*mjH2p+(p2o/pg.dot(p2out) + p2i/pg.dot(p2in))*(qg.m2()*MHpp/2.))/q1.m2();
 
     U1mm=(jgam.dot(mjH2m)*j2gm+2.*p1o*MHmm)/(p1out+pg).m2();
     U1mp=(jgam.dot(mjH2p)*j2gm+2.*p1o*MHmp)/(p1out+pg).m2();
     U1pm=(jgap.dot(mjH2m)*j2gp+2.*p1o*MHpm)/(p1out+pg).m2();
     U1pp=(jgap.dot(mjH2p)*j2gp+2.*p1o*MHpp)/(p1out+pg).m2();
     U2mm=((-1.)*j2gm.dot(mjH2m)*jgam+2.*p1i*MHmm)/(p1in-pg).m2();
     U2mp=((-1.)*j2gm.dot(mjH2p)*jgam+2.*p1i*MHmp)/(p1in-pg).m2();
     U2pm=((-1.)*j2gp.dot(mjH2m)*jgap+2.*p1i*MHpm)/(p1in-pg).m2();
     U2pp=((-1.)*j2gp.dot(mjH2p)*jgap+2.*p1i*MHpp)/(p1in-pg).m2();
 
     const double cf=HEJ::C_F;
     double amm,amp,apm,app;
 
     amm=cf*(2.*vre(Lmm-U1mm,Lmm+U2mm))+2.*cf*cf/3.*vabs2(U1mm+U2mm);
     amp=cf*(2.*vre(Lmp-U1mp,Lmp+U2mp))+2.*cf*cf/3.*vabs2(U1mp+U2mp);
     apm=cf*(2.*vre(Lpm-U1pm,Lpm+U2pm))+2.*cf*cf/3.*vabs2(U1pm+U2pm);
     app=cf*(2.*vre(Lpp-U1pp,Lpp+U2pp))+2.*cf*cf/3.*vabs2(U1pp+U2pp);
     double ampsq=-(amm+amp+apm+app)/(q2.m2()*qH2.m2());
 
   // if ((vabs2(Lmm-U1mm+U2mm)+vabs2(Lmm)-vabs2(U1mm)-vabs2(U2mm))/(2*vre(Lmm-U1mm,Lmm+U2mm)) > 1.0000001)
   //   std::cout << " Big Problem!! " << vabs2(Lmm-U1mm+U2mm)+vabs2(Lmm)-vabs2(U1mm)-vabs2(U2mm) << "  " << 2*vre(Lmm-U1mm,Lmm+U2mm) << std::endl;
   // if ((vabs2(Lmm-U1mm+U2mm)+vabs2(Lmm)-vabs2(U1mm)-vabs2(U2mm))/(2*vre(Lmm-U1mm,Lmm+U2mm)) < 0.9999999)
   //   std::cout << " Big Problem!! " << vabs2(Lmm-U1mm+U2mm)+vabs2(Lmm)-vabs2(U1mm)-vabs2(U2mm) << "  " << 2*vre(Lmm-U1mm,Lmm+U2mm) << std::endl;
 
     // Now add the t-channels for the Higgs
     double th=qH1.m2()*qg.m2();
     ampsq/=th;
     ampsq/=16.;
     //Higgs coupling is included in Hjets.C
     ampsq*=4.*4./(9.*9.);  // Factor of (Cf/Ca) for each quark to match MH2qQ.
 
     return ampsq;
 }
 
 double jM2unogqHg (CLHEP::HepLorentzVector pg, CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector p1in, CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in, CLHEP::HepLorentzVector qH1, CLHEP::HepLorentzVector qH2, double mt, bool incBot, double mb)
 {
     //  This construction is taking rapidity order: pg > p1out >> p2out
     //  std::cerr<<"This Uno Current: "<<p1out<<"  "<<p1in<<"  "<<p2out<<"  "<<p2in<<"  "<<pg<<std::endl;
 
     CLHEP::HepLorentzVector q1=p1in-p1out;  // Top End
     CLHEP::HepLorentzVector q2=-(p2in-p2out);   // Bottom End
     CLHEP::HepLorentzVector qg=p1in-p1out-pg;  // Extra bit post-gluon
 
     CCurrent mj1m,mj1p,mj2m,mj2p,mjH2m,mjH2p;
     mj1p=joi(p1out,true,p1in,true);
     mj1m=joi(p1out,false,p1in,false);
     mjH2p=jH(p2out,true,p2in,true,qH1,qH2, mt, incBot, mb);
     mjH2m=jH(p2out,false,p2in,false,qH1,qH2, mt, incBot, mb);
 
     // Dot products of these which occur again and again
     COM MHmp=mj1m.dot(mjH2p);  // And now for the Higgs ones
     COM MHmm=mj1m.dot(mjH2m);
     COM MHpp=mj1p.dot(mjH2p);
     COM MHpm=mj1p.dot(mjH2m);
 
     // Currents with pg
     CCurrent jgam,jgap,j2gm,j2gp;
     j2gp=joo(p1out,true,pg,true);
     j2gm=joo(p1out,false,pg,false);
     jgap=joi(pg,true,p1in,true);
     jgam=joi(pg,false,p1in,false);
 
     CCurrent qsum(q1+qg);
 
     CCurrent Lmp,Lmm,Lpp,Lpm,U1mp,U1mm,U1pp,U1pm,U2mp,U2mm,U2pp,U2pm,p2o(p2out),p2i(p2in);
     CCurrent p1o(p1out);
     CCurrent p1i(p1in);
 
     Lmm=(qsum*(MHmm) + (-2.*mjH2m.dot(pg))*mj1m+2.*mj1m.dot(pg)*mjH2m+(p2o/pg.dot(p2out) + p2i/pg.dot(p2in))*(qg.m2()*MHmm/2.))/q1.m2();
     Lmp=(qsum*(MHmp) + (-2.*mjH2p.dot(pg))*mj1m+2.*mj1m.dot(pg)*mjH2p+(p2o/pg.dot(p2out) + p2i/pg.dot(p2in))*(qg.m2()*MHmp/2.))/q1.m2();
     Lpm=(qsum*(MHpm) + (-2.*mjH2m.dot(pg))*mj1p+2.*mj1p.dot(pg)*mjH2m+(p2o/pg.dot(p2out) + p2i/pg.dot(p2in))*(qg.m2()*MHpm/2.))/q1.m2();
     Lpp=(qsum*(MHpp) + (-2.*mjH2p.dot(pg))*mj1p+2.*mj1p.dot(pg)*mjH2p+(p2o/pg.dot(p2out) + p2i/pg.dot(p2in))*(qg.m2()*MHpp/2.))/q1.m2();
 
     U1mm=(jgam.dot(mjH2m)*j2gm+2.*p1o*MHmm)/(p1out+pg).m2();
     U1mp=(jgam.dot(mjH2p)*j2gm+2.*p1o*MHmp)/(p1out+pg).m2();
     U1pm=(jgap.dot(mjH2m)*j2gp+2.*p1o*MHpm)/(p1out+pg).m2();
     U1pp=(jgap.dot(mjH2p)*j2gp+2.*p1o*MHpp)/(p1out+pg).m2();
     U2mm=((-1.)*j2gm.dot(mjH2m)*jgam+2.*p1i*MHmm)/(p1in-pg).m2();
     U2mp=((-1.)*j2gm.dot(mjH2p)*jgam+2.*p1i*MHmp)/(p1in-pg).m2();
     U2pm=((-1.)*j2gp.dot(mjH2m)*jgap+2.*p1i*MHpm)/(p1in-pg).m2();
     U2pp=((-1.)*j2gp.dot(mjH2p)*jgap+2.*p1i*MHpp)/(p1in-pg).m2();
 
     const double cf=HEJ::C_F;
     double amm,amp,apm,app;
 
     amm=cf*(2.*vre(Lmm-U1mm,Lmm+U2mm))+2.*cf*cf/3.*vabs2(U1mm+U2mm);
     amp=cf*(2.*vre(Lmp-U1mp,Lmp+U2mp))+2.*cf*cf/3.*vabs2(U1mp+U2mp);
     apm=cf*(2.*vre(Lpm-U1pm,Lpm+U2pm))+2.*cf*cf/3.*vabs2(U1pm+U2pm);
     app=cf*(2.*vre(Lpp-U1pp,Lpp+U2pp))+2.*cf*cf/3.*vabs2(U1pp+U2pp);
     double ampsq=-(amm+amp+apm+app)/(q2.m2()*qH2.m2());
 
   // if ((vabs2(Lmm-U1mm+U2mm)+vabs2(Lmm)-vabs2(U1mm)-vabs2(U2mm))/(2*vre(Lmm-U1mm,Lmm+U2mm)) > 1.0000001)
   //   std::cout << " Big Problem!! " << vabs2(Lmm-U1mm+U2mm)+vabs2(Lmm)-vabs2(U1mm)-vabs2(U2mm) << "  " << 2*vre(Lmm-U1mm,Lmm+U2mm) << std::endl;
   // if ((vabs2(Lmm-U1mm+U2mm)+vabs2(Lmm)-vabs2(U1mm)-vabs2(U2mm))/(2*vre(Lmm-U1mm,Lmm+U2mm)) < 0.9999999)
   //   std::cout << " Big Problem!! " << vabs2(Lmm-U1mm+U2mm)+vabs2(Lmm)-vabs2(U1mm)-vabs2(U2mm) << "  " << 2*vre(Lmm-U1mm,Lmm+U2mm) << std::endl;
 
     // Now add the t-channels for the Higgs
     double th=qH1.m2()*qg.m2();
     ampsq/=th;
     ampsq/=16.;
     ampsq*=4./9.*4./9.;  // Factor of (Cf/Ca) for each quark to match MH2qQ.
     // here we need 2 to match with the normalization
     // gq is 9./4. times the qQ
     //Higgs coupling is included in Hjets.C
 
     const double K = K_g(p2out, p2in);
 
     return ampsq*K/C_A*9./4.; //ca/cf = 9/4
 }
 
 double jM2unogqbarHg (CLHEP::HepLorentzVector pg, CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector p1in, CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in, CLHEP::HepLorentzVector qH1, CLHEP::HepLorentzVector qH2, double mt, bool incBot, double mb)
 {
     //  This construction is taking rapidity order: pg > p1out >> p2out
     //  std::cerr<<"This Uno Current: "<<p1out<<"  "<<p1in<<"  "<<p2out<<"  "<<p2in<<"  "<<pg<<std::endl;
 
     CLHEP::HepLorentzVector q1=p1in-p1out;  // Top End
     CLHEP::HepLorentzVector q2=-(p2in-p2out);   // Bottom End
     CLHEP::HepLorentzVector qg=p1in-p1out-pg;  // Extra bit post-gluon
 
     CCurrent mj1m,mj1p,mj2m,mj2p,mjH2m,mjH2p;
     mj1p=jio(p1in,true,p1out,true);
     mj1m=jio(p1in,false,p1out,false);
     mjH2p=jH(p2out,true,p2in,true,qH1,qH2, mt, incBot, mb);
     mjH2m=jH(p2out,false,p2in,false,qH1,qH2, mt, incBot, mb);
 
     // Dot products of these which occur again and again
     COM MHmp=mj1m.dot(mjH2p);  // And now for the Higgs ones
     COM MHmm=mj1m.dot(mjH2m);
     COM MHpp=mj1p.dot(mjH2p);
     COM MHpm=mj1p.dot(mjH2m);
 
     // Currents with pg
     CCurrent jgam,jgap,j2gm,j2gp;
     j2gp=joo(pg,true,p1out,true);
     j2gm=joo(pg,false,p1out,false);
     jgap=jio(p1in,true,pg,true);
     jgam=jio(p1in,false,pg,false);
 
     CCurrent qsum(q1+qg);
 
     CCurrent Lmp,Lmm,Lpp,Lpm,U1mp,U1mm,U1pp,U1pm,U2mp,U2mm,U2pp,U2pm,p2o(p2out),p2i(p2in);
     CCurrent p1o(p1out);
     CCurrent p1i(p1in);
 
     Lmm=(qsum*(MHmm) + (-2.*mjH2m.dot(pg))*mj1m+2.*mj1m.dot(pg)*mjH2m+(p2o/pg.dot(p2out) + p2i/pg.dot(p2in))*(qg.m2()*MHmm/2.))/q1.m2();
     Lmp=(qsum*(MHmp) + (-2.*mjH2p.dot(pg))*mj1m+2.*mj1m.dot(pg)*mjH2p+(p2o/pg.dot(p2out) + p2i/pg.dot(p2in))*(qg.m2()*MHmp/2.))/q1.m2();
     Lpm=(qsum*(MHpm) + (-2.*mjH2m.dot(pg))*mj1p+2.*mj1p.dot(pg)*mjH2m+(p2o/pg.dot(p2out) + p2i/pg.dot(p2in))*(qg.m2()*MHpm/2.))/q1.m2();
     Lpp=(qsum*(MHpp) + (-2.*mjH2p.dot(pg))*mj1p+2.*mj1p.dot(pg)*mjH2p+(p2o/pg.dot(p2out) + p2i/pg.dot(p2in))*(qg.m2()*MHpp/2.))/q1.m2();
 
     U1mm=(jgam.dot(mjH2m)*j2gm+2.*p1o*MHmm)/(p1out+pg).m2();
     U1mp=(jgam.dot(mjH2p)*j2gm+2.*p1o*MHmp)/(p1out+pg).m2();
     U1pm=(jgap.dot(mjH2m)*j2gp+2.*p1o*MHpm)/(p1out+pg).m2();
     U1pp=(jgap.dot(mjH2p)*j2gp+2.*p1o*MHpp)/(p1out+pg).m2();
     U2mm=((-1.)*j2gm.dot(mjH2m)*jgam+2.*p1i*MHmm)/(p1in-pg).m2();
     U2mp=((-1.)*j2gm.dot(mjH2p)*jgam+2.*p1i*MHmp)/(p1in-pg).m2();
     U2pm=((-1.)*j2gp.dot(mjH2m)*jgap+2.*p1i*MHpm)/(p1in-pg).m2();
     U2pp=((-1.)*j2gp.dot(mjH2p)*jgap+2.*p1i*MHpp)/(p1in-pg).m2();
 
     const double cf=HEJ::C_F;
     double amm,amp,apm,app;
 
     amm=cf*(2.*vre(Lmm-U1mm,Lmm+U2mm))+2.*cf*cf/3.*vabs2(U1mm+U2mm);
     amp=cf*(2.*vre(Lmp-U1mp,Lmp+U2mp))+2.*cf*cf/3.*vabs2(U1mp+U2mp);
     apm=cf*(2.*vre(Lpm-U1pm,Lpm+U2pm))+2.*cf*cf/3.*vabs2(U1pm+U2pm);
     app=cf*(2.*vre(Lpp-U1pp,Lpp+U2pp))+2.*cf*cf/3.*vabs2(U1pp+U2pp);
     double ampsq=-(amm+amp+apm+app)/(q2.m2()*qH2.m2());
 
   // if ((vabs2(Lmm-U1mm+U2mm)+vabs2(Lmm)-vabs2(U1mm)-vabs2(U2mm))/(2*vre(Lmm-U1mm,Lmm+U2mm)) > 1.0000001)
   //   std::cout << " Big Problem!! " << vabs2(Lmm-U1mm+U2mm)+vabs2(Lmm)-vabs2(U1mm)-vabs2(U2mm) << "  " << 2*vre(Lmm-U1mm,Lmm+U2mm) << std::endl;
   // if ((vabs2(Lmm-U1mm+U2mm)+vabs2(Lmm)-vabs2(U1mm)-vabs2(U2mm))/(2*vre(Lmm-U1mm,Lmm+U2mm)) < 0.9999999)
   //   std::cout << " Big Problem!! " << vabs2(Lmm-U1mm+U2mm)+vabs2(Lmm)-vabs2(U1mm)-vabs2(U2mm) << "  " << 2*vre(Lmm-U1mm,Lmm+U2mm) << std::endl;
 
     // Now add the t-channels for the Higgs
     double th=qH1.m2()*qg.m2();
     ampsq/=th;
     ampsq/=16.;
     ampsq*=4./9.*4./9.;  // Factor of (Cf/Ca) for each quark to match MH2qQ.
     // here we need 2 to match with the normalization
     // gq is 9./4. times the qQ
     //Higgs coupling is included in Hjets.C
 
     const double K = K_g(p2out, p2in);
 
     return ampsq*K/C_F;
 }
 
 double jM2unobqHQg (CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector p1in, CLHEP::HepLorentzVector pg, CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in, CLHEP::HepLorentzVector qH1, CLHEP::HepLorentzVector qH2, double mt, bool incBot, double mb)
 {
     // std::cout << "####################\n";
     // std::cout << "# p1in : "<<p1in<< " "<<p1in.plus()<<" "<<p1in.minus()<<std::endl;
     // std::cout << "# p2in : "<<p2in<< " "<<p2in.plus()<<" "<<p2in.minus()<<std::endl;
     // std::cout << "# p1out : "<<p1out<< " "<<p1out.rapidity()<<std::endl;
     // std::cout << "# (qH1-qH2) : "<<(qH1-qH2)<< " "<<(qH1-qH2).rapidity()<<std::endl;
     // std::cout << "# pg : "<<pg<< " "<<pg.rapidity()<<std::endl;
     // std::cout << "# p2out : "<<p2out<< " "<<p2out.rapidity()<<std::endl;
     // std::cout << "####################\n";
 
     CLHEP::HepLorentzVector q1=p1in-p1out;  // Top End
     CLHEP::HepLorentzVector q2=-(p2in-p2out-pg);  // Extra bit pre-gluon
     CLHEP::HepLorentzVector q3=-(p2in-p2out);   // Bottom End
 
     //  std::cerr<<"Current:  "<<q1.m2()<<"  "<<q2.m2()<<std::endl;
     CCurrent mjH1m,mjH1p,mj2m,mj2p;
     mjH1p=jHtop(p1out,true,p1in,true,qH1,qH2, mt, incBot, mb);
     mjH1m=jHtop(p1out,false,p1in,false,qH1,qH2, mt, incBot, mb);
     mj2p=joi(p2out,true,p2in,true);
     mj2m=joi(p2out,false,p2in,false);
 
     // Dot products of these which occur again and again
     COM MHmp=mjH1m.dot(mj2p);  // And now for the Higgs ones
     COM MHmm=mjH1m.dot(mj2m);
     COM MHpp=mjH1p.dot(mj2p);
     COM MHpm=mjH1p.dot(mj2m);
 
     // Currents with pg
     CCurrent jgbm,jgbp,j2gm,j2gp;
     j2gp=joo(p2out,true,pg,true);
     j2gm=joo(p2out,false,pg,false);
     jgbp=joi(pg,true,p2in,true);
     jgbm=joi(pg,false,p2in,false);
 
     CCurrent qsum(q2+q3);
 
     CCurrent Lmp,Lmm,Lpp,Lpm,U1mp,U1mm,U1pp,U1pm,U2mp,U2mm,U2pp,U2pm,p1o(p1out),p1i(p1in);
     CCurrent p2o(p2out);
     CCurrent p2i(p2in);
 
     CCurrent pplus((p1in+p1out)/2.);
     CCurrent pminus((p2in+p2out)/2.);
 
     // COM test=pminus.dot(p1in);
 
     Lmm=((-1.)*qsum*(MHmm) + (-2.*mjH1m.dot(pg))*mj2m+2.*mj2m.dot(pg)*mjH1m
         + (p1o/pg.dot(p1out) + p1i/pg.dot(p1in))*(q2.m2()*MHmm/2.))/q3.m2();
     Lmp=((-1.)*qsum*(MHmp) + (-2.*mjH1m.dot(pg))*mj2p+2.*mj2p.dot(pg)*mjH1m
         +(p1o/pg.dot(p1out) + p1i/pg.dot(p1in))*(q2.m2()*MHmp/2.))/q3.m2();
     Lpm=((-1.)*qsum*(MHpm) + (-2.*mjH1p.dot(pg))*mj2m+2.*mj2m.dot(pg)*mjH1p
         +(p1o/pg.dot(p1out) + p1i/pg.dot(p1in))*(q2.m2()*MHpm/2.))/q3.m2();
     Lpp=((-1.)*qsum*(MHpp) + (-2.*mjH1p.dot(pg))*mj2p+2.*mj2p.dot(pg)*mjH1p
         +(p1o/pg.dot(p1out) + p1i/pg.dot(p1in))*(q2.m2()*MHpp/2.))/q3.m2();
     U1mm=(jgbm.dot(mjH1m)*j2gm+2.*p2o*MHmm)/(p2out+pg).m2();
     U1mp=(jgbp.dot(mjH1m)*j2gp+2.*p2o*MHmp)/(p2out+pg).m2();
     U1pm=(jgbm.dot(mjH1p)*j2gm+2.*p2o*MHpm)/(p2out+pg).m2();
     U1pp=(jgbp.dot(mjH1p)*j2gp+2.*p2o*MHpp)/(p2out+pg).m2();
     U2mm=((-1.)*j2gm.dot(mjH1m)*jgbm+2.*p2i*MHmm)/(p2in-pg).m2();
     U2mp=((-1.)*j2gp.dot(mjH1m)*jgbp+2.*p2i*MHmp)/(p2in-pg).m2();
     U2pm=((-1.)*j2gm.dot(mjH1p)*jgbm+2.*p2i*MHpm)/(p2in-pg).m2();
     U2pp=((-1.)*j2gp.dot(mjH1p)*jgbp+2.*p2i*MHpp)/(p2in-pg).m2();
 
     const double cf=HEJ::C_F;
     double amm,amp,apm,app;
 
     amm=cf*(2.*vre(Lmm-U1mm,Lmm+U2mm))+2.*cf*cf/3.*vabs2(U1mm+U2mm);
     amp=cf*(2.*vre(Lmp-U1mp,Lmp+U2mp))+2.*cf*cf/3.*vabs2(U1mp+U2mp);
     apm=cf*(2.*vre(Lpm-U1pm,Lpm+U2pm))+2.*cf*cf/3.*vabs2(U1pm+U2pm);
     app=cf*(2.*vre(Lpp-U1pp,Lpp+U2pp))+2.*cf*cf/3.*vabs2(U1pp+U2pp);
     // 1/3. = 1/C_A ?
     double ampsq=-(amm+amp+apm+app)/(q1.m2()*qH1.m2());
 
   // if ((vabs2(Lmm-U1mm+U2mm)+vabs2(Lmm)-vabs2(U1mm)-vabs2(U2mm))/(2*vre(Lmm-U1mm,Lmm+U2mm)) > 1.0000001)
   //   std::cout << " Big Problem!! " << vabs2(Lmm-U1mm+U2mm)+vabs2(Lmm)-vabs2(U1mm)-vabs2(U2mm) << "  " << 2*vre(Lmm-U1mm,Lmm+U2mm) << std::endl;
   // if ((vabs2(Lmm-U1mm+U2mm)+vabs2(Lmm)-vabs2(U1mm)-vabs2(U2mm))/(2*vre(Lmm-U1mm,Lmm+U2mm)) < 0.9999999)
   //   std::cout << " Big Problem!! " << vabs2(Lmm-U1mm+U2mm)+vabs2(Lmm)-vabs2(U1mm)-vabs2(U2mm) << "  " << 2*vre(Lmm-U1mm,Lmm+U2mm) << std::endl;
 
     // Now add the t-channels for the Higgs
     const double th=qH2.m2()*q2.m2();
     ampsq/=th;
     ampsq/=16.;
     ampsq*=HEJ::C_F*HEJ::C_F/(HEJ::C_A*HEJ::C_A);  // Factor of (Cf/Ca) for each quark to match MH2qQ.
 
     return ampsq;
 }
 
 
 
 double jM2unobqbarHQg (CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector p1in, CLHEP::HepLorentzVector pg, CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in, CLHEP::HepLorentzVector qH1, CLHEP::HepLorentzVector qH2, double mt, bool incBot, double mb)
 {
 
     CLHEP::HepLorentzVector q1=p1in-p1out;  // Top End
     CLHEP::HepLorentzVector q2=-(p2in-p2out-pg);  // Extra bit pre-gluon
     CLHEP::HepLorentzVector q3=-(p2in-p2out);   // Bottom End
 
     //  std::cerr<<"Current:  "<<q1.m2()<<"  "<<q2.m2()<<std::endl;
     CCurrent mjH1m,mjH1p,mj2m,mj2p;
     mjH1p=jioHtop(p1in,true,p1out,true,qH1,qH2, mt, incBot, mb);
     mjH1m=jioHtop(p1in,false,p1out,false,qH1,qH2, mt, incBot, mb);
     mj2p=joi(p2out,true,p2in,true);
     mj2m=joi(p2out,false,p2in,false);
 
     // Dot products of these which occur again and again
     COM MHmp=mjH1m.dot(mj2p);  // And now for the Higgs ones
     COM MHmm=mjH1m.dot(mj2m);
     COM MHpp=mjH1p.dot(mj2p);
     COM MHpm=mjH1p.dot(mj2m);
 
 
     // Currents with pg
     CCurrent jgbm,jgbp,j2gm,j2gp;
     j2gp=joo(p2out,true,pg,true);
     j2gm=joo(p2out,false,pg,false);
     jgbp=joi(pg,true,p2in,true);
     jgbm=joi(pg,false,p2in,false);
 
     CCurrent qsum(q2+q3);
 
     CCurrent Lmp,Lmm,Lpp,Lpm,U1mp,U1mm,U1pp,U1pm,U2mp,U2mm,U2pp,U2pm,p1o(p1out),p1i(p1in);
     CCurrent p2o(p2out);
     CCurrent p2i(p2in);
 
     CCurrent pplus((p1in+p1out)/2.);
     CCurrent pminus((p2in+p2out)/2.);
 
     // COM test=pminus.dot(p1in);
 
 
     Lmm=((-1.)*qsum*(MHmm) + (-2.*mjH1m.dot(pg))*mj2m+2.*mj2m.dot(pg)*mjH1m+(p1o/pg.dot(p1out) + p1i/pg.dot(p1in))*(q2.m2()*MHmm/2.))/q3.m2();
     Lmp=((-1.)*qsum*(MHmp) + (-2.*mjH1m.dot(pg))*mj2p+2.*mj2p.dot(pg)*mjH1m+(p1o/pg.dot(p1out) + p1i/pg.dot(p1in))*(q2.m2()*MHmp/2.))/q3.m2();
     Lpm=((-1.)*qsum*(MHpm) + (-2.*mjH1p.dot(pg))*mj2m+2.*mj2m.dot(pg)*mjH1p+(p1o/pg.dot(p1out) + p1i/pg.dot(p1in))*(q2.m2()*MHpm/2.))/q3.m2();
     Lpp=((-1.)*qsum*(MHpp) + (-2.*mjH1p.dot(pg))*mj2p+2.*mj2p.dot(pg)*mjH1p+(p1o/pg.dot(p1out) + p1i/pg.dot(p1in))*(q2.m2()*MHpp/2.))/q3.m2();
     U1mm=(jgbm.dot(mjH1m)*j2gm+2.*p2o*MHmm)/(p2out+pg).m2();
     U1mp=(jgbp.dot(mjH1m)*j2gp+2.*p2o*MHmp)/(p2out+pg).m2();
     U1pm=(jgbm.dot(mjH1p)*j2gm+2.*p2o*MHpm)/(p2out+pg).m2();
     U1pp=(jgbp.dot(mjH1p)*j2gp+2.*p2o*MHpp)/(p2out+pg).m2();
     U2mm=((-1.)*j2gm.dot(mjH1m)*jgbm+2.*p2i*MHmm)/(p2in-pg).m2();
     U2mp=((-1.)*j2gp.dot(mjH1m)*jgbp+2.*p2i*MHmp)/(p2in-pg).m2();
     U2pm=((-1.)*j2gm.dot(mjH1p)*jgbm+2.*p2i*MHpm)/(p2in-pg).m2();
     U2pp=((-1.)*j2gp.dot(mjH1p)*jgbp+2.*p2i*MHpp)/(p2in-pg).m2();
 
     const double cf=HEJ::C_F;
     double amm,amp,apm,app;
 
     amm=cf*(2.*vre(Lmm-U1mm,Lmm+U2mm))+2.*cf*cf/3.*vabs2(U1mm+U2mm);
     amp=cf*(2.*vre(Lmp-U1mp,Lmp+U2mp))+2.*cf*cf/3.*vabs2(U1mp+U2mp);
     apm=cf*(2.*vre(Lpm-U1pm,Lpm+U2pm))+2.*cf*cf/3.*vabs2(U1pm+U2pm);
     app=cf*(2.*vre(Lpp-U1pp,Lpp+U2pp))+2.*cf*cf/3.*vabs2(U1pp+U2pp);
     double ampsq=-(amm+amp+apm+app)/(q1.m2()*qH1.m2());
 
   // if ((vabs2(Lmm-U1mm+U2mm)+vabs2(Lmm)-vabs2(U1mm)-vabs2(U2mm))/(2*vre(Lmm-U1mm,Lmm+U2mm)) > 1.0000001)
   //   std::cout << " Big Problem!! " << vabs2(Lmm-U1mm+U2mm)+vabs2(Lmm)-vabs2(U1mm)-vabs2(U2mm) << "  " << 2*vre(Lmm-U1mm,Lmm+U2mm) << std::endl;
   // if ((vabs2(Lmm-U1mm+U2mm)+vabs2(Lmm)-vabs2(U1mm)-vabs2(U2mm))/(2*vre(Lmm-U1mm,Lmm+U2mm)) < 0.9999999)
   //   std::cout << " Big Problem!! " << vabs2(Lmm-U1mm+U2mm)+vabs2(Lmm)-vabs2(U1mm)-vabs2(U2mm) << "  " << 2*vre(Lmm-U1mm,Lmm+U2mm) << std::endl;
 
     // Now add the t-channels for the Higgs
     double th=qH2.m2()*q2.m2();
     ampsq/=th;
     ampsq/=16.;
     ampsq*=4.*4./(9.*9.);  // Factor of (Cf/Ca) for each quark to match MH2qQ.
     //Higgs coupling is included in Hjets.C
 
     return ampsq;
 }
 
 
 
 double jM2unobqHQbarg (CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector p1in, CLHEP::HepLorentzVector pg, CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in, CLHEP::HepLorentzVector qH1, CLHEP::HepLorentzVector qH2, double mt, bool incBot, double mb)
 {
 
     CLHEP::HepLorentzVector q1=p1in-p1out;  // Top End
     CLHEP::HepLorentzVector q2=-(p2in-p2out-pg);  // Extra bit pre-gluon
     CLHEP::HepLorentzVector q3=-(p2in-p2out);   // Bottom End
 
     //  std::cerr<<"Current:  "<<q1.m2()<<"  "<<q2.m2()<<std::endl;
     CCurrent mjH1m,mjH1p,mj2m,mj2p;
     mjH1p=jHtop(p1out,true,p1in,true,qH1,qH2,mt, incBot, mb);
     mjH1m=jHtop(p1out,false,p1in,false,qH1,qH2,mt, incBot, mb);
     mj2p=jio(p2in,true,p2out,true);
     mj2m=jio(p2in,false,p2out,false);
 
     // Dot products of these which occur again and again
     COM MHmp=mjH1m.dot(mj2p);  // And now for the Higgs ones
     COM MHmm=mjH1m.dot(mj2m);
     COM MHpp=mjH1p.dot(mj2p);
     COM MHpm=mjH1p.dot(mj2m);
 
 
     // Currents with pg
     CCurrent jgbm,jgbp,j2gm,j2gp;
     j2gp=joo(pg,true,p2out,true);
     j2gm=joo(pg,false,p2out,false);
     jgbp=jio(p2in,true,pg,true);
     jgbm=jio(p2in,false,pg,false);
 
     CCurrent qsum(q2+q3);
 
     CCurrent Lmp,Lmm,Lpp,Lpm,U1mp,U1mm,U1pp,U1pm,U2mp,U2mm,U2pp,U2pm,p1o(p1out),p1i(p1in);
     CCurrent p2o(p2out);
     CCurrent p2i(p2in);
 
     CCurrent pplus((p1in+p1out)/2.);
     CCurrent pminus((p2in+p2out)/2.);
 
     // COM test=pminus.dot(p1in);
 
 
     Lmm=((-1.)*qsum*(MHmm) + (-2.*mjH1m.dot(pg))*mj2m+2.*mj2m.dot(pg)*mjH1m+(p1o/pg.dot(p1out) + p1i/pg.dot(p1in))*(q2.m2()*MHmm/2.))/q3.m2();
     Lmp=((-1.)*qsum*(MHmp) + (-2.*mjH1m.dot(pg))*mj2p+2.*mj2p.dot(pg)*mjH1m+(p1o/pg.dot(p1out) + p1i/pg.dot(p1in))*(q2.m2()*MHmp/2.))/q3.m2();
     Lpm=((-1.)*qsum*(MHpm) + (-2.*mjH1p.dot(pg))*mj2m+2.*mj2m.dot(pg)*mjH1p+(p1o/pg.dot(p1out) + p1i/pg.dot(p1in))*(q2.m2()*MHpm/2.))/q3.m2();
     Lpp=((-1.)*qsum*(MHpp) + (-2.*mjH1p.dot(pg))*mj2p+2.*mj2p.dot(pg)*mjH1p+(p1o/pg.dot(p1out) + p1i/pg.dot(p1in))*(q2.m2()*MHpp/2.))/q3.m2();
     U1mm=(jgbm.dot(mjH1m)*j2gm+2.*p2o*MHmm)/(p2out+pg).m2();
     U1mp=(jgbp.dot(mjH1m)*j2gp+2.*p2o*MHmp)/(p2out+pg).m2();
     U1pm=(jgbm.dot(mjH1p)*j2gm+2.*p2o*MHpm)/(p2out+pg).m2();
     U1pp=(jgbp.dot(mjH1p)*j2gp+2.*p2o*MHpp)/(p2out+pg).m2();
     U2mm=((-1.)*j2gm.dot(mjH1m)*jgbm+2.*p2i*MHmm)/(p2in-pg).m2();
     U2mp=((-1.)*j2gp.dot(mjH1m)*jgbp+2.*p2i*MHmp)/(p2in-pg).m2();
     U2pm=((-1.)*j2gm.dot(mjH1p)*jgbm+2.*p2i*MHpm)/(p2in-pg).m2();
     U2pp=((-1.)*j2gp.dot(mjH1p)*jgbp+2.*p2i*MHpp)/(p2in-pg).m2();
 
     const double cf=HEJ::C_F;
     double amm,amp,apm,app;
 
     amm=cf*(2.*vre(Lmm-U1mm,Lmm+U2mm))+2.*cf*cf/3.*vabs2(U1mm+U2mm);
     amp=cf*(2.*vre(Lmp-U1mp,Lmp+U2mp))+2.*cf*cf/3.*vabs2(U1mp+U2mp);
     apm=cf*(2.*vre(Lpm-U1pm,Lpm+U2pm))+2.*cf*cf/3.*vabs2(U1pm+U2pm);
     app=cf*(2.*vre(Lpp-U1pp,Lpp+U2pp))+2.*cf*cf/3.*vabs2(U1pp+U2pp);
     double ampsq=-(amm+amp+apm+app)/(q1.m2()*qH1.m2());
 
   // if ((vabs2(Lmm-U1mm+U2mm)+vabs2(Lmm)-vabs2(U1mm)-vabs2(U2mm))/(2*vre(Lmm-U1mm,Lmm+U2mm)) > 1.0000001)
   //   std::cout << " Big Problem!! " << vabs2(Lmm-U1mm+U2mm)+vabs2(Lmm)-vabs2(U1mm)-vabs2(U2mm) << "  " << 2*vre(Lmm-U1mm,Lmm+U2mm) << std::endl;
   // if ((vabs2(Lmm-U1mm+U2mm)+vabs2(Lmm)-vabs2(U1mm)-vabs2(U2mm))/(2*vre(Lmm-U1mm,Lmm+U2mm)) < 0.9999999)
   //   std::cout << " Big Problem!! " << vabs2(Lmm-U1mm+U2mm)+vabs2(Lmm)-vabs2(U1mm)-vabs2(U2mm) << "  " << 2*vre(Lmm-U1mm,Lmm+U2mm) << std::endl;
 
     // Now add the t-channels for the Higgs
     double th=qH2.m2()*q2.m2();
     ampsq/=th;
     ampsq/=16.;
     ampsq*=4.*4./(9.*9.);  // Factor of (Cf/Ca) for each quark to match MH2qQ.
     //Higgs coupling is included in Hjets.C
 
     return ampsq;
 }
 
 
 
 double jM2unobqbarHQbarg (CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector p1in, CLHEP::HepLorentzVector pg, CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in, CLHEP::HepLorentzVector qH1, CLHEP::HepLorentzVector qH2, double mt, bool incBot, double mb)
 {
 
     CLHEP::HepLorentzVector q1=p1in-p1out;  // Top End
     CLHEP::HepLorentzVector q2=-(p2in-p2out-pg);  // Extra bit pre-gluon
     CLHEP::HepLorentzVector q3=-(p2in-p2out);   // Bottom End
 
     //  std::cerr<<"Current:  "<<q1.m2()<<"  "<<q2.m2()<<std::endl;
     CCurrent mjH1m,mjH1p,mj2m,mj2p;
     mjH1p=jioHtop(p1in,true,p1out,true,qH1,qH2,mt, incBot, mb);
     mjH1m=jioHtop(p1in,false,p1out,false,qH1,qH2,mt, incBot, mb);
     mj2p=jio(p2in,true,p2out,true);
     mj2m=jio(p2in,false,p2out,false);
 
     // Dot products of these which occur again and again
     COM MHmp=mjH1m.dot(mj2p);  // And now for the Higgs ones
     COM MHmm=mjH1m.dot(mj2m);
     COM MHpp=mjH1p.dot(mj2p);
     COM MHpm=mjH1p.dot(mj2m);
 
 
     // Currents with pg
     CCurrent jgbm,jgbp,j2gm,j2gp;
     j2gp=joo(pg,true,p2out,true);
     j2gm=joo(pg,false,p2out,false);
     jgbp=jio(p2in,true,pg,true);
     jgbm=jio(p2in,false,pg,false);
 
     CCurrent qsum(q2+q3);
 
     CCurrent Lmp,Lmm,Lpp,Lpm,U1mp,U1mm,U1pp,U1pm,U2mp,U2mm,U2pp,U2pm,p1o(p1out),p1i(p1in);
     CCurrent p2o(p2out);
     CCurrent p2i(p2in);
 
     CCurrent pplus((p1in+p1out)/2.);
     CCurrent pminus((p2in+p2out)/2.);
 
     // COM test=pminus.dot(p1in);
 
 
     Lmm=((-1.)*qsum*(MHmm) + (-2.*mjH1m.dot(pg))*mj2m+2.*mj2m.dot(pg)*mjH1m+(p1o/pg.dot(p1out) + p1i/pg.dot(p1in))*(q2.m2()*MHmm/2.))/q3.m2();
     Lmp=((-1.)*qsum*(MHmp) + (-2.*mjH1m.dot(pg))*mj2p+2.*mj2p.dot(pg)*mjH1m+(p1o/pg.dot(p1out) + p1i/pg.dot(p1in))*(q2.m2()*MHmp/2.))/q3.m2();
     Lpm=((-1.)*qsum*(MHpm) + (-2.*mjH1p.dot(pg))*mj2m+2.*mj2m.dot(pg)*mjH1p+(p1o/pg.dot(p1out) + p1i/pg.dot(p1in))*(q2.m2()*MHpm/2.))/q3.m2();
     Lpp=((-1.)*qsum*(MHpp) + (-2.*mjH1p.dot(pg))*mj2p+2.*mj2p.dot(pg)*mjH1p+(p1o/pg.dot(p1out) + p1i/pg.dot(p1in))*(q2.m2()*MHpp/2.))/q3.m2();
     U1mm=(jgbm.dot(mjH1m)*j2gm+2.*p2o*MHmm)/(p2out+pg).m2();
     U1mp=(jgbp.dot(mjH1m)*j2gp+2.*p2o*MHmp)/(p2out+pg).m2();
     U1pm=(jgbm.dot(mjH1p)*j2gm+2.*p2o*MHpm)/(p2out+pg).m2();
     U1pp=(jgbp.dot(mjH1p)*j2gp+2.*p2o*MHpp)/(p2out+pg).m2();
     U2mm=((-1.)*j2gm.dot(mjH1m)*jgbm+2.*p2i*MHmm)/(p2in-pg).m2();
     U2mp=((-1.)*j2gp.dot(mjH1m)*jgbp+2.*p2i*MHmp)/(p2in-pg).m2();
     U2pm=((-1.)*j2gm.dot(mjH1p)*jgbm+2.*p2i*MHpm)/(p2in-pg).m2();
     U2pp=((-1.)*j2gp.dot(mjH1p)*jgbp+2.*p2i*MHpp)/(p2in-pg).m2();
 
     const double cf=HEJ::C_F;
     double amm,amp,apm,app;
 
     amm=cf*(2.*vre(Lmm-U1mm,Lmm+U2mm))+2.*cf*cf/3.*vabs2(U1mm+U2mm);
     amp=cf*(2.*vre(Lmp-U1mp,Lmp+U2mp))+2.*cf*cf/3.*vabs2(U1mp+U2mp);
     apm=cf*(2.*vre(Lpm-U1pm,Lpm+U2pm))+2.*cf*cf/3.*vabs2(U1pm+U2pm);
     app=cf*(2.*vre(Lpp-U1pp,Lpp+U2pp))+2.*cf*cf/3.*vabs2(U1pp+U2pp);
     double ampsq=-(amm+amp+apm+app)/(q1.m2()*qH1.m2());
 
   // if ((vabs2(Lmm-U1mm+U2mm)+vabs2(Lmm)-vabs2(U1mm)-vabs2(U2mm))/(2*vre(Lmm-U1mm,Lmm+U2mm)) > 1.0000001)
   //   std::cout << " Big Problem!! " << vabs2(Lmm-U1mm+U2mm)+vabs2(Lmm)-vabs2(U1mm)-vabs2(U2mm) << "  " << 2*vre(Lmm-U1mm,Lmm+U2mm) << std::endl;
   // if ((vabs2(Lmm-U1mm+U2mm)+vabs2(Lmm)-vabs2(U1mm)-vabs2(U2mm))/(2*vre(Lmm-U1mm,Lmm+U2mm)) < 0.9999999)
   //   std::cout << " Big Problem!! " << vabs2(Lmm-U1mm+U2mm)+vabs2(Lmm)-vabs2(U1mm)-vabs2(U2mm) << "  " << 2*vre(Lmm-U1mm,Lmm+U2mm) << std::endl;
 
     // Now add the t-channels for the Higgs
     double th=qH2.m2()*q2.m2();
     ampsq/=th;
     ampsq/=16.;
     ampsq*=4.*4./(9.*9.);  // Factor of (Cf/Ca) for each quark to match MH2qQ.
     //Higgs coupling is included in Hjets.C
 
 
 
     return ampsq;
 }
 
 double jM2unobgHQg (CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector p1in, CLHEP::HepLorentzVector pg, CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in, CLHEP::HepLorentzVector qH1, CLHEP::HepLorentzVector qH2, double mt, bool incBot, double mb)
 {
     // std::cout << "####################\n";
     // std::cout << "# p1in : "<<p1in<< " "<<p1in.plus()<<" "<<p1in.minus()<<std::endl;
     // std::cout << "# p2in : "<<p2in<< " "<<p2in.plus()<<" "<<p2in.minus()<<std::endl;
     // std::cout << "# p1out : "<<p1out<< " "<<p1out.rapidity()<<std::endl;
     // std::cout << "# (qH1-qH2) : "<<(qH1-qH2)<< " "<<(qH1-qH2).rapidity()<<std::endl;
     // std::cout << "# pg : "<<pg<< " "<<pg.rapidity()<<std::endl;
     // std::cout << "# p2out : "<<p2out<< " "<<p2out.rapidity()<<std::endl;
     // std::cout << "####################\n";
 
     CLHEP::HepLorentzVector q1=p1in-p1out;  // Top End
     CLHEP::HepLorentzVector q2=-(p2in-p2out-pg);  // Extra bit pre-gluon
     CLHEP::HepLorentzVector q3=-(p2in-p2out);   // Bottom End
 
     //  std::cerr<<"Current:  "<<q1.m2()<<"  "<<q2.m2()<<std::endl;
     CCurrent mjH1m,mjH1p,mj2m,mj2p;
     mjH1p=jHtop(p1out,true,p1in,true,qH1,qH2,mt, incBot, mb);
     mjH1m=jHtop(p1out,false,p1in,false,qH1,qH2,mt, incBot, mb);
     mj2p=joi(p2out,true,p2in,true);
     mj2m=joi(p2out,false,p2in,false);
 
     // Dot products of these which occur again and again
     COM MHmp=mjH1m.dot(mj2p);  // And now for the Higgs ones
     COM MHmm=mjH1m.dot(mj2m);
     COM MHpp=mjH1p.dot(mj2p);
     COM MHpm=mjH1p.dot(mj2m);
 
     // Currents with pg
     CCurrent jgbm,jgbp,j2gm,j2gp;
     j2gp=joo(p2out,true,pg,true);
     j2gm=joo(p2out,false,pg,false);
     jgbp=joi(pg,true,p2in,true);
     jgbm=joi(pg,false,p2in,false);
 
     CCurrent qsum(q2+q3);
 
     CCurrent Lmp,Lmm,Lpp,Lpm,U1mp,U1mm,U1pp,U1pm,U2mp,U2mm,U2pp,U2pm,p1o(p1out),p1i(p1in);
     CCurrent p2o(p2out);
     CCurrent p2i(p2in);
 
     CCurrent pplus((p1in+p1out)/2.);
     CCurrent pminus((p2in+p2out)/2.);
 
     // COM test=pminus.dot(p1in);
 
     Lmm=((-1.)*qsum*(MHmm) + (-2.*mjH1m.dot(pg))*mj2m+2.*mj2m.dot(pg)*mjH1m+(p1o/pg.dot(p1out) + p1i/pg.dot(p1in))*(q2.m2()*MHmm/2.))/q3.m2();
     Lmp=((-1.)*qsum*(MHmp) + (-2.*mjH1m.dot(pg))*mj2p+2.*mj2p.dot(pg)*mjH1m+(p1o/pg.dot(p1out) + p1i/pg.dot(p1in))*(q2.m2()*MHmp/2.))/q3.m2();
     Lpm=((-1.)*qsum*(MHpm) + (-2.*mjH1p.dot(pg))*mj2m+2.*mj2m.dot(pg)*mjH1p+(p1o/pg.dot(p1out) + p1i/pg.dot(p1in))*(q2.m2()*MHpm/2.))/q3.m2();
     Lpp=((-1.)*qsum*(MHpp) + (-2.*mjH1p.dot(pg))*mj2p+2.*mj2p.dot(pg)*mjH1p+(p1o/pg.dot(p1out) + p1i/pg.dot(p1in))*(q2.m2()*MHpp/2.))/q3.m2();
     U1mm=(jgbm.dot(mjH1m)*j2gm+2.*p2o*MHmm)/(p2out+pg).m2();
     U1mp=(jgbp.dot(mjH1m)*j2gp+2.*p2o*MHmp)/(p2out+pg).m2();
     U1pm=(jgbm.dot(mjH1p)*j2gm+2.*p2o*MHpm)/(p2out+pg).m2();
     U1pp=(jgbp.dot(mjH1p)*j2gp+2.*p2o*MHpp)/(p2out+pg).m2();
     U2mm=((-1.)*j2gm.dot(mjH1m)*jgbm+2.*p2i*MHmm)/(p2in-pg).m2();
     U2mp=((-1.)*j2gp.dot(mjH1m)*jgbp+2.*p2i*MHmp)/(p2in-pg).m2();
     U2pm=((-1.)*j2gm.dot(mjH1p)*jgbm+2.*p2i*MHpm)/(p2in-pg).m2();
     U2pp=((-1.)*j2gp.dot(mjH1p)*jgbp+2.*p2i*MHpp)/(p2in-pg).m2();
 
     const double cf=HEJ::C_F;
     double amm,amp,apm,app;
 
     amm=cf*(2.*vre(Lmm-U1mm,Lmm+U2mm))+2.*cf*cf/3.*vabs2(U1mm+U2mm);
     amp=cf*(2.*vre(Lmp-U1mp,Lmp+U2mp))+2.*cf*cf/3.*vabs2(U1mp+U2mp);
     apm=cf*(2.*vre(Lpm-U1pm,Lpm+U2pm))+2.*cf*cf/3.*vabs2(U1pm+U2pm);
     app=cf*(2.*vre(Lpp-U1pp,Lpp+U2pp))+2.*cf*cf/3.*vabs2(U1pp+U2pp);
     double ampsq=-(amm+amp+apm+app)/(q1.m2()*qH1.m2());
 
   // if ((vabs2(Lmm-U1mm+U2mm)+vabs2(Lmm)-vabs2(U1mm)-vabs2(U2mm))/(2*vre(Lmm-U1mm,Lmm+U2mm)) > 1.0000001)
   //   std::cout << " Big Problem!! " << vabs2(Lmm-U1mm+U2mm)+vabs2(Lmm)-vabs2(U1mm)-vabs2(U2mm) << "  " << 2*vre(Lmm-U1mm,Lmm+U2mm) << std::endl;
   // if ((vabs2(Lmm-U1mm+U2mm)+vabs2(Lmm)-vabs2(U1mm)-vabs2(U2mm))/(2*vre(Lmm-U1mm,Lmm+U2mm)) < 0.9999999)
   //   std::cout << " Big Problem!! " << vabs2(Lmm-U1mm+U2mm)+vabs2(Lmm)-vabs2(U1mm)-vabs2(U2mm) << "  " << 2*vre(Lmm-U1mm,Lmm+U2mm) << std::endl;
 
     // Now add the t-channels for the Higgs
     double th=qH2.m2()*q2.m2();
     ampsq/=th;
     ampsq/=16.;
     ampsq*=4./9.*4./9.;  // Factor of (Cf/Ca) for each quark to match MH2qQ.
     // need twice to match the normalization
     //Higgs coupling is included in Hjets.C
 
     const double K = K_g(p1out, p1in);
 
     return ampsq*K/C_F;
 }
 
 
 
 double jM2unobgHQbarg (CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector p1in, CLHEP::HepLorentzVector pg, CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in, CLHEP::HepLorentzVector qH1, CLHEP::HepLorentzVector qH2, double mt, bool incBot, double mb)
 {
 
     CLHEP::HepLorentzVector q1=p1in-p1out;  // Top End
     CLHEP::HepLorentzVector q2=-(p2in-p2out-pg);  // Extra bit pre-gluon
     CLHEP::HepLorentzVector q3=-(p2in-p2out);   // Bottom End
 
     //  std::cerr<<"Current:  "<<q1.m2()<<"  "<<q2.m2()<<std::endl;
     CCurrent mjH1m,mjH1p,mj2m,mj2p;
     mjH1p=jHtop(p1out,true,p1in,true,qH1,qH2,mt, incBot, mb);
     mjH1m=jHtop(p1out,false,p1in,false,qH1,qH2,mt, incBot, mb);
     mj2p=jio(p2in,true,p2out,true);
     mj2m=jio(p2in,false,p2out,false);
 
     // Dot products of these which occur again and again
     COM MHmp=mjH1m.dot(mj2p);  // And now for the Higgs ones
     COM MHmm=mjH1m.dot(mj2m);
     COM MHpp=mjH1p.dot(mj2p);
     COM MHpm=mjH1p.dot(mj2m);
 
 
     // Currents with pg
     CCurrent jgbm,jgbp,j2gm,j2gp;
     j2gp=joo(pg,true,p2out,true);
     j2gm=joo(pg,false,p2out,false);
     jgbp=jio(p2in,true,pg,true);
     jgbm=jio(p2in,false,pg,false);
 
     CCurrent qsum(q2+q3);
 
     CCurrent Lmp,Lmm,Lpp,Lpm,U1mp,U1mm,U1pp,U1pm,U2mp,U2mm,U2pp,U2pm,p1o(p1out),p1i(p1in);
     CCurrent p2o(p2out);
     CCurrent p2i(p2in);
 
     CCurrent pplus((p1in+p1out)/2.);
     CCurrent pminus((p2in+p2out)/2.);
 
     // COM test=pminus.dot(p1in);
 
 
     Lmm=((-1.)*qsum*(MHmm) + (-2.*mjH1m.dot(pg))*mj2m+2.*mj2m.dot(pg)*mjH1m+(p1o/pg.dot(p1out) + p1i/pg.dot(p1in))*(q2.m2()*MHmm/2.))/q3.m2();
     Lmp=((-1.)*qsum*(MHmp) + (-2.*mjH1m.dot(pg))*mj2p+2.*mj2p.dot(pg)*mjH1m+(p1o/pg.dot(p1out) + p1i/pg.dot(p1in))*(q2.m2()*MHmp/2.))/q3.m2();
     Lpm=((-1.)*qsum*(MHpm) + (-2.*mjH1p.dot(pg))*mj2m+2.*mj2m.dot(pg)*mjH1p+(p1o/pg.dot(p1out) + p1i/pg.dot(p1in))*(q2.m2()*MHpm/2.))/q3.m2();
     Lpp=((-1.)*qsum*(MHpp) + (-2.*mjH1p.dot(pg))*mj2p+2.*mj2p.dot(pg)*mjH1p+(p1o/pg.dot(p1out) + p1i/pg.dot(p1in))*(q2.m2()*MHpp/2.))/q3.m2();
     U1mm=(jgbm.dot(mjH1m)*j2gm+2.*p2o*MHmm)/(p2out+pg).m2();
     U1mp=(jgbp.dot(mjH1m)*j2gp+2.*p2o*MHmp)/(p2out+pg).m2();
     U1pm=(jgbm.dot(mjH1p)*j2gm+2.*p2o*MHpm)/(p2out+pg).m2();
     U1pp=(jgbp.dot(mjH1p)*j2gp+2.*p2o*MHpp)/(p2out+pg).m2();
     U2mm=((-1.)*j2gm.dot(mjH1m)*jgbm+2.*p2i*MHmm)/(p2in-pg).m2();
     U2mp=((-1.)*j2gp.dot(mjH1m)*jgbp+2.*p2i*MHmp)/(p2in-pg).m2();
     U2pm=((-1.)*j2gm.dot(mjH1p)*jgbm+2.*p2i*MHpm)/(p2in-pg).m2();
     U2pp=((-1.)*j2gp.dot(mjH1p)*jgbp+2.*p2i*MHpp)/(p2in-pg).m2();
 
     const double cf=HEJ::C_F;
     double amm,amp,apm,app;
 
     amm=cf*(2.*vre(Lmm-U1mm,Lmm+U2mm))+2.*cf*cf/3.*vabs2(U1mm+U2mm);
     amp=cf*(2.*vre(Lmp-U1mp,Lmp+U2mp))+2.*cf*cf/3.*vabs2(U1mp+U2mp);
     apm=cf*(2.*vre(Lpm-U1pm,Lpm+U2pm))+2.*cf*cf/3.*vabs2(U1pm+U2pm);
     app=cf*(2.*vre(Lpp-U1pp,Lpp+U2pp))+2.*cf*cf/3.*vabs2(U1pp+U2pp);
     double ampsq=-(amm+amp+apm+app)/(q1.m2()*qH1.m2());
 
   // if ((vabs2(Lmm-U1mm+U2mm)+vabs2(Lmm)-vabs2(U1mm)-vabs2(U2mm))/(2*vre(Lmm-U1mm,Lmm+U2mm)) > 1.0000001)
   //   std::cout << " Big Problem!! " << vabs2(Lmm-U1mm+U2mm)+vabs2(Lmm)-vabs2(U1mm)-vabs2(U2mm) << "  " << 2*vre(Lmm-U1mm,Lmm+U2mm) << std::endl;
   // if ((vabs2(Lmm-U1mm+U2mm)+vabs2(Lmm)-vabs2(U1mm)-vabs2(U2mm))/(2*vre(Lmm-U1mm,Lmm+U2mm)) < 0.9999999)
   //   std::cout << " Big Problem!! " << vabs2(Lmm-U1mm+U2mm)+vabs2(Lmm)-vabs2(U1mm)-vabs2(U2mm) << "  " << 2*vre(Lmm-U1mm,Lmm+U2mm) << std::endl;
 
     // Now add the t-channels for the Higgs
     double th=qH2.m2()*q2.m2();
     ampsq/=th;
     ampsq/=16.;
     ampsq*=4./9.*4./9.;  // Factor of (Cf/Ca) for each quark to match MH2qQ.
     //Higgs coupling is included in Hjets.C
 
     const double K = K_g(p1out, p1in);
 
     return ampsq*K/C_F; //ca/cf = 9/4
 
 }
 
 // Begin finite mass stuff
 #ifdef HEJ_BUILD_WITH_QCDLOOP
 namespace {
 
 
   // All the stuff needed for the box functions in qg->qgH now...
 
   //COM E1(CLHEP::HepLorentzVector k1, CLHEP::HepLorentzVector k2, CLHEP::HepLorentzVector k3, CLHEP::HepLorentzVector k4, double mq)
   COM E1(CLHEP::HepLorentzVector k1, CLHEP::HepLorentzVector k2, CLHEP::HepLorentzVector kh, double mq)
   {
     //CLHEP::HepLorentzVector q2=k3+k4;
     CLHEP::HepLorentzVector q2=-(k1+k2+kh);
     double Delta, Sigma, S1, S2, s12, s34;
     S1 = 2.*k1.dot(q2);
     S2 = 2.*k2.dot(q2);
     s12 = 2.*k1.dot(k2);
     //s34 = 2.*k3.dot(k4);
     s34 = q2.m2();
     Delta = s12*s34 - S1*S2;
     Sigma = 4.*s12*s34 - pow(S1+S2,2);
 
     return looprwfactor*(-s12*D0DD(k2, k1, q2, mq)*(1 - 8.*mq*mq/s12 + S2/(2.*s12) +
           S2*(s12 - 8.*mq*mq)*(s34 + S1)/(2.*s12*Delta) +
           2.*(s34 + S1)*(s34 + S1)/Delta +
           S2*pow((s34 + S1),3)/Delta/Delta) - ((s12 + S2)*C0DD(k2,
             k1 + q2, mq) -
           s12*C0DD(k1, k2, mq) + (S1 - S2)*C0DD(k1 + k2, q2, mq) -
           S1*C0DD(k1, q2,
             mq))*(S2*(s12 - 4.*mq*mq)/(2.*s12*Delta) +
           2.*(s34 + S1)/Delta +
           S2*pow((s34 + S1),2)/Delta/Delta) + (C0DD(k1, q2, mq) -
           C0DD(k1 + k2, q2, mq))*(1. - 4.*mq*mq/s12) -
        C0DD(k1 + k2, q2, mq)*2.*s34/
          S1 - (B0DD(k1 + q2, mq) -
           B0DD(k1 + k2 + q2, mq))*2.*s34*(s34 +
            S1)/(S1*Delta) + (B0DD(q2, mq) -
           B0DD(k1 + k2 + q2, mq) +
           s12*C0DD(k1 + k2, q2,
             mq))*(2.*s34*(s34 +
              S1)*(S1 - S2)/(Delta*Sigma) +
           2.*s34*(s34 + S1)/(S1*Delta)) + (B0DD(k1 + k2, mq) -
           B0DD(k1 + k2 + q2,
            mq) - (s34 + S1 + S2)*C0DD(k1 + k2, q2, mq))*2.*(s34 +
           S1)*(2.*s12*s34 -
            S2*(S1 + S2))/(Delta*Sigma));
   }
   //COM F1(CLHEP::HepLorentzVector k1, CLHEP::HepLorentzVector k2, CLHEP::HepLorentzVector k3, CLHEP::HepLorentzVector k4, double mq)
   COM F1(CLHEP::HepLorentzVector k1, CLHEP::HepLorentzVector k2, CLHEP::HepLorentzVector kh, double mq)
   {
     //CLHEP::HepLorentzVector q2=k3+k4;
     CLHEP::HepLorentzVector q2 = -(k1+k2+kh);
     double Delta, Sigma, S1, S2, s12, s34;
     S1 = 2.*k1.dot(q2);
     S2 = 2.*k2.dot(q2);
     s12 = 2.*k1.dot(k2);
     //s34 = 2.*k3.dot(k4);
     s34 = q2.m2();
     Delta = s12*s34 - S1*S2;
     Sigma = 4.*s12*s34 - pow(S1+S2,2);
 
     return looprwfactor*(-S2*D0DD(k1, k2, q2,
          mq)*(0.5 - (s12 - 8.*mq*mq)*(s34 + S2)/(2.*Delta) -
           s12*pow((s34 + S2),3)/Delta/Delta) + ((s12 + S1)*C0DD(k1,
             k2 + q2, mq) -
           s12*C0DD(k1, k2, mq) - (S1 - S2)*C0DD(k1 + k2, q2, mq) -
           S2*C0DD(k2, q2,
             mq))*(S2*(s12 - 4.*mq*mq)/(2.*s12*Delta) +
           S2*pow((s34 + S2),2)/Delta/Delta) - (C0DD(k1 + k2, q2, mq) - C0DD(k1, k2 + q2, mq))*(1. - 4.*mq*mq/s12) -
        C0DD(k1, k2 + q2, mq) + (B0DD(k2 + q2, mq) -
           B0DD(k1 + k2 + q2,
            mq))*2.*pow((s34 + S2),2)/((s12 + S1)*Delta) - (B0DD(
            q2, mq) - B0DD(k1 + k2 + q2, mq) +
           s12*C0DD(k1 + k2, q2, mq))*2.*s34*(s34 +
           S2)*(S2 - S1)/(Delta*Sigma) + (B0DD(
            k1 + k2, mq) -
           B0DD(k1 + k2 + q2,
            mq) - (s34 + S1 + S2)*C0DD(k1 + k2, q2, mq))*2.*(s34 +
           S2)*(2.*s12*s34 -
            S2*(S1 + S2))/(Delta*Sigma));
   }
   //COM G1(CLHEP::HepLorentzVector k1, CLHEP::HepLorentzVector k2, CLHEP::HepLorentzVector k3, CLHEP::HepLorentzVector k4, double mq)
   COM G1(CLHEP::HepLorentzVector k1, CLHEP::HepLorentzVector k2, CLHEP::HepLorentzVector kh, double mq)
   {
     //CLHEP::HepLorentzVector q2=k3+k4;
     CLHEP::HepLorentzVector q2 = -(k1+k2+kh);
     double Delta, S1, S2, s12, s34;
     S1 = 2.*k1.dot(q2);
     S2 = 2.*k2.dot(q2);
     s12 = 2.*k1.dot(k2);
     //s34 = 2.*k3.dot(k4);
     s34 = q2.m2();
     Delta = s12*s34 - S1*S2;
 
     return looprwfactor*(S2*D0DD(k1, q2, k2,
          mq)*(Delta/s12/s12 - 4.*mq*mq/s12) -
        S2*((s12 + S1)*C0DD(k1, k2 + q2, mq) -
           S1*C0DD(k1, q2, mq))*(1./
            s12/s12 - (s12 - 4.*mq*mq)/(2.*s12*Delta)) -
        S2*((s12 + S2)*C0DD(k1 + q2, k2, mq) -
           S2*C0DD(k2, q2, mq))*(1./
            s12/s12 + (s12 - 4.*mq*mq)/(2.*s12*Delta)) -
        C0DD(k1, q2, mq) - (C0DD(k1, k2 + q2, mq) -
           C0DD(k1, q2, mq))*4.*mq*mq/
          s12 + (B0DD(k1 + q2, mq) - B0DD(k1 + k2 + q2, mq))*2./
          s12 + (B0DD(k1 + q2, mq) -
           B0DD(q2, mq))*2.*s34/(s12*S1) + (B0DD(k2 + q2, mq) -
           B0DD(k1 + k2 + q2, mq))*2.*(s34 + S2)/(s12*(s12 + S1)));
   }
   //COM E4(CLHEP::HepLorentzVector k1, CLHEP::HepLorentzVector k2, CLHEP::HepLorentzVector k3, CLHEP::HepLorentzVector k4, double mq)
   COM E4(CLHEP::HepLorentzVector k1, CLHEP::HepLorentzVector k2, CLHEP::HepLorentzVector kh, double mq)
   {
     //CLHEP::HepLorentzVector q2=k3+k4;
     CLHEP::HepLorentzVector q2 = -(k1+k2+kh);
     double Delta, Sigma, S1, S2, s12, s34;
     S1 = 2.*k1.dot(q2);
     S2 = 2.*k2.dot(q2);
     s12 = 2.*k1.dot(k2);
     //s34 = 2.*k3.dot(k4);
     s34 = q2.m2();
     Delta = s12*s34 - S1*S2;
     Sigma = 4.*s12*s34 - pow(S1+S2,2);
 
     return looprwfactor* (-s12*D0DD(k2, k1, q2,
          mq)*(0.5 - (S1 - 8.*mq*mq)*(s34 + S1)/(2.*Delta) -
           s12*pow((s34 + S1),3)/Delta/Delta) + ((s12 + S2)*C0DD(k2,
             k1 + q2, mq) -
           s12*C0DD(k1, k2, mq) + (S1 - S2)*C0DD(k1 + k2, q2, mq) -
           S1*C0DD(k1, q2, mq))*((S1 - 4.*mq*mq)/(2.*Delta) +
            s12*pow((s34 + S1),2)/Delta/Delta) -
        C0DD(k1 + k2, q2, mq) + (B0DD(k1 + q2, mq) -
           B0DD(k1 + k2 + q2, mq))*(2.*s34/Delta +
           2.*s12*(s34 + S1)/((s12 + S2)*Delta)) - (B0DD(
            q2, mq) - B0DD(k1 + k2 + q2, mq) +
           s12*C0DD(k1 + k2, q2,
             mq))*((2.*s34*(2.*s12*s34 - S2*(S1 + S2) +
               s12*(S1 -
                  S2)))/(Delta*Sigma)) + (B0DD(k1 + k2, mq) -
           B0DD(k1 + k2 + q2, mq) - (s34 + S1 + S2)*C0DD(k1 + k2, q2, mq))*((2.*s12*(2.*s12*s34 - S1*(S1 + S2) +
               s34*(S2 - S1)))/(Delta*Sigma)));
   }
   //COM F4(CLHEP::HepLorentzVector k1, CLHEP::HepLorentzVector k2, CLHEP::HepLorentzVector k3, CLHEP::HepLorentzVector k4, double mq)
   COM F4(CLHEP::HepLorentzVector k1, CLHEP::HepLorentzVector k2, CLHEP::HepLorentzVector kh, double mq)
   {
     //CLHEP::HepLorentzVector q2=k3+k4;
     CLHEP::HepLorentzVector q2 = -(k1+k2+kh);
     double Delta, Sigma, S1, S2, s12, s34;
     S1 = 2.*k1.dot(q2);
     S2 = 2.*k2.dot(q2);
     s12 = 2.*k1.dot(k2);
     //s34 = 2.*k3.dot(k4);
     s34 = q2.m2();
     Delta = s12*s34 - S1*S2;
     Sigma = 4.*s12*s34 - pow(S1+S2,2);
 
     return looprwfactor* (-s12*D0DD(k1, k2, q2,
          mq)*(0.5 + (S1 - 8.*mq*mq)*(s34 + S2)/(2.*Delta) +
           s12*pow((s34 + S2),3)/Delta/Delta) - ((s12 + S1)*C0DD(k1,
             k2 + q2, mq) -
           s12*C0DD(k1, k2, mq) - (S1 - S2)*C0DD(k1 + k2, q2, mq) -
           S2*C0DD(k2, q2, mq))*((S1 - 4.*mq*mq)/(2.*Delta) +
            s12*pow((s34 + S2),2)/Delta/Delta) -
        C0DD(k1 + k2, q2, mq) - (B0DD(k2 + q2, mq) -
           B0DD(k1 + k2 + q2, mq))*2.*(s34 +
            S2)/Delta + (B0DD(q2, mq) -
           B0DD(k1 + k2 + q2, mq) +
           s12*C0DD(k1 + k2, q2, mq))*2.*s34*(2.*s12*s34 -
            S1*(S1 + S2) +
            s12*(S2 - S1))/(Delta*Sigma) - (B0DD(k1 + k2, mq) -
           B0DD(k1 + k2 + q2, mq) - (s34 + S1 + S2)*C0DD(k1 + k2, q2, mq))*(2.*s12*(2.*s12*s34 - S2*(S1 + S2) +
              s34*(S1 - S2))/(Delta*Sigma)));
   }
   //COM G4(CLHEP::HepLorentzVector k1, CLHEP::HepLorentzVector k2, CLHEP::HepLorentzVector k3, CLHEP::HepLorentzVector k4, double mq)
   COM G4(CLHEP::HepLorentzVector k1, CLHEP::HepLorentzVector k2, CLHEP::HepLorentzVector kh, double mq)
   {
     //CLHEP::HepLorentzVector q2=k3+k4;
     CLHEP::HepLorentzVector q2 = -(k1+k2+kh);
     double Delta, S1, S2, s12, s34;
     S1 = 2.*k1.dot(q2);
     S2 = 2.*k2.dot(q2);
     s12 = 2.*k1.dot(k2);
     //s34 = 2.*k3.dot(k4);
     s34 = q2.m2();
     Delta = s12*s34 - S1*S2;
 
     return looprwfactor* (-D0DD(k1, q2, k2,
           mq)*(Delta/s12 + (s12 + S1)/2. -
           4.*mq*mq) + ((s12 + S1)*C0DD(k1, k2 + q2, mq) -
           S1*C0DD(k1, q2, mq))*(1./
            s12 - (S1 - 4.*mq*mq)/(2.*Delta)) + ((s12 + S2)*C0DD(
             k1 + q2, k2, mq) -
           S2*C0DD(k2, q2, mq))*(1./
            s12 + (S1 - 4.*mq*mq)/(2.*Delta)) + (B0DD(
            k1 + k2 + q2, mq) -
           B0DD(k1 + q2, mq))*2./(s12 + S2));
   }
   //COM E10(CLHEP::HepLorentzVector k1, CLHEP::HepLorentzVector k2, CLHEP::HepLorentzVector k3, CLHEP::HepLorentzVector k4, double mq)
   COM E10(CLHEP::HepLorentzVector k1, CLHEP::HepLorentzVector k2, CLHEP::HepLorentzVector kh, double mq)
   {
     //CLHEP::HepLorentzVector q2=k3+k4;
     CLHEP::HepLorentzVector q2 = -(k1+k2+kh);
     double Delta, Sigma, S1, S2, s12, s34;
     S1 = 2.*k1.dot(q2);
     S2 = 2.*k2.dot(q2);
     s12 = 2.*k1.dot(k2);
     //s34 = 2.*k3.dot(k4);
     s34 = q2.m2();
     Delta = s12*s34 - S1*S2;
     Sigma = 4.*s12*s34 - pow(S1+S2,2);
 
     return looprwfactor*(-s12*D0DD(k2, k1, q2, mq)*((s34 + S1)/Delta +
            12.*mq*mq*S1*(s34 + S1)/Delta/Delta -
            4.*s12*S1*pow((s34 + S1),3)/Delta/Delta/Delta) - ((s12 + S2)*C0DD(k2, k1 + q2, mq) -
            s12*C0DD(k1, k2, mq) + (S1 - S2)*C0DD(k1 + k2, q2, mq) -
             S1*C0DD(k1, q2, mq))*(1./Delta +
            4.*mq*mq*S1/Delta/Delta -
            4.*s12*S1*pow((s34 + S1),2)/Delta/Delta/Delta) +
         C0DD(k1 + k2, q2, mq)*(4.*s12*s34*(S1 - S2)/(Delta*Sigma) -
            4.*(s12 -
               2.*mq*mq)*(2.*s12*s34 -
                S1*(S1 + S2))/(Delta*Sigma)) + (B0DD(k1 + q2, mq) -
            B0DD(k1 + k2 + q2, mq))*(4.*(s34 + S1)/((s12 + S2)*Delta) +
            8.*S1*(s34 + S1)/Delta/Delta) + (B0DD(q2, mq) -
            B0DD(k1 + k2 + q2, mq) +
            s12*C0DD(k1 + k2, q2, mq))*(12.*s34*(2.*s12 + S1 +
               S2)*(2.*s12*s34 -
                S1*(S1 + S2))/(Delta*Sigma*Sigma) -
            4.*s34*(4.*s12 + 3.*S1 +
                S2)/(Delta*Sigma) +
            8.*s12*s34*(s34*(s12 + S2) -
                S1*(s34 +
                   S1))/(Delta*Delta*Sigma)) + (B0DD(k1 + k2, mq) -
            B0DD(k1 + k2 + q2, mq) - (s34 + S1 + S2)*C0DD(k1 + k2, q2,
              mq))*(12.*s12*(2.*s34 + S1 +
               S2)*(2.*s12*s34 -
                S1*(S1 + S2))/(Delta*Sigma*Sigma) +
            8.*s12*S1*(s34*(s12 + S2) -
                S1*(s34 +
                   S1))/(Delta*Delta*Sigma))) + (COM(0.,1.)/(4.*M_PI*M_PI))*((2.*s12*s34 -
           S1*(S1 + S2))/(Delta*Sigma));
   }
 
   //COM F10(CLHEP::HepLorentzVector k1, CLHEP::HepLorentzVector k2, CLHEP::HepLorentzVector k3, CLHEP::HepLorentzVector k4, double mq)
   COM F10(CLHEP::HepLorentzVector k1, CLHEP::HepLorentzVector k2, CLHEP::HepLorentzVector kh, double mq)
   {
     //CLHEP::HepLorentzVector q2=k3+k4;
     CLHEP::HepLorentzVector q2 = -(k1+k2+kh);
     double Delta, Sigma, S1, S2, s12, s34;
     S1 = 2.*k1.dot(q2);
     S2 = 2.*k2.dot(q2);
     s12 = 2.*k1.dot(k2);
     //s34 = 2.*k3.dot(k4);
     s34 = q2.m2();
     Delta = s12*s34 - S1*S2;
     Sigma = 4.*s12*s34 - pow(S1+S2,2);
 
     return looprwfactor* (s12*D0DD(k1, k2, q2,
           mq)*((s34 + S2)/Delta - 4.*mq*mq/Delta +
            12.*mq*mq*s34*(s12 + S1)/Delta/Delta -
            4.*s12*pow((s34 + S2),2)/Delta/Delta -
            4.*s12*S1*pow((s34 + S2),3)/Delta/Delta/Delta) + ((s12 + S1)*C0DD(k1, k2 + q2, mq) -
            s12*C0DD(k1, k2, mq) - (S1 - S2)*C0DD(k1 + k2, q2, mq) -
             S2*C0DD(k2, q2, mq))*(1./Delta +
            4.*mq*mq*S1/Delta/Delta -
            4.*s12*(s34 + S2)/Delta/Delta -
            4.*s12*S1*pow((s34 + S2),2)/Delta/Delta/Delta) -
         C0DD(k1 + k2, q2, mq)*(4.*s12*s34/(S2*Delta) +
            4.*s12*s34*(S2 - S1)/(Delta*Sigma) +
            4.*(s12 -
               2.*mq*mq)*(2.*s12*s34 -
                S1*(S1 + S2))/(Delta*Sigma)) - (B0DD(
             k2 + q2, mq) -
            B0DD(k1 + k2 + q2, mq))*(4.*s34/(S2*Delta) +
            8.*s34*(s12 + S1)/Delta/Delta) - (B0DD(q2, mq) -
            B0DD(k1 + k2 + q2, mq) +
            s12*C0DD(k1 + k2, q2,
              mq))*(-12*s34*(2*s12 + S1 +
               S2)*(2.*s12*s34 -
                S1*(S1 + S2))/(Delta*Sigma*Sigma) -
            4.*s12*s34*s34/(S2*Delta*Delta) +
            4.*s34*S1/(Delta*Sigma) -
            4.*s34*(s12*s34*(2.*s12 + S2) -
                S1*S1*(2.*s12 +
                   S1))/(Delta*Delta*Sigma)) - (B0DD(k1 + k2, mq) -
            B0DD(k1 + k2 + q2, mq) - (s34 + S1 + S2)*C0DD(k1 + k2, q2, mq))*(-12.*s12*(2.*s34 + S1 +
               S2)*(2.*s12*s34 -
                S1*(S1 + S2))/(Delta*Sigma*Sigma) +
            8.*s12*(2.*s34 + S1)/(Delta*Sigma) -
            8.*s12*s34*(2.*s12*s34 - S1*(S1 + S2) +
                s12*(S2 -
                   S1))/(Delta*Delta*Sigma))) + (COM(0.,1.)/(4.*M_PI*M_PI))*((2.*s12*s34 -
           S1*(S1 + S2))/(Delta*Sigma));
 
   }
 
   //COM G10(CLHEP::HepLorentzVector k1, CLHEP::HepLorentzVector k2, CLHEP::HepLorentzVector k3, CLHEP::HepLorentzVector k4, double mq)
   COM G10(CLHEP::HepLorentzVector k1, CLHEP::HepLorentzVector k2, CLHEP::HepLorentzVector kh, double mq)
   {
     //CLHEP::HepLorentzVector q2=k3+k4;
     CLHEP::HepLorentzVector q2 = -(k1+k2+kh);
     double Delta, S1, S2, s12, s34;
     S1 = 2.*k1.dot(q2);
     S2 = 2.*k2.dot(q2);
     s12 = 2.*k1.dot(k2);
     //s34 = 2.*k3.dot(k4);
     s34 = q2.m2();
     Delta = s12*s34 - S1*S2;
 
     return looprwfactor* (-D0DD(k1, q2, k2, mq)*(1. +
           4.*S1*mq*mq/Delta) + ((s12 + S1)*C0DD(k1,
             k2 + q2, mq) -
           S1*C0DD(k1, q2, mq))*(1./Delta +
           4.*S1*mq*mq/Delta/Delta) - ((s12 + S2)*C0DD(k1 + q2,
             k2, mq) - S2*C0DD(k2, q2, mq))*(1./Delta +
           4.*S1*mq*mq/Delta/Delta) + (B0DD(k1 + k2 + q2, mq) -
           B0DD(k1 + q2, mq))*4.*(s34 +
            S1)/(Delta*(s12 + S2)) + (B0DD(q2, mq) -
           B0DD(k2 + q2, mq))*4.*s34/(Delta*S2));
   }
 
   //COM H1(CLHEP::HepLorentzVector k1, CLHEP::HepLorentzVector k2, CLHEP::HepLorentzVector k3, CLHEP::HepLorentzVector k4, double mq)
   COM H1(CLHEP::HepLorentzVector k1, CLHEP::HepLorentzVector k2, CLHEP::HepLorentzVector kh, double mq)
   {
     //return E1(k1,k2,k3,k4,mq)+F1(k1,k2,k3,k4,mq)+G1(k1,k2,k3,k4,mq);
     return E1(k1,k2,kh,mq)+F1(k1,k2,kh,mq)+G1(k1,k2,kh,mq);
   }
   //COM H4(CLHEP::HepLorentzVector k1, CLHEP::HepLorentzVector k2, CLHEP::HepLorentzVector k3, CLHEP::HepLorentzVector k4, double mq)
   COM H4(CLHEP::HepLorentzVector k1, CLHEP::HepLorentzVector k2, CLHEP::HepLorentzVector kh, double mq)
   {
     //return E4(k1,k2,k3,k4,mq)+F4(k1,k2,k3,k4,mq)+G4(k1,k2,k3,k4,mq);
     return E4(k1,k2,kh,mq)+F4(k1,k2,kh,mq)+G4(k1,k2,kh,mq);
   }
   //COM H10(CLHEP::HepLorentzVector k1, CLHEP::HepLorentzVector k2, CLHEP::HepLorentzVector k3, CLHEP::HepLorentzVector k4, double mq)
   COM H10(CLHEP::HepLorentzVector k1, CLHEP::HepLorentzVector k2, CLHEP::HepLorentzVector kh, double mq)
   {
     //return E10(k1,k2,k3,k4,mq)+F10(k1,k2,k3,k4,mq)+G10(k1,k2,k3,k4,mq);
     return E10(k1,k2,kh,mq)+F10(k1,k2,kh,mq)+G10(k1,k2,kh,mq);
   }
   //COM H2(CLHEP::HepLorentzVector k1, CLHEP::HepLorentzVector k2, CLHEP::HepLorentzVector k3, CLHEP::HepLorentzVector k4, double mq)
   COM H2(CLHEP::HepLorentzVector k1, CLHEP::HepLorentzVector k2, CLHEP::HepLorentzVector kh, double mq)
   {
     //return -1.*H1(k2,k1,k3,k4,mq);
     return -1.*H1(k2,k1,kh,mq);
   }
   //COM H5(CLHEP::HepLorentzVector k1, CLHEP::HepLorentzVector k2, CLHEP::HepLorentzVector k3, CLHEP::HepLorentzVector k4, double mq)
   COM H5(CLHEP::HepLorentzVector k1, CLHEP::HepLorentzVector k2, CLHEP::HepLorentzVector kh, double mq)
   {
     //return -1.*H4(k2,k1,k3,k4,mq);
     return -1.*H4(k2,k1,kh,mq);
   }
   //COM H12(CLHEP::HepLorentzVector k1, CLHEP::HepLorentzVector k2, CLHEP::HepLorentzVector k3, CLHEP::HepLorentzVector k4, double mq)
   COM H12(CLHEP::HepLorentzVector k1, CLHEP::HepLorentzVector k2, CLHEP::HepLorentzVector kh, double mq)
   {
     //return -1.*H10(k2,k1,k3,k4,mq);
     return -1.*H10(k2,k1,kh,mq);
   }
 
   // FL and FT functions
   COM FL(CLHEP::HepLorentzVector q1, CLHEP::HepLorentzVector q2, double mq)
   {
     CLHEP::HepLorentzVector Q = q1 + q2;
     double detQ2 = q1.m2()*q2.m2() - q1.dot(q2)*q1.dot(q2);
     return -1./(2.*detQ2)*((2.-
            3.*q1.m2()*q2.dot(Q)/detQ2)*(B0DD(q1, mq) -
            B0DD(Q, mq)) + (2. -
            3.*q2.m2()*q1.dot(Q)/detQ2)*(B0DD(q2, mq) -
            B0DD(Q, mq)) - (4.*mq*mq + q1.m2() + q2.m2() +
            Q.m2() - 3.*q1.m2()*q2.m2()*Q.m2()/detQ2)*C0DD(
           q1, q2, mq) - 2.);
   }
   COM FT(CLHEP::HepLorentzVector q1, CLHEP::HepLorentzVector q2, double mq)
   {
     CLHEP::HepLorentzVector Q = q1 + q2;
     double detQ2 = q1.m2()*q2.m2() - q1.dot(q2)*q1.dot(q2);
     return -1./(2.*detQ2)*(Q.m2()*(B0DD(q1, mq) + B0DD(q2, mq) - 2.*B0DD(Q, mq) -
             2.*q1.dot(q2)*C0DD(q1, q2, mq)) + (q1.m2() -
             q2.m2()) *(B0DD(q1, mq) - B0DD(q2, mq))) -
       q1.dot(q2)*FL(q1, q2, mq);
   }
 
   CLHEP::HepLorentzVector ParityFlip(CLHEP::HepLorentzVector p)
   {
     CLHEP::HepLorentzVector flippedVector;
     flippedVector.setE(p.e());
     flippedVector.setX(-p.x());
     flippedVector.setY(-p.y());
     flippedVector.setZ(-p.z());
     return flippedVector;
   }
   /// @brief HC amp for qg->qgH with finite top (i.e. j^{++}_H)
   void g_gH_HC(CLHEP::HepLorentzVector pa, CLHEP::HepLorentzVector p1,
     CLHEP::HepLorentzVector pH, double mq, current &retAns)
   {
     current cura1,pacur,p1cur,pHcur,conjeps1,conjepsH1,epsa,epsHa,epsHapart1,
       epsHapart2,conjepsH1part1,conjepsH1part2;
     COM ang1a,sqa1;
 
     const double F = 4.*mq*mq/HEJ::vev;
     // Easier to have the whole thing as current object so I can use cdot functionality.
     // Means I need to write pa,p1 as current objects
     to_current(pa, pacur);
     to_current(p1,p1cur);
     to_current(pH,pHcur);
     bool gluonforward = true;
     if(pa.z() < 0)
       gluonforward = false;
     //HEJ gauge
     jio(pa,false,p1,false,cura1);
 
     if(gluonforward){
       // sqrt(2pa_-/p1_-)*p1_perp/abs(p1_perp)
       ang1a = sqrt(pa.plus()*p1.minus())*(p1.x()+COM(0.,1.)*p1.y())/p1.perp();
       // sqrt(2pa_-/p1_-)*p1_perp*/abs(p1_perp)
       sqa1 = sqrt(pa.plus()*p1.minus())*(p1.x()-COM(0.,1.)*p1.y())/p1.perp();
     } else {
       ang1a = sqrt(pa.minus()*p1.plus());
       sqa1 = sqrt(pa.minus()*p1.plus());
     }
 
 
     const double prop = (pa-p1-pH).m2();
 
     cmult(-1./sqrt(2)/ang1a,cura1,conjeps1);
     cmult(1./sqrt(2)/sqa1,cura1,epsa);
 
     const COM Fta = FT(-pa,pa-pH,mq)/(pa-pH).m2();
     const COM Ft1 = FT(-p1-pH,p1,mq)/(p1+pH).m2();
 
     const COM h4 = H4(p1,-pa,pH,mq);
     const COM h5 = H5(p1,-pa,pH,mq);
     const COM h10 = H10(p1,-pa,pH,mq);
     const COM h12 = H12(p1,-pa,pH,mq);
 
 
     cmult(Fta*pa.dot(pH), epsa, epsHapart1);
     cmult(-1.*Fta*cdot(pHcur,epsa), pacur, epsHapart2);
     cmult(Ft1*cdot(pHcur,conjeps1), p1cur, conjepsH1part1);
     cmult(-Ft1*p1.dot(pH), conjeps1, conjepsH1part2);
     cadd(epsHapart1, epsHapart2, epsHa);
     cadd(conjepsH1part1, conjepsH1part2, conjepsH1);
     const COM aH1 = cdot(pHcur, cura1);
 
     current T1,T2,T3,T4,T5,T6,T7,T8,T9,T10;
 
     if(gluonforward){
       cmult(sqrt(2.)*sqrt(p1.plus()/pa.plus())*prop/sqa1, conjepsH1, T1);
       cmult(-sqrt(2.)*sqrt(pa.plus()/p1.plus())*prop/ang1a, epsHa, T2);
     }
     else{
       cmult(-sqrt(2.)*sqrt(p1.minus()/pa.minus())
           *((p1.x()-COM(0.,1.)*p1.y())/p1.perp())*prop/sqa1, conjepsH1, T1);
       cmult(sqrt(2.)*sqrt(pa.minus()/p1.minus())
           *((p1.x()-COM(0.,1.)*p1.y())/p1.perp())*prop/ang1a, epsHa, T2);
     }
 
     cmult(sqrt(2.)/ang1a*aH1, epsHa, T3);
     cmult(sqrt(2.)/sqa1*aH1, conjepsH1, T4);
 
     cmult(-sqrt(2.)*Fta*pa.dot(p1)*aH1/sqa1, conjeps1, T5);
     cmult(-sqrt(2.)*Ft1*pa.dot(p1)*aH1/ang1a, epsa, T6);
 
     cmult(-aH1/sqrt(2.)/sqa1*h4*8.*COM(0.,1.)*M_PI*M_PI, conjeps1, T7);
     cmult(aH1/sqrt(2.)/ang1a*h5*8.*COM(0.,1.)*M_PI*M_PI, epsa, T8);
     cmult(aH1*aH1/2./ang1a/sqa1*h10*8.*COM(0.,1.)*M_PI*M_PI, pacur, T9);
     cmult(-aH1*aH1/2./ang1a/sqa1*h12*8.*COM(0.,1.)*M_PI*M_PI, p1cur, T10);
 
     current ans;
     for(int i=0;i<4;i++)
     {
         ans[i] = T1[i]+T2[i]+T3[i]+T4[i]+T5[i]+T6[i]+T7[i]+T8[i]+T9[i]+T10[i];
     }
 
     retAns[0] = F/prop*ans[0];
     retAns[1] = F/prop*ans[1];
     retAns[2] = F/prop*ans[2];
     retAns[3] = F/prop*ans[3];
   }
 
   /// @brief HNC amp for qg->qgH with finite top (i.e. j^{+-}_H)
   void g_gH_HNC(CLHEP::HepLorentzVector pa, CLHEP::HepLorentzVector p1, CLHEP::HepLorentzVector pH, double mq, current &retAns)
   {
     const double F = 4.*mq*mq/HEJ::vev;
     COM ang1a,sqa1;
     current conjepsH1,epsHa,p1cur,pacur,pHcur,conjeps1,epsa,paplusp1cur,
       p1minuspacur,cur1a,cura1,epsHapart1,epsHapart2,conjepsH1part1,
       conjepsH1part2;
     // Find here if pa, meaning the gluon, is forward or backward
     bool gluonforward = true;
     if(pa.z() < 0)
       gluonforward = false;
 
     jio(pa,true,p1,true,cura1);
     joi(p1,true,pa,true,cur1a);
 
     to_current(pa,pacur);
     to_current(p1,p1cur);
     to_current(pH,pHcur);
     to_current(pa+p1,paplusp1cur);
     to_current(p1-pa,p1minuspacur);
     const COM aH1 = cdot(pHcur,cura1);
     const COM oneHa = std::conj(aH1); // = cdot(pHcur,cur1a)
 
     if(gluonforward){
       // sqrt(2pa_-/p1_-)*p1_perp/abs(p1_perp)
       ang1a = sqrt(pa.plus()*p1.minus())*(p1.x()+COM(0.,1.)*p1.y())/p1.perp();
       // sqrt(2pa_-/p1_-)*p1_perp*/abs(p1_perp)
       sqa1 = sqrt(pa.plus()*p1.minus())*(p1.x()-COM(0.,1.)*p1.y())/p1.perp();
       }
     else {
       ang1a = sqrt(pa.minus()*p1.plus());
       sqa1 = sqrt(pa.minus()*p1.plus());
     }
 
     const double prop = (pa-p1-pH).m2();
 
     cmult(1./sqrt(2)/sqa1, cur1a, epsa);
     cmult(-1./sqrt(2)/sqa1, cura1, conjeps1);
     const COM phase = cdot(conjeps1, epsa);
     const COM Fta = FT(-pa,pa-pH,mq)/(pa-pH).m2();
     const COM Ft1 = FT(-p1-pH,p1,mq)/(p1+pH).m2();
     const COM Falpha = FT(p1-pa,pa-p1-pH,mq);
     const COM Fbeta = FL(p1-pa,pa-p1-pH,mq);
 
     const COM h1 = H1(p1,-pa, pH, mq);
     const COM h2 = H2(p1,-pa, pH, mq);
     const COM h4 = H4(p1,-pa, pH, mq);
     const COM h5 = H5(p1,-pa, pH, mq);
     const COM h10 = H10(p1,-pa, pH, mq);
     const COM h12 = H12(p1,-pa, pH, mq);
 
     cmult(Fta*pa.dot(pH), epsa, epsHapart1);
     cmult(-1.*Fta*cdot(pHcur,epsa), pacur, epsHapart2);
     cmult(Ft1*cdot(pHcur,conjeps1), p1cur, conjepsH1part1);
     cmult(-Ft1*p1.dot(pH), conjeps1, conjepsH1part2);
     cadd(epsHapart1, epsHapart2, epsHa);
     cadd(conjepsH1part1, conjepsH1part2, conjepsH1);
 
     current T1,T2,T3,T4,T5a,T5b,T6,T7,T8a,T8b,T9,T10,T11a,
       T11b,T12a,T12b,T13;
 
     if(gluonforward){
       cmult(sqrt(2.)*sqrt(p1.plus()/pa.plus())*prop/sqa1, conjepsH1, T1);
       cmult(-sqrt(2.)*sqrt(pa.plus()/p1.plus())*prop/sqa1, epsHa, T2);
     }
     else{
       cmult(-sqrt(2.)*sqrt(p1.minus()/pa.minus())*((p1.x()-COM(0.,1.)*p1.y())/p1.perp())
           *prop/sqa1, conjepsH1, T1);
       cmult(sqrt(2.)*sqrt(pa.minus()/p1.minus())*((p1.x()+COM(0.,1.)*p1.y())/p1.perp())
           *prop/sqa1, epsHa, T2);
     }
 
     const COM boxdiagFact = 8.*COM(0.,1.)*M_PI*M_PI;
 
     cmult(aH1*sqrt(2.)/sqa1, epsHa, T3);
     cmult(oneHa*sqrt(2.)/sqa1, conjepsH1, T4);
     cmult(-2.*phase*Fta*pa.dot(pH), p1cur, T5a);
     cmult(2.*phase*Ft1*p1.dot(pH), pacur, T5b);
     cmult(-sqrt(2.)*Fta*p1.dot(pa)*oneHa/sqa1, conjeps1, T6);
     cmult(-sqrt(2.)*Ft1*pa.dot(p1)*aH1/sqa1, epsa, T7);
 
     cmult(-boxdiagFact*phase*h2, pacur, T8a);
     cmult(boxdiagFact*phase*h1, p1cur, T8b);
     cmult(boxdiagFact*aH1/sqrt(2.)/sqa1*h5, epsa, T9);
     cmult(-boxdiagFact*oneHa/sqrt(2.)/sqa1*h4, conjeps1, T10);
     cmult(boxdiagFact*aH1*oneHa/2./sqa1/sqa1*h10, pacur, T11a);
     cmult(-boxdiagFact*aH1*oneHa/2./sqa1/sqa1*h12, p1cur, T11b);
 
     cmult(-phase/(pa-p1).m2()*Falpha*(p1-pa).dot(pa-p1-pH), paplusp1cur, T12a);
     cmult(phase/(pa-p1).m2()*Falpha*(pa+p1).dot(pa-p1-pH), p1minuspacur, T12b);
     cmult(-phase*Fbeta*(pa-p1-pH).m2(), paplusp1cur, T13);
 
     current ans;
     for(int i=0;i<4;i++)
     {
       ans[i] = T1[i]+T2[i]+T3[i]+T4[i]+T5a[i]+T5b[i]+T6[i]+T7[i]+T8a[i]+T8b[i]+T9[i]+T10[i]+T11a[i]+T11b[i]+T12a[i]+T12b[i]+T13[i];
     }
 
     retAns[0] = F/prop*ans[0];
     retAns[1] = F/prop*ans[1];
     retAns[2] = F/prop*ans[2];
     retAns[3] = F/prop*ans[3];
   }
 
 } // namespace anonymous
 // JDC - new amplitude with Higgs emitted close to gluon with full mt effects. Keep usual HEJ-style function call
 double MH2gq_outsideH(CLHEP::HepLorentzVector p1out, CLHEP::HepLorentzVector p1in, CLHEP::HepLorentzVector p2out, CLHEP::HepLorentzVector p2in, CLHEP::HepLorentzVector pH, double mq, bool includeBottom, double mq2)
 {
 
   current cur2bplus,cur2bminus, cur2bplusFlip, cur2bminusFlip;
   current retAns,retAnsb;
   joi(p2out,true,p2in,true,cur2bplus);
   joi(p2out,false,p2in,false,cur2bminus);
   joi(ParityFlip(p2out),true,ParityFlip(p2in),true,cur2bplusFlip);
   joi(ParityFlip(p2out),false,ParityFlip(p2in),false,cur2bminusFlip);
 
   COM app1,app2,apm1,apm2;
   COM app3, app4, apm3, apm4;
 
   if(!includeBottom)
   {
     g_gH_HC(p1in,p1out,pH,mq,retAns);
     app1=cdot(retAns,cur2bplus);
     app2=cdot(retAns,cur2bminus);
 
     g_gH_HC(ParityFlip(p1in),ParityFlip(p1out),ParityFlip(pH),mq,retAns);
     app3=cdot(retAns,cur2bplusFlip);
     app4=cdot(retAns,cur2bminusFlip);
 
     // And non-conserving bits
     g_gH_HNC(p1in,p1out,pH,mq,retAns);
     apm1=cdot(retAns,cur2bplus);
     apm2=cdot(retAns,cur2bminus);
 
     g_gH_HNC(ParityFlip(p1in),ParityFlip(p1out),ParityFlip(pH),mq,retAns);
     apm3=cdot(retAns,cur2bplusFlip);
     apm4=cdot(retAns,cur2bminusFlip);
   } else {
     g_gH_HC(p1in,p1out,pH,mq,retAns);
     g_gH_HC(p1in,p1out,pH,mq2,retAnsb);
     app1=cdot(retAns,cur2bplus) + cdot(retAnsb,cur2bplus);
     app2=cdot(retAns,cur2bminus) + cdot(retAnsb,cur2bminus);
 
     g_gH_HC(ParityFlip(p1in),ParityFlip(p1out),ParityFlip(pH),mq,retAns);
     g_gH_HC(ParityFlip(p1in),ParityFlip(p1out),ParityFlip(pH),mq2,retAnsb);
     app3=cdot(retAns,cur2bplusFlip) + cdot(retAnsb,cur2bplusFlip);
     app4=cdot(retAns,cur2bminusFlip) + cdot(retAnsb,cur2bminusFlip);
 
     // And non-conserving bits
     g_gH_HNC(p1in,p1out,pH,mq,retAns);
     g_gH_HNC(p1in,p1out,pH,mq2,retAnsb);
     apm1=cdot(retAns,cur2bplus) + cdot(retAnsb,cur2bplus);
     apm2=cdot(retAns,cur2bminus) + cdot(retAnsb,cur2bminus);
 
     g_gH_HNC(ParityFlip(p1in),ParityFlip(p1out),ParityFlip(pH),mq,retAns);
     g_gH_HNC(ParityFlip(p1in),ParityFlip(p1out),ParityFlip(pH),mq2,retAnsb);
     apm3=cdot(retAns,cur2bplusFlip) + cdot(retAnsb,cur2bplusFlip);
     apm4=cdot(retAns,cur2bminusFlip) + cdot(retAnsb,cur2bminusFlip);
   }
 
   return abs2(app1) + abs2(app2) + abs2(app3) + abs2(app4) + abs2(apm1)
     + abs2(apm2) + abs2(apm3) + abs2(apm4);
 }
 #endif // HEJ_BUILD_WITH_QCDLOOP
 
 double C2gHgm(CLHEP::HepLorentzVector p2, CLHEP::HepLorentzVector p1, CLHEP::HepLorentzVector pH)
 {
   static double A=1./(3.*M_PI*HEJ::vev);
   // Implements Eq. (4.22) in hep-ph/0301013 with modifications to incoming plus momenta
   double s12,p1p,p2p;
   COM p1perp,p3perp,phperp;
   // Determine first whether this is the case p1p\sim php>>p3p og the opposite
   s12=p1.invariantMass2(-p2);
   if (p2.pz()>0.) { // case considered in hep-ph/0301013
     p1p=p1.plus();
     p2p=p2.plus();
   } else { // opposite case
     p1p=p1.minus();
     p2p=p2.minus();
   }
   p1perp=p1.px()+COM(0,1)*p1.py();
   phperp=pH.px()+COM(0,1)*pH.py();
   p3perp=-(p1perp+phperp);
 
   COM temp=COM(0,1)*A/(2.*s12)*(p2p/p1p*conj(p1perp)*p3perp+p1p/p2p*p1perp*conj(p3perp));
   temp=temp*conj(temp);
   return temp.real();
 }
 
 double C2gHgp(CLHEP::HepLorentzVector p2, CLHEP::HepLorentzVector p1, CLHEP::HepLorentzVector pH)
 {
   static double A=1./(3.*M_PI*HEJ::vev);
   // Implements Eq. (4.23) in hep-ph/0301013
   double s12,php,p1p,phm;
   COM p1perp,p3perp,phperp;
   // Determine first whether this is the case p1p\sim php>>p3p or the opposite
   s12=p1.invariantMass2(-p2);
   if (p2.pz()>0.) { // case considered in hep-ph/0301013
     php=pH.plus();
     phm=pH.minus();
     p1p=p1.plus();
   } else { // opposite case
     php=pH.minus();
     phm=pH.plus();
     p1p=p1.minus();
   }
   p1perp=p1.px()+COM(0,1)*p1.py();
   phperp=pH.px()+COM(0,1)*pH.py();
   p3perp=-(p1perp+phperp);
 
   COM temp=-COM(0,1)*A/(2.*s12)*( conj(p1perp*p3perp)*pow(php/p1p,2)/(1.+php/p1p)
     +s12*(pow(conj(phperp),2)/(pow(abs(phperp),2)+p1p*phm)
       -pow(conj(p3perp)
       +(1.+php/p1p)*conj(p1perp),2)/((1.+php/p1p)*(pH.m2()+2.*p1.dot(pH)))) );
   temp=temp*conj(temp);
   return temp.real();
 }
 
 double C2qHqm(CLHEP::HepLorentzVector p2, CLHEP::HepLorentzVector p1, CLHEP::HepLorentzVector pH)
 {
   static double A=1./(3.*M_PI*HEJ::vev);
   // Implements Eq. (4.22) in hep-ph/0301013
   double s12,p2p,p1p;
   COM p1perp,p3perp,phperp;
   // Determine first whether this is the case p1p\sim php>>p3p or the opposite
   s12=p1.invariantMass2(-p2);
   if (p2.pz()>0.) { // case considered in hep-ph/0301013
     p2p=p2.plus();
     p1p=p1.plus();
   } else { // opposite case
     p2p=p2.minus();
     p1p=p1.minus();
   }
   p1perp=p1.px()+COM(0,1)*p1.py();
   phperp=pH.px()+COM(0,1)*pH.py();
   p3perp=-(p1perp+phperp);
 
   COM temp=A/(2.*s12)*( sqrt(p2p/p1p)*p3perp*conj(p1perp)
     +sqrt(p1p/p2p)*p1perp*conj(p3perp) );
   temp=temp*conj(temp);
   return temp.real();
 }
diff --git a/src/get_analysis.cc b/src/get_analysis.cc
index dfcc5d5..14ee951 100644
--- a/src/get_analysis.cc
+++ b/src/get_analysis.cc
@@ -1,38 +1,38 @@
 /**
- *  \authors   Jeppe Andersen, Tuomas Hapola, Marian Heil, Andreas Maier, Jennifer Smillie
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
  *  \date      2019
  *  \copyright GPLv2 or later
  */
 #include "HEJ/get_analysis.hh"
 
 #include <dlfcn.h>
 #include <string>
 
 #include "yaml-cpp/yaml.h"
 
 #include "HEJ/EmptyAnalysis.hh"
 #include "HEJ/RivetAnalysis.hh"
 
 namespace HEJ{
 
   std::unique_ptr<Analysis> get_analysis(YAML::Node const & parameters){
     if(!parameters["plugin"]){
       if(parameters["rivet"])
         return RivetAnalysis::create(parameters);
       return EmptyAnalysis::create(parameters);
     }
 
     using AnalysisMaker = std::unique_ptr<Analysis> (*)(YAML::Node);
     const auto plugin_name = parameters["plugin"].as<std::string>();
     auto handle = dlopen(plugin_name.c_str(), RTLD_NOW);
     char * error = dlerror();
     if(error != nullptr) throw std::runtime_error(error);
 
     void * sym = dlsym(handle, "make_analysis");
     error = dlerror();
     if(error != nullptr) throw std::runtime_error(error);
     auto make_analysis = reinterpret_cast<AnalysisMaker>(sym);
 
     return make_analysis(parameters);
   }
 }
diff --git a/src/kinematics.cc b/src/kinematics.cc
index a878155..ce88056 100644
--- a/src/kinematics.cc
+++ b/src/kinematics.cc
@@ -1,27 +1,27 @@
 /**
- *  \authors   Jeppe Andersen, Tuomas Hapola, Marian Heil, Andreas Maier, Jennifer Smillie
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
  *  \date      2019
  *  \copyright GPLv2 or later
  */
 #include "HEJ/kinematics.hh"
 
 #include "fastjet/PseudoJet.hh"
 
 #include "HEJ/Particle.hh"
 
 namespace HEJ{
   //reconstruct incoming momenta from momentum conservation
     std::tuple<fastjet::PseudoJet, fastjet::PseudoJet> incoming_momenta(
         std::vector<Particle> const & outgoing
     ){
       double xa(0.), xb(0.);
       for(auto const & out: outgoing){
         xa += out.p.e() - out.p.pz();
         xb += out.p.e() + out.p.pz();
       }
       return std::tuple<fastjet::PseudoJet, fastjet::PseudoJet>{
         {0,0,-xa/2.,xa/2.},
         {0,0,xb/2.,xb/2.}
       };
     }
 }
diff --git a/src/make_RNG.cc b/src/make_RNG.cc
index b1a3661..d96c67f 100644
--- a/src/make_RNG.cc
+++ b/src/make_RNG.cc
@@ -1,35 +1,35 @@
 /**
- *  \authors   Jeppe Andersen, Tuomas Hapola, Marian Heil, Andreas Maier, Jennifer Smillie
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
  *  \date      2019
  *  \copyright GPLv2 or later
  */
 #include "HEJ/make_RNG.hh"
 
 #include <algorithm>
 #include <locale>
 
 #include "HEJ/exceptions.hh"
 #include "HEJ/Mixmax.hh"
 #include "HEJ/Ranlux64.hh"
 
 namespace HEJ {
   std::unique_ptr<HEJ::RNG> make_RNG(
       std::string const & name,
       optional<std::string> const & seed
   ) {
     std::string lname;
     std::transform(
         begin(name), end(name), std::back_inserter(lname),
         [](char c) { return std::tolower(c, std::locale()); }
     );
     if(lname == "mixmax") {
       if(seed) return std::make_unique<Mixmax>(std::stol(*seed));
       return std::make_unique<Mixmax>();
     }
     if(lname == "ranlux64") {
       if(seed) return std::make_unique<Ranlux64>(*seed);
       return std::make_unique<Ranlux64>();
     }
     throw std::invalid_argument{"Unknown random number generator: " + name};
   }
 }
diff --git a/src/make_writer.cc b/src/make_writer.cc
index 0e15d61..5a7efaf 100644
--- a/src/make_writer.cc
+++ b/src/make_writer.cc
@@ -1,31 +1,31 @@
 /**
- *  \authors   Jeppe Andersen, Tuomas Hapola, Marian Heil, Andreas Maier, Jennifer Smillie
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
  *  \date      2019
  *  \copyright GPLv2 or later
  */
 #include "HEJ/make_writer.hh"
 
 #include "HEJ/exceptions.hh"
 #include "HEJ/HepMCWriter.hh"
 #include "HEJ/LesHouchesWriter.hh"
 
 namespace HEJ{
   std::unique_ptr<EventWriter> make_format_writer(
       FileFormat format, std::string const & outfile,
       LHEF::HEPRUP const & heprup
   ){
     switch(format){
     case Les_Houches:
       return std::unique_ptr<EventWriter>{
         new LesHouchesWriter{outfile, heprup}
       };
     case HepMC:
       return std::unique_ptr<EventWriter>{
         new HepMCWriter{outfile, heprup}
       };
     default:
       throw std::logic_error("unhandled file format");
     }
   }
 
 }
diff --git a/src/resummation_jet.cc b/src/resummation_jet.cc
index 0c41dbe..28cec70 100644
--- a/src/resummation_jet.cc
+++ b/src/resummation_jet.cc
@@ -1,113 +1,113 @@
 /**
- *  \authors   Jeppe Andersen, Tuomas Hapola, Marian Heil, Andreas Maier, Jennifer Smillie
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
  *  \date      2019
  *  \copyright GPLv2 or later
  */
 #include "HEJ/resummation_jet.hh"
 
 #include <assert.h>
 #include <math.h>
 #include <stdio.h>
 
 #include <boost/numeric/ublas/lu.hpp>
 #include <boost/numeric/ublas/matrix.hpp>
 
 #include "fastjet/PseudoJet.hh"
 
 #include "HEJ/utility.hh"
 
 namespace HEJ{
 
   std::vector<fastjet::PseudoJet> resummation_jet_momenta(
       std::vector<fastjet::PseudoJet> const & p_born,
       fastjet::PseudoJet const & qperp
   ) {
     // for "new" reshuffling p^B = p + qperp*|p^B|/P^B
     double Pperp_born = 0.;
     for(auto const & p: p_born) Pperp_born += p.perp();
 
     std::vector<fastjet::PseudoJet> p_res;
     p_res.reserve(p_born.size());
     for(auto & pB: p_born) {
       const double px = pB.px() - qperp.px()*pB.perp()/Pperp_born;
       const double py = pB.py() - qperp.py()*pB.perp()/Pperp_born;
       const double pperp = sqrt(px*px + py*py);
       // keep the rapidities fixed
       const double pz = pperp*sinh(pB.rapidity());
       const double E = pperp*cosh(pB.rapidity());
       p_res.emplace_back(px, py, pz, E);
       assert(
           HEJ::nearby_ep(
               p_res.back().rapidity(),
               pB.rapidity(),
               1e-5
           )
       );
     }
     return p_res;
   }
 
   namespace{
     enum coordinates : size_t {
       x1, x2
     };
 
 
     namespace ublas = boost::numeric::ublas;
 
     template<class Matrix>
     double det(ublas::matrix_expression<Matrix> const& m) {
 
       ublas::permutation_matrix<size_t> pivots{m().size1()};
       Matrix mLu{m()};
 
       const auto is_singular = lu_factorize(mLu, pivots);
 
       if(is_singular) return 0.;
 
       double det = 1.0;
       for (std::size_t i = 0; i < pivots.size(); ++i){
         if (pivots(i) != i) det = -det;
 
         det *= mLu(i,i);
       }
 
       return det;
     }
 
     using ublas::matrix;
   }
 
   double resummation_jet_weight(
       std::vector<fastjet::PseudoJet> const & p_born,
       fastjet::PseudoJet const & qperp
   ) {
 
     static constexpr int num_coordinates = 2;
     auto Jacobian = matrix<double>{
       num_coordinates*p_born.size(),
       num_coordinates*p_born.size()
     };
     double P_perp = 0.;
     for(auto const & J: p_born) P_perp += J.perp();
 
     for(size_t l = 0; l < p_born.size(); ++l){
       const double Jl_perp = p_born[l].perp();
       for(size_t lp = 0; lp < p_born.size(); ++lp){
         const int delta_l = l == lp;
         const double Jlp_perp = p_born[lp].perp();
         for(size_t x = x1; x <= x2; ++x){
           for(size_t xp = x1; xp <= x2; ++xp){
             const int delta_x = x == xp;
             Jacobian(2*l + x, 2*lp + xp) =
               + delta_l*delta_x
               - qperp[x]*p_born[lp][xp]/(P_perp*Jlp_perp)*(
                   + delta_l - Jl_perp/P_perp
               );
           }
         }
       }
     }
     return det(Jacobian);
   }
 }
diff --git a/src/stream.cc b/src/stream.cc
index d856096..c4843eb 100644
--- a/src/stream.cc
+++ b/src/stream.cc
@@ -1,32 +1,32 @@
 /**
- *  \authors   Jeppe Andersen, Tuomas Hapola, Marian Heil, Andreas Maier, Jennifer Smillie
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
  *  \date      2019
  *  \copyright GPLv2 or later
  */
 #include "HEJ/stream.hh"
 
 #include <boost/iostreams/filter/gzip.hpp>
 
 namespace HEJ{
 
   namespace{
     bool is_gzip(std::ifstream & file){
       static constexpr char magic_bytes[] = {'\x1f', '\x8b'};
       if(file.peek() != magic_bytes[0]) return false;
       file.get();
       const char second = file.peek();
       file.unget();
       return second == magic_bytes[1];
     }
   }
 
   istream::istream(std::string const & filename):
     file_{filename, std::ios_base::in | std::ios_base::binary},
     stream_{new boost_istream()}
   {
     if(is_gzip(file_)){
       stream_->push(boost::iostreams::gzip_decompressor{});
     }
     stream_->push(file_);
   }
 }
diff --git a/t/check_hepmc.cc b/t/check_hepmc.cc
index 066f510..f6b7620 100644
--- a/t/check_hepmc.cc
+++ b/t/check_hepmc.cc
@@ -1,21 +1,26 @@
+/**
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
+ *  \date      2019
+ *  \copyright GPLv2 or later
+ */
 #include <iostream>
 #include <stdexcept>
 
 #include "HepMC/ReaderAscii.h"
 
 static constexpr double ep = 1e-3;
 
 int main(int argn, char** argv) {
   if(argn != 2){
     std::cerr << "Usage: check_hepmc hepmc_file\n";
     return EXIT_FAILURE;
   }
 
   HepMC::ReaderAscii input{argv[1]};
   if(input.failed()) throw std::runtime_error{"failed to open HepMC file"};
   while(true){
     HepMC::GenEvent ev{};
     if ( !input.read_event(ev) || ev.event_number() == 0 ) break;
     if(input.failed()) throw std::runtime_error{"failed to read HepMC event"};
   }
 }
diff --git a/t/check_lhe.cc b/t/check_lhe.cc
index 0823d48..dd60ee7 100644
--- a/t/check_lhe.cc
+++ b/t/check_lhe.cc
@@ -1,29 +1,34 @@
+/**
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
+ *  \date      2019
+ *  \copyright GPLv2 or later
+ */
 #include <iostream>
 
 #include "HEJ/stream.hh"
 #include "LHEF/LHEF.h"
 
 static constexpr double ep = 1e-3;
 
 int main(int argn, char** argv) {
   if(argn != 2){
     std::cerr << "Usage: check_lhe lhe_file\n";
     return EXIT_FAILURE;
   }
 
   HEJ::istream in{argv[1]};
   LHEF::Reader reader{in};
 
   std::vector<double> xsec_ref(reader.heprup.NPRUP, 0.);
   while(reader.readEvent()){
     xsec_ref[reader.hepeup.IDPRUP-1] += reader.hepeup.weight();
   }
 
   for(size_t i = 0; i < xsec_ref.size(); ++i){
     std::cout << xsec_ref[i] << '\t' << reader.heprup.XSECUP[i] << '\n';
     if(std::abs(xsec_ref[i]/reader.heprup.XSECUP[i] - 1) > ep){
       std::cerr << "Cross sections deviate substantially";
       return EXIT_FAILURE;
     }
   }
 }
diff --git a/t/check_res.cc b/t/check_res.cc
index 26ce3c1..e9374c0 100644
--- a/t/check_res.cc
+++ b/t/check_res.cc
@@ -1,136 +1,141 @@
+/**
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
+ *  \date      2019
+ *  \copyright GPLv2 or later
+ */
 #include <iostream>
 
 #include "LHEF/LHEF.h"
 
 #include "HEJ/Event.hh"
 #include "HEJ/EventReweighter.hh"
 #include "HEJ/Mixmax.hh"
 #include "HEJ/stream.hh"
 
 #define ASSERT(x) if(!(x)) { \
     std::cerr << "Assertion '" #x "' failed.\n"; \
     return EXIT_FAILURE; \
   }
 
 namespace{
   const fastjet::JetDefinition jet_def{fastjet::kt_algorithm, 0.4};
   const fastjet::JetDefinition Born_jet_def{jet_def};
   constexpr double Born_jetptmin = 30;
   constexpr double extpartonptmin = 30;
   constexpr double max_ext_soft_pt_fraction =
     std::numeric_limits<double>::infinity();
   constexpr double jetptmin = 35;
   constexpr bool log_corr = false;
   using EventTreatment = HEJ::EventTreatment;
   using namespace HEJ::event_type;
   HEJ::EventTreatMap treat{
     {no_2_jets, EventTreatment::discard},
     {bad_final_state, EventTreatment::discard},
     {nonHEJ, EventTreatment::discard},
     {unof, EventTreatment::discard},
     {unob, EventTreatment::discard},
     {qqxexb, EventTreatment::discard},
     {qqxexf, EventTreatment::discard},
     {qqxmid, EventTreatment::discard},
     {FKL, EventTreatment::reweight}
   };
 
   /// true if colour is allowed for particle
   bool correct_colour(HEJ::Particle const & part){
     if(HEJ::is_AWZH_boson(part) && !part.colour) return true;
     if(!part.colour) return false;
     int const colour = part.colour->first;
     int const anti_colour = part.colour->second;
     if(part.type == HEJ::ParticleID::gluon)
       return colour != anti_colour && colour > 0 && anti_colour > 0;
     if(HEJ::is_quark(part))
       return anti_colour == 0 && colour > 0;
     return colour == 0 && anti_colour > 0;
   }
   bool correct_colour(HEJ::Event const & ev){
     if(!HEJ::event_type::is_HEJ(ev.type()))
       return true;
     for(auto const & part: ev.incoming()){
       if(!correct_colour(part))
         return false;
     }
     for(auto const & part: ev.outgoing()){
       if(!correct_colour(part))
         return false;
     }
     return true;
   }
 };
 
 int main(int argn, char** argv) {
   if(argn == 5 && std::string(argv[4]) == "uno"){
     --argn;
     treat[unof] = EventTreatment::reweight;
     treat[unob] = EventTreatment::reweight;
     treat[FKL] = EventTreatment::discard;
   }
   if(argn != 4){
     std::cerr << "Usage: check_res eventfile xsection tolerance [uno]";
     return EXIT_FAILURE;
   }
 
   const double xsec_ref = std::stod(argv[2]);
   const double tolerance = std::stod(argv[3]);
 
   HEJ::istream in{argv[1]};
   LHEF::Reader reader{in};
 
   HEJ::PhaseSpacePointConfig psp_conf;
   psp_conf.jet_param = HEJ::JetParameters{jet_def, jetptmin};
   psp_conf.min_extparton_pt = extpartonptmin;
   psp_conf.max_ext_soft_pt_fraction = max_ext_soft_pt_fraction;
   HEJ::MatrixElementConfig ME_conf;
   ME_conf.log_correction = log_corr;
   ME_conf.Higgs_coupling = HEJ::HiggsCouplingSettings{};
   HEJ::EventReweighterConfig conf;
   conf.psp_config = std::move(psp_conf);
   conf.ME_config = std::move(ME_conf);
   conf.jet_param = psp_conf.jet_param;
   conf.treat = treat;
 
   reader.readEvent();
   const bool has_Higgs = std::find(
       begin(reader.hepeup.IDUP),
       end(reader.hepeup.IDUP),
       25
   ) != end(reader.hepeup.IDUP);
   const double mu = has_Higgs?125.:91.188;
   HEJ::ScaleGenerator scale_gen{
     {{std::to_string(mu), HEJ::FixedScale{mu}}}, {}, 1.
   };
   HEJ::Mixmax ran{};
   HEJ::EventReweighter hej{reader.heprup, std::move(scale_gen), conf, ran};
 
   double xsec = 0.;
   double xsec_err = 0.;
   do{
     auto ev_data = HEJ::Event::EventData{reader.hepeup};
     ev_data.reconstruct_intermediate();
     HEJ::Event ev{
       ev_data.cluster(
         Born_jet_def, Born_jetptmin
       )
     };
     auto resummed_events = hej.reweight(ev, 100);
     for(auto const & ev: resummed_events) {
       ASSERT(correct_colour(ev));
       xsec += ev.central().weight;
       xsec_err +=  ev.central().weight*ev.central().weight;
     }
   } while(reader.readEvent());
   xsec_err = std::sqrt(xsec_err);
   const double significance =
     std::abs(xsec - xsec_ref) / std::sqrt( xsec_err*xsec_err + tolerance*tolerance );
   std::cout << xsec_ref << " +/- " << tolerance << " ~ "
     << xsec << " +- " << xsec_err << " => " << significance << " sigma\n";
 
   if(significance > 3.){
     std::cerr << "Cross section is off by over 3 sigma!\n";
     return EXIT_FAILURE;
   }
 }
diff --git a/t/scales.cc b/t/scales.cc
index 64ca2c4..220ad1c 100644
--- a/t/scales.cc
+++ b/t/scales.cc
@@ -1,8 +1,13 @@
+/**
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
+ *  \date      2019
+ *  \copyright GPLv2 or later
+ */
 #include "HEJ/Event.hh"
 
 extern "C"
 __attribute__((visibility("default")))
 double softest_jet_pt(HEJ::Event const & ev){
   const auto softest_jet = sorted_by_pt(ev.jets()).back();
   return softest_jet.perp();
 }
diff --git a/t/test_ME_generic.cc b/t/test_ME_generic.cc
index 7ad3f45..d59e3e0 100644
--- a/t/test_ME_generic.cc
+++ b/t/test_ME_generic.cc
@@ -1,132 +1,140 @@
+/**
+ *  \brief     Generic tester for the ME for a given set of PSP
+ *
+ *  \note      reference weights and PSP (as LHE file) have to be given as
+ *             _individual_ files
+ *
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
+ *  \date      2019
+ *  \copyright GPLv2 or later
+ */
 
-// Generic tester for the ME for a given set of PSP
-// reference weights and PSP (as LHE file) have to be given as _individual_ files
 
 #include <fstream>
 #include <random>
 #include <algorithm>
 
 #include "LHEF/LHEF.h"
 
 #include "HEJ/MatrixElement.hh"
 #include "HEJ/Event.hh"
 #include "HEJ/YAMLreader.hh"
 #include "HEJ/stream.hh"
 
 constexpr double alpha_s = 0.118;
 constexpr double ep = 1e-5;
 
 void shuffle_particles(HEJ::Event::EventData & ev) {
   static std::mt19937_64 ran{0};
   std::shuffle(begin(ev.incoming), end(ev.incoming), ran);
   std::shuffle(begin(ev.outgoing), end(ev.outgoing), ran);
 }
 
 void dump(HEJ::Event const & ev){
   {
     LHEF::Writer writer{std::cout};
     std::cout << std::setprecision(6);
     writer.hepeup = to_HEPEUP(std::move(ev), nullptr);
     writer.writeEvent();
   }
   std::cout << "Rapidity ordering:\n";
   for(const auto & part: ev.outgoing()){
     std::cout << std::setw(2) << part.type << ": "<<  std::setw(7) << part.rapidity() << std::endl;
   }
 }
 
 enum MEComponent {tree, virt};
 
 MEComponent guess_component(std::string const & data_file) {
   if(data_file.find("virt") != data_file.npos) return MEComponent::virt;
   return MEComponent::tree;
 }
 
 int main(int argn, char** argv){
   if(argn != 4 && argn != 5){
     std::cerr << "\n# Usage:\n."<< argv[0] <<" config.yml ME_weights input_file.lhe\n\n";
     return EXIT_FAILURE;
   }
   bool OUTPUT_MODE = false;
   if(argn == 5 && std::string("OUTPUT")==std::string(argv[4]))
       OUTPUT_MODE = true;
   const HEJ::Config config = HEJ::load_config(argv[1]);
 
   std::fstream wgt_file;
   if ( OUTPUT_MODE ) {
     std::cout << "_______________________USING OUTPUT MODE!_______________________" << std::endl;
     wgt_file.open(argv[2], std::fstream::out);
     wgt_file.precision(10);
   } else {
     wgt_file.open(argv[2], std::fstream::in);
   }
 
   HEJ::istream in{argv[3]};
   LHEF::Reader reader{in};
 
   const auto component = guess_component(argv[2]);
 
   HEJ::MatrixElement ME{
     [](double){ return alpha_s; },
     HEJ::to_MatrixElementConfig(config)
   };
   double max_ratio = 0.;
   size_t idx_max_ratio = 0;
 
   HEJ::Event ev_max_ratio(HEJ::Event::EventData{}.cluster(
       config.resummation_jets.def,0
     )
   );
   double av_ratio = 0;
 
   size_t i = 0;
   while(reader.readEvent()){
     ++i;
 
     HEJ::Event::EventData data{reader.hepeup};
     shuffle_particles(data);
 
     HEJ::Event event{
       data.cluster(
         config.resummation_jets.def,
         config.resummation_jets.min_pt
       )
     };
     const double our_ME = (component == MEComponent::tree)?
       ME.tree(event).central:
       ME.virtual_corrections(event).central
       ;
 
     if ( OUTPUT_MODE ) {
       wgt_file << our_ME << std::endl;
     } else {
       std::string line;
       if(!std::getline(wgt_file,line)) break;
       const double ref_ME = std::stod(line);
       const double diff = std::abs(our_ME/ref_ME-1.);
       av_ratio+=diff;
       if( diff > max_ratio ) {
         max_ratio = diff;
         idx_max_ratio = i;
         ev_max_ratio = event;
       }
       if( diff > ep ){
         size_t precision(std::cout.precision());
         std::cout.precision(16);
         std::cout<< "Large difference in PSP " << i << "\nis: "<<our_ME << " should: " << ref_ME << " => difference: " << diff << std::endl;
         std::cout.precision(precision);
         dump(event);
         return EXIT_FAILURE;
       }
     }
   }
   wgt_file.close();
   if ( !OUTPUT_MODE ) {
     size_t precision(std::cout.precision());
     std::cout.precision(16);
     std::cout << "Avg ratio after " << i << " PSP: " << av_ratio/i << std::endl;
     std::cout << "maximal ratio at " << idx_max_ratio << ": " << max_ratio << std::endl;
     std::cout.precision(precision);
   }
   return EXIT_SUCCESS;
 }
diff --git a/t/test_classify.cc b/t/test_classify.cc
index 13081e3..34276ac 100644
--- a/t/test_classify.cc
+++ b/t/test_classify.cc
@@ -1,62 +1,67 @@
+/**
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
+ *  \date      2019
+ *  \copyright GPLv2 or later
+ */
 #include <random>
 #include <algorithm>
 
 #include "LHEF/LHEF.h"
 #include "HEJ/stream.hh"
 #include "HEJ/event_types.hh"
 #include "HEJ/Event.hh"
 
 namespace{
   constexpr double min_jet_pt = 30.;
   const fastjet::JetDefinition jet_def{fastjet::kt_algorithm, 0.4};
   using namespace HEJ::event_type;
   static const std::vector<EventType> results{
     unob,FKL,FKL,FKL,FKL,FKL,FKL,unob,FKL,unob,FKL,FKL,FKL,unof,FKL,unob,FKL,
     FKL,unob,unob,FKL,FKL,unob,FKL,FKL,FKL,FKL,FKL,FKL,FKL,FKL,FKL,FKL,FKL,unof,
     FKL,FKL,unof,FKL,FKL,FKL,FKL,FKL,unof,FKL,FKL,FKL,unof,FKL,FKL,unob,unof,
     FKL,unof,FKL,unob,FKL,FKL,unob,FKL,unob,unof,unob,unof,FKL,FKL,FKL,FKL,FKL,
     FKL,FKL,FKL,FKL,FKL,FKL,FKL,unob,FKL,FKL,FKL,FKL,FKL,FKL,FKL,FKL,unob,FKL,
     FKL,FKL,FKL,unof,FKL,unob,FKL,FKL,FKL,FKL,FKL,FKL,FKL,FKL,FKL,FKL,unob,FKL,
     FKL,FKL,FKL,FKL,unob,FKL,unob,unob,FKL,FKL,FKL,FKL,FKL,FKL,FKL,FKL,FKL,unof,unob,FKL
   };
 
   void shuffle_particles(HEJ::Event::EventData & ev) {
     static std::mt19937_64 ran{0};
     std::shuffle(begin(ev.incoming), end(ev.incoming), ran);
     std::shuffle(begin(ev.outgoing), end(ev.outgoing), ran);
   }
 }
 
 int main(int argn, char** argv) {
   if(argn != 2){
     std::cerr << "Usage: test_classify eventfile";
     return EXIT_FAILURE;
   }
 
   HEJ::istream in{argv[1]};
   LHEF::Reader reader{in};
   LHEF::Writer writer{std::cerr};
   writer.heprup = reader.heprup;
 
   for(auto const & expected: results){
     reader.readEvent();
     HEJ::Event::EventData data{reader.hepeup};
     shuffle_particles(data);
     const HEJ::Event ev{
       data.cluster(
         jet_def, min_jet_pt
       )
     };
 
     if(ev.type() != expected){
       using HEJ::event_type::names;
       writer.hepeup = reader.hepeup;
       std::cerr << "wrong classification of event:\n";
       writer.writeEvent();
       std::cerr << "classified as " << names[ev.type()]
                 << ", is " << names[expected] << '\n';
       return EXIT_FAILURE;
     }
   }
 
 }
diff --git a/t/test_colours.cc b/t/test_colours.cc
index d2c29e6..6f0fea1 100644
--- a/t/test_colours.cc
+++ b/t/test_colours.cc
@@ -1,221 +1,226 @@
+/**
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
+ *  \date      2019
+ *  \copyright GPLv2 or later
+ */
 #include <random>
 #include <stdexcept>
 #include <utility>
 
 #include "HEJ/Event.hh"
 #include "HEJ/RNG.hh"
 
 #define ASSERT(x) if(!(x)) { \
     throw std::logic_error("Assertion '" #x "' failed."); \
   }
 
 /// biased RNG to connect always to colour
 class dum_rnd: public HEJ::DefaultRNG {
 public:
   dum_rnd() = default;
   double flat() override {
     return 0.;
   };
 };
 
 void shuffle_particles(HEJ::Event::EventData & ev) {
   static std::mt19937_64 ran{0};
   std::shuffle(begin(ev.incoming), end(ev.incoming), ran);
   std::shuffle(begin(ev.outgoing), end(ev.outgoing), ran);
 }
 
 void dump_event(HEJ::Event const & ev){
   for(auto const & in: ev.incoming()){
     std::cerr << "in type=" << in.type
       << ", colour={" << (*in.colour).first
       << ", " << (*in.colour).second << "}\n";
   }
   for(auto const & out: ev.outgoing()){
     std::cerr << "out type=" << out.type << ", colour={";
     if(out.colour)
       std::cerr << (*out.colour).first << ", " << (*out.colour).second;
     else
       std::cerr << "non, non";
     std::cerr << "}\n";
   }
 }
 
 /// true if colour is allowed for particle
 bool correct_colour(HEJ::Particle const & part){
   if(HEJ::is_AWZH_boson(part) && !part.colour) return true;
   if(!part.colour) return false;
   int const colour = part.colour->first;
   int const anti_colour = part.colour->second;
   if(part.type == HEJ::ParticleID::gluon)
     return colour != anti_colour && colour > 0 && anti_colour > 0;
   if(HEJ::is_quark(part))
     return anti_colour == 0 && colour > 0;
   return colour == 0 && anti_colour > 0;
 }
 
 bool correct_colour(HEJ::Event const & ev){
   for(auto const & part: ev.incoming()){
     if(!correct_colour(part))
       return false;
   }
   for(auto const & part: ev.outgoing()){
     if(!correct_colour(part))
       return false;
   }
   return true;
 }
 
 bool match_expected(
   HEJ::Event const & ev,
   std::vector<HEJ::Colour> const & expected
 ){
   ASSERT(ev.outgoing().size()+2==expected.size());
   for(size_t i=0; i<ev.incoming().size(); ++i){
     ASSERT(ev.incoming()[i].colour);
     if( *ev.incoming()[i].colour != expected[i])
       return false;
   }
   for(size_t i=2; i<ev.outgoing().size()+2; ++i){
     if( ev.outgoing()[i-2].colour ){
       if( *ev.outgoing()[i-2].colour != expected[i] )
         return false;
     } else if( expected[i].first != 0 || expected[i].second != 0)
       return false;
   }
   return true;
 }
 
 void check_event(
   HEJ::Event::EventData unc_ev, std::vector<HEJ::Colour> const & expected_colours
 ){
   shuffle_particles(unc_ev); // make sure incoming order doesn't matter
   HEJ::Event ev{unc_ev.cluster(
     fastjet::JetDefinition(fastjet::JetAlgorithm::antikt_algorithm, 0.4), 30.)
   };
   ASSERT(HEJ::event_type::is_HEJ(ev.type()));
   dum_rnd rng;
   ASSERT(ev.generate_colours(rng));
   if(!correct_colour(ev)){
     std::cerr << "Found illegal colours for event\n";
     dump_event(ev);
     throw std::invalid_argument("Illegal colour set");
   }
   if(!match_expected(ev, expected_colours)){
     std::cerr << "Colours didn't match expectation. Found\n";
     dump_event(ev);
     std::cerr << "but expected\n";
     for(auto const & col: expected_colours){
       std::cerr << "colour={" << col.first << ", " << col.second << "}\n";
     }
     throw std::logic_error("Colours did not match expectation");
   }
 }
 
 int main() {
   HEJ::Event::EventData ev;
   std::vector<HEJ::Colour> expected_colours(7);
 
   /// pure gluon
   ev.incoming[0] =      { HEJ::ParticleID::gluon, {   0,   0,-427, 427}, {}};
   ev.incoming[1] =      { HEJ::ParticleID::gluon, {   0,   0, 851, 851}, {}};
   ev.outgoing.push_back({ HEJ::ParticleID::gluon, { 196, 124, -82, 246}, {}});
   ev.outgoing.push_back({ HEJ::ParticleID::gluon, {-167,-184,  16, 249}, {}});
   ev.outgoing.push_back({ HEJ::ParticleID::higgs, { 197, 180, 168, 339}, {}});
   ev.outgoing.push_back({ HEJ::ParticleID::gluon, {-190, -57, 126, 235}, {}});
   ev.outgoing.push_back({ HEJ::ParticleID::gluon, { -36, -63, 196, 209}, {}});
 
   expected_colours[0] = {502, 501};
   expected_colours[1] = {509, 502};
   expected_colours[2] = {503, 501};
   expected_colours[3] = {505, 503};
   expected_colours[4] = {  0,   0};
   expected_colours[5] = {507, 505};
   expected_colours[6] = {509, 507};
   check_event(ev, expected_colours);
 
   /// last g to Qx (=> gQx -> g ... Qx)
   ev.incoming[1].type = HEJ::ParticleID::d_bar;
   ev.outgoing[4].type = HEJ::ParticleID::d_bar;
   // => only end changes
   expected_colours[1].first = 0;
   expected_colours[6].first = 0;
   check_event(ev, expected_colours);
 
   {
     // don't overwrite
     auto new_expected = expected_colours;
     auto new_ev = ev;
     /// uno forward (=> gQx -> g ... Qx g)
     std::swap(new_ev.outgoing[3].type, new_ev.outgoing[4].type);
     // => uno quarks eats colour and gluon connects to anti-colour
     new_expected[5] = {0, expected_colours[3].first};
     new_expected[6] = {expected_colours[0].first, expected_colours[0].first+2};
     new_expected[1].second += 2; // one more anti-colour in line
     check_event(new_ev, new_expected);
   }
 
   /// swap Qx <-> Q (=> gQ -> g ... Q)
   ev.incoming[1].type = HEJ::ParticleID::d;
   ev.outgoing[4].type = HEJ::ParticleID::d;
   // => swap: colour<->anti && inital<->final
   std::swap(expected_colours[1], expected_colours[6]);
   std::swap(expected_colours[1].first, expected_colours[1].second);
   std::swap(expected_colours[6].first, expected_colours[6].second);
   check_event(ev, expected_colours);
 
   /// first g to qx (=> qxQ -> qx ... Q)
   ev.incoming[0].type = HEJ::ParticleID::u_bar;
   ev.outgoing[0].type = HEJ::ParticleID::u_bar;
   expected_colours[0] = {  0, 501};
   // => shift anti-colour index one up
   expected_colours[1].first -= 2;
   expected_colours[5] = expected_colours[3];
   expected_colours[3] = expected_colours[2];
   expected_colours[2] = {  0, 502};
   check_event(ev, expected_colours);
 
   {
     // don't overwrite
     auto new_expected = expected_colours;
     auto new_ev = ev;
     /// uno backward (=> qxQ -> g qx ... Q)
     std::swap(new_ev.outgoing[0].type, new_ev.outgoing[1].type);
     // => uno gluon connects to quark colour
     new_expected[3] = expected_colours[2];
     new_expected[2] = {expected_colours[0].second+2, expected_colours[0].second};
     check_event(new_ev, new_expected);
 
     /// swap qx <-> q (=> qQ -> g q ... Q)
     new_ev.incoming[0].type = HEJ::ParticleID::u;
     new_ev.outgoing[1].type = HEJ::ParticleID::u;
     // => swap: colour<->anti && inital<->final
     std::swap(new_expected[0], new_expected[3]);
     std::swap(new_expected[0].first, new_expected[0].second);
     std::swap(new_expected[3].first, new_expected[3].second);
     // => & connect first gluon with remaining anti-colour
     new_expected[2] = {new_expected[0].first, new_expected[0].first+2};
     // shift colour line one down
     new_expected[1].first-=2;
     new_expected[5].first-=2;
     new_expected[5].second-=2;
     // shift anti-colour line one up
     new_expected[6].first+=2;
     check_event(new_ev, new_expected);
   }
 
   {
     // don't overwrite
     auto new_expected = expected_colours;
     auto new_ev = ev;
     /// uno forward (=> qxQ -> qx ... Q g)
     std::swap(new_ev.outgoing[3].type, new_ev.outgoing[4].type);
     // => uno gluon connects to remaining colour
     new_expected[5] = expected_colours[6];
     new_expected[6] = {expected_colours[3].first+2, expected_colours[3].first};
     check_event(new_ev, new_expected);
   }
 
   /// @TODO add qqx test when implemented (it should work)
 
   return EXIT_SUCCESS;
 }
diff --git a/t/test_descriptions.cc b/t/test_descriptions.cc
index 30c66a6..2d3c9d2 100644
--- a/t/test_descriptions.cc
+++ b/t/test_descriptions.cc
@@ -1,62 +1,67 @@
+/**
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
+ *  \date      2019
+ *  \copyright GPLv2 or later
+ */
 #include <iostream>
 #include <cstddef>
 
 #include "HEJ/Event.hh"
 #include "HEJ/EventReweighter.hh"
 #include "HEJ/ScaleFunction.hh"
 
 #define ASSERT(x) if(!(x)) { \
     std::cerr << "Assertion '" #x "' failed.\n"; \
     return EXIT_FAILURE; \
   }
 
 int main() {
   constexpr double mu = 125.;
   HEJ::ScaleFunction fun{"125", HEJ::FixedScale{mu}};
   ASSERT(fun.name() == "125");
 
   HEJ::ScaleGenerator scale_gen{
     {std::move(fun)}, {0.5, 1, 2.}, 2.1
   };
   HEJ::Event::EventData tmp;
   tmp.outgoing.push_back(
       {HEJ::ParticleID::gluon, fastjet::PtYPhiM(50., -1., 0.3, 0.), {}}
   );
   tmp.outgoing.push_back(
       {HEJ::ParticleID::gluon, fastjet::PtYPhiM(30., 1., -0.3, 0.), {}}
   );
   HEJ::Event ev{
     tmp.cluster(
       fastjet::JetDefinition{fastjet::kt_algorithm, 0.4}, 20.
     )
   };
 
   auto rescaled = scale_gen(std::move(ev));
   ASSERT(rescaled.central().description->scale_name == "125");
   for(auto const & var: rescaled.variations()) {
     ASSERT(var.description->scale_name == "125");
   }
   ASSERT(rescaled.central().description->mur_factor == 1.);
   ASSERT(rescaled.central().description->muf_factor == 1.);
 
   ASSERT(rescaled.variations(0).description->mur_factor == 1.);
   ASSERT(rescaled.variations(0).description->muf_factor == 1.);
 
   ASSERT(rescaled.variations(1).description->mur_factor == 0.5);
   ASSERT(rescaled.variations(1).description->muf_factor == 0.5);
 
   ASSERT(rescaled.variations(2).description->mur_factor == 0.5);
   ASSERT(rescaled.variations(2).description->muf_factor == 1.);
 
   ASSERT(rescaled.variations(3).description->mur_factor == 1.);
   ASSERT(rescaled.variations(3).description->muf_factor == 0.5);
 
   ASSERT(rescaled.variations(4).description->mur_factor == 1.);
   ASSERT(rescaled.variations(4).description->muf_factor == 2.);
 
   ASSERT(rescaled.variations(5).description->mur_factor == 2.);
   ASSERT(rescaled.variations(5).description->muf_factor == 1.);
 
   ASSERT(rescaled.variations(6).description->mur_factor == 2.);
   ASSERT(rescaled.variations(6).description->muf_factor == 2.);
 }
diff --git a/t/test_hdf5.cc b/t/test_hdf5.cc
index 8b14d31..08f7c10 100644
--- a/t/test_hdf5.cc
+++ b/t/test_hdf5.cc
@@ -1,40 +1,45 @@
+/**
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
+ *  \date      2019
+ *  \copyright GPLv2 or later
+ */
 #include <iostream>
 
 #include "HEJ/EventReader.hh"
 
 int main(int argc, char** argv) {
   if(argc != 2) {
     std::cerr << "Usage: " << argv[0] << " file.hdf5\n";
     return EXIT_FAILURE;
   }
   auto reader = HEJ::make_reader(argv[1]);
   if(
       reader->heprup().EBMUP != std::make_pair(7000., 7000.)
       || reader->heprup().PDFSUP != std::make_pair(13000, 13000)
   ) {
     std::cerr << "Read incorrect init parameters\n";
     return EXIT_FAILURE;
   }
   int nevent = 0;
   while(reader->read_event()) {
     ++nevent;
     if(reader->hepeup().NUP != 13) {
       std::cerr << "Read wrong number of particles: "
                 << reader->hepeup().NUP << " != 13 in event " << nevent;
       return EXIT_FAILURE;
     }
     for(size_t i = 0; i < 2; ++i) {
       for(size_t j = 0; j < 2; ++j) {
         if(reader->hepeup().PUP[i][j] != 0) {
           std::cerr << "Non-vanishing transverse momentum in incoming particle"
                     " in event " << nevent;
           return EXIT_FAILURE;
         }
       }
     }
   }
   if(nevent != 51200) {
     std::cerr << "Wrong number of events " << nevent << " != 51200\n";
     return EXIT_FAILURE;
   }
 }
diff --git a/t/test_parameters.cc b/t/test_parameters.cc
index 2261167..796a4c7 100644
--- a/t/test_parameters.cc
+++ b/t/test_parameters.cc
@@ -1,71 +1,76 @@
+/**
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
+ *  \date      2019
+ *  \copyright GPLv2 or later
+ */
 #include <iostream>
 #include <stdexcept>
 
 #include "HEJ/Parameters.hh"
 
 #define ASSERT(x) if(!(x)) { \
     throw std::logic_error("Assertion '" #x "' failed."); \
   }
 
 
 namespace {
   bool same_description(
     HEJ::EventParameters const & par1, HEJ::EventParameters const & par2
   ){
     if( par1.mur!=par2.mur || par1.muf!=par2.muf ) return false;
     auto const & des1 = par1.description;
     auto const & des2 = par2.description;
     if(bool(des1) != bool(des2)) return false; // only one set
     if(!des1) return true; // both not set
     return   (des1->mur_factor == des2->mur_factor)
           && (des1->muf_factor == des2->muf_factor)
           && (des1->scale_name == des2->scale_name);
   }
 
   void same_description(
     HEJ::Parameters<HEJ::EventParameters> const & par1,
     HEJ::Parameters<HEJ::EventParameters> const & par2
   ){
     ASSERT(same_description(par1.central, par2.central));
     ASSERT(par1.variations.size() == par2.variations.size());
     for(size_t i=0; i<par1.variations.size(); ++i)
       ASSERT( same_description(par1.variations[i], par2.variations[i]) );
   }
 }
 
 int main() {
   HEJ::Parameters<HEJ::EventParameters> ev_param;
   ev_param.central = HEJ::EventParameters{ 1,1,1.1,
     std::make_shared<HEJ::ParameterDescription>("a", 1.,1.) };
   ev_param.variations.emplace_back(
     HEJ::EventParameters{ 2,2,2.2,
       std::make_shared<HEJ::ParameterDescription>("b", 2.,2.)
     });
   ev_param.variations.emplace_back(
     HEJ::EventParameters{ 3,3,3.3,
       std::make_shared<HEJ::ParameterDescription>("c", 3.,3.)
     });
 
   HEJ::Weights weights;
   weights.central = 4.4;
   weights.variations.push_back(5.5);
   weights.variations.push_back(6.6);
 
   HEJ::Parameters<HEJ::EventParameters> mult_param;
   mult_param = ev_param*weights;
   same_description(ev_param, mult_param);
   ASSERT(mult_param.central.weight == ev_param.central.weight*weights.central);
   for(size_t i=0; i<weights.variations.size(); ++i)
     ASSERT(mult_param.variations[i].weight
       == ev_param.variations[i].weight*weights.variations[i]);
 
   HEJ::Parameters<HEJ::EventParameters> div_param;
   div_param = ev_param/weights;
   same_description(ev_param, div_param);
   ASSERT(div_param.central.weight == ev_param.central.weight/weights.central);
   for(size_t i=0; i<weights.variations.size(); ++i)
     ASSERT(div_param.variations[i].weight
       == ev_param.variations[i].weight/weights.variations[i]);
 
   return EXIT_SUCCESS;
 }
diff --git a/t/test_psp.cc b/t/test_psp.cc
index 87355c6..da35994 100644
--- a/t/test_psp.cc
+++ b/t/test_psp.cc
@@ -1,65 +1,70 @@
+/**
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
+ *  \date      2019
+ *  \copyright GPLv2 or later
+ */
 #include "LHEF/LHEF.h"
 #include "HEJ/stream.hh"
 #include "HEJ/config.hh"
 #include "HEJ/event_types.hh"
 #include "HEJ/Event.hh"
 #include "HEJ/PhaseSpacePoint.hh"
 #include "HEJ/Ranlux64.hh"
 
 namespace{
   constexpr int max_trials = 100;
   constexpr double extpartonptmin = 45.;
   constexpr double max_ext_soft_pt_fraction =
     std::numeric_limits<double>::infinity();
   const fastjet::JetDefinition jet_def{fastjet::kt_algorithm, 0.4};
   constexpr double min_jet_pt = 50;
 
 };
 
 int main(int argn, char** argv) {
   if(argn != 2){
     std::cerr << "Usage: " << argv[0] << " eventfile";
     return EXIT_FAILURE;
   }
 
   HEJ::istream in{argv[1]};
   LHEF::Reader reader{in};
   LHEF::Writer writer{std::cerr};
   writer.heprup = reader.heprup;
 
 
   HEJ::PhaseSpacePointConfig conf;
   conf.jet_param = HEJ::JetParameters{jet_def, min_jet_pt};
   conf.min_extparton_pt = extpartonptmin;
   conf.max_ext_soft_pt_fraction = max_ext_soft_pt_fraction;
 
   HEJ::Ranlux64 ran{};
 
   while(reader.readEvent()){
     const HEJ::Event ev{
       HEJ::Event::EventData{reader.hepeup}( jet_def, min_jet_pt )
     };
     for(int trial = 0; trial < max_trials; ++trial){
       HEJ::PhaseSpacePoint psp{ev, conf, ran};
       if(psp.weight() != 0){
         HEJ::Event::EventData tmp_ev;
         tmp_ev.incoming = psp.incoming();
         tmp_ev.outgoing = psp.outgoing();
         tmp_ev.parameters.central = {0,0,0};
         HEJ::Event out_ev{ tmp_ev(jet_def, min_jet_pt) };
         if(out_ev.type() != ev.type()){
           using HEJ::event_type::names;
           std::cerr << "Wrong class of phase space point:\n"
             "original event of class " << names[ev.type()] << ":\n";
           writer.hepeup = reader.hepeup;
           writer.writeEvent();
           std::cerr << "Phase space point of class " << names[out_ev.type()] << ":\n";
           writer.hepeup = to_HEPEUP(out_ev, &writer.heprup);
           writer.writeEvent();
           return EXIT_FAILURE;
         }
       }
     }
   }
 
 }
diff --git a/t/test_scale_arithmetics.cc b/t/test_scale_arithmetics.cc
index b640726..ff27844 100644
--- a/t/test_scale_arithmetics.cc
+++ b/t/test_scale_arithmetics.cc
@@ -1,99 +1,100 @@
-
-// Generic tester for the ME for a given set of PSP
-// reference weights and PSP (as LHE file) have to be given as _individual_ files
-
+/**
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
+ *  \date      2019
+ *  \copyright GPLv2 or later
+ */
 #include <fstream>
 #include <random>
 #include <algorithm>
 
 #include "LHEF/LHEF.h"
 
 #include "HEJ/EventReweighter.hh"
 #include "HEJ/make_RNG.hh"
 #include "HEJ/Event.hh"
 #include "HEJ/YAMLreader.hh"
 #include "HEJ/stream.hh"
 
 constexpr double alpha_s = 0.118;
 constexpr double ep = 1e-13;
 
 void dump(HEJ::Event const & ev){
   {
     LHEF::Writer writer{std::cout};
     std::cout << std::setprecision(6);
     writer.hepeup = to_HEPEUP(std::move(ev), nullptr);
     writer.writeEvent();
   }
   std::cout << "Rapidity ordering:\n";
   for(const auto & part: ev.outgoing()){
     std::cout << std::setw(2) << part.type << ": "<<  std::setw(7) << part.rapidity() << std::endl;
   }
 }
 
 void shuffle_particles(HEJ::Event::EventData & ev) {
   static std::mt19937_64 ran{0};
   std::shuffle(begin(ev.incoming), end(ev.incoming), ran);
   std::shuffle(begin(ev.outgoing), end(ev.outgoing), ran);
 }
 
 int main(int argn, char** argv){
   if(argn != 3){
     std::cerr << "\n# Usage:\n."<< argv[0] <<" config.yml input_file.lhe\n\n";
     return EXIT_FAILURE;
   }
   HEJ::Config config = HEJ::load_config(argv[1]);
   config.scales = HEJ::to_ScaleConfig(
       YAML::Load("scales: [H_T, 1 * H_T, 2/2 * H_T, 2*H_T/2, H_T/2*2, H_T/2/2*4, H_T*H_T/H_T]")
   );
 
   HEJ::istream in{argv[2]};
   LHEF::Reader reader{in};
 
   auto ran = HEJ::make_RNG(config.rng.name, config.rng.seed);
 
   HEJ::ScaleGenerator scale_gen{
     config.scales.base,
     config.scales.factors,
     config.scales.max_ratio
   };
 
   HEJ::EventReweighter resum{
     reader.heprup,
     std::move(scale_gen),
     to_EventReweighterConfig(config),
     *ran
   };
 
   size_t i = 0;
   while(reader.readEvent()){
     ++i;
 
     HEJ::Event::EventData data{reader.hepeup};
     shuffle_particles(data);
 
     HEJ::Event event{
       data.cluster(
         config.resummation_jets.def,
         config.resummation_jets.min_pt
       )
     };
 
     auto resummed = resum.reweight(event, config.trials);
     for(auto && ev: resummed) {
       for(auto &&var: ev.variations()) {
         if(std::abs(var.muf - ev.central().muf) > ep) {
           std::cerr
             << std::setprecision(15)
             << "unequal scales: " << var.muf
             << " != " << ev.central().muf << '\n'
             << "in resummed event:\n";
           dump(ev);
           std::cerr << "\noriginal event:\n";
           dump(event);
           return EXIT_FAILURE;
         }
       }
     }
   }
 
 }
diff --git a/t/test_scale_import.cc b/t/test_scale_import.cc
index 0b3fd88..36b72e9 100644
--- a/t/test_scale_import.cc
+++ b/t/test_scale_import.cc
@@ -1,29 +1,34 @@
+/**
+ *  \authors   The HEJ collaboration (see AUTHORS for details)
+ *  \date      2019
+ *  \copyright GPLv2 or later
+ */
 #include <stdexcept>
 #include <iostream>
 
 #include "HEJ/YAMLreader.hh"
 #include "HEJ/Event.hh"
 
 int main(int argc, char** argv) {
   constexpr double ep = 1e-7;
   if (argc != 2) {
     throw std::logic_error{"wrong number of args"};
   }
   const HEJ::Config config = HEJ::load_config(argv[1]);
 
   HEJ::Event::EventData tmp;
   tmp.outgoing.push_back(
       {HEJ::ParticleID::gluon, fastjet::PtYPhiM(50., -1., 0.3, 0.), {}}
   );
   tmp.outgoing.push_back(
       {HEJ::ParticleID::gluon, fastjet::PtYPhiM(30., 1., -0.3, 0.), {}}
   );
   HEJ::Event ev{
     tmp(fastjet::JetDefinition{fastjet::kt_algorithm, 0.4}, 20.)
   };
 
   const double softest_pt = config.scales.base[0](ev);
   if(std::abs(softest_pt-30.) > ep){
     throw std::logic_error{"wrong softest pt"};
   }
 }