diff --git a/FixedOrderGen/configFO.yml b/FixedOrderGen/configFO.yml
index 9ed278f..2000ad5 100644
--- a/FixedOrderGen/configFO.yml
+++ b/FixedOrderGen/configFO.yml
@@ -1,63 +1,64 @@
 # number of generated events
 events: 200
 
 jets:
   min pt: 20
   peak pt: 30
   algorithm: antikt
   R: 0.4
   max rapidity: 5
 
 beam:
   energy: 6500
   particles: [p, p]
 
 pdf: 230000
 
 process: p p => h 4j
 
 # fraction of events with two extremal emissions in one direction
 # that contain an unordered emission
 unordered fraction: 0.05
 
 scales: max jet pperp
 
 event output:
   - HEJFO.lhe
 #  - RHEJ.lhe
 #  - RHEJ_events.hepmc
 
 Higgs properties:
   mass: 125
   width: 0.004165
   decays: {into: [photon, photon], branching ratio: 0.0023568762400521404}
 
 random generator:
   name: mixmax
+  # seed: 1
 
 # unweighting parameters
 # remove to obtain weighted events
 unweight:
   sample size: 200  # should be similar to "events:", but not more than ~10000
   max deviation: 0
 
 # to use a rivet analysis
 #
 # analysis:
 #   rivet: MC_XS # rivet analysis name
 #   output: RHEJ # name of the yoda files, ".yoda" and scale suffix will be added
 #
 # to use a custom analysis
 #
 # analysis:
 #   plugin: /path/to/libmyanalysis.so
 #   my analysis parameter: some value
 
 # parameters for Higgs-gluon couplings
 # this requires compilation with qcdloop
 #
 # Higgs coupling:
 #   use impact factors: false
 #   mt: 174
 #   include bottom: true
 #   mb: 4.7
