diff --git a/CepGen/Core/Exception.h b/CepGen/Core/Exception.h
index 800b4ab..eb610ae 100644
--- a/CepGen/Core/Exception.h
+++ b/CepGen/Core/Exception.h
@@ -1,202 +1,202 @@
 #ifndef CepGen_Core_Exception_h
 #define CepGen_Core_Exception_h
 
 #include <csignal>
 
 #include "CepGen/Core/Logger.h"
 
 namespace cepgen
 {
   /// A generic exception type
   /// \author Laurent Forthomme <laurent.forthomme@cern.ch>
   /// \date 27 Mar 2015
   struct Exception
   {
     explicit inline Exception() = default;
     virtual ~Exception() noexcept = default;
     /// Enumeration of exception severities
     enum class Type {
       undefined = -1, ///< Irregular exception
       debug, ///< Debugging information to be enabled
       verbatim, ///< Raw information
       info, ///< Prettified information
       warning, ///< Casual non-stopping warning
       error, ///< General non-stopping error
       fatal ///< Critical and stopping error
     };
     /// Stream operator (null and void)
     template<class T> Exception& operator<<( const T& ) { return *this; }
     /// Dump the full exception information in a given output stream
     /// \param[inout] os the output stream where the information is dumped
     virtual void dump( std::ostream& os = *utils::Logger::get().output ) const = 0;
     /// Exception message
     virtual std::string message() const = 0;
   };
 
   /// A simple exception handler
   /// \date 24 Mar 2015
   class LoggedException : public Exception
   {
     public:
       /// Generic constructor
       /// \param[in] module exception classifier
       /// \param[in] type exception type
       /// \param[in] id exception code (useful for logging)
       explicit inline LoggedException( const char* module = "", Type type = Type::undefined, short id = 0 ) :
         module_( module ), type_( type ), error_num_( id ) {}
       /// Generic constructor
       /// \param[in] from method invoking the exception
       /// \param[in] module exception classifier
       /// \param[in] type exception type
       /// \param[in] id exception code (useful for logging)
       explicit inline LoggedException( const char* from, const char* module, Type type = Type::undefined, short id = 0 ) :
         from_( from ), module_( module ), type_( type ), error_num_( id ) {}
       /// Copy constructor
       inline LoggedException( const LoggedException& rhs ) :
         from_( rhs.from_ ), module_( rhs.module_ ), message_( rhs.message_.str() ),
         type_( rhs.type_ ), error_num_( rhs.error_num_ ) {}
       /// Default destructor (potentially killing the process)
       inline ~LoggedException() noexcept override {
         if ( type_ != Type::undefined )
           dump();
         // we stop this process' execution on fatal exception
         if ( type_ == Type::fatal && raise( SIGINT ) != 0 )
           exit( 0 );
       }
 
       //----- Overloaded stream operators
 
       /// Generic templated message feeder operator
       template<typename T>
       inline friend const LoggedException& operator<<( const LoggedException& exc, T var ) {
         LoggedException& nc_except = const_cast<LoggedException&>( exc );
         nc_except.message_ << var;
         return exc;
       }
       /// Pipe modifier operator
       inline friend const LoggedException& operator<<( const LoggedException& exc, std::ios_base&( *f )( std::ios_base& ) ) {
         LoggedException& nc_except = const_cast<LoggedException&>( exc );
         f( nc_except.message_ );
         return exc;
       }
 
       inline std::string message() const override {
         return message_.str();
       }
 
       /// Extract the origin of the exception
       inline std::string from() const { return from_; }
       /// Extract the exception code
       inline int errorNumber() const { return error_num_; }
       /// Extract the exception type
       inline Type type() const { return type_; }
       /// Extract a human-readable (and colourified) version of the exception type
       inline std::string typeString() const {
         switch ( type() ) {
           case Type::warning: return "\033[34;1mWarning\033[0m";
           case Type::info: return "\033[32;1mInfo.\033[0m";
           case Type::debug: return "\033[33;1mDebug\033[0m";
           case Type::error: return "\033[31;1mError\033[0m";
           case Type::fatal: return "\033[31;1mFatal\033[0m";
           case Type::undefined: default: return "\33[7;1mUndefined\033[0m";
         }
       }
 
       inline void dump( std::ostream& os = *utils::Logger::get().output ) const override {
         os << fullMessage() << std::endl;
       }
       /// Extract a one-line summary of the exception
       inline std::string shortMessage() const {
         std::ostringstream os;
         os << "[" << typeString() << "]";
-        if ( type_ == Type::warning )
+        if ( type_ == Type::warning || type_ == Type::debug )
           os << " \033[30;4m" << from_ << "\033[0m\n";
         os << "\t" << message_.str();
         return os.str();
       }
 
     private:
       inline static char* now() {
         static char buffer[10];
         time_t rawtime;
         time( &rawtime );
         struct tm* timeinfo = localtime( &rawtime );
         strftime( buffer, 10, "%H:%M:%S", timeinfo );
         return buffer;
       }
       /// Extract a full exception message
       inline std::string fullMessage() const {
         switch ( type_ ) {
           case Type::info:
           case Type::debug:
           case Type::warning:
             return shortMessage();
           case Type::verbatim:
             return message_.str();
           default: {
             std::ostringstream os;
             os << "================================== Exception ========================= " << now()
                << "\n Class:       " << typeString() << std::endl;
             if ( !from_.empty() )
               os << " Raised by: " << from_ << "\n"
                  << " " << message_.str() << std::endl;
             if ( errorNumber() != 0 )
               os << "-------------------------------------------------------------------------------"
                  << "\n Error #" << error_num_ << std::endl;
             os << "===============================================================================";
             return os.str();
           }
         }
       }
       /// Origin of the exception
       std::string from_;
       /// Exception classificator
       std::string module_;
       /// Message to throw
       std::ostringstream message_;
       /// Exception type
       Type type_;
       /// Integer exception number
       short error_num_;
   };
 
   /// Placeholder for debugging messages if logging threshold is not reached
   /// \date Apr 2018
   struct NullStream : Exception
   {
     using Exception::Exception;
     /// Empty constructor
     inline NullStream() {}
     /// Empty constructor
     inline NullStream( const LoggedException& ) {}
     void dump( std::ostream& os = *utils::Logger::get().output ) const override {}
     std::string message() const override { return ""; }
   };
 }
 
 #define CG_LOG( mod ) \
   ( !CG_LOG_MATCH( mod, information ) ) \
   ? cepgen::NullStream() \
   : cepgen::LoggedException( __PRETTY_FUNCTION__, mod, cepgen::Exception::Type::verbatim )
 #define CG_INFO( mod ) \
   ( !CG_LOG_MATCH( mod, information ) ) \
   ? cepgen::NullStream() \
   : cepgen::LoggedException( __PRETTY_FUNCTION__, mod, cepgen::Exception::Type::info )
 #define CG_DEBUG( mod ) \
   ( !CG_LOG_MATCH( mod, debug ) ) \
   ? cepgen::NullStream() \
   : cepgen::LoggedException( __PRETTY_FUNCTION__, mod, cepgen::Exception::Type::debug )
 #define CG_DEBUG_LOOP( mod ) \
   ( !CG_LOG_MATCH( mod, debugInsideLoop ) ) \
   ? cepgen::NullStream() \
   : cepgen::LoggedException( __PRETTY_FUNCTION__, mod, cepgen::Exception::Type::debug )
 #define CG_WARNING( mod ) \
   ( !CG_LOG_MATCH( mod, warning ) ) \
   ? cepgen::NullStream() \
   : cepgen::LoggedException( __PRETTY_FUNCTION__, mod, cepgen::Exception::Type::warning )
 #define CG_ERROR( mod ) \
   cepgen::LoggedException( __PRETTY_FUNCTION__, mod, cepgen::Exception::Type::error )
 #define CG_FATAL( mod ) \
   cepgen::LoggedException( __PRETTY_FUNCTION__, mod, cepgen::Exception::Type::fatal )
 
 #endif
diff --git a/CepGen/Core/utils.h b/CepGen/Core/utils.h
index bb63dee..e94fac1 100644
--- a/CepGen/Core/utils.h
+++ b/CepGen/Core/utils.h
@@ -1,50 +1,50 @@
 #ifndef CepGen_Core_utils_h
 #define CepGen_Core_utils_h
 
 #include <string>
 #include <vector>
 #include <numeric>
 
 namespace cepgen
 {
   /// Format a string using a printf style format descriptor.
   std::string Form( const std::string fmt, ... );
   /// Human-readable boolean printout
   inline const char* yesno( const bool& test ) { return ( test ) ? "\033[32;1myes\033[0m" : "\033[31;1mno\033[0m"; }
   //inline const char* boldify( const char* str ) { const std::string out = std::string( "\033[33;1m" ) + std::string( str ) + std::string( "\033[0m" ); return out.c_str(); }
   /// Boldify a string for TTY-type output streams
   inline std::string boldify( const std::string& str ) { return Form( "\033[1m%s\033[0m", str.c_str() ); }
   /// Boldify a string for TTY-type output streams
   inline std::string boldify( const char* str ) { return boldify( std::string( str ) ); }
   /// Boldify a double floating point number for TTY-type output streams
   inline std::string boldify( const double& dbl ) { return boldify( Form("%.2f", dbl ) ); }
   /// Boldify an integer for TTY-type output streams
   inline std::string boldify( const int& i ) { return boldify( Form("% d", i ) ); }
   /// Boldify an unsigned integer for TTY-type output streams
   inline std::string boldify( const unsigned int& ui ) { return boldify( Form("%d", ui ) ); }
   /// Boldify an unsigned long integer for TTY-type output streams
   inline std::string boldify( const unsigned long& ui ) { return boldify( Form("%lu", ui ) ); }
   /// TTY-type enumeration of colours
   enum class Colour { gray = 30, red = 31, green = 32, yellow = 33, blue = 34, purple = 35 };
   /// Colourise a string for TTY-type output streams
   inline std::string colourise( const std::string& str, const Colour& col ) { return Form( "\033[%d%s\033[0m", (int)col, str.c_str() ); }
   /// Replace all occurences of a text by another
   size_t replace_all( std::string& str, const std::string& from, const std::string& to );
   namespace utils
   {
     /// Add a trailing "s" when needed
     inline const char* s( unsigned short num ) { return ( num > 1 ) ? "s" : ""; }
     /// Helper to print a vector
     template<class T> std::string repr( const std::vector<T>& vec, const std::string& sep = "," ) {
       return std::accumulate( std::next( vec.begin() ), vec.end(),
         std::to_string( *vec.begin() ), [&sep]( std::string str, T xv ) {
           return std::move( str )+sep+std::to_string( xv );
         } );
     }
   }
 }
 
 /// Provide a random number generated along a uniform distribution between 0 and 1
-#define drand() static_cast<double>( rand()/RAND_MAX )
+#define drand() (double)rand()/RAND_MAX
 
 #endif
