diff --git a/src/PhaseSpacePoint.cc b/src/PhaseSpacePoint.cc
index 820fdd7..c17fe39 100644
--- a/src/PhaseSpacePoint.cc
+++ b/src/PhaseSpacePoint.cc
@@ -1,689 +1,688 @@
 /**
  *  \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)); }
     );
     // assert that it is a q-q_bar pair.
     assert(firstquark != born_parton.cend()-2);
     assert(  ( is_quark(*firstquark) && is_antiquark(*(firstquark+1)) )
           || ( is_antiquark(*firstquark) && is_quark(*(firstquark+1)) ));
 
     // 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+1) ]
             == 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}) };
 
     // assert that jets didn't move
     assert(nearby_ep( ( event.jets().cbegin()+firstjet_idx )->rapidity(),
                       jets[ firstjet_idx ].rapidity(),   1e-2) );
     assert(nearby_ep( ( event.jets().cbegin()+firstjet_idx+1 )->rapidity(),
                       jets[ firstjet_idx+1 ].rapidity(), 1e-2) );
 
     // 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},
     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.) {
       status_ = failed_reshuffle;
       return;
     }
     if(! pass_resummation_cuts(jets)){
       status_ = failed_resummation_cuts;
       weight_ = 0.;
       return;
     }
     std::vector<fastjet::PseudoJet> jet_partons = split(jets, ng_jets);
     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), {}});
     }
+    assert(!outgoing_.empty());
 
     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.) {
         //! @TODO optimise s.t. this is not possible
         status_ = StatusCode::gluon_in_qqx;
         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