diff --git a/FixedOrderGen/include/PhaseSpacePoint.hh b/FixedOrderGen/include/PhaseSpacePoint.hh
index 506fb0f..b419caa 100644
--- a/FixedOrderGen/include/PhaseSpacePoint.hh
+++ b/FixedOrderGen/include/PhaseSpacePoint.hh
@@ -1,199 +1,199 @@
 /** \file PhaseSpacePoint.hh
  *  \brief Contains the PhaseSpacePoint Class
  */
 
 #pragma once
 
 #include <vector>
 
 #include "RHEJ/utility.hh"
 #include "RHEJ/Event.hh"
 #include "RHEJ/PDG_codes.hh"
 #include "RHEJ/PDF.hh"
 #include "RHEJ/RNG.hh"
 
 #include "Status.hh"
 #include "JetParameters.hh"
 #include "HiggsProperties.hh"
 
 namespace HEJFOG{
   class Process;
 
   using RHEJ::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_def          The Jet Definition Used
      * @param jetptmin         Minimum Jet Transverse Momentum
      * @param rapmax           Maximum parton rapidity
      * @param pdf              The pdf set (used for sampling)
      * @param subl_chance      Chance to turn a potentially subleading
      *                         emission (uno or central qqx) into an actual one
      *
      * 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,
       RHEJ::PDF & pdf, double E_beam,
       double subl_chance,
       HiggsProperties const & higgs_properties,
       RHEJ::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<int, std::vector<Particle>> const & decays() const{
       return decays_;
     }
 
-    private:
+  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,
         RHEJ::RNG & ran
     );
     Particle gen_boson(
         RHEJ::ParticleID bosonid, double mass, double width,
         RHEJ::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(
         std::array<RHEJ::ParticleID, 2> const & ids,
         RHEJ::PDF & pdf, double E_beam,
         double uf,
         RHEJ::RNG & ran
     );
     RHEJ::ParticleID generate_incoming_id(
         double x, double uf, RHEJ::PDF & pdf,
         RHEJ::RNG & ran
     );
 
     bool momentum_conserved(double ep) const;
 
     RHEJ::Particle const & most_backward_FKL(
         std::vector<RHEJ::Particle> const & partons
     ) const;
     RHEJ::Particle const & most_forward_FKL(
         std::vector<RHEJ::Particle> const & partons
     ) const;
     RHEJ::Particle & most_backward_FKL(std::vector<RHEJ::Particle> & partons) const;
     RHEJ::Particle & most_forward_FKL(std::vector<RHEJ::Particle> & partons) const;
     bool extremal_FKL_ok(
         std::vector<fastjet::PseudoJet> const & partons
     ) const;
     double random_normal(double stddev, RHEJ::RNG & ran);
     /**
      * @brief Turns a FKL configuration into a subleading one
      *
      * @param chance            Change to switch to subleading configuration
      * @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, Process const & proc, RHEJ::RNG & ran);
     void turn_to_uno(bool can_be_uno_backward, bool can_be_uno_forward, RHEJ::RNG & ran);
     void turn_to_qqx(RHEJ::RNG & ran);
     std::vector<Particle> decay_boson(
         RHEJ::Particle const & parent,
         std::vector<Decay> const & decays,
         RHEJ::RNG & ran
     );
     Decay select_decay_channel(
         std::vector<Decay> const & decays,
         RHEJ::RNG & ran
     );
     double gen_hard_pt(
         int np, double ptmin, double ptmax, double y,
         RHEJ::RNG & ran
     );
     double gen_soft_pt(int np, double ptmax, RHEJ::RNG & ran);
     double gen_parton_pt(
         int count, JetParameters const & jet_param, double ptmax, double y,
         RHEJ::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<int, std::vector<Particle>> decays_;
   };
   RHEJ::UnclusteredEvent to_UnclusteredEvent(PhaseSpacePoint const & psp);
 }
diff --git a/FixedOrderGen/src/PhaseSpacePoint.cc b/FixedOrderGen/src/PhaseSpacePoint.cc
index 09b787b..80648ad 100644
--- a/FixedOrderGen/src/PhaseSpacePoint.cc
+++ b/FixedOrderGen/src/PhaseSpacePoint.cc
@@ -1,605 +1,631 @@
 #include "PhaseSpacePoint.hh"
 
 #include <random>
 #include <algorithm>
 
+#include "RHEJ/Constants.hh"
 #include "RHEJ/kinematics.hh"
 #include "RHEJ/utility.hh"
 #include "RHEJ/exceptions.hh"
 
 #include "Process.hh"
 
 #include <CLHEP/Random/Randomize.h>
 #include <CLHEP/Random/RanluxEngine.h>
 
 using namespace RHEJ;
 
 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();
   RHEJ::UnclusteredEvent to_UnclusteredEvent(PhaseSpacePoint const & psp){
     RHEJ::UnclusteredEvent result;
     result.incoming = psp.incoming();
     std::sort(
         begin(result.incoming), end(result.incoming),
         [](Particle o1, Particle o2){return o1.p.pz()<o2.p.pz();}
     );
     assert(result.incoming.size() == 2);
     result.outgoing = psp.outgoing();
     assert(
         std::is_sorted(
             begin(result.outgoing), end(result.outgoing),
             RHEJ::rapidity_less{}
         )
     );
     assert(result.outgoing.size() >= 2);
     result.decays = psp.decays();
     result.central.mur = NaN;
     result.central.muf = NaN;
     result.central.weight = psp.weight();
     return result;
   }
 
   namespace{
     bool can_swap_to_uno(
         RHEJ::Particle const & p1, RHEJ::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<RHEJ::Particle>::const_iterator first,
-      std::vector<RHEJ::Particle>::const_iterator last){
-      return 1 < std::count_if(first, last, [](Particle const & p)
-        {return p.type == pid::gluon;});
+      std::vector<Particle>::const_iterator first,
+      std::vector<Particle>::const_iterator last){
+      return 1 < count_gluons(first,last);
     }
   }
 
   void PhaseSpacePoint::maybe_turn_to_subl(
       double chance,
       Process const & proc,
       RHEJ::RNG & ran
   ){
     if(proc.njets <= 2) return;
     assert(outgoing_.size() >= 2);
 
     // decide what kind of subleading process is allowed
     bool allow_uno = false;
     const size_t nout = outgoing_.size();
     const bool can_be_uno_backward = can_swap_to_uno(
       outgoing_[0], outgoing_[1]
     );
     const bool can_be_uno_forward = 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(proc.boson && is_AWZ_boson(*(proc.boson))) {
       allow_qqx = can_change_to_qqx(outgoing_.cbegin(), outgoing_.cend());
       if(incoming_[0].type == pid::gluon && incoming_[1].type == pid::gluon) {
         /// enforce qqx for initial gg
         allow_uno = false;
         chance = 1.;
       }
     }
 
     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) {
         std::cout << "only qqx" << std::endl;
         turn_to_qqx(ran);
       } else {
         assert( allow_uno && allow_qqx);
         std::cout << "both qqx and uno" << std::endl;
         if(ran.flat() < 0.5) turn_to_uno(can_be_uno_backward, can_be_uno_forward, ran);
         else turn_to_qqx(ran);
         weight_ *= 2.;
       }
     } else weight_ /= 1 - chance;
 
   }
 
   void PhaseSpacePoint::turn_to_uno(
       const bool can_be_uno_backward, const bool can_be_uno_forward,
       RHEJ::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(RHEJ::RNG & ran){
-    /// @TODO implement
-    std::cerr << "turn_to_qqx not implemented yet. Here is a random number instead "
-      << ran.flat() <<".\n";
+    /// 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;});
+    auto last = std::find_if(first, outgoing_.end(),
+          [](Particle const & p){return is_quark(p) || is_antiquark(p);});
+    const size_t ng = count_gluons(first,last);
+    if(ng < 2)
+      throw std::logic_error("not enough gluons to create qqx");
+    // select flavour of quark
+    const double r1 = 2.*ran.flat()-1.;
+    weight_ *= n_f*2;
+    int flavour = pid::down;
+    for (double sum = 1./n_f; sum < std::abs(r1); sum += 1./n_f)
+      ++flavour;
+    flavour*=r1<0.?-1:1;
+    // select gluon for switch
+    first += floor((ng-1) * ran.flat());
+    auto next = std::find_if(first+1, last,
+      [](Particle const & p){return p.type == pid::gluon;});
+    assert(next != outgoing_.end());
+    weight_ *= (ng-1);
+    first->type = ParticleID(flavour);
+    next->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};
   }
 
   PhaseSpacePoint::PhaseSpacePoint(
     Process const & proc,
     JetParameters const & jet_param,
     RHEJ::PDF & pdf, double E_beam,
     double subl_chance,
     HiggsProperties const & h,
     RHEJ::RNG & ran
   )
   {
     assert(proc.njets >= 2);
     status_ = good;
     weight_ = 1;
 
     const int nout = proc.njets + (proc.boson?1:0);
     outgoing_.reserve(nout);
 
     // generate parton momenta
     const bool is_pure_jets = !proc.boson;
     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;
 
     if(proc.boson){
       switch (*proc.boson) {
       case pid::Higgs:{
         auto Hparticle=gen_boson(pid::higgs, h.mass, h.width, ran);
         auto pos=std::upper_bound(
             begin(outgoing_),end(outgoing_),Hparticle,rapidity_less{}
         );
         outgoing_.insert(pos, Hparticle);
         if(! h.decays.empty()){
           const int boson_idx = std::distance(begin(outgoing_), pos);
           decays_.emplace(
               boson_idx,
               decay_boson(outgoing_[boson_idx], h.decays, ran)
           );
         }
         break;
       }
       case pid::Wp:{
         /// @TODO implement, currently this is just a relabel Higgs
         ///@{
         std::cout << "Emission of Wp not implemented yet.\nThis is just a place holder" << std::endl;
         auto Hparticle=gen_boson(pid::Wp, h.mass, h.width, ran);
         auto pos=std::upper_bound(
             begin(outgoing_),end(outgoing_),Hparticle,rapidity_less{}
         );
         outgoing_.insert(pos, Hparticle);
         if(! h.decays.empty()){
           const int boson_idx = std::distance(begin(outgoing_), pos);
           decays_.emplace(
               boson_idx,
               decay_boson(outgoing_[boson_idx], h.decays, ran)
           );
         }
         ///@}
         break;
       }
       case pid::Wm:{
         /// @TODO implement, currently this is just a relabel Higgs
         ///@{
         std::cout << "Emission of Wm not implemented yet.\nThis is just a place holder" << std::endl;
         auto Hparticle=gen_boson(pid::higgs, h.mass, h.width, ran);
         auto pos=std::upper_bound(
             begin(outgoing_),end(outgoing_),Hparticle,rapidity_less{}
         );
         outgoing_.insert(pos, Hparticle);
         if(! h.decays.empty()){
           const int boson_idx = std::distance(begin(outgoing_), pos);
           decays_.emplace(
               boson_idx,
               decay_boson(outgoing_[boson_idx], h.decays, ran)
           );
         }
         ///@}
         break;
       }
       case pid::photon:
       case pid::Z:
       default:
         throw std::logic_error("Emission of boson of unsupported type");
       }
     }
     // normalisation of momentum-conserving delta function
     weight_ *= pow(2*M_PI, 4);
 
     /// @TODO currently gg->Wgg allowed, change this!
     reconstruct_incoming(proc.incoming, 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, proc, ran);
 
     #ifndef NDEBUG
     if(proc.boson && is_AWZ_boson(*proc.boson)){
       // check that W has a quark to couple to
       auto const partons = filter_partons(outgoing_);
       /// @TODO extend assert to also check flavour? Maybe easier in separate cmake test
       assert(partons.front().type != pid::gluon
         || partons.back().type != pid::gluon  // extremal quark
         || ( (partons.size() > 3 )            // or central qqx
           && ( 1<std::count_if(partons.cbegin()+1, partons.cend()-1,
                 [](Particle const & p) {return p.type != pid::gluon;})
               ))
       );
     }
     #endif
   }
 
   double PhaseSpacePoint::gen_hard_pt(
       int np , double ptmin, double ptmax, double y,
       RHEJ::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, RHEJ::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,
       RHEJ::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,
       RHEJ::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;
   }
 
   Particle PhaseSpacePoint::gen_boson(
       RHEJ::ParticleID bosonid, double mass, double width,
       RHEJ::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
     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(!RHEJ::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(!RHEJ::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(!RHEJ::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(!RHEJ::is_parton(partons[last_idx])) return partons[last_idx-1];
     return partons[last_idx];
   }
 
   void PhaseSpacePoint::reconstruct_incoming(
       std::array<RHEJ::ParticleID, 2> const & ids,
       RHEJ::PDF & pdf, double E_beam,
       double uf,
       RHEJ::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;
     }
     // pick pdfs
     /** @TODO
      *  ufa, ufb don't correspond to our final scale choice.
      *  The reversed HEJ scale generators currently expect a full event as input,
      *  so fixing this is not completely trivial
      */
     if(ids[0] == pid::proton || ids[0] == pid::p_bar){
       const double ufa=uf;
       incoming_[0].type = generate_incoming_id(xa, ufa, pdf, ran);
     }
     else {
       incoming_[0].type = ids[0];
     }
     if(ids[1] == pid::proton || ids[1] == pid::p_bar){
       const double ufb=uf;
       incoming_[1].type = generate_incoming_id(xb, ufb, pdf, ran);
     }
     else {
       incoming_[1].type = ids[1];
     }
     assert(momentum_conserved(1e-7));
   }
 
   RHEJ::ParticleID PhaseSpacePoint::generate_incoming_id(
       double x, double uf, RHEJ::PDF & pdf,
       RHEJ::RNG & ran
   ){
     const double pdfg=fabs(pdf.pdfpt(0,x,uf,pid::gluon));
     const double pdfu=fabs(pdf.pdfpt(0,x,uf,pid::up));
     const double pdfd=fabs(pdf.pdfpt(0,x,uf,pid::down));
     const double pdfux=fabs(pdf.pdfpt(0,x,uf,pid::u_bar));
     const double pdfdx=fabs(pdf.pdfpt(0,x,uf,pid::d_bar));
     const double pdfc=fabs(pdf.pdfpt(0,x,uf,pid::charm));
     const double pdfs=fabs(pdf.pdfpt(0,x,uf,pid::strange));
     const double pdfsx=fabs(pdf.pdfpt(0,x,uf,pid::s_bar));
     const double pdfb=fabs(pdf.pdfpt(0,x,uf,pid::b));
 
     const double pdftot=pdfg+4.0/9.0*(pdfu + pdfd + pdfux + pdfdx +pdfs +pdfsx + 2.0*(pdfc +pdfb ));
     const double r1=pdftot*ran.flat();
     double sum;
 
     if (r1<(sum=pdfg)) {
       weight_*=pdftot/pdfg;
       return pid::gluon;
     }
     if (r1<(sum+=(4./9.)*pdfu)) {
       weight_*=pdftot/((4./9.)*pdfu);
       return pid::up;
     }
     else if (r1<(sum+=(4./9.)*pdfd)) {
       weight_*=pdftot/((4./9.)*pdfd);
       return pid::down;
     }
     else if (r1<(sum+=(4./9.)*pdfux)) {
       weight_*=pdftot/((4./9.)*pdfux);
       return pid::u_bar;
     }
     else if (r1<(sum+=(4./9.)*pdfdx)) {
       weight_*=pdftot/((4./9.)*pdfdx);
       return pid::d_bar;
     }
     else if (r1<(sum+=(4./9.)*pdfc)) {
       weight_*=pdftot/((4./9.)*pdfc);
       return pid::c;
     }
     else if (r1<(sum+=(4./9.)*pdfc)){
       weight_*=pdftot/((4./9.)*pdfc);
       return pid::c_bar;
     }
     else if (r1<(sum+=(4./9.)*pdfs)) {
       weight_*=pdftot/((4./9.)*pdfs);
       return pid::s;
     }
     else if (r1<(sum+=(4./9.)*pdfsx)) {
       weight_*=pdftot/((4./9.)*pdfsx);
       return pid::s_bar;
     }
     else if (r1<(sum+=(4./9.)*pdfb)) {
       weight_*=pdftot/((4./9.)*pdfb);
       return pid::b;
     }
     else if (r1<=(sum+=(4./9.)*pdfb)) {
       weight_*=pdftot/((4./9.)*pdfb);
       return pid::b_bar;
     }
     std::cerr << "Error in choosing incoming parton: "<<x<<" "<<uf<<" "<<sum<<" "<<pdftot<<" "<<r1
       <<" "<<pdfg+4./9.*(pdfu+pdfux+pdfd+pdfdx+pdfs+pdfsx+2.*(pdfc+pdfb))<<std::endl;
     throw std::logic_error{"Failed to choose parton flavour"};
   }
 
   double PhaseSpacePoint::random_normal(
       double stddev,
       RHEJ::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,
       RHEJ::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(
       RHEJ::Particle const & parent,
       std::vector<Decay> const & decays,
       RHEJ::RNG & ran
   ){
     const auto channel = select_decay_channel(decays, ran);
     if(channel.products.size() != 2){
       throw RHEJ::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;
   }
 }