diff --git a/CepGen/Event/Momentum.cpp b/CepGen/Event/Momentum.cpp
index f94fc60..a9ca937 100644
--- a/CepGen/Event/Momentum.cpp
+++ b/CepGen/Event/Momentum.cpp
@@ -1,387 +1,392 @@
 #include "CepGen/Event/Particle.h"
 
 #include "CepGen/Core/Exception.h"
 #include "CepGen/Core/utils.h"
 
 #include <math.h>
 #include <iomanip>
 
 namespace cepgen
 {
   using Momentum = Particle::Momentum;
 
   //----- Particle momentum methods
 
   Momentum::Momentum() :
     px_( 0. ), py_( 0. ), pz_( 0. ), p_( 0. ), energy_( 0. )
   {}
 
   Momentum::Momentum( double x, double y, double z, double t ) :
     px_( x ), py_( y ), pz_( z ), energy_( t )
   {
     computeP();
   }
 
   Momentum::Momentum( double* p ) :
     px_( p[0] ), py_( p[1] ), pz_( p[2] ), energy_( p[3] )
   {
     computeP();
   }
 
   //--- static constructors
 
   Momentum
   Momentum::fromPtEtaPhi( double pt, double eta, double phi, double e )
   {
     const double px = pt*cos( phi ),
                  py = pt*sin( phi ),
                  pz = pt*sinh( eta );
     return Momentum( px, py, pz, e );
   }
 
   Momentum
   Momentum::fromPThetaPhi( double p, double theta, double phi, double e )
   {
     const double px = p*sin( theta )*cos( phi ),
                  py = p*sin( theta )*sin( phi ),
                  pz = p*cos( theta );
     return Momentum( px, py, pz, e );
   }
 
   Momentum
   Momentum::fromPxPyPzE( double px, double py, double pz, double e )
   {
     return Momentum( px, py, pz, e );
   }
 
   Momentum
   Momentum::fromPxPyPzM( double px, double py, double pz, double m )
   {
     Momentum mom( px, py, pz );
     mom.setMass( m );
     return mom;
   }
 
   Momentum
   Momentum::fromPxPyYM( double px, double py, double rap, double m )
   {
     const double pt = std::hypot( px, py ), et = std::hypot( pt, m );
     return Momentum( px, py, et*sinh( rap ), et*cosh( rap ) );
   }
 
   //--- arithmetic operators
 
   Momentum&
   Momentum::operator+=( const Momentum& mom )
   {
     px_ += mom.px_;
     py_ += mom.py_;
     pz_ += mom.pz_;
     energy_ += mom.energy_;
     computeP();
     return *this;
   }
 
   Momentum&
   Momentum::operator-=( const Momentum& mom )
   {
     px_ -= mom.px_;
     py_ -= mom.py_;
     pz_ -= mom.pz_;
     energy_ -= mom.energy_;
     computeP();
     return *this;
   }
 
   bool
   Momentum::operator==( const Momentum& mom ) const
   {
     return ( px_ == mom.px_
           && py_ == mom.py_
           && pz_ == mom.pz_
           && energy_ == mom.energy_ );
   }
 
   double
   Momentum::threeProduct( const Momentum& mom ) const
   {
     CG_DEBUG_LOOP( "Momentum" )
       << "  (" << px_ << ", " << py_ << ", " << pz_ << ")\n\t"
       << "* (" << mom.px_ << ", " << mom.py_ << ", " << mom.pz_ << ")\n\t"
       << "= " << px_*mom.px_+py_*mom.py_+pz_*mom.pz_;
     return px_*mom.px_+py_*mom.py_+pz_*mom.pz_;
   }
 
   double
   Momentum::fourProduct( const Momentum& mom ) const
   {
     CG_DEBUG_LOOP( "Momentum" )
       << "  (" << px_ << ", " << py_ << ", " << pz_ << ", " << energy_ << ")\n\t"
       << "* (" << mom.px_ << ", " << mom.py_ << ", " << mom.pz_ << ", " << mom.energy_ << ")\n\t"
       << "= " << energy_*mom.energy_-threeProduct(mom);
     return energy_*mom.energy_-threeProduct(mom);
   }
 
   double
   Momentum::crossProduct( const Momentum& mom ) const
   {
     return px_*mom.py_-py_*mom.px_;
   }
 
   double
   Momentum::operator*=( const Momentum& mom )
   {
     return threeProduct( mom );
   }
 
   Momentum&
   Momentum::operator*=( double c )
   {
     px_ *= c;
     py_ *= c;
     pz_ *= c;
     energy_ *= c;
     computeP();
     return *this;
   }
 
   Momentum
   operator*( const Momentum& mom, double c )
   {
     Momentum out = mom;
     out *= c;
     return out;
   }
 
   Momentum
   operator*( double c, const Momentum& mom )
   {
     Momentum out = mom;
     out *= c;
     return out;
   }
 
   Momentum
   operator+( const Momentum& mom1, const Momentum& mom2 )
   {
     Momentum out = mom1;
     out += mom2;
     return out;
   }
 
   Momentum
   operator-( const Momentum& mom1, const Momentum& mom2 )
   {
     Momentum out = mom1;
     out -= mom2;
     return out;
   }
 
   Momentum
   operator-( const Momentum& mom )
   {
     return Momentum()-mom;
   }
 
   double
   operator*( const Momentum& mom1, const Momentum& mom2 )
   {
     Momentum tmp = mom1;
     return tmp.threeProduct( mom2 );
   }
 
   //--- various setters
 
   void
   Momentum::setMass2( double m2 )
   {
     energy_ = sqrt( p2()+m2 );
   }
 
   void
   Momentum::setP( double px, double py, double pz, double e )
   {
     setP( px, py, pz );
     setEnergy( e );
   }
 
   void
   Momentum::setP( double px, double py, double pz )
   {
     px_ = px;
     py_ = py;
     pz_ = pz;
     computeP();
   }
 
   void
   Momentum::computeP()
   {
     p_ = std::hypot( pt(), pz_ );
   }
 
   void
   Momentum::truncate( double tolerance )
   {
     if ( px_ <= tolerance )
       px_ = 0.;
     if ( py_ <= tolerance )
       py_ = 0.;
     if ( pz_ <= tolerance )
       pz_ = 0.;
     if ( energy_ <= tolerance )
       energy_ = 0.;
     computeP();
   }
 
   //--- various getters
 
   double
   Momentum::operator[]( unsigned int i ) const
   {
     switch ( i ) {
       case 0: return px_;
       case 1: return py_;
       case 2: return pz_;
       case 3: return energy_;
       default:
         throw CG_FATAL( "Momentum" ) << "Failed to retrieve the component " << i << "!";
     }
   }
 
   double&
   Momentum::operator[]( const unsigned int i )
   {
     switch ( i ) {
       case 0: return px_;
       case 1: return py_;
       case 2: return pz_;
       case 3: return energy_;
       default:
         throw CG_FATAL( "Momentum" ) << "Failed to retrieve the component " << i << "!";
     }
   }
 
   const std::vector<double>
   Momentum::pVector() const
   {
     return std::vector<double>{ px(), py(), pz(), energy(), mass() };
   }
 
   double
   Momentum::mass() const
   {
     if ( mass2() >= 0. )
       return sqrt( mass2() );
     return -sqrt( -mass2() );
   }
 
   double
   Momentum::theta() const
   {
     return atan2( pt(), pz() );
   }
 
   double
   Momentum::phi() const
   {
     return atan2( py(), px() );
   }
 
   double
   Momentum::pt() const
   {
     return std::hypot( px_, py_ );
   }
 
   double
   Momentum::pt2() const
   {
     return px_*px_+py_*py_;
   }
 
   double
   Momentum::eta() const
   {
     const int sign = ( pz()/fabs( pz() ) );
     return ( pt() != 0. )
       ? log( ( p()+fabs( pz() ) )/pt() )*sign
       : 9999.*sign;
   }
 
   double
   Momentum::rapidity() const
   {
     const int sign = ( pz()/fabs( pz() ) );
     return ( energy() >= 0. )
       ? log( ( energy()+pz() )/( energy()-pz() ) )*0.5
       : 999.*sign;
   }
 
   //--- boosts/rotations
 
   Momentum&
   Momentum::betaGammaBoost( double gamma, double betagamma )
   {
-    if ( gamma == 1. && betagamma == 0. ) return *this; // trivial case
+    if ( gamma == 1. && betagamma == 0. )
+      return *this; // trivial case
+
     const double pz = pz_, e = energy_;
-    pz_ = gamma*pz+betagamma*e;
+    pz_      = gamma*pz+betagamma*e;
     energy_  = gamma*e +betagamma*pz;
     computeP();
     return *this;
   }
 
   Momentum&
-  Momentum::lorentzBoost( const Momentum& p )
+  Momentum::lorentzBoost( const Momentum& p2 )
   {
     //--- do not boost on a system at rest
-    if ( p.p() == 0. )
+    if ( p2.p() == 0. )
       return *this;
 
-    const double m = p.mass();
-    const double pf4 = fourProduct( p )/m;
-    const double fn = ( pf4+energy_ )/( p.energy_+m );
-    *this -= p*fn;
+    const double m = p2.mass();
+    const double pf4 = ( px_*p2.px_+py_*p2.py_+pz_*p2.pz_+energy_*p2.energy_ )/m;
+    const double fn = ( pf4+energy_ )/( p2.energy_+m );
+    *this += p2*fn;
     energy_ = pf4;
     return *this;
   }
 
   Momentum&
   Momentum::rotatePhi( double phi, double sign )
   {
-    const double px = px_*cos( phi )+py_*sin( phi )*sign,
-                 py =-px_*sin( phi )+py_*cos( phi )*sign;
+    const double sphi = sin( phi ), cphi = cos( phi );
+    const double px =  px_*cphi + sign*py_*sphi,
+                 py = -px_*sphi + sign*py_*cphi;
     px_ = px;
     py_ = py;
     return *this;
   }
 
   Momentum&
   Momentum::rotateThetaPhi( double theta, double phi )
   {
+    const double ctheta = cos( theta ), stheta = sin( theta );
+    const double cphi = cos( phi ), sphi = sin( phi );
     double rotmtx[3][3], mom[3]; //FIXME check this! cos(phi)->-sin(phi) & sin(phi)->cos(phi) --> phi->phi+pi/2 ?
-    rotmtx[0][0] = -sin( phi ); rotmtx[0][1] = -cos( theta )*cos( phi ); rotmtx[0][2] =  sin( theta )*cos( phi );
-    rotmtx[1][0] =  cos( phi ); rotmtx[1][1] = -cos( theta )*sin( phi ); rotmtx[1][2] =  sin( theta )*sin( phi );
-    rotmtx[2][0] =  0.;         rotmtx[2][1] =  sin( theta );            rotmtx[2][2] =  cos( theta );
+    rotmtx[0][0] = -sphi; rotmtx[0][1] = -ctheta*cphi; rotmtx[0][2] = stheta*cphi;
+    rotmtx[1][0] =  cphi; rotmtx[1][1] = -ctheta*sphi; rotmtx[1][2] = stheta*sphi;
+    rotmtx[2][0] =  0.;   rotmtx[2][1] =  stheta;      rotmtx[2][2] = ctheta;
 
     for ( unsigned short i = 0; i < 3; ++i ) {
       mom[i] = 0.;
       for ( unsigned short j = 0; j < 3; ++j )
         mom[i] += rotmtx[i][j]*operator[]( j );
     }
     setP( mom[0], mom[1], mom[2] );
     return *this;
   }
 
   //--- printout
 
   std::ostream&
   operator<<( std::ostream& os, const Momentum& mom )
   {
     return os << "(" << std::fixed
       << std::setw( 9 ) << mom.energy_ << "|"
       << std::setw( 9 ) << mom.px_ << " "
       << std::setw( 9 ) << mom.py_ << " "
       << std::setw( 9 ) << mom.pz_ << ")" << std::defaultfloat;
   }
 }
diff --git a/CepGen/Hadronisers/Pythia6Hadroniser.cpp b/CepGen/Hadronisers/Pythia6Hadroniser.cpp
index 4885cca..de6bfb3 100644
--- a/CepGen/Hadronisers/Pythia6Hadroniser.cpp
+++ b/CepGen/Hadronisers/Pythia6Hadroniser.cpp
@@ -1,345 +1,344 @@
 #include "CepGen/Hadronisers/GenericHadroniser.h"
 #include "CepGen/Hadronisers/HadronisersHandler.h"
 
 #include "CepGen/Event/Event.h"
 #include "CepGen/Event/Particle.h"
 
 #include "CepGen/Physics/PDG.h"
 
 #include "CepGen/Core/ParametersList.h" //FIXME
 #include "CepGen/Core/Exception.h"
 #include "CepGen/Core/utils.h"
 
 #include <algorithm>
 
 /// Maximal number of characters to fetch for the particle's name
 #define NAME_CHR 16
 
 extern "C"
 {
   /// Get the particle's mass in GeV from the Pythia6 module
   extern double pymass_( int& );
   /// Get the resonant particle's width in GeV from the Pythia6 module
   //extern double pywidt_( int& );
   /// Launch the Pythia6 fragmentation
   extern void pyexec_();
   /// Set a parameter value to the Pythia6 module
   extern void pygive_( const char*, int );
   extern void pyckbd_();
   /// List all the particles in the event in a human-readable format
   extern void pylist_( int& );
   /// Join two coloured particles in a colour singlet
   extern void pyjoin_( int&, int& );
   /// Get a particle's human-readable name from the Pythia6 module
   extern void pyname_( int&, char*, int );
   /// Get kinematic information on a particle from the Pythia6 module
   extern double pyp_( int&, int& );
   /// Store one parton/particle in the PYJETS common block
   extern void py1ent_( int&, int&, double&, double&, double& );
   void pystop_() {}
 
   /// Particles content of the event
   extern struct
   {
     /// Number of particles in the event
     int n;
     int npad;
     /// Particles' general information (status, PDG id, mother, daughter 1, daughter 2)
     int k[5][4000];
     /// Particles' kinematics, in GeV (px, py, pz, E, M)
     double p[5][4000];
     /// Primary vertex for the particles
     double v[5][4000];
   } pyjets_;
 }
 
 namespace cepgen
 {
   namespace hadr
   {
     /**
      * Full interface to the Pythia6 @cite Sjostrand:2006za algorithm. It can be used in a single particle decay mode as well as a full event hadronisation using the string model, as in Jetset.
      * @brief Pythia6 hadronisation algorithm
      */
     class Pythia6Hadroniser : public GenericHadroniser
     {
       public:
         Pythia6Hadroniser( const ParametersList& );
 
         void setParameters( const Parameters& ) override {}
         void readString( const char* param ) override;
         void init() override {}
         bool run( Event& ev, double& weight, bool full ) override;
 
         void setCrossSection( double xsec, double xsec_err ) override {}
         //bool hadronise( const Particle* );
 
       private:
         static constexpr unsigned short MAX_PART_STRING = 3;
         static constexpr unsigned short MAX_STRING_EVENT = 2;
 
         inline static double pymass(int pdgid_) { return pymass_(pdgid_); }
         //inline static double pywidt(int pdgid_) { return pywidt_(pdgid_); }
         inline static void pyexec() { pyexec_(); }
         inline static void pyckbd() { pyckbd_(); }
         inline static void pygive( const std::string& line ) { pygive_( line.c_str(), line.length() ); }
         inline static void pylist( int mlist ) { pylist_( mlist ); }
         inline static double pyp( int role, int qty ) { return pyp_( role, qty ); }
         //inline static void py1ent( int* kf, double* pe, double theta, double phi ) { int one=1; py1ent_( &one, kf, pe, theta, phi ); }
         //inline static void py1ent( int* kf, double* pe, double theta, double phi ) { py1ent_( 1, kf, pe, theta, phi ); }
         inline static std::string pyname( int pdgid ) {
           char out[NAME_CHR];
           std::string s;
           pyname_( pdgid, out, NAME_CHR );
           s = std::string( out, NAME_CHR );
           //s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun<int, int>(std::isspace))).base(), s.end());
           s.erase( remove( s.begin(), s.end(), ' ' ), s.end() );
           return s;
         }
         /**
          * \brief Connect entries with colour flow information
          * \param[in] njoin_ Number of particles to join in the colour flow
          * \param[in] ijoin_ List of particles unique identifier to join in the colour flow
          */
         inline static void pyjoin( int njoin, int ijoin[2] ) { return pyjoin_( njoin, *ijoin ); }
         bool prepareHadronisation( Event& );
+        std::pair<short,short> pickPartonsContent() const;
         struct EventProperties
         {
           unsigned int str_in_evt = 0;
           unsigned int num_part_in_str[MAX_STRING_EVENT] = { 0 };
           int jlrole[MAX_STRING_EVENT] = { -1 };
           int jlpsf[MAX_STRING_EVENT][MAX_PART_STRING] = { 0 };
         };
         EventProperties fillParticles( const Event& ) const;
     };
 
     Pythia6Hadroniser::Pythia6Hadroniser( const ParametersList& plist ) :
       GenericHadroniser( plist, "pythia6" )
     {}
 
     void
     Pythia6Hadroniser::readString( const char* param )
     {
       pygive( param );
     }
 
     bool
     Pythia6Hadroniser::run( Event& ev, double& weight, bool full )
     {
       weight = 1.;
-      if ( full )
+      //if ( full )
         prepareHadronisation( ev );
 
       if ( utils::Logger::get().level >= utils::Logger::Level::debug ) {
         CG_DEBUG( "Pythia6Hadroniser" ) << "Dump of the event before the hadronisation:";
         ev.dump();
       }
 
       //--- fill Pythia 6 common blocks
       auto prop = fillParticles( ev );
 
       CG_DEBUG( "Pythia6Hadroniser" )
         << "Passed the string construction stage.\n\t"
         << " " << prop.str_in_evt << " string objects were identified and constructed.";
 
       unsigned int oldnpart = pyjets_.n;
 
       for ( unsigned short i = 0; i < prop.str_in_evt; ++i ) {
         if ( prop.num_part_in_str[i] < 2 )
           continue;
 
         std::ostringstream dbg;
         for ( unsigned short j = 0; j < prop.num_part_in_str[i]; ++j )
           if ( prop.jlpsf[i][j] != -1 )
             dbg << Form( "\n\t * %2d (pdgId=%4d)", prop.jlpsf[i][j], pyjets_.k[1][prop.jlpsf[i][j]-1] );
 
         CG_INFO( "Pythia6Hadroniser" )
           << "Joining " << prop.num_part_in_str[i] << " particle" << utils::s( prop.num_part_in_str[i] )
           << " in a same string (" << i << ")"
           << dbg.str();
 
         pyjoin( prop.num_part_in_str[i], prop.jlpsf[i] );
       }
       pyexec();
 
       int criteria = oldnpart+1;
       for ( unsigned int i = 0; i < MAX_STRING_EVENT; ++i )
         criteria += prop.num_part_in_str[i];
 
       if ( pyjets_.k[1][criteria] == 2212
         && pyjets_.k[0][criteria] == 1 ) {
         CG_WARNING( "Pythia6Hadroniser" ) << "System is non-inelastic.";
         return false;
       }
 
       // We filter the first particles already present in the event
       for ( unsigned int p = oldnpart; p < (unsigned int)pyjets_.n; ++p ) {
         const Particle::Role role = pyjets_.k[2][p] != 0
           ? ev[pyjets_.k[2][p]-1].role() // child particle inherits its mother's role
           : Particle::Role::UnknownRole;
 
         auto& pa = ev.addParticle( role );
         pa.setId( p );
         pa.setPdgId( (short)pyjets_.k[1][p] );
         pa.setStatus( static_cast<Particle::Status>( pyjets_.k[0][p] ) );
         pa.setMomentum( Particle::Momentum( pyjets_.p[0][p], pyjets_.p[1][p], pyjets_.p[2][p], pyjets_.p[3][p] ) );
         pa.setMass( pyjets_.p[4][p] );
         //pa.setCharge( (float)pyp( p+1, 6 ) );
         if ( role != Particle::Role::UnknownRole ) {
           auto& moth = ev[pyjets_.k[2][p]-1];
           moth.setStatus( role == Particle::Role::CentralSystem
                           ? Particle::Status::Resonance
                           : Particle::Status::Fragmented );
           pa.addMother( moth );
         }
       }
       return true;
     }
 
     bool
     Pythia6Hadroniser::prepareHadronisation( Event& ev )
     {
       CG_DEBUG( "Pythia6Hadroniser" ) << "Hadronisation preparation called.";
 
       for ( const auto& part : ev.particles() ) {
-        //--- loop over all undecayed particles
-        if ( part.status() != Particle::Status::Undecayed )
+        if ( part.status() != Particle::Status::Unfragmented )
           continue;
+        //--- only loop over all protons to be fragmented
 
-        //--- proton to be fragmented
-        const double ranudq = drand();
-        short singlet_id = 0, doublet_id = 0;
-        if ( ranudq < 1./9. ) {
-          singlet_id = PDG::down;
-          doublet_id = 2203; // uu1 diquark
-        }
-        else if ( ranudq < 5./9. ) {
-          singlet_id = PDG::up;
-          doublet_id = 2101; // ud0 diquark
-        }
-        else {
-          singlet_id = PDG::up;
-          doublet_id = 2103; // ud1 diquark
-        }
-        const double ulmdq = pymass( doublet_id );
-        const double ulmq = pymass( singlet_id );
-        CG_INFO("") <<ulmdq<<"|"<<ulmq;
+part.dump();
 
-        // Choose random direction in MX frame
-        const double ranmxp = 2.*M_PI*drand();       // phi angle
-        const double ranmxt = acos( 2.*drand()-1. ); // theta angle
+        const auto partons = pickPartonsContent();
+        const double mx2 = part.mass2();
+        const double mq = pymass( partons.first ), mq2 = mq*mq;
+        const double mdq = pymass( partons.second ), mdq2 = mdq*mdq;
 
-        // Compute momentum of decay particles from MX
-        const double pmxp = std::sqrt( std::pow( part.mass2() - ulmdq*ulmdq + ulmq*ulmq, 2 ) / ( 4.*part.mass2() ) - ulmq*ulmq );
+        //--- choose random direction in MX frame
+        const double phi = 2.*M_PI*drand(), theta = acos( 2.*drand()-1. ); // theta angle
 
-        // Build 4-vectors and boost decay particles
+        //--- compute momentum of decay particles from MX
+        const double px = std::sqrt( std::pow( mx2-mdq2+mq2, 2 ) / ( 4.*mx2 ) - mq2 );
 
-        // Start with the singlet
-        const auto pmxda_1 = Particle::Momentum::fromPThetaPhi( pmxp, ranmxt, ranmxp, std::hypot( pmxp, ulmq ) ), pmxda_2 = -pmxda_1;
+        //--- build 4-vectors and boost decay particles
+        const auto pq = Particle::Momentum::fromPThetaPhi( px, theta, phi, std::hypot( px, mq ) );
 
+        //--- singlet
         auto singl_mom = part.momentum();
-        singl_mom.lorentzBoost( pmxda_1 );
+        singl_mom.lorentzBoost( pq );
 
-        auto& singlet = ev.addParticle( part.role() );
-        singlet.setPdgId( singlet_id );
-        singlet.setStatus( Particle::Status::DebugResonance );
-        singlet.setMomentum( singl_mom );
-        //singlet.setMass(); //FIXME
+        auto& quark = ev.addParticle( part.role(), false );
+        quark.setPdgId( partons.first );
+        quark.setStatus( Particle::Status::DebugResonance );
+        quark.setMomentum( singl_mom );
 
-        // Continue with the doublet
+        //--- doublet
         auto doubl_mom = part.momentum();
-        doubl_mom.lorentzBoost( pmxda_2 );
-
-        auto& doublet = ev.addParticle( part.role() );
-        doublet.setPdgId( doublet_id );
-        doublet.setStatus( Particle::Status::DebugResonance );
-        doublet.setMomentum( doubl_mom );
-        //std::cout << "doublet, mass = " << doublet.mass() << std::endl;
-        //doublet.setMass(); //FIXME
-        doublet.dump();
-
-        if ( part.numDaughters() == 0 ) {
-          singlet.addMother( ev[part.id()] );
-          doublet.addMother( ev[part.id()] );
+        doubl_mom.lorentzBoost( -pq );
+
+        auto& diquark = ev.addParticle( part.role(), false );
+        diquark.setPdgId( partons.second );
+        diquark.setStatus( Particle::Status::DebugResonance );
+        diquark.setMomentum( doubl_mom );
+
+        std::cout << (singl_mom+doubl_mom)-part.momentum() << "|" << part.numDaughters() << std::endl;
+        /*if ( part.numDaughters() == 0 ) {
+          quark.addMother( ev[part.id()] );
+          diquark.addMother( ev[part.id()] );
           CG_DEBUG( "Pythia6Hadroniser" )
             << "Quark/diquark content succesfully added to the event.";
         }
         else { // Quark/diquark content already present in the event
           CG_WARNING( "Pythia6Hadroniser" )
             << "Quark/diquark content already present in the event!\n\t"
             << "Role of these particles: " << part.role() << ".";
 
           for ( const auto& daug : part.daughters() ) {
             if ( ev[daug].pdgId() == PDG::up || ev[daug].pdgId() == PDG::down ) {
               //--- quark
-              singlet.addMother( ev[part.id()] );
-              ev[daug] = singlet;
+              quark.addMother( ev[part.id()] );
+              ev[daug] = quark;
               CG_DEBUG( "Pythia6Hadroniser" ) << "Singlet replaced.";
             }
             else {
               //--- diquark
-              doublet.addMother( ev[part.id()] );
-              ev[daug] = doublet;
+              diquark.addMother( ev[part.id()] );
+              ev[daug] = diquark;
               CG_DEBUG( "Pythia6Hadroniser" ) << "Doublet replaced";
             }
           }
-        }
+        }*/
+        ev[part.id()].setStatus( Particle::Status::Fragmented );
       }
+ev.dump();
+
       return true;
     }
 
     Pythia6Hadroniser::EventProperties
     Pythia6Hadroniser::fillParticles( const Event& ev ) const
     {
       pyjets_.n = 0;
 
       //--- initialising the string fragmentation variables
       EventProperties out;
       out.str_in_evt = 0;
 
       for ( const auto& role : ev.roles() ) { // loop on roles
         unsigned int part_in_str = 0;
         for ( const auto& part : ev[role] ) {
           unsigned int np = part.id();
 
           pyjets_.p[0][np] = part.momentum().px();
           pyjets_.p[1][np] = part.momentum().py();
           pyjets_.p[2][np] = part.momentum().pz();
           pyjets_.p[3][np] = part.energy();
           pyjets_.p[4][np] = part.mass();
           pyjets_.k[0][np] = part.status() <= Particle::Status::Undefined
             ? 21 // incoming beam
             : (int)part.status();
           pyjets_.k[1][np] = part.integerPdgId();
           pyjets_.k[2][np] = part.mothers().empty()
             ? 0 // no mother
             : *( part.mothers().begin() )+1; // mother
           const auto& daug = part.daughters();
           if ( daug.empty() )
             pyjets_.k[3][np] = pyjets_.k[4][np] = 0; // no daughters
           else {
             pyjets_.k[3][np] = *daug.begin()+1; // daughter 1
             pyjets_.k[4][np] = *daug.rbegin()+1; // daughter 2
           }
           for ( int i = 0; i < 5; ++i )
             pyjets_.v[i][np] = 0.;
 
           if ( part.status() == Particle::Status::DebugResonance ) {
             pyjets_.k[0][np] = 1; //FIXME PYTHIA/JETSET workaround
             out.jlpsf[out.str_in_evt][part_in_str] = part.id()+1;
             out.num_part_in_str[out.str_in_evt]++;
             part_in_str++;
           }
           pyjets_.n++;
         }
         if ( out.jlrole[out.str_in_evt] > 0 )
           out.str_in_evt++;
       }
       return out;
     }
+
+    std::pair<short,short>
+    Pythia6Hadroniser::pickPartonsContent() const
+    {
+      const double ranudq = drand();
+      if ( ranudq < 1./9. )
+        return { PDG::down, 2203 }; // (d,uu1)
+      if ( ranudq < 5./9. )
+        return { PDG::up, 2101 };   // (u,ud0)
+      return { PDG::up, 2103 };     // (u,ud1)
+    }
+
   }
 }
 
 // register hadroniser and define alias
 REGISTER_HADRONISER( pythia6, Pythia6Hadroniser )
 
diff --git a/CepGen/Processes/GamGamLL.cpp b/CepGen/Processes/GamGamLL.cpp
index e42fa0c..6061181 100644
--- a/CepGen/Processes/GamGamLL.cpp
+++ b/CepGen/Processes/GamGamLL.cpp
@@ -1,1113 +1,1148 @@
 #include "CepGen/Processes/GamGamLL.h"
 #include "CepGen/Processes/ProcessesHandler.h"
 
 #include "CepGen/Event/Event.h"
 
 #include "CepGen/Core/Exception.h"
 
 #include "CepGen/Physics/Constants.h"
 #include "CepGen/Physics/FormFactors.h"
 #include "CepGen/Physics/PDG.h"
 
 namespace cepgen
 {
   namespace proc
   {
     GamGamLL::GamGamLL( const ParametersList& params ) :
       GenericProcess( params, "lpair", "pp → p(*) ( ɣɣ → l⁺l¯ ) p(*)" ),
       n_opt_( params.get<int>( "nopt", 0 ) ),
       pair_ ( params.get<ParticleProperties>( "pair" ).pdgid ),
       ep1_( 0. ), ep2_( 0. ), p_cm_( 0. ),
       ec4_( 0. ), pc4_( 0. ), mc4_( 0. ), w4_( 0. ),
       p12_( 0. ), p1k2_( 0. ), p2k1_( 0. ),
       p13_( 0. ), p14_( 0. ), p25_( 0. ),
       q1dq_( 0. ), q1dq2_( 0. ),
       s1_( 0. ), s2_( 0. ),
       epsi_( 0. ),
       g5_( 0. ), g6_( 0. ), a5_( 0. ), a6_( 0. ), bb_( 0. ),
       gram_( 0. ),
       dd1_( 0. ), dd2_( 0. ), dd3_( 0. ), dd4_( 0. ), dd5_( 0. ),
       delta_( 0. ),
       g4_( 0. ), sa1_( 0. ), sa2_( 0. ),
       sl1_( 0. ),
       cos_theta4_( 0. ), sin_theta4_( 0. ),
       al4_( 0. ), be4_( 0. ), de3_( 0. ), de5_( 0. ),
       pt4_( 0. ),
       jacobian_( 0. )
     {}
 
     //---------------------------------------------------------------------------------------------
 
     void
     GamGamLL::addEventContent()
     {
       GenericProcess::setEventContent( {
         { Particle::IncomingBeam1, PDG::proton },
         { Particle::IncomingBeam2, PDG::proton },
         { Particle::Parton1, PDG::photon },
         { Particle::Parton2, PDG::photon }
       }, {
         { Particle::OutgoingBeam1, { PDG::proton } },
         { Particle::OutgoingBeam2, { PDG::proton } },
         { Particle::CentralSystem, { pair_, pair_ } }
       } );
     }
 
     unsigned int
     GamGamLL::numDimensions() const
     {
       switch ( kin_.mode ) {
         case KinematicsMode::ElectronProton: default:
           throw CG_FATAL( "GamGamLL" )
             << "Process mode " << kin_.mode << " not (yet) supported! "
             << "Please contact the developers to consider an implementation.";
         case KinematicsMode::ElasticElastic:
           return 7;
         case KinematicsMode::ElasticInelastic:
         case KinematicsMode::InelasticElastic:
           return 8;
         case KinematicsMode::InelasticInelastic:
           return 9;
       }
     }
 
     //---------------------------------------------------------------------------------------------
 
     void
     GamGamLL::setKinematics( const Kinematics& kin )
     {
       GenericProcess::setKinematics( kin );
 
       masses_.Ml2 = (*event_)[Particle::CentralSystem][0].mass2();
 
       w_limits_ = kin_.cuts.central.mass_single;
       if ( !w_limits_.hasMax() )
         w_limits_.max() = s_;
       // The minimal energy for the central system is its outgoing leptons' mass energy (or wmin_ if specified)
       if ( !w_limits_.hasMin() )
         w_limits_.min() = 4.*masses_.Ml2;
       // The maximal energy for the central system is its CM energy with the outgoing particles' mass energy substracted (or wmax if specified)
       w_limits_.max() = std::min( pow( sqs_-MX_-MY_, 2 ), w_limits_.max() );
 
       CG_DEBUG_LOOP( "GamGamLL:setKinematics" )
         << "w limits = " << w_limits_ << "\n\t"
         << "wmax/wmin = " << w_limits_.max()/w_limits_.min();
-
-      q2_limits_ = kin_.cuts.initial.q2;
-      mx_limits_ = kin_.cuts.remnants.mass_single;
     }
 
     //---------------------------------------------------------------------------------------------
 
     bool
     GamGamLL::pickin()
     {
       CG_DEBUG_LOOP( "GamGamLL" )
         << "Optimised mode? " << n_opt_;
 
       jacobian_ = 0.;
 
       w4_ = mc4_*mc4_;
 
       // sig1 = sigma and sig2 = sigma' in [1]
       const double sig = mc4_+MY_;
       double sig1 = sig*sig;
 
       CG_DEBUG_LOOP( "GamGamLL" )
         << "mc4 = " << mc4_ << "\n\t"
         << "sig1 = " << sig1 << ".";
 
       // Mass difference between the first outgoing particle
       // and the first incoming particle
       masses_.w31 = masses_.MX2-w1_;
       // Mass difference between the second outgoing particle
       // and the second incoming particle
       masses_.w52 = masses_.MY2-w2_;
       // Mass difference between the two incoming particles
       masses_.w12 = w1_-w2_;
       // Mass difference between the central two-photons system
       // and the second outgoing particle
       const double d6 = w4_-masses_.MY2;
 
       CG_DEBUG_LOOP( "GamGamLL" )
         << "w1 = " << w1_ << "\n\t"
         << "w2 = " << w2_ << "\n\t"
         << "w3 = " << masses_.MX2 << "\n\t"
         << "w4 = " << w4_ << "\n\t"
         << "w5 = " << masses_.MY2;;
 
       CG_DEBUG_LOOP( "GamGamLL" )
         << "w31 = " << masses_.w31 << "\n\t"
         << "w52 = " << masses_.w52 << "\n\t"
         << "w12 = " << masses_.w12;;
 
       const double ss = s_+masses_.w12;
 
       const double rl1 = ss*ss-4.*w1_*s_; // lambda(s, m1**2, m2**2)
       if ( rl1 <= 0. ) {
         CG_WARNING( "GamGamLL" ) << "rl1 = " << rl1 << " <= 0";
         return false;
       }
       sl1_ = sqrt( rl1 );
 
       s2_ = 0.;
       double ds2 = 0.;
       if ( n_opt_ == 0 ) {
         const double smax = s_+masses_.MX2-2.*MX_*sqs_;
-        map( x(2), Limits( sig1, smax ), s2_, ds2, "s2" );
+        const auto s2 = map( x(2), Limits( sig1, smax ), "s2" );
+        s2_ = s2.first;
+        ds2 = s2.second;
         sig1 = s2_; //FIXME!!!!!!!!!!!!!!!!!!!!
       }
 
       CG_DEBUG_LOOP( "GamGamLL" )
         << "s2 = " << s2_;
 
       const double sp = s_+masses_.MX2-sig1, d3 = sig1-w2_;
       const double rl2 = sp*sp-4.*s_*masses_.MX2; // lambda(s, m3**2, sigma)
       if ( rl2 <= 0. ) {
         CG_DEBUG( "GamGamLL" ) << "rl2 = " << rl2 << " <= 0";
         return false;
       }
       const double sl2 = sqrt( rl2 );
 
       double t1_max = w1_+masses_.MX2-( ss*sp+sl1_*sl2 )/( 2.*s_ ), // definition from eq. (A.4) in [1]
              t1_min = ( masses_.w31*d3+( d3-masses_.w31 )*( d3*w1_-masses_.w31*w2_ )/s_ )/t1_max; // definition from eq. (A.5) in [1]
 
       // FIXME dropped in CDF version
-      if ( t1_max > -q2_limits_.min() ) {
-        CG_WARNING( "GamGamLL" ) << "t1max = " << t1_max << " > -q2min = " << ( -q2_limits_.min() );
+      if ( t1_max > -kin_.cuts.initial.q2.min() ) {
+        CG_WARNING( "GamGamLL" ) << "t1max = " << t1_max << " > -q2min = " << -kin_.cuts.initial.q2.min();
         return false;
       }
-      if ( t1_min < -q2_limits_.max() && q2_limits_.hasMax() ) {
-        CG_DEBUG( "GamGamLL" ) << "t1min = " << t1_min << " < -q2max = " << -q2_limits_.max();
+      if ( t1_min < -kin_.cuts.initial.q2.max() && kin_.cuts.initial.q2.hasMax() ) {
+        CG_DEBUG( "GamGamLL" ) << "t1min = " << t1_min << " < -q2max = " << -kin_.cuts.initial.q2.max();
         return false;
       }
-      if ( t1_max < -q2_limits_.max() && q2_limits_.hasMax() )
-        t1_max = -q2_limits_.max();
-      if ( t1_min > -q2_limits_.min() && q2_limits_.hasMin() )
-        t1_min = -q2_limits_.min();
+      if ( t1_max < -kin_.cuts.initial.q2.max() && kin_.cuts.initial.q2.hasMax() )
+        t1_max = -kin_.cuts.initial.q2.max();
+      if ( t1_min > -kin_.cuts.initial.q2.min() && kin_.cuts.initial.q2.hasMin() )
+        t1_min = -kin_.cuts.initial.q2.min();
       /////
 
       // t1, the first photon propagator, is defined here
-      t1_ = 0.;
-      double dt1 = 0.;
-      map( x(0), Limits( t1_min, t1_max ), t1_, dt1, "t1" );
-      // changes wrt mapt1 : dx->-dx
-      dt1 *= -1.;
+      const auto t1 = map( x(0), Limits( t1_min, t1_max ), "t1" );
+      t1_ = t1.first;
+      const double dt1 = -t1.second; // changes wrt mapt1 : dx->-dx
 
       CG_DEBUG_LOOP( "GamGamLL" )
         << "Definition of t1 = " << t1_ << " according to\n\t"
         << "(t1min, t1max) = (" << t1_min << ", " << t1_max << ")";
 
       dd4_ = w4_-t1_;
 
       const double d8 = t1_-w2_, t13 = t1_-w1_-masses_.MX2;
 
       sa1_ = -pow( t1_-masses_.w31, 2 )/4.+w1_*t1_;
       if ( sa1_ >= 0. ) {
         CG_WARNING( "GamGamLL" ) << "sa1_ = " << sa1_ << " >= 0";
         return false;
       }
 
       const double sl3 = sqrt( -sa1_ );
 
       Limits s2_lim;
       s2_lim.min() = sig*sig;
       // one computes splus and (s2x=s2max)
       double splus;
       if ( w1_ != 0. ) {
         const double inv_w1 = 1./w1_;
         const double sb = masses_.MX2 + 0.5 * ( s_*( t1_-masses_.w31 )+masses_.w12*t13 )*inv_w1,
                      sd = sl1_*sl3*inv_w1,
                      se =( s_*( t1_*( s_+t13-w2_ )-w2_*masses_.w31 )+masses_.MX2*( masses_.w12*d8+w2_*masses_.MX2 ) )*inv_w1;
 
         if ( fabs( ( sb-sd )/sd ) >= 1. ) {
           splus = sb-sd;
           s2_lim.max() = se/splus;
         }
         else {
           s2_lim.max() = sb+sd;
           splus = se/s2_lim.max();
         }
       }
       else { // 3
         s2_lim.max() = ( s_*( t1_*( s_+d8-masses_.MX2 )-w2_*masses_.MX2 )+w2_*masses_.MX2*( w2_+masses_.MX2-t1_ ) )/( ss*t13 );
         splus = s2_lim.min();
       }
       // 4
       double s2x = s2_lim.max();
 
       CG_DEBUG_LOOP( "GamGamLL" )
         << "s2x = s2max = " << s2x;
 
       if ( n_opt_ < 0 ) { // 5
         if ( splus > s2_lim.min() ) {
           s2_lim.min() = splus;
           CG_DEBUG_LOOP( "GamGamLL" )
             << "min(sig2) truncated to splus = " << splus;
         }
-        if ( n_opt_ < -1 )
-          map( x(2), s2_lim, s2_, ds2, "s2" );
-        else
-          mapla( t1_, w2_, x(2), s2_lim, s2_, ds2 ); // n_opt_==-1
+        const auto s2 = n_opt_ < -1
+          ? map( x(2), s2_lim, "s2" )
+          : mapla( t1_, w2_, x(2), s2_lim ); // n_opt_==-1
+        s2_ = s2.first;
+        ds2 = s2.second;
         s2x = s2_;
       }
       else if ( n_opt_ == 0 )
         s2x = s2_; // 6
 
       CG_DEBUG_LOOP( "GamGamLL" )
         << "s2x = " << s2x;
 
       // 7
       const double r1 = s2x-d8, r2 = s2x-d6;
 
       const double rl4 = ( r1*r1-4.*w2_*s2x )*( r2*r2-4.*masses_.MY2*s2x );
       if ( rl4 <= 0. ) {
         CG_DEBUG_LOOP( "GamGamLL" )
           << "rl4 = " << rl4 << " <= 0";
         return false;
       }
       const double sl4 = sqrt( rl4 );
 
       // t2max, t2min definitions from eq. (A.12) and (A.13) in [1]
       const double t2_max = w2_+masses_.MY2-( r1*r2+sl4 )/s2x * 0.5,
                    t2_min = ( masses_.w52*dd4_+( dd4_-masses_.w52 )*( dd4_*w2_-masses_.w52*t1_ )/s2x )/t2_max;
 
       // t2, the second photon propagator, is defined here
-      t2_ = 0.;
-      double dt2 = 0.;
-      map( x(1), Limits( t2_min, t2_max ), t2_, dt2, "t2" );
-      // changes wrt mapt2 : dx->-dx
-
-      dt2 *= -1.;
+      const auto t2 = map( x(1), Limits( t2_min, t2_max ), "t2" );
+      t2_ = t2.first;
+      const double dt2 = -t2.second; // changes wrt mapt2 : dx->-dx
 
       // \f$\delta_6=m_4^2-m_5^2\f$ as defined in Vermaseren's paper
       const double tau = t1_-t2_,
                    r3 = dd4_-t2_,
                    r4 = masses_.w52-t2_;
 
       CG_DEBUG_LOOP( "GamGamLL" )
+        << "tau= " << tau << "\n\t"
         << "r1 = " << r1 << "\n\t"
         << "r2 = " << r2 << "\n\t"
         << "r3 = " << r3 << "\n\t"
         << "r4 = " << r4;
 
       const double b = r3*r4-2.*( t1_+w2_ )*t2_;
       const double c = t2_*d6*d8+( d6-d8 )*( d6*w2_-d8*masses_.MY2 );
 
       const double t25 = t2_-w2_-masses_.MY2;
 
       sa2_ = -0.25 * r4*r4 + w2_*t2_;
       if ( sa2_ >= 0. ) {
-        CG_WARNING( "GamGamLL" )
-          <<  "sa2_ = " << sa2_ << " >= 0";
+        CG_WARNING( "GamGamLL" ) <<  "sa2_ = " << sa2_ << " >= 0";
         return false;
       }
 
       const double sl6 = 2.*sqrt( -sa2_ );
 
       g4_ = -r3*r3/4.+t1_*t2_;
       if ( g4_ >= 0. ) {
         CG_WARNING( "GamGamLL" ) << "g4_ = " << g4_ << " >= 0";
         return false;
       }
 
       const double sl7 = 2.*sqrt( -g4_ ),
                    sl5 = sl6*sl7;
 
       double s2p;
       if ( fabs( ( sl5-b )/sl5 ) >= 1. ) {
         s2p = 0.5 * ( sl5-b )/t2_;
         s2_lim.min() = c/( t2_*s2p );
       }
       else { // 8
         s2_lim.min() = 0.5 * ( -sl5-b )/t2_;
         s2p = c/( t2_*s2_lim.min() );
       }
       // 9
-      if ( n_opt_ > 1 )
-        map( x( 2 ), s2_lim, s2_, ds2, "s2" );
-      else if ( n_opt_ == 1 )
-        mapla( t1_, w2_, x( 2 ), s2_lim, s2_, ds2 );
+      if ( n_opt_ >= 1 ) {
+        const auto s2 = n_opt_ > 1
+          ? map( x( 2 ), s2_lim, "s2" )
+          : mapla( t1_, w2_, x( 2 ), s2_lim );
+        s2_ = s2.first;
+        ds2 = s2.second;
+      }
 
       const double ap = -0.25*pow( s2_+d8, 2 )+s2_*t1_;
 
       dd1_ = 0.25 * ( s2_-s2_lim.max() ) * ( w1_ != 0. ? ( splus-s2_ ) * w1_ : ss * t13 );
       dd2_ = 0.25 * ( s2_-s2_lim.min() ) * ( s2p-s2_ ) * t2_;
 
       CG_DEBUG_LOOP( "GamGamLL" )
         << "t2      = " << t2_ << "\n\t"
         << "s2      = " << s2_ << "\n\t"
         << "s2p     = " << s2p << "\n\t"
         << "splus   = " << splus << "\n\t"
-        << "s2 range= " << s2_lim << "\n\t"
-        << "dd2     = " << dd2_;;
+        << "s2 range= " << s2_lim;
 
       const double yy4 = cos( M_PI*x( 3 ) );
       const double dd = dd1_*dd2_;
       p12_ = 0.5 * ( s_-w1_-w2_ );
       const double st = s2_-t1_-w2_;
       const double delb = ( 2.*w2_*r3+r4*st )*( 4.*p12_*t1_-( t1_-masses_.w31 )*st )/( 16.*ap );
 
+      CG_DEBUG_LOOP( "GamGamLL" )
+        << std::scientific
+        << "dd = " << dd << ", "
+        << "dd1 = " << dd1_ << ", "
+        << "dd2 = " << dd2_ << std::fixed;
+
       if ( dd <= 0. ) {
-        CG_DEBUG_LOOP( "GamGamLL" )
-          << std::scientific
-          << "dd = " << dd << " <= 0\n\t"
-          << "dd1 = " << dd1_ << "\t"
-          << "dd2 = " << dd2_ << std::fixed;
+        CG_WARNING( "GamGamLL:pickin" ) << "dd = " << dd << " <= 0.";
         return false;
       }
 
       delta_ = delb - yy4*st*sqrt( dd )/ ap * 0.5;
       s1_ = t2_+w1_+( 2.*p12_*r3-4.*delta_ )/st;
 
       if ( ap >= 0. ) {
-        CG_DEBUG_LOOP( "GamGamLL" )
-          <<  "ap = " << ap << " >= 0";
+        CG_WARNING( "GamGamLL:pickin" ) <<  "ap = " << ap << " >= 0";
         return false;
       }
 
       jacobian_ = ds2 * dt1 * dt2 * 0.125 * M_PI*M_PI/( sl1_*sqrt( -ap ) );
 
       CG_DEBUG_LOOP( "GamGamLL" )
-        << "Jacobian = " << std::scientific << jacobian_ << std::fixed;
+        << "ds2=" << ds2 << ", dt1=" << dt1 << ", dt2=" << dt2 << "\n\t"
+        << "Jacobian=" << std::scientific << jacobian_ << std::fixed;
 
       gram_ = ( 1.-yy4*yy4 )*dd/ap;
 
       p13_ = -0.5 * t13;
       p14_ =  0.5 * ( tau+s1_-masses_.MX2 );
       p25_ = -0.5 * t25;
 
       p1k2_ = 0.5 * ( s1_-t2_-w1_ );
       p2k1_ = 0.5 * st;
 
       if ( w2_ != 0. ) {
         const double inv_w2 = 1./w2_;
         const double sbb = 0.5 * ( s_*( t2_-masses_.w52 )-masses_.w12*t25 )*inv_w2 + masses_.MY2,
                      sdd = 0.5 * sl1_*sl6*inv_w2,
                      see = ( s_*( t2_*( s_+t25-w1_ )-w1_*masses_.w52 )+masses_.MY2*( w1_*masses_.MY2-masses_.w12*( t2_-w1_ ) ) )*inv_w2;
         double s1m = 0., s1p = 0.;
         if ( sbb/sdd >= 0. ) {
           s1p = sbb+sdd;
           s1m = see/s1p;
         }
         else {
           s1m = sbb-sdd;
           s1p = see/s1m;
         } // 12
         dd3_ = -0.25 * w2_*( s1p-s1_ )*( s1m-s1_ ); // 13
       }
       else { // 14
         const double s1p = ( s_*( t2_*( s_-masses_.MY2+t2_-w1_ )-w1_*masses_.MY2 )+w1_*masses_.MY2*( w1_+masses_.MY2-t2_ ) )/( t25*( s_-masses_.w12 ) );
         dd3_ = -0.25 * t25*( s_-masses_.w12 )*( s1p-s1_ );
       }
       // 15
       //const double acc3 = (s1p-s1_)/(s1p+s1_);
 
       const double ssb = t2_+0.5 * w1_-r3*( masses_.w31-t1_ )/t1_,
                    ssd = sl3*sl7/t1_,
                    sse = ( t2_-w1_ )*( w4_-masses_.MX2 )+( t2_-w4_+masses_.w31 )*( ( t2_-w1_)*masses_.MX2-(w4_-masses_.MX2)*w1_)/t1_;
 
       double s1pp, s1pm;
       if ( ssb/ssd >= 0. ) {
         s1pp = ssb+ssd;
         s1pm = sse/s1pp;
       }
       else { // 16
         s1pm = ssb-ssd;
         s1pp = sse/s1pm;
       }
       // 17
       dd4_ = -0.25 * t1_*( s1_-s1pp )*( s1_-s1pm );
       //const double acc4 = ( s1_-s1pm )/( s1_+s1pm );
       dd5_ = dd1_+dd3_+( ( p12_*( t1_-masses_.w31 )*0.5-w1_*p2k1_ )*( p2k1_*( t2_-masses_.w52 )-w2_*r3 )
                         -delta_*( 2.*p12_*p2k1_-w2_*( t1_-masses_.w31 ) ) ) / p2k1_;
 
       return true;
     }
 
     //---------------------------------------------------------------------------------------------
 
     bool
     GamGamLL::orient()
     {
       if ( !pickin() || jacobian_ == 0. ) {
         CG_DEBUG_LOOP( "GamGamLL" )
           << "Pickin failed! Jacobian = " << jacobian_;
         return false;
       }
 
       const double re = 0.5 / sqs_;
       ep1_ = re*( s_+masses_.w12 );
       ep2_ = re*( s_-masses_.w12 );
 
       CG_DEBUG_LOOP( "GamGamLL" )
         << std::scientific
         << " re = " << re << "\n\t"
         << "w12 = " << masses_.w12
         << std::fixed;
       CG_DEBUG_LOOP( "GamGamLL" )
         << "Incoming particles' energy = " << ep1_ << ", " << ep2_;
 
       p_cm_ = re*sl1_;
 
       de3_ = re*( s2_-masses_.MX2+masses_.w12 );
       de5_ = re*( s1_-masses_.MY2-masses_.w12 );
 
       // Final state energies
       const double ep3 = ep1_-de3_,
                    ep5 = ep2_-de5_;
       ec4_ = de3_+de5_;
 
       if ( ec4_ < mc4_ ) {
         CG_WARNING( "GamGamLL" )
           << "ec4_ = " << ec4_ << " < mc4_ = " << mc4_ << "\n\t"
           << "==> de3 = " << de3_ << ", de5 = " << de5_;
         return false;
       }
 
       // What if the protons' momenta are not along the z-axis?
       pc4_ = sqrt( ec4_*ec4_-mc4_*mc4_ );
 
       if ( pc4_ == 0. ) {
         CG_WARNING( "GamGamLL" ) << "pzc4 is null and should not be...";
         return false;
       }
 
       CG_DEBUG_LOOP( "GamGamLL" )
         << "Central system's energy: E4 = " << ec4_ << "\n\t"
         << "               momentum: p4 = " << pc4_ << "\n\t"
         << "         invariant mass: m4 = " << mc4_ << "\n\t"
         << "Outgoing particles' energy: E3 = " << ep3 << "\n\t"
         << "                            E5 = " << ep5;
 
       const double pp3 = sqrt( ep3*ep3-masses_.MX2 ), pt3 = sqrt( dd1_/s_ )/p_cm_;
       const double pp5 = sqrt( ep5*ep5-masses_.MY2 ), pt5 = sqrt( dd3_/s_ )/p_cm_;
 
       const double sin_theta3 = pt3/pp3, sin_theta5 = pt5/pp5;
 
       CG_DEBUG_LOOP( "GamGamLL" )
         << std::scientific
         << "sin(theta3) = " << sin_theta3 << "\n\t"
         << "sin(theta5) = " << sin_theta5
         << std::fixed;
 
       if ( sin_theta3 > 1. ) {
         CG_WARNING( "GamGamLL" )
           << "sin(theta3) = " << sin_theta3 << " > 1";
         return false;
       }
       if ( sin_theta5 > 1. ) {
         CG_WARNING( "GamGamLL" )
           << "sin(theta5) = " << sin_theta5 << " > 1";
         return false;
       }
 
       const double ct3 = ( ep1_*ep3 < p13_ ? -1. : +1. )*sqrt( 1.-sin_theta3*sin_theta3 );
       const double ct5 = ( ep2_*ep5 > p25_ ? -1. : +1. )*sqrt( 1.-sin_theta5*sin_theta5 );
 
       CG_DEBUG_LOOP( "GamGamLL" )
         << "ct3 = " << ct3 << "\n\t"
         << "ct5 = " << ct5;
 
       if ( dd5_ < 0. ) {
         CG_WARNING( "GamGamLL" )
           <<  "dd5 = " << dd5_ << " < 0";
         return false;
       }
 
       // Centre of mass system kinematics (theta4 and phi4)
       pt4_ = sqrt( dd5_/s_ )/p_cm_;
       sin_theta4_ = pt4_/pc4_;
 
       if ( sin_theta4_ > 1. ) {
         CG_WARNING( "GamGamLL" )
           << "st4 = " << sin_theta4_ << " > 1";
         return false;
       }
 
       cos_theta4_ = sqrt( 1.-sin_theta4_*sin_theta4_ );
       if ( ep1_*ec4_ < p14_ )
         cos_theta4_ *= -1.;
 
       al4_ = 1.-cos_theta4_;
       be4_ = 1.+cos_theta4_;
 
       if ( cos_theta4_ < 0. )
         be4_ = sin_theta4_*sin_theta4_/al4_;
       else
         al4_ = sin_theta4_*sin_theta4_/be4_;
 
       CG_DEBUG_LOOP( "GamGamLL" )
         << "ct4 = " << cos_theta4_ << "\n\t"
         << "al4 = " << al4_ << ", be4 = " << be4_;
 
       const double rr  = sqrt( -gram_/s_ )/( p_cm_*pt4_ );
       const double sin_phi3 =  rr / pt3, sin_phi5 = -rr / pt5;
 
       if ( fabs( sin_phi3 ) > 1. ) {
         CG_WARNING( "GamGamLL" )
-          << "sin(phi_3) = " << sin_phi3 << " while it must be in [-1 ; 1]";
+          << "sin(phi_3) = " << sin_phi3 << " while it must be in (" << Limits( -1., 1. ) << ")";
         return false;
       }
       if ( fabs( sin_phi5 ) > 1. ) {
         CG_WARNING( "GamGamLL" )
-          << "sin(phi_5) = " << sin_phi5 << " while it must be in [-1 ; 1]";
+          << "sin(phi_5) = " << sin_phi5 << " while it must be in (" << Limits( -1., 1. ) << ")";
         return false;
       }
 
       const double cos_phi3 = -sqrt( 1.-sin_phi3*sin_phi3 ), cos_phi5 = -sqrt( 1.-sin_phi5*sin_phi5 );
 
       p3_lab_ = Particle::Momentum( pp3*sin_theta3*cos_phi3, pp3*sin_theta3*sin_phi3, pp3*ct3, ep3 );
       p5_lab_ = Particle::Momentum( pp5*sin_theta5*cos_phi5, pp5*sin_theta5*sin_phi5, pp5*ct5, ep5 );
 
       const double a1 = p3_lab_.px()-p5_lab_.px();
 
       CG_DEBUG_LOOP( "GamGamLL" )
         << "Kinematic quantities\n\t"
         << "cos(theta3) = " << ct3 << "\t" << "sin(theta3) = " << sin_theta3 << "\n\t"
         << "cos( phi3 ) = " << cos_phi3 << "\t" << "sin( phi3 ) = " << sin_phi3 << "\n\t"
         << "cos(theta4) = " << cos_theta4_ << "\t" << "sin(theta4) = " << sin_theta4_ << "\n\t"
         << "cos(theta5) = " << ct5 << "\t" << "sin(theta5) = " << sin_theta5 << "\n\t"
         << "cos( phi5 ) = " << cos_phi5 << "\t" << "sin( phi5 ) = " << sin_phi5 << "\n\t"
         << "a1 = " << a1;
 
       if ( fabs( pt4_+p3_lab_.px()+p5_lab_.px() ) < fabs( fabs( a1 )-pt4_ ) ) {
         CG_DEBUG_LOOP( "GamGamLL" )
           << "|pt4+pt3*cos(phi3)+pt5*cos(phi5)| < | |a1|-pt4 |\n\t"
           << "pt4 = " << pt4_ << "\t"
           << "pt5 = " << pt5 << "\n\t"
           << "cos(phi3) = " << cos_phi3 << "\t"
           << "cos(phi5) = " << cos_phi5 << "\n\t"
           << "a1 = " << a1;
         return true;
       }
       if ( a1 < 0. )
-        p5_lab_[0] = -p5_lab_.px();
+        p5_lab_[0] *= -1.;
       else
-        p3_lab_[0] = -p3_lab_.px();
+        p3_lab_[0] *= -1.;
       return true;
     }
 
     //---------------------------------------------------------------------------------------------
 
     double
-    GamGamLL::computeOutgoingPrimaryParticlesMasses( double x, double outmass, double lepmass, double& dw )
+    GamGamLL::computeOutgoingPrimaryParticlesMasses( double x, double outmass, double lepmass, double& dmx )
     {
-      const double mx0 = mp_+PDG::get().mass( PDG::piZero ); // 1.07
-      const double wx2min = pow( std::max( mx0, mx_limits_.min() ), 2 ),
-                   wx2max = pow( std::min( sqs_-outmass-2.*lepmass, mx_limits_.max() ), 2 );
+      const double mx0 = mp_+PDG::get().mass( PDG::piPlus ); // 1.07
+//      const double mx0 = 1.07;//FIXME
+      const Limits wx_lim( pow( std::max( mx0, kin_.cuts.remnants.mass_single.min() ), 2 ),
+                           pow( std::min( sqs_-outmass-2.*lepmass, kin_.cuts.remnants.mass_single.max() ), 2 ) );
 
-      double mx2 = 0., dmx2 = 0.;
-      map( x, Limits( wx2min, wx2max ), mx2, dmx2, "mx2" );
+      const auto w = map( x, wx_lim, "mx2" );
+      const double mx = sqrt( w.first );
+      dmx = sqrt( w.second );
 
       CG_DEBUG_LOOP( "GamGamLL" )
-        << "mX^2 in range (" << wx2min << ", " << wx2max << "), x = " << x << "\n\t"
-        << "mX^2 = " << mx2 << ", d(mX^2) = " << dmx2 << "\n\t"
-        << "mX = " << sqrt( mx2 ) << ", d(mX) = " << sqrt( dmx2 );
+        << "mX² in range " << wx_lim << ", x = " << x << "\n\t"
+        << "mX² = " << w.first << ", dmX² = " << w.second << "\n\t"
+        << "mX = " << mx << ", dmX = " << dmx;
 
-      dw = sqrt( dmx2 );
-      return sqrt( mx2 );
+      return mx;
     }
 
     //---------------------------------------------------------------------------------------------
 
     void
     GamGamLL::beforeComputeWeight()
     {
       if ( !GenericProcess::is_point_set_ ) return;
 
       const Particle& p1 = event_->getOneByRole( Particle::IncomingBeam1 ),
                      &p2 = event_->getOneByRole( Particle::IncomingBeam2 );
 
       ep1_ = p1.energy();
       ep2_ = p2.energy();
 
       switch ( kin_.mode ) {
         case KinematicsMode::ElectronProton: default:
           throw CG_FATAL( "GamGamLL" ) << "Case not yet supported!";
         case KinematicsMode::ElasticElastic:
           masses_.dw31 = masses_.dw52 = 0.; break;
         case KinematicsMode::InelasticElastic: {
           const double m = computeOutgoingPrimaryParticlesMasses( x( 7 ), p1.mass(), sqrt( masses_.Ml2 ), masses_.dw31 );
           event_->getOneByRole( Particle::OutgoingBeam1 ).setMass( m );
           event_->getOneByRole( Particle::OutgoingBeam2 ).setMass( PDG::get().mass( p2.pdgId() ) );
         } break;
         case KinematicsMode::ElasticInelastic: {
           const double m = computeOutgoingPrimaryParticlesMasses( x( 7 ), p2.mass(), sqrt( masses_.Ml2 ), masses_.dw52 );
           event_->getOneByRole( Particle::OutgoingBeam1 ).setMass( PDG::get().mass( p1.pdgId() ) );
           event_->getOneByRole( Particle::OutgoingBeam2 ).setMass( m );
         } break;
         case KinematicsMode::InelasticInelastic: {
           const double mx = computeOutgoingPrimaryParticlesMasses( x( 7 ), p2.mass(), sqrt( masses_.Ml2 ), masses_.dw31 );
           event_->getOneByRole( Particle::OutgoingBeam1 ).setMass( mx );
           const double my = computeOutgoingPrimaryParticlesMasses( x( 8 ), p1.mass(), sqrt( masses_.Ml2 ), masses_.dw52 );
           event_->getOneByRole( Particle::OutgoingBeam2 ).setMass( my );
         } break;
       }
       MX_ = event_->getOneByRole( Particle::OutgoingBeam1 ).mass();
       MY_ = event_->getOneByRole( Particle::OutgoingBeam2 ).mass();
       masses_.MX2 = MX_*MX_;
       masses_.MY2 = MY_*MY_;
     }
 
     //---------------------------------------------------------------------------------------------
 
     double
     GamGamLL::computeWeight()
     {
       CG_DEBUG_LOOP( "GamGamLL" )
         << "sqrt(s) = " << sqs_ << " GeV\n\t"
         << "m(X1) = " << MX_ << " GeV\t"
         << "m(X2) = " << MY_ << " GeV";
 
       // compute the two-photon energy for this point
-      w4_ = 0.;
-      double dw4 = 0.;
-      map( x( 4 ), w_limits_, w4_, dw4, "w4" );
+      const auto w4 = map( x( 4 ), w_limits_, "w4" );
+      w4_ = w4.first;
+      const double dw4 = w4.second;
       mc4_ = sqrt( w4_ );
 
       CG_DEBUG_LOOP( "GamGamLL" )
         << "Computed value for w4 = " << w4_ << " → mc4 = " << mc4_;
 
       if ( !orient() )
         return 0.;
 
       if ( jacobian_ == 0. ) {
         CG_WARNING( "GamGamLL" ) << "dj = " << jacobian_;
         return 0.;
       }
 
       if ( t1_ > 0. ) {
         CG_WARNING( "GamGamLL" ) << "t1 = " << t1_ << " > 0";
         return 0.;
       }
       if ( t2_ > 0. ) {
         CG_WARNING( "GamGamLL" ) << "t2 = " << t2_ << " > 0";
         return 0.;
       }
 
       const double ecm6 = w4_ / ( 2.*mc4_ ),
                    pp6cm = sqrt( ecm6*ecm6-masses_.Ml2 );
 
       jacobian_ *= dw4*pp6cm/( mc4_*constants::SCONSTB*s_ );
 
       // Let the most obscure part of this code begin...
 
       const double e1mp1 = w1_ / ( ep1_+p_cm_ ),
                    e3mp3 = masses_.MX2 / ( p3_lab_.energy()+p3_lab_.p() );
 
       const double al3 = pow( sin( p3_lab_.theta() ), 2 )/( 1.+( p3_lab_.theta() ) );
 
       // 2-photon system kinematics ?!
       const double eg = ( w4_+t1_-t2_ )/( 2.*mc4_ );
       double pg = sqrt( eg*eg-t1_ );
 
       const double pgx = -p3_lab_.px()*cos_theta4_-sin_theta4_*( de3_-e1mp1 + e3mp3 + p3_lab_.p()*al3 ),
                    pgy = -p3_lab_.py(),
                    pgz = mc4_*de3_/( ec4_+pc4_ )-ec4_*de3_*al4_/mc4_-p3_lab_.px()*ec4_*sin_theta4_/mc4_+ec4_*cos_theta4_/mc4_*( p3_lab_.p()*al3+e3mp3-e1mp1 );
 
       CG_DEBUG_LOOP( "GamGamLL" ) << "pg = " << Particle::Momentum( pgx, pgy, pgz );
 
       const double pgp = std::hypot( pgx, pgy ), // outgoing proton (3)'s transverse momentum
                    pgg = std::hypot( pgp, pgz ); // outgoing proton (3)'s momentum
       if ( pgg > pgp*0.9 && pgg > pg )
         pg = pgg; //FIXME ???
 
       // angles for the 2-photon system ?!
       const double cpg = pgx/pgp, spg = pgy/pgp;
       const double stg = pgp/pg;
 
       const int theta_sign = ( pgz>0. ) ? 1 : -1;
       const double ctg = theta_sign*sqrt( 1.-stg*stg );
 
       double xx6 = x( 5 );
 
       const double amap = 0.5 * ( w4_-t1_-t2_ ),
                    bmap = 0.5 * sqrt( ( pow( w4_-t1_-t2_, 2 )-4.*t1_*t2_ )*( 1.-4.*masses_.Ml2/w4_ ) ),
                    ymap = ( amap+bmap )/( amap-bmap ),
                    beta = pow( ymap, 2.*xx6-1. );
       xx6 = 0.5 * ( 1. + amap/bmap*( beta-1. )/( beta+1. ) );
       xx6 = std::max( 0., std::min( xx6, 1. ) ); // xx6 in [0., 1.]
 
       CG_DEBUG_LOOP( "GamGamLL" )
         << "amap = " << amap << "\n\t"
         << "bmap = " << bmap << "\n\t"
         << "ymap = " << ymap << "\n\t"
         << "beta = " << beta;
 
       // 3D rotation of the first outgoing lepton wrt the CM system
       const double theta6cm = acos( 1.-2.*xx6 );
 
       // match the Jacobian
       jacobian_ *= ( amap+bmap*cos( theta6cm ) );
       jacobian_ *= ( amap-bmap*cos( theta6cm ) );
       jacobian_ /= amap;
       jacobian_ /= bmap;
       jacobian_ *= log( ymap );
       jacobian_ *= 0.5;
 
       CG_DEBUG_LOOP( "GamGamLL" ) << "Jacobian = " << jacobian_;
 
       CG_DEBUG_LOOP( "GamGamLL" )
         << "ctcm6 = " << cos( theta6cm ) << "\n\t"
         << "stcm6 = " << sin( theta6cm );
 
       const double phi6cm = 2.*M_PI*x( 6 );
 
       // First outgoing lepton's 3-momentum in the centre of mass system
       Particle::Momentum p6cm = Particle::Momentum::fromPThetaPhi( pp6cm, theta6cm, phi6cm );
 
       CG_DEBUG_LOOP( "GamGamLL" ) << "p3cm6 = " << p6cm;
 
       const double h1 = stg*p6cm.pz()+ctg*p6cm.px();
       const double pc6z = ctg*p6cm.pz()-stg*p6cm.px(), pc6x = cpg*h1-spg*p6cm.py();
 
       const double qcx = 2.*pc6x, qcz = 2.*pc6z;
       // qcy == QCY is never defined
 
       const double el6 = ( ec4_*ecm6+pc4_*pc6z ) / mc4_,
                    h2  = ( ec4_*pc6z+pc4_*ecm6 ) / mc4_;
 
       CG_DEBUG_LOOP( "GamGamLL" ) << "h1 = " << h1 << "\n\th2 = " << h2;
 
       // first outgoing lepton's 3-momentum
       const double p6x = cos_theta4_*pc6x+sin_theta4_*h2,
                    p6y = cpg*p6cm.py()+spg*h1,
                    p6z = cos_theta4_*h2-sin_theta4_*pc6x;
 
       // first outgoing lepton's kinematics
       p6_cm_ = Particle::Momentum( p6x, p6y, p6z, el6 );
       CG_DEBUG_LOOP( "GamGamLL" ) << "p6(cm) = " << p6_cm_;
 
       const double hq = ec4_*qcz/mc4_;
 
       const Particle::Momentum qve(
         cos_theta4_*qcx+sin_theta4_*hq,
         2.*p6y,
         cos_theta4_*hq-sin_theta4_*qcx,
         pc4_*qcz/mc4_ // energy
       );
 
       // Available energy for the second lepton is the 2-photon system's energy with the first lepton's energy removed
       const double el7 = ec4_-el6;
 
       CG_DEBUG_LOOP( "GamGamLL" )
         << "Outgoing kinematics\n\t"
         << " first outgoing lepton: p = " << p6_cm_.p() << ", E = " << p6_cm_.energy() << "\n\t"
         << "second outgoing lepton: p = " << p7_cm_.p() << ", E = " << p7_cm_.energy();
 
       // Second outgoing lepton's 3-momentum
       const double p7x = -p6x + pt4_,
                    p7y = -p6y,
                    p7z = -p6z + pc4_*cos_theta4_;
 
       // second outgoing lepton's kinematics
       p7_cm_ = Particle::Momentum( p7x, p7y, p7z, el7 );
 
       //p6_cm_ = Particle::Momentum(pl6*st6*cp6, pl6*st6*sp6, pl6*ct6, el6);
       //p7_cm_ = Particle::Momentum(pl7*st7*cp7, pl7*st7*sp7, pl7*ct7, el7);
 
       q1dq_ = eg*( 2.*ecm6-mc4_ )-2.*pg*p6cm.pz();
-      q1dq2_ = ( w4_-t1_-t2_ ) * 0.5;
+      q1dq2_ = 0.5*( w4_-t1_-t2_ );
+
+
+      CG_DEBUG_LOOP( "GamGamLL" )
+        << "ecm6 = " << ecm6 << ", mc4 = " << mc4_ << "\n\t"
+        << "eg = " << eg << ", pg = " << pg << "\n\t"
+        << "q1dq = " << q1dq_ << ", q1dq2 = " << q1dq2_;
 
       const double phi3 = p3_lab_.phi(), cos_phi3 = cos( phi3 ), sin_phi3 = sin( phi3 ),
                    phi5 = p5_lab_.phi(), cos_phi5 = cos( phi5 ), sin_phi5 = sin( phi5 );
 
       bb_ = t1_*t2_+( w4_*pow( sin( theta6cm ), 2 ) + 4.*masses_.Ml2*pow( cos( theta6cm ), 2 ) )*pg*pg;
 
       const double c1 = p3_lab_.pt() * ( qve.px()*sin_phi3  - qve.py()*cos_phi3   ),
                    c2 = p3_lab_.pt() * ( qve.pz()*ep1_ - qve.energy() *p_cm_ ),
                    c3 = ( masses_.w31*ep1_*ep1_ + 2.*w1_*de3_*ep1_ - w1_*de3_*de3_ + p3_lab_.pt2()*ep1_*ep1_ ) / ( p3_lab_.energy()*p_cm_ + p3_lab_.pz()*ep1_ );
 
       const double b1 = p5_lab_.pt() * ( qve.px()*sin_phi5  - qve.py()*cos_phi5   ),
                    b2 = p5_lab_.pt() * ( qve.pz()*ep2_ + qve.energy() *p_cm_ ),
                    b3 = ( masses_.w52*ep2_*ep2_ + 2.*w2_*de5_*ep2_ - w2_*de5_*de5_ + p5_lab_.pt2()*ep2_*ep2_ ) / ( ep2_*p5_lab_.pz() - p5_lab_.energy()*p_cm_ );
 
       const double r12 =  c2*sin_phi3 + qve.py()*c3,
                    r13 = -c2*cos_phi3 - qve.px()*c3;
 
       const double r22 =  b2*sin_phi5 + qve.py()*b3,
                    r23 = -b2*cos_phi5 - qve.px()*b3;
 
       epsi_ = p12_*c1*b1 + r12*r22 + r13*r23;
 
       g5_ = w1_*c1*c1 + r12*r12 + r13*r13;
       g6_ = w2_*b1*b1 + r22*r22 + r23*r23;
 
       const double pt3 = p3_lab_.pt(), pt5 = p5_lab_.pt();
       a5_ = -( qve.px()*cos_phi3 + qve.py()*sin_phi3 )*pt3*p1k2_
             -( ep1_*qve.energy()-p_cm_*qve.pz() )*( cos_phi3*cos_phi5 + sin_phi3*sin_phi5 )*pt3*pt5
             +( de5_*qve.pz()+qve.energy()*( p_cm_+p5_lab_.pz() ) )*c3;
       a6_ = -( qve.px()*cos_phi5 + qve.py()*sin_phi5 )*pt5*p2k1_
             -( ep2_*qve.energy()+p_cm_*qve.pz() )*( cos_phi3*cos_phi5 + sin_phi3*sin_phi5 )*pt3*pt5
             +( de3_*qve.pz()-qve.energy()*( p_cm_-p3_lab_.pz() ) )*b3;
 
       CG_DEBUG_LOOP( "GamGamLL" )
         << "a5 = " << a5_ << "\n\t"
         << "a6 = " << a6_;
 
       ////////////////////////////////////////////////////////////////
       // END of GAMGAMLL subroutine in the FORTRAN version
       ////////////////////////////////////////////////////////////////
 
       const Particle::Momentum cm = event_->getOneByRole( Particle::IncomingBeam1 ).momentum()
                                   + event_->getOneByRole( Particle::IncomingBeam2 ).momentum();
 
       ////////////////////////////////////////////////////////////////
       // INFO from f.f
       ////////////////////////////////////////////////////////////////
 
       const double gamma = cm.energy() / sqs_, betgam = cm.pz() / sqs_;
 
       //--- kinematics computation for both leptons
 
+      CG_DEBUG_LOOP( "GamGamLL:gmufil" )
+        << "unboosted P(l1)=" << p6_cm_ << "\n\t"
+        << "unboosted P(l2)=" << p7_cm_;
+
       p6_cm_.betaGammaBoost( gamma, betgam );
       p7_cm_.betaGammaBoost( gamma, betgam );
 
       //--- cut on mass of final hadronic system (MX/Y)
 
-      if ( mx_limits_.valid() ) {
+      if ( kin_.cuts.remnants.mass_single.valid() ) {
         if ( ( kin_.mode == KinematicsMode::InelasticElastic
             || kin_.mode == KinematicsMode::InelasticInelastic )
-          && !mx_limits_.passes( MX_ ) )
+          && !kin_.cuts.remnants.mass_single.passes( MX_ ) )
           return 0.;
         if ( ( kin_.mode == KinematicsMode::ElasticInelastic
             || kin_.mode == KinematicsMode::InelasticInelastic )
-          && !mx_limits_.passes( MY_ ) )
+          && !kin_.cuts.remnants.mass_single.passes( MY_ ) )
           return 0.;
       }
 
       //--- cut on the proton's Q2 (first photon propagator T1)
 
       if ( !kin_.cuts.initial.q2.passes( -t1_ ) )
         return 0.;
 
       //--- cuts on outgoing leptons' kinematics
 
       if ( !kin_.cuts.central.mass_sum.passes( ( p6_cm_+p7_cm_ ).mass() ) )
         return 0.;
 
       //----- cuts on the individual leptons
 
       if ( kin_.cuts.central.pt_single.valid() ) {
         const Limits& pt_limits = kin_.cuts.central.pt_single;
         if ( !pt_limits.passes( p6_cm_.pt() ) || !pt_limits.passes( p7_cm_.pt() ) )
           return 0.;
       }
 
       if ( kin_.cuts.central.energy_single.valid() ) {
         const Limits& energy_limits = kin_.cuts.central.energy_single;
         if ( !energy_limits.passes( p6_cm_.energy() ) || !energy_limits.passes( p7_cm_.energy() ) )
           return 0.;
       }
 
       if ( kin_.cuts.central.eta_single.valid() ) {
         const Limits& eta_limits = kin_.cuts.central.eta_single;
         if ( !eta_limits.passes( p6_cm_.eta() ) || !eta_limits.passes( p7_cm_.eta() ) )
           return 0.;
       }
 
       //--- compute the structure functions factors
 
       switch ( kin_.mode ) { // inherited from CDF version
         case KinematicsMode::ElectronProton: default: jacobian_ *= periPP( 1, 2 ); break;
         case KinematicsMode::ElasticElastic:          jacobian_ *= periPP( 2, 2 ); break;
         case KinematicsMode::InelasticElastic:        jacobian_ *= periPP( 3, 2 )*pow( masses_.dw31, 2 ); break;
         case KinematicsMode::ElasticInelastic:        jacobian_ *= periPP( 3, 2 )*pow( masses_.dw52, 2 ); break;
         case KinematicsMode::InelasticInelastic:      jacobian_ *= periPP( 3, 3 )*pow( masses_.dw31*masses_.dw52, 2 ); break;
       }
 
+      CG_DEBUG_LOOP( "GamGamLL:f" )
+        << "kinematics mode: " << kin_.mode << "\n\t"
+        << "Jacobian: " << jacobian_;
+
       //--- compute the event weight using the Jacobian
 
       return constants::GEVM2_TO_PB*jacobian_;
     }
 
     //---------------------------------------------------------------------------------------------
 
     void
     GamGamLL::fillKinematics( bool )
     {
       const Particle::Momentum cm = (*event_)[Particle::IncomingBeam1][0].momentum()
                                   + (*event_)[Particle::IncomingBeam2][0].momentum();
 
       const double gamma  = cm.energy()/sqs_, betgam = cm.pz()/sqs_;
 
+      CG_DEBUG_LOOP( "GamGamLL:gmufil" )
+        << "sqrt(s)=" << sqs_ << " GeV, initial two-proton system: " << cm << "\n\t"
+        << "gamma=" << gamma << ", betgam=" << betgam;
+
       Particle::Momentum plab_ip1 = Particle::Momentum( 0., 0.,  p_cm_, ep1_ ).betaGammaBoost( gamma, betgam );
       Particle::Momentum plab_ip2 = Particle::Momentum( 0., 0., -p_cm_, ep2_ ).betaGammaBoost( gamma, betgam );
+
+      CG_DEBUG_LOOP( "GamGamLL:gmufil" )
+        << "unboosted PX=" << p3_lab_ << "\n\t"
+        << "unboosted PY=" << p5_lab_;
+
       p3_lab_.betaGammaBoost( gamma, betgam );
       p5_lab_.betaGammaBoost( gamma, betgam );
 
+      CG_DEBUG_LOOP( "GamGamLL:gmufil" )
+        << "boosted PX=" << p3_lab_ << "\n\t"
+        << "boosted PY=" << p5_lab_ << "\n\t"
+        << "boosted P(l1)=" << p6_cm_ << "\n\t"
+        << "boosted P(l2)=" << p7_cm_;
+
       //----- parameterise a random rotation around z-axis
-      const int rany = ( rand() >= 0.5 * RAND_MAX ) ? 1 : -1,
-                ransign = ( rand() >= 0.5 * RAND_MAX ) ? 1 : -1;
-      const double ranphi = ( 0.5 * rand()/RAND_MAX )*2.*M_PI;
+      const short rany = drand() > 0.5 ? 1 : -1, ransign = drand() > 0.5 ? 1 : -1;
+      const double ranphi = 2*drand()*M_PI;
+
+      Particle::Momentum plab_ph1 = plab_ip1-p3_lab_;
+      plab_ph1.rotatePhi( ranphi, rany );
 
-      Particle::Momentum plab_ph1 = ( plab_ip1-p3_lab_ ).rotatePhi( ranphi, rany );
-      Particle::Momentum plab_ph2 = ( plab_ip2-p5_lab_ ).rotatePhi( ranphi, rany );
+      Particle::Momentum plab_ph2 = plab_ip2-p5_lab_;
+      plab_ph2.rotatePhi( ranphi, rany );
 
       p3_lab_.rotatePhi( ranphi, rany );
       p5_lab_.rotatePhi( ranphi, rany );
       p6_cm_.rotatePhi( ranphi, rany );
       p7_cm_.rotatePhi( ranphi, rany );
 
+      CG_DEBUG_LOOP( "GamGamLL:gmufil" )
+        << "boosted+rotated PX=" << p3_lab_ << "\n\t"
+        << "boosted+rotated PY=" << p5_lab_ << "\n\t"
+        << "boosted+rotated P(l1)=" << p6_cm_ << "\n\t"
+        << "boosted+rotated P(l2)=" << p7_cm_;
+
       /*if ( symmetrise_ && rand() >= .5*RAND_MAX ) {
         p6_cm_.mirrorZ();
         p7_cm_.mirrorZ();
       }*/
 
       //----- incoming protons
       event_->getOneByRole( Particle::IncomingBeam1 ).setMomentum( plab_ip1 );
       event_->getOneByRole( Particle::IncomingBeam2 ).setMomentum( plab_ip2 );
 
       //----- first outgoing proton
-      Particle& op1 = event_->getOneByRole( Particle::OutgoingBeam1 );
+      auto& op1 = event_->getOneByRole( Particle::OutgoingBeam1 );
 
       op1.setMomentum( p3_lab_ );
       switch ( kin_.mode ) {
         case KinematicsMode::ElasticElastic:
         case KinematicsMode::ElasticInelastic:
         default:
           op1.setStatus( Particle::Status::FinalState ); // stable proton
           break;
         case KinematicsMode::InelasticElastic:
         case KinematicsMode::InelasticInelastic:
           op1.setStatus( Particle::Status::Unfragmented ); // fragmenting remnants
           op1.setMass( MX_ );
           break;
       }
 
       //----- second outgoing proton
-      Particle& op2 = event_->getOneByRole( Particle::OutgoingBeam2 );
+      auto& op2 = event_->getOneByRole( Particle::OutgoingBeam2 );
       op2.setMomentum( p5_lab_ );
       switch ( kin_.mode ) {
         case KinematicsMode::ElasticElastic:
         case KinematicsMode::InelasticElastic:
         default:
           op2.setStatus( Particle::Status::FinalState ); // stable proton
           break;
         case KinematicsMode::ElasticInelastic:
         case KinematicsMode::InelasticInelastic:
           op2.setStatus( Particle::Status::Unfragmented ); // fragmenting remnants
           op2.setMass( MY_ );
           break;
       }
 
       //----- first incoming photon
-      Particle& ph1 = event_->getOneByRole( Particle::Parton1 );
+      auto& ph1 = event_->getOneByRole( Particle::Parton1 );
       ph1.setMomentum( plab_ph1 );
 
       //----- second incoming photon
-      Particle& ph2 = event_->getOneByRole( Particle::Parton2 );
+      auto& ph2 = event_->getOneByRole( Particle::Parton2 );
       ph2.setMomentum( plab_ph2 );
 
-      Particles& central_system = (*event_)[Particle::CentralSystem];
+      auto& central_system = (*event_)[Particle::CentralSystem];
 
       //----- first outgoing lepton
-      Particle& ol1 = central_system[0];
+      auto& ol1 = central_system[0];
       ol1.setPdgId( ol1.pdgId(), ransign );
       ol1.setMomentum( p6_cm_ );
       ol1.setStatus( Particle::Status::FinalState );
 
       //----- second outgoing lepton
-      Particle& ol2 = central_system[1];
+      auto& ol2 = central_system[1];
       ol2.setPdgId( ol2.pdgId(), -ransign );
       ol2.setMomentum( p7_cm_ );
       ol2.setStatus( Particle::Status::FinalState );
 
       //----- intermediate two-lepton system
       event_->getOneByRole( Particle::Intermediate ).setMomentum( p6_cm_+p7_cm_ );
     }
 
     //---------------------------------------------------------------------------------------------
 
     double
     GamGamLL::periPP( int nup_, int ndown_ )
     {
-      CG_DEBUG_LOOP( "GamGamLL" )
+      CG_DEBUG_LOOP( "GamGamLL:peripp" )
         << " Nup  = " << nup_ << "\n\t"
         << "Ndown = " << ndown_;
 
       FormFactors fp1, fp2;
       formFactors( -t1_, -t2_, fp1, fp2 );
 
-      CG_DEBUG_LOOP( "GamGamLL" )
+      CG_DEBUG_LOOP( "GamGamLL:peripp" )
         << "u1 = " << fp1.FM << "\n\t"
         << "u2 = " << fp1.FE << "\n\t"
         << "v1 = " << fp2.FM << "\n\t"
         << "v2 = " << fp2.FE;
 
       const double qqq = q1dq_*q1dq_,
                    qdq = 4.*masses_.Ml2-w4_;
       const double t11 = 64. *(  bb_*( qqq-g4_-qdq*( t1_+t2_+2.*masses_.Ml2 ) )-2.*( t1_+2.*masses_.Ml2 )*( t2_+2.*masses_.Ml2 )*qqq ) * t1_*t2_, // magnetic-magnetic
                    t12 = 128.*( -bb_*( dd2_+g6_ )-2.*( t1_+2.*masses_.Ml2 )*( sa2_*qqq+a6_*a6_ ) ) * t1_, // electric-magnetic
                    t21 = 128.*( -bb_*( dd4_+g5_ )-2.*( t2_+2.*masses_.Ml2 )*( sa1_*qqq+a5_*a5_ ) ) * t2_, // magnetic-electric
                    t22 = 512.*(  bb_*( delta_*delta_-gram_ )-pow( epsi_-delta_*( qdq+q1dq2_ ), 2 )-sa1_*a6_*a6_-sa2_*a5_*a5_-sa1_*sa2_*qqq ); // electric-electric
 
       const double peripp = ( fp1.FM*fp2.FM*t11 + fp1.FE*fp2.FM*t21 + fp1.FM*fp2.FE*t12 + fp1.FE*fp2.FE*t22 ) / pow( 2.*t1_*t2_*bb_, 2 );
 
-      CG_DEBUG_LOOP( "GamGamLL" )
+      CG_DEBUG_LOOP( "GamGamLL:peripp" )
+        << "bb = " << bb_ << ", qqq = " << qqq << ", qdq = " << qdq << "\n\t"
         << "t11 = " << t11 << "\t" << "t12 = " << t12 << "\n\t"
         << "t21 = " << t21 << "\t" << "t22 = " << t22 << "\n\t"
-        << "⇒ PeriPP = " << peripp;
+        << "=> PeriPP = " << peripp;
 
       return peripp;
     }
 
-    void
-    GamGamLL::map( double expo, const Limits& lim, double& out, double& dout, const std::string& var_name_ )
+    std::pair<double,double>
+    GamGamLL::map( double expo, const Limits& lim, const std::string& var_name_ )
     {
-      const double y = lim.max()/lim.min();
-      out = lim.min()*pow( y, expo );
-      dout = out*log( y );
+      const double y = lim.max()/lim.min(), out = lim.min()*pow( y, expo ), dout = out*log( y );
       CG_DEBUG_LOOP( "GamGamLL:map" )
-        << "Mapping variable \"" << var_name_ << "\"\n\t"
-        << "limits = " << lim << "\n\t"
-        << "max/min = " << y << "\n\t"
-        << "exponent = " << expo << "\n\t"
-        << "output = " << out << "\n\t"
-        << "d(output) = " << dout;
+        << "Mapping variable \"" << var_name_ << "\" in range (" << lim << ")"
+        << " (max/min = " << y << ")\n\t"
+        << "exponent = " << expo << " => "
+        << "x = " << out << ", dx = " << dout;
+      return { out, dout };
     }
 
-    void
-    GamGamLL::mapla( double y, double z, int u, const Limits& lim, double& out, double& dout )
+    std::pair<double,double>
+    GamGamLL::mapla( double y, double z, int u, const Limits& lim )
     {
       const double xmb = lim.min()-y-z, xpb = lim.max()-y-z;
       const double c = -4.*y*z;
-      const double alp = sqrt( xpb*xpb + c ), alm = sqrt( xmb*xmb + c );
+      const double alp = sqrt( xpb*xpb+c ), alm = sqrt( xmb*xmb+c );
       const double am = xmb+alm, ap = xpb+alp;
       const double yy = ap/am, zz = pow( yy, u );
 
-      out = y+z+( am*zz - c / ( am*zz ) ) / 2.;
+      const double out = y+z+0.5*( am*zz-c / ( am*zz ) );
       const double ax = sqrt( pow( out-y-z, 2 )+c );
-      dout = ax*log( yy );
+      return { out, ax*log( yy ) };
     }
 
     void
     GamGamLL::formFactors( double q1, double q2, FormFactors& fp1, FormFactors& fp2 ) const
     {
       const double mx2 = MX_*MX_, my2 = MY_*MY_;
 
       switch ( kin_.mode ) {
         case KinematicsMode::ElasticElastic: default: {
           fp1 = FormFactors::protonElastic( -t1_ );
           fp2 = FormFactors::protonElastic( -t2_ );
         } break;
         case KinematicsMode::ElasticInelastic: {
           fp1 = FormFactors::protonElastic( -t1_ );
           fp2 = FormFactors::protonInelastic( -t2_, w2_, my2, *kin_.structure_functions );
         } break;
         case KinematicsMode::InelasticElastic: {
           fp1 = FormFactors::protonInelastic( -t1_, w1_, mx2, *kin_.structure_functions );
           fp2 = FormFactors::protonElastic( -t2_ );
         } break;
         case KinematicsMode::InelasticInelastic: {
           fp1 = FormFactors::protonInelastic( -t1_, w1_, mx2, *kin_.structure_functions );
           fp2 = FormFactors::protonInelastic( -t2_, w2_, my2, *kin_.structure_functions );
         } break;
       }
     }
   }
 }
 // register process and define aliases
 REGISTER_PROCESS( lpair, GamGamLL )
 REGISTER_PROCESS( gamgamll, GamGamLL )
diff --git a/CepGen/Processes/GamGamLL.h b/CepGen/Processes/GamGamLL.h
index d41260a..a0f4555 100644
--- a/CepGen/Processes/GamGamLL.h
+++ b/CepGen/Processes/GamGamLL.h
@@ -1,216 +1,213 @@
 #ifndef CepGen_Processes_GamGamLL_h
 #define CepGen_Processes_GamGamLL_h
 
 #include "CepGen/Processes/GenericProcess.h"
 
 namespace cepgen
 {
   namespace proc
   {
     /**
      * Full class of methods and objects to compute the full analytic matrix element
      * \cite Vermaseren:1982cz for the \f$\gamma\gamma\to\ell^{+}\ell^{-}\f$ process
      * according to a set of kinematic constraints provided for the incoming and
      * outgoing particles (the Kinematics object).
      * The \a f function created by this Process child has its \a _ndim -dimensional
      * coordinates mapped as :
      * - 0 = \f$t_1\f$, first incoming photon's virtuality
      * - 1 = \f$t_2\f$, second incoming photon's virtuality
      * - 2 = \f$s_2\f$ mapping
      * - 3 = yy4 = \f$\cos\left(\pi x_3\right)\f$ definition
      * - 4 = \f$w_4\f$, the two-photon system's invariant mass
      * - 5 = xx6 = \f$\frac{1}{2}\left(1-\cos\theta^{\rm CM}_6\right)\f$ definition (3D rotation of the first outgoing lepton with respect to the two-photon centre-of-mass system). If the \a nm_ optimisation flag is set this angle coefficient value becomes
      *   \f[\frac{1}{2}\left(\frac{a_{\rm map}}{b_{\rm map}}\frac{\beta-1}{\beta+1}+1\right)\f]
      *   with \f$a_{\rm map}=\frac{1}{2}\left(w_4-t_1-t_2\right)\f$, \f$b_{\rm map}=\frac{1}{2}\sqrt{\left(\left(w_4-t_1-t_2\right)^2-4t_1t_2\right)\left(1-4\frac{w_6}{w_4}\right)}\f$, and \f$\beta=\left(\frac{a_{\rm map}+b_{\rm map}}{a_{\rm map}-b_{\rm map}}\right)^{2x_5-1}\f$
      *   and the Jacobian element is scaled by a factor \f$\frac{1}{2}\frac{\left(a_{\rm map}^2-b_{\rm map}^2\cos^2\theta^{\rm CM}_6\right)}{a_{\rm map}b_{\rm map}}\log\left(\frac{a_{\rm map}+b_{\rm map}}{a_{\rm map}-b_{\rm map}}\right)\f$
      * - 6 = _phicm6_, or \f$\phi_6^{\rm CM}\f$ the rotation angle of the dilepton system in the centre-of-mass
      *   system
      * - 7 = \f$x_q\f$, \f$w_X\f$ mappings, as used in the single- and double-dissociative
      *   cases only
      * \brief Compute the matrix element for a CE \f$\gamma\gamma\to\ell^{+}\ell^{-}\f$
      *  process
      */
     class GamGamLL : public GenericProcess
     {
       public:
         /// \brief Class constructor: set the mandatory parameters before integration and events generation
         /// \param[in] params General process parameters (nopt = Optimisation, legacy from LPAIR)
         explicit GamGamLL( const ParametersList& params = ParametersList() );
         ProcessPtr clone( const ParametersList& params ) const override {
           return ProcessPtr( new GamGamLL( *this ) );
         }
 
         void addEventContent() override;
         void beforeComputeWeight() override;
         double computeWeight() override;
         unsigned int numDimensions() const override;
         void setKinematics( const Kinematics& cuts ) override;
         void fillKinematics( bool ) override;
         /// Compute the ougoing proton remnant mass
         /// \param[in] x A random number (between 0 and 1)
         /// \param[in] outmass The maximal outgoing particles' invariant mass
         /// \param[in] lepmass The outgoing leptons' mass
-        /// \param[out] dw The size of the integration bin
+        /// \param[out] dmx The size of the integration bin
         /// \return Mass of the outgoing proton remnant
-        double computeOutgoingPrimaryParticlesMasses( double x, double outmass, double lepmass, double& dw );
+        double computeOutgoingPrimaryParticlesMasses( double x, double outmass, double lepmass, double& dmx );
         /// Set all the kinematic variables for the outgoing proton remnants, and prepare the hadronisation
         /// \param[in] part Particle to "prepare" for the hadronisation to be performed
         void prepareHadronisation( Particle *part );
 
       private:
         /**
          * Calculate energies and momenta of the
          *  1st, 2nd (resp. the "proton-like" and the "electron-like" incoming particles),
          *  3rd (the "proton-like" outgoing particle),
          *  4th (the two-photons central system), and
          *  5th (the "electron-like" outgoing particle) particles in the overall centre-of-mass frame.
          * \brief Energies/momenta computation for the various particles, in the CM system
          * \return Success state of the operation
          */
         bool orient();
         /**
          * Compute the expression of the matrix element squared for the \f$\gamma\gamma\rightarrow\ell^{+}\ell^{-}\f$ process.
          * It returns the value of the convolution of the form factor or structure functions with the central two-photons matrix element squared.
          * \brief Computes the matrix element squared for the requested process
          * \return Full matrix element for the two-photon production of a pair of spin\f$-\frac{1}{2}-\f$point particles.
          *  It is noted as \f[
          *  M = \frac{1}{4bt_1 t_2}\sum_{i=1}^2\sum_{j=1}^2 u_i v_j t_{ij} = \frac{1}{4}\frac{u_1 v_1 t_{11}+u_2 v_1 t_{21}+u_1 v_2 t_{12}+u_2 v_2 t_{22}}{t_1 t_2 b}
          * \f] where \f$b\f$ = \a bb_ is defined in \a ComputeWeight as : \f[
          *  b = t_1 t_2+\left(w_{\gamma\gamma}\sin^2{\theta^{\rm CM}_6}+4m_\ell\cos^2{\theta^{\rm CM}_6}\right) p_g^2
          * \f]
          */
         double periPP( int, int );
         /**
          * Describe the kinematics of the process \f$p_1+p_2\to p_3+p_4+p_5\f$ in terms of Lorentz-invariant variables.
          * These variables (along with others) will then be fed into the \a PeriPP method (thus are essential for the evaluation of the full matrix element).
          * \return Success state of the operation
          */
         bool pickin();
 
         /// Internal switch for the optimised code version (LPAIR legacy ; unimplemented here)
         int n_opt_;
         pdgid_t pair_;
 
         Limits w_limits_;
-        Limits q2_limits_;
-        Limits mx_limits_;
         struct Masses
         {
           /// squared mass of the first proton-like outgoing particle
           double MX2 = 0.;
           /// squared mass of the second proton-like outgoing particle
           double MY2 = 0.;
           /// squared mass of the outgoing leptons
           double Ml2 = 0.;
           /// \f$\delta_2=m_1^2-m_2^2\f$ as defined in \cite Vermaseren:1982cz
           double w12 = 0.;
           /// \f$\delta_1=m_3^2-m_1^2\f$ as defined in \cite Vermaseren:1982cz
           double w31 = 0.;
           double dw31 = 0.;
           /// \f$\delta_4=m_5^2-m_2^2\f$ as defined in \cite Vermaseren:1982cz
           double w52 = 0.;
           double dw52 = 0.;
         } masses_;
 
         /// energy of the first proton-like incoming particle
         double ep1_;
         /// energy of the second proton-like incoming particle
         double ep2_;
         double p_cm_;
 
         /// energy of the two-photon central system
         double ec4_;
         /// 3-momentum norm of the two-photon central system
         double pc4_;
         /// mass of the two-photon central system
         double mc4_;
         /// squared mass of the two-photon central system
         double w4_;
 
         /// \f$p_{12} = \frac{1}{2}\left(s-m_{p_1}^2-m_{p_2}^2\right)\f$
         double p12_;
         double p1k2_, p2k1_;
         /// \f$p_{13} = -\frac{1}{2}\left(t_1-m_{p_1}^2-m_{p_3}^2\right)\f$
         double p13_;
         double p14_, p25_;
 
         double q1dq_, q1dq2_;
 
         double s1_, s2_;
 
         double epsi_;
         double g5_, g6_;
         double a5_, a6_;
         double bb_;
 
         double gram_;
         double dd1_, dd2_, dd3_;
         /// \f$\delta_5=m_4^2-t_1\f$ as defined in Vermaseren's paper
         /// \cite Vermaseren:1982cz for the full definition of this quantity
         double dd4_;
         double dd5_;
         /**
          * Invariant used to tame divergences in the matrix element computation. It is defined as
          * \f[\Delta = \left(p_1\cdot p_2\right)\left(q_1\cdot q_2\right)-\left(p_1\cdot q_2\right)\left(p_2\cdot q_1\right)\f]
          * with \f$p_i, q_i\f$ the 4-momenta associated to the incoming proton-like particle and to the photon emitted from it.
          */
         double delta_;
         double g4_;
         double sa1_, sa2_;
 
         double sl1_;
 
         /// cosine of the polar angle for the two-photons centre-of-mass system
         double cos_theta4_;
         /// sine of the polar angle for the two-photons centre-of-mass system
         double sin_theta4_;
 
         double al4_;
         double be4_;
         double de3_, de5_;
         double pt4_;
 
         /// Kinematics of the first incoming proton
         Particle::Momentum p1_lab_;
         /// Kinematics of the second incoming proton
         Particle::Momentum p2_lab_;
         /// Kinematics of the first outgoing proton
         Particle::Momentum p3_lab_;
         /// Kinematics of the two-photon system (in the two-proton CM)
         Particle::Momentum p4_lab_;
         /// Kinematics of the second outgoing proton
         Particle::Momentum p5_lab_;
         /// Kinematics of the first outgoing lepton (in the two-proton CM)
         Particle::Momentum p6_cm_;
         /// Kinematics of the second outgoing lepton (in the two-proton CM)
         Particle::Momentum p7_cm_;
         double jacobian_;
 
       private:
         /**
          * Define modified variables of integration to avoid peaks integrations (see \cite Vermaseren:1982cz for details)
          * Return a set of two modified variables of integration to maintain the stability of the integrant. These two new variables are :
          * - \f$y_{out} = x_{min}\left(\frac{x_{max}}{x_{min}}\right)^{exp}\f$ the new variable
          * - \f$\mathrm dy_{out} = x_{min}\left(\frac{x_{max}}{x_{min}}\right)^{exp}\log\frac{x_{min}}{x_{max}}\f$, the new variable's differential form
          * \brief Redefine the variables of integration in order to avoid the strong peaking of the integrant
          * \param[in] expo Exponant
          * \param[in] lim Min/maximal value of the variable
-         * \param[out] out The new variable definition
-         * \param[out] dout The bin width the new variable definition
          * \param[in] var_name The variable name
+         * \return A pair containing the value and the bin width the new variable definition
          * \note This method overrides the set of `mapxx` subroutines in ILPAIR, with a slight difference according to the sign of the
          *  \f$\mathrm dy_{out}\f$ parameter :
          *  - left unchanged :
          * > `mapw2`, `mapxq`, `mapwx`, `maps2`
          *  - opposite sign :
          * > `mapt1`, `mapt2`
          */
-        void map( double expo, const Limits& lim, double& out, double& dout, const std::string& var_name = "" );
-        void mapla( double y, double z, int u, const Limits& lim, double& x, double& d );
+        std::pair<double,double> map( double expo, const Limits& lim, const std::string& var_name = "" );
+        std::pair<double,double> mapla( double y, double z, int u, const Limits& lim );
         /// Compute the electric/magnetic form factors for the two considered \f$Q^{2}\f$ momenta transfers
         void formFactors( double q1, double q2, FormFactors& fp1, FormFactors& fp2 ) const;
     };
   }
 }
 
 #endif
diff --git a/CepGen/Processes/GenericProcess.cpp b/CepGen/Processes/GenericProcess.cpp
index 19e1ae3..155635a 100644
--- a/CepGen/Processes/GenericProcess.cpp
+++ b/CepGen/Processes/GenericProcess.cpp
@@ -1,232 +1,237 @@
 #include "CepGen/Processes/GenericProcess.h"
 
 #include "CepGen/Core/Exception.h"
 
 #include "CepGen/Event/Event.h"
 
 #include "CepGen/Physics/Constants.h"
 #include "CepGen/Physics/FormFactors.h"
 #include "CepGen/Physics/PDG.h"
 
 namespace cepgen
 {
   namespace proc
   {
     const double GenericProcess::mp_ = PDG::get().mass( PDG::proton );
     const double GenericProcess::mp2_ = GenericProcess::mp_*GenericProcess::mp_;
 
     GenericProcess::GenericProcess( const ParametersList& params, const std::string& name, const std::string& description, bool has_event ) :
       params_( params ), name_( name ), description_( description ),
       first_run( true ),
       s_( -1. ), sqs_( -1. ),
       MX_( -1. ), MY_( -1. ), w1_( -1. ), w2_( -1. ),
       t1_( -1. ), t2_( -1. ),
       has_event_( has_event ), event_( new Event ),
       is_point_set_( false )
     {}
 
     GenericProcess::GenericProcess( const GenericProcess& proc ) :
       params_( proc.params_ ), name_( proc.name_ ), description_( proc.description_ ),
       first_run( proc.first_run ),
       s_( proc.s_ ), sqs_( proc.sqs_ ),
       MX_( proc.MX_ ), MY_( proc.MY_ ), w1_( proc.w1_ ), w2_( proc.w2_ ),
       t1_( -1. ), t2_( -1. ), kin_( proc.kin_ ),
       has_event_( proc.has_event_ ), event_( new Event( *proc.event_.get() ) ),
       is_point_set_( false )
     {}
 
     GenericProcess&
     GenericProcess::operator=( const GenericProcess& proc )
     {
       params_ = proc.params_;
       name_ = proc.name_; description_ = proc.description_;
       first_run = proc.first_run;
       s_ = proc.s_; sqs_ = proc.sqs_;
       MX_ = proc.MX_; MY_ = proc.MY_; w1_ = proc.w1_; w2_ = proc.w2_;
       kin_ = proc.kin_;
       has_event_ = proc.has_event_; event_.reset( new Event( *proc.event_.get() ) );
       is_point_set_ = false;
       return *this;
     }
 
     void
     GenericProcess::setPoint( const unsigned int ndim, double* x )
     {
       x_ = std::vector<double>( x, x+ndim );
       is_point_set_ = true;
 
       if ( CG_LOG_MATCH( "Process:dumpPoint", debugInsideLoop ) )
         dumpPoint();
       clearEvent();
     }
 
     double
     GenericProcess::x( unsigned int idx ) const
     {
       if ( idx >= x_.size() )
         return -1.;
       return x_[idx];
     }
 
     void
     GenericProcess::clearEvent()
     {
       event_->restore();
     }
 
     void
     GenericProcess::setKinematics( const Kinematics& kin )
     {
       kin_ = kin;
       prepareKinematics();
     }
 
     void
     GenericProcess::prepareKinematics()
     {
       if ( !isKinematicsDefined() )
         throw CG_FATAL( "GenericProcess" ) << "Kinematics not properly defined for the process.";
 
       const HeavyIon hi1( kin_.incoming_beams.first.pdg ), hi2( kin_.incoming_beams.second.pdg );
       const double m1 = hi1 ? HeavyIon::mass( hi1 ) : PDG::get().mass( kin_.incoming_beams.first.pdg );
       const double m2 = hi2 ? HeavyIon::mass( hi2 ) : PDG::get().mass( kin_.incoming_beams.second.pdg );
       // at some point introduce non head-on colliding beams?
       const auto p1 = Particle::Momentum::fromPxPyPzM( 0., 0., +kin_.incoming_beams.first .pz, m1 );
       const auto p2 = Particle::Momentum::fromPxPyPzM( 0., 0., -kin_.incoming_beams.second.pz, m2 );
       setIncomingKinematics( p1, p2 );
 
       s_ = ( p1+p2 ).mass2();
       sqs_ = sqrt( s_ );
 
       w1_ = p1.mass2();
       w2_ = p2.mass2();
 
       CG_DEBUG( "GenericProcess" ) << "Kinematics successfully prepared!\n"
         << "  √s = " << sqs_*1.e-3 << " TeV,\n"
         << "  p₁ = " << p1 << ", mass=" << p1.mass() << " GeV\n"
-        << "  p₂ = " << p1 << ", mass=" << p2.mass() << " GeV.";
+        << "  p₂ = " << p2 << ", mass=" << p2.mass() << " GeV.";
     }
 
     void
     GenericProcess::dumpPoint() const
     {
       std::ostringstream os;
       for ( unsigned short i = 0; i < x_.size(); ++i )
         os << Form( "  x(%2d) = %8.6f\n\t", i, x_[i] );
       CG_INFO( "GenericProcess" )
         << "Number of integration parameters: " << x_.size() << "\n\t"
         << os.str() << ".";
     }
 
     void
     GenericProcess::setEventContent( const IncomingState& ini, const OutgoingState& fin )
     {
       if ( !has_event_ )
         return;
 
       event_->clear();
       //----- add the particles in the event
 
       //--- incoming state
       for ( const auto& ip : ini ) {
         Particle& p = event_->addParticle( ip.first );
         const auto& part_info = PDG::get()( ip.second );
         p.setPdgId( ip.second, part_info.charge/3. );
         p.setMass( part_info.mass );
         if ( ip.first == Particle::IncomingBeam1
           || ip.first == Particle::IncomingBeam2 )
           p.setStatus( Particle::Status::PrimordialIncoming );
         if ( ip.first == Particle::Parton1
           || ip.first == Particle::Parton2 )
           p.setStatus( Particle::Status::Incoming );
       }
       //--- central system (if not already there)
       const auto& central_system = ini.find( Particle::CentralSystem );
       if ( central_system == ini.end() ) {
         Particle& p = event_->addParticle( Particle::Intermediate );
         p.setPdgId( PDG::invalid );
         p.setStatus( Particle::Status::Propagator );
       }
       //--- outgoing state
       for ( const auto& opl : fin ) { // pair(role, list of PDGids)
         for ( const auto& pdg : opl.second ) {
           Particle& p = event_->addParticle( opl.first );
           const auto& part_info = PDG::get()( pdg );
           p.setPdgId( pdg, part_info.charge/3. );
           p.setMass( part_info.mass );
         }
       }
 
       //----- define the particles parentage
 
       const Particles parts = event_->particles();
       for ( const auto& p : parts ) {
         Particle& part = (*event_)[p.id()];
         switch ( part.role() ) {
           case Particle::OutgoingBeam1:
           case Particle::Parton1:
             part.addMother( event_->getOneByRole( Particle::IncomingBeam1 ) );
             break;
           case Particle::OutgoingBeam2:
           case Particle::Parton2:
             part.addMother( event_->getOneByRole( Particle::IncomingBeam2 ) );
             break;
           case Particle::Intermediate:
             part.addMother( event_->getOneByRole( Particle::Parton1 ) );
             part.addMother( event_->getOneByRole( Particle::Parton2 ) );
             break;
           case Particle::CentralSystem:
             part.addMother( event_->getOneByRole( Particle::Intermediate ) );
             break;
           default: break;
         }
       }
 
       //----- freeze the event as it is
 
       event_->freeze();
       last_event = event_;
     }
 
     void
     GenericProcess::setIncomingKinematics( const Particle::Momentum& p1, const Particle::Momentum& p2 )
     {
-      if ( !has_event_ )
+      if ( !has_event_ || !event_ )
         return;
 
-      event_->getOneByRole( Particle::IncomingBeam1 ).setMomentum( p1 );
-      event_->getOneByRole( Particle::IncomingBeam2 ).setMomentum( p2 );
+      CG_DEBUG( "GenericProcess:incomingBeams" )
+        << "Incoming primary particles:\n\t"
+        << p1 << "\n\t"
+        << p2;
+
+      (*event_)[Particle::IncomingBeam1][0].setMomentum( p1 );
+      (*event_)[Particle::IncomingBeam2][0].setMomentum( p2 );
     }
 
     bool
     GenericProcess::isKinematicsDefined()
     {
       if ( !has_event_ )
         return true;
 
       // check the incoming state
       bool is_incoming_state_set =
         ( !(*event_)[Particle::IncomingBeam1].empty() && !(*event_)[Particle::IncomingBeam2].empty() );
 
       // check the outgoing state
       bool is_outgoing_state_set =
         ( !(*event_)[Particle::OutgoingBeam1].empty() && !(*event_)[Particle::OutgoingBeam2].empty()
        && !(*event_)[Particle::CentralSystem].empty() );
 
       // combine both states
       return is_incoming_state_set && is_outgoing_state_set;
     }
 
     std::ostream&
     operator<<( std::ostream& os, const GenericProcess& proc )
     {
       return os << proc.name().c_str();
     }
 
     std::ostream&
     operator<<( std::ostream& os, const GenericProcess* proc )
     {
       return os << proc->name().c_str();
     }
   }
 }
diff --git a/test/test_singlepoint.cpp b/test/test_singlepoint.cpp
index dbcc63e..ac9f16c 100644
--- a/test/test_singlepoint.cpp
+++ b/test/test_singlepoint.cpp
@@ -1,29 +1,29 @@
 #include "CepGen/Cards/Handler.h"
 #include "CepGen/Generator.h"
 
 #include "CepGen/Core/Exception.h"
 
 using namespace std;
 
 int main( int argc, char* argv[] )
 {
   if ( argc < 2 )
     throw CG_FATAL( "main" ) << "Usage: " << argv[0] << " input-card";
 
   cepgen::Generator gen;
   gen.setParameters( cepgen::card::Handler::parse( argv[1] ) );
   CG_INFO( "main" ) << gen.parametersPtr();
 
   cepgen::utils::Logger::get().level = cepgen::utils::Logger::Level::debugInsideLoop;
 
-  vector<double> x( 12, 0.3 );
+  vector<double> x( 12, 0.6 );
 
   cout << "point: ";
   string delim;
   for ( const auto& v : x )
     cout << delim << v, delim = ", ";
   cout << endl;
   cout << "weight: " << gen.computePoint( &x[0] ) << endl;
 
   return 0;
 }