inline double get_random(AIDA::IHistogram1D& a_histo,
const std::vector<double>& a_integral) {
 //same logic as CERN-ROOT/TH1.cxx/GetRandom().
   double r = rflat_shoot();
   int xbins = a_histo.axis().bins();
   int ibin = binary_search(xbins,a_integral.data(),r);

   double x = a_histo.axis().binLowerEdge(ibin);
   if(r>a_integral[ibin]) {
      x += a_histo.axis().binWidth(ibin)*
         (r-a_integral[ibin])/(a_integral[ibin+1]-a_integral[ibin]);
   }
   return x;
}
inline bool get_integral(AIDA::IHistogram1D& a_histo,
std::vector<double>& a_integral) {
   a_integral.clear();
   a_integral.push_back(0);
   int xbins = a_histo.axis().bins();
   for(int index=0;index<xbins;index++) {
      double h = a_histo.binHeight(index);
      a_integral.push_back(h+a_integral.back());
   }
 // normalize integral to 1 :
   double w = a_integral.back();
   if(w==0) return false;
   std::vector<double>::iterator it;
   for(it=a_integral.begin();it!=a_integral.end();++it){
      *it /= w;
   }
   return true;
}
void	EUTelProcessorTrackAnalysis::initialiseResidualVsPositionHistograms(){
	int NBinX;
	double MinX;
	double MaxX;
	int NBinY;
	double MinY;
	double MaxY;
	double MinZ;
	double MaxZ;

	std::string _histoInfoFileName="histoInfo.xml";
	auto_ptr<EUTelHistogramManager> histoMgr( new EUTelHistogramManager( _histoInfoFileName ));
	EUTelHistogramInfo    * histoInfo;
	bool                    isHistoManagerAvailable;

	try {
			isHistoManagerAvailable = histoMgr->init( );
	} catch ( ios::failure& e ) {
			streamlog_out( ERROR5 ) << "I/O problem with " << _histoInfoFileName << "\n"
							<< "Continuing without histogram manager using default settings"    << endl;
			isHistoManagerAvailable = false;
	} catch ( ParseException& e ) {
			streamlog_out( ERROR5 ) << e.what( ) << "\n"
							<< "Continuing without histogram manager using default settings" << endl;
			isHistoManagerAvailable = false;
	}

	std::stringstream sstm;
	std::string residGblFitHistName;
	std::string histTitle;
	for (size_t i = 0; i < _sensorIDs.size() ; ++i){
		/////////////////////////////////////////////////////////////////////////////XY residual plots with position
		sstm << "ResidualX" << _sensorIDs.at(i);
		residGblFitHistName = sstm.str();
		sstm.str(std::string());
		sstm << "ResidualsX. Plane " <<  _sensorIDs.at(i) << ";X direction; Y direction";
		histTitle = sstm.str();
		sstm.str(std::string(""));
		histoInfo = histoMgr->getHistogramInfo(residGblFitHistName);
		NBinX = ( isHistoManagerAvailable && histoInfo ) ? histoInfo->_xBin : 40;
		MinX =  ( isHistoManagerAvailable && histoInfo ) ? histoInfo->_xMin :-10 ;
		MaxX =  ( isHistoManagerAvailable && histoInfo ) ? histoInfo->_xMax : 10;
		NBinY = ( isHistoManagerAvailable && histoInfo ) ? histoInfo->_yBin : 20;
		MinY =  ( isHistoManagerAvailable && histoInfo ) ? histoInfo->_yMin : -5;
		MaxY =  ( isHistoManagerAvailable && histoInfo ) ? histoInfo->_yMax : 5;
		MinZ =  ( isHistoManagerAvailable && histoInfo ) ? histoInfo->_zMin : -20;
		MaxZ =  ( isHistoManagerAvailable && histoInfo ) ? histoInfo->_zMax : 20;
		AIDA::IProfile2D *  residGblFitX =	marlin::AIDAProcessor::histogramFactory(this)->createProfile2D(residGblFitHistName,  NBinX, MinX, MaxX, NBinY, MinY, MaxY, MinZ,MaxZ);
		if (residGblFitX) {
				residGblFitX->setTitle(histTitle);
				_mapFromSensorIDToHistogramX.insert(std::make_pair(_sensorIDs.at(i), residGblFitX));
		} else {
				streamlog_out(ERROR2) << "Problem booking the " << (residGblFitHistName) << std::endl;
				streamlog_out(ERROR2) << "Very likely a problem with path name. Switching off histogramming and continue w/o" << std::endl;
		}
		sstm.str(std::string(""));
	}
	for(size_t i = 0; i < _sensorIDs.size() ; ++i){
		sstm << "ResidualY" << _sensorIDs.at(i);
		residGblFitHistName = sstm.str();
		sstm.str(std::string());
		sstm << "ResidualsY. Plane " <<  _sensorIDs.at(i) << ";X direction; Y direction";
		histTitle = sstm.str();
		sstm.str(std::string(""));
		histoInfo = histoMgr->getHistogramInfo(residGblFitHistName);
		NBinX = ( isHistoManagerAvailable && histoInfo ) ? histoInfo->_xBin : 40;//every 500 micron there is a bin
		MinX =  ( isHistoManagerAvailable && histoInfo ) ? histoInfo->_xMin :-10 ;
		MaxX =  ( isHistoManagerAvailable && histoInfo ) ? histoInfo->_xMax : 10;
		NBinY = ( isHistoManagerAvailable && histoInfo ) ? histoInfo->_yBin : 20;//every 500 micron there is a bin
		MinY =  ( isHistoManagerAvailable && histoInfo ) ? histoInfo->_yMin : -5;
		MaxY =  ( isHistoManagerAvailable && histoInfo ) ? histoInfo->_yMax : 5;
		MinZ =  ( isHistoManagerAvailable && histoInfo ) ? histoInfo->_zMin : -20;
		MaxZ =  ( isHistoManagerAvailable && histoInfo ) ? histoInfo->_zMax : 20;
		AIDA::IProfile2D *  residGblFitY =	marlin::AIDAProcessor::histogramFactory(this)->createProfile2D(residGblFitHistName,  NBinX, MinX, MaxX, NBinY, MinY, MaxY, MinZ,MaxZ);
		if (residGblFitY) {
				residGblFitY->setTitle(histTitle);
				_mapFromSensorIDToHistogramY.insert(std::make_pair(_sensorIDs.at(i), residGblFitY));
		} else {
				streamlog_out(ERROR2) << "Problem booking the " << (residGblFitHistName) << std::endl;
				streamlog_out(ERROR2) << "Very likely a problem with path name. Switching off histogramming and continue w/o" << std::endl;
		}
		sstm.str(std::string(""));
	}
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/////////////////////////////////////////////////////////////////////////////////////The incidence angles for each plane
	for(size_t i = 0; i < _sensorIDs.size() ; ++i){
		sstm << "IncidenceXZ" << _sensorIDs.at(i);
		residGblFitHistName = sstm.str();
		sstm.str(std::string());
		sstm << "Incidence local Tx (XZ plane). Plane " <<  _sensorIDs.at(i);
		histTitle = sstm.str();
		sstm.str(std::string(""));
		histoInfo = histoMgr->getHistogramInfo(residGblFitHistName);
		NBinX = ( isHistoManagerAvailable && histoInfo ) ? histoInfo->_xBin : 40000;
		MinX =  ( isHistoManagerAvailable && histoInfo ) ? histoInfo->_xMin :-0.05 ;
		MaxX =  ( isHistoManagerAvailable && histoInfo ) ? histoInfo->_xMax : 0.005;
		AIDA::IHistogram1D * incidenceGblFitXZ = marlin::AIDAProcessor::histogramFactory(this)->createHistogram1D(residGblFitHistName, NBinX, MinX, MaxX); 

		if (incidenceGblFitXZ){
				incidenceGblFitXZ->setTitle(histTitle);
				_mapFromSensorIDToKinkXZ.insert(std::make_pair(_sensorIDs.at(i), incidenceGblFitXZ));
		} else {
				streamlog_out(ERROR2) << "Problem booking the " << (residGblFitHistName) << std::endl;
				streamlog_out(ERROR2) << "Very likely a problem with path name. Switching off histogramming and continue w/o" << std::endl;
		}
		sstm.str(std::string(""));
	}
	for(size_t i = 0; i < _sensorIDs.size() ; ++i){
		sstm << "IncidenceYZ" << _sensorIDs.at(i);
		residGblFitHistName = sstm.str();
		sstm.str(std::string());
		sstm << "Incidence local Ty (YZ plane). Plane " <<  _sensorIDs.at(i);
		histTitle = sstm.str();
		sstm.str(std::string(""));
		histoInfo = histoMgr->getHistogramInfo(residGblFitHistName);
		NBinX = ( isHistoManagerAvailable && histoInfo ) ? histoInfo->_xBin : 40;
		MinX =  ( isHistoManagerAvailable && histoInfo ) ? histoInfo->_xMin :-0.001 ;
		MaxX =  ( isHistoManagerAvailable && histoInfo ) ? histoInfo->_xMax : 0.001;
		AIDA::IHistogram1D * incidenceGblFitYZ = marlin::AIDAProcessor::histogramFactory(this)->createHistogram1D(residGblFitHistName, NBinX, MinX, MaxX); 

		if (incidenceGblFitYZ) {
				incidenceGblFitYZ->setTitle(histTitle);
				_mapFromSensorIDToKinkYZ.insert(std::make_pair(_sensorIDs.at(i), incidenceGblFitYZ));
		} else {
				streamlog_out(ERROR2) << "Problem booking the " << (residGblFitHistName) << std::endl;
				streamlog_out(ERROR2) << "Very likely a problem with path name. Switching off histogramming and continue w/o" << std::endl;
		}
		sstm.str(std::string(""));
	}
	/////////////////////////////////////////////////////////////////////////////////////// 
}
int main(int argc,char* argv[]) {

   AIDA::IAnalysisFactory* af = AIDA_createAnalysisFactory();
   if(!af) return EXIT_FAILURE;

   AIDA::ITreeFactory* trf = af->createTreeFactory();
   if(!trf) return EXIT_FAILURE;
   AIDA::ITree* tree = trf->create();
   delete trf;
   if(!tree) return EXIT_FAILURE;

   AIDA::IHistogramFactory* hf = af->createHistogramFactory(*tree);
   if(!hf) return EXIT_FAILURE;
   AIDA::IHistogram1D* h1d = hf->createHistogram1D("master 1d","master 1d",50,-3,3);
   AIDA::IHistogram1D* hr = hf->createHistogram1D("random","random",50,-3,3);
   delete hf;
   if(!h1d) return EXIT_FAILURE;
   if(!hr) return EXIT_FAILURE;

   { // fill the "master" histogram with a gaussian centered at zero, and a rms of one
      for (int i=0; i<1000000; i++) {
         h1d->fill(rgauss_shoot());
      }
   }

   { // fill the "random" histogram with values sampled from the "master"
      std::vector<double> integral;
      if(!get_integral(*h1d,integral)) return EXIT_FAILURE;
      for (int i=0; i<1000000; i++) {
         hr->fill(get_random(*h1d,integral));
      }
   }

   AIDA::IPlotterFactory* pf = af->createPlotterFactory(argc,argv);
   if(pf) {
      AIDA::IPlotter* plotter = pf->create();
      delete pf;
      if(plotter) {
         plotter->createRegions(1,2,0);
         plotter->region(0)->plot(*h1d);
         plotter->region(1)->plot(*hr);
         plotter->show();
         plotter->interact();
         delete plotter;
      }
   } else {
      // Printing some statistical values of the "master" histogram
      std::cout << "Master :" << std::endl;
      std::cout << "Title  : " << h1d->title() << std::endl;
      std::cout << "Entries: " << h1d->entries() << std::endl;
      std::cout << "Mean   : " << h1d->mean() << std::endl;
      std::cout << "RMS    : " << h1d->rms() << std::endl;      
      std::cout << "========" << std::endl;
      // ... and the same for the "randomly sampled" histogram
      std::cout << "Sampled:" << std::endl;
      std::cout << "Title  : " << hr->title() << std::endl;
      std::cout << "Entries: " << hr->entries() << std::endl;
      std::cout << "Mean   : " << hr->mean() << std::endl;
      std::cout << "RMS    : " << hr->rms() << std::endl;      
   }

   delete af;

   std::cout << "That's it !" << std::endl;

   return EXIT_SUCCESS;
}
StatusCode TrackVertexMonitor::execute()
{

  LHCb::RecVertex::Range pvcontainer  = get<LHCb::RecVertex::Range>(m_pvContainerName) ;
  LHCb::Track::Range alltracks = get<LHCb::Track::Range>( m_trackContainerName );

  TrackTypePredicate isLong( LHCb::Track::Long ) ;
  TrackFlagPredicate isBackward( LHCb::Track::Backward ) ;
  TrackFlagPredicate isForward( LHCb::Track::Backward,false ) ;

  // lists needed
  // - primary vertices
  // - all tracks
  // - long tracks
  // - backward tracks
  // for now I'll just create the track lists from the Best container
  typedef std::vector<const LHCb::Track*> TrackVector ;

  // number of primary vertices
  plot(pvcontainer.size(),"NumPrimaryVertices",-0.5,10.5,11) ;

  BOOST_FOREACH( const LHCb::RecVertex* pv, pvcontainer ) {
    TrackVector tracks = myconvert(pv->tracks()) ;
    TrackVector forwardtracks = myselect(tracks,isForward) ;
    TrackVector backwardtracks =  myselect(tracks,isBackward) ;
    TrackVector longtracks =  myselect(tracks,isLong) ;

    // number of tracks per primary vertex
    plot( tracks.size(), "NumTracksPerPV",-0.5,99.5,50) ;
    // number of long tracks per primary vertex
    plot( longtracks.size(), "NumLongTracksPerPV",-0.5,99.5,50) ;
    // number of backward tracks per primary vertex
    plot( backwardtracks.size(), "NumBackTracksPerPV",-0.5,99.5,50) ;
    // chisquare
    plot( pv->chi2() / pv->nDoF(), "PV chisquare per dof",0.,3.,150) ;
    // position with crap hack for vertices at exactly 0
    if(std::abs(pv->position().x()) > 0.00001 && std::abs(pv->position().y()) > 0.00001 ){
      //info() << "pvx " << pv->position().x() << endmsg;
      plot( pv->position().x(), "PV x position",-m_rpvmax,m_rpvmax) ;
      plot( pv->position().y(), "PV y position",-m_rpvmax,m_rpvmax) ;
      plot( pv->position().z(), "PV z position", m_zpvmin,m_zpvmax) ;
      plot( pv->position().z(), "PV z position (wide)", m_zpvmin_wide,m_zpvmax_wide) ;
    }

    if( std::abs( pv->position().y() ) < m_rpvmax )
      profile1D( pv->position().z(), pv->position().y(),"PV y versus z",m_zpvmin,m_zpvmax,m_nprbins) ;
    if( std::abs( pv->position().x() ) < m_rpvmax )
      profile1D( pv->position().z(), pv->position().x(),"PV x versus z",m_zpvmin,m_zpvmax,m_nprbins) ;

    // refit the primary vertex with only the long tracks
    if(longtracks.size()>=2) {
      LHCb::RecVertex* longvertex = m_vertexer->fit( longtracks ) ;
      if(longvertex) plot( longvertex->chi2() / longvertex->nDoF(), "PV long chisquare per dof",0,10) ;
      delete longvertex ;
    }

    // now split the primary vertex in left and right tracks
    TrackVector lefttracks = myselect(tracks,TrackVeloSidePredicate(+1)) ;
    TrackVector righttracks =  myselect(tracks,TrackVeloSidePredicate(-1)) ;
    if( lefttracks.size() >= 2 && righttracks.size() >= 2 ) {
      // fit two vertices
      LHCb::RecVertex* leftvertex  = m_vertexer->fit( lefttracks ) ;

      if( leftvertex ) {
        plot( leftvertex->position().x(), "PV left x",-m_rpvmax,m_rpvmax) ;
        plot( leftvertex->position().y(), "PV left y",-m_rpvmax,m_rpvmax) ;
        plot( leftvertex->position().z(), "PV left z", m_zpvmin,m_zpvmax) ;
        if( leftSensor ) {
          plot( -(leftSensor->globalToVeloHalfBox(leftvertex->position())).x(), "PV left-Left half x",-m_rpvmax/2,m_rpvmax/2) ;
          plot( -(leftSensor->globalToVeloHalfBox(leftvertex->position())).y(), "PV left-Left half y",-m_rpvmax/2,m_rpvmax/2) ;
        }
      }
      LHCb::RecVertex* rightvertex = m_vertexer->fit( righttracks ) ;
      if( rightvertex) {
        plot( rightvertex->position().x(), "PV right x",-m_rpvmax,m_rpvmax) ;
        plot( rightvertex->position().y(), "PV right y",-m_rpvmax,m_rpvmax) ;
        plot( rightvertex->position().z(), "PV right z", m_zpvmin,m_zpvmax) ;
        if( rightSensor ) {
          plot( -(rightSensor->globalToVeloHalfBox(rightvertex->position())).x(), "PV right-Right half x",-m_rpvmax/2,m_rpvmax/2) ;
          plot( -(rightSensor->globalToVeloHalfBox(rightvertex->position())).y(), "PV right-Right half y",-m_rpvmax/2,m_rpvmax/2) ;
        }
      }
      if( leftvertex && rightvertex) {
        // draw the difference
        Gaudi::XYZVector dx = leftvertex->position() - rightvertex->position() ;

        plot( dx.x(), "PV left-right delta x",-0.1,0.1) ;
        plot( dx.y(), "PV left-right delta y",-0.1,0.1) ;
        plot( dx.z(), "PV left-right delta z",-1,1) ;
        if( std::abs( dx.y() ) < m_ipmaxprof )
          profile1D( pv->position().z(), dx.y(),"PV left-right delta y versus z",m_zpvmin,m_zpvmax,m_nprbins) ;
        if( std::abs( dx.x() ) < m_ipmaxprof )
          profile1D( pv->position().z(), dx.x(),"PV left-right delta x versus z",m_zpvmin,m_zpvmax,m_nprbins) ;

        // draw the pull of the difference
        Gaudi::SymMatrix3x3 cov = leftvertex->covMatrix() + rightvertex->covMatrix() ;
        plot( dx.x()/std::sqrt(cov(0,0)), "PV left-right delta x pull",-5,5) ;
        plot( dx.y()/std::sqrt(cov(1,1)), "PV left-right delta y pull",-5,5) ;
        plot( dx.z()/std::sqrt(cov(2,2)), "PV left-right delta z pull",-5,5) ;
        // draw the chisquares
        plot( leftvertex->chi2() / leftvertex->nDoF(), "PV left chisquare per dof",0,10) ;
        plot( rightvertex->chi2() / rightvertex->nDoF(), "PV right chisquare per dof",0,10) ;
      }
      delete leftvertex ;
      delete rightvertex ;
    }

    if( forwardtracks.size() >= 2 && backwardtracks.size() >= 2 ) {
      // fit two vertices
      LHCb::RecVertex* forwardvertex  = m_vertexer->fit( forwardtracks ) ;
      LHCb::RecVertex* backwardvertex = m_vertexer->fit( backwardtracks ) ;
      if( forwardvertex && backwardvertex) {
        Gaudi::XYZVector dx = forwardvertex->position() - backwardvertex->position() ;

        // draw the difference
        plot( dx.x(), "PV forward-backward delta x",-m_ipmax,m_ipmax) ;
        plot( dx.y(), "PV forward-backward delta y",-m_ipmax,m_ipmax) ;
        plot( dx.z(), "PV forward-backward delta z",-m_dzmax,m_dzmax) ;
        if( std::abs( dx.y() ) < m_ipmaxprof )
          profile1D( pv->position().z(), dx.y(),"PV forward-backward delta y versus z",m_zpvmin,m_zpvmax,m_nprbins) ;
        if( std::abs( dx.x() ) < m_ipmaxprof )
          profile1D( pv->position().z(), dx.x(),"PV forward-backward delta x versus z",m_zpvmin,m_zpvmax,m_nprbins) ;

        // draw the pull of the difference
        Gaudi::SymMatrix3x3 cov = forwardvertex->covMatrix() + backwardvertex->covMatrix() ;
        plot( dx.x()/std::sqrt(cov(0,0)), "PV forward-backward delta x pull",-5,5) ;
        plot( dx.y()/std::sqrt(cov(1,1)), "PV forward-backward delta y pull",-5,5) ;
        plot( dx.z()/std::sqrt(cov(2,2)), "PV forward-backward delta z pull",-5,5) ;
        // draw the chisquares
        plot( forwardvertex->chi2() / forwardvertex->nDoF(), "PV forward chisquare/dof",0,10) ;
        plot( backwardvertex->chi2() / backwardvertex->nDoF(), "PV backward chisquare/dof",0,10) ;

      }
      delete forwardvertex ;
      delete backwardvertex ;
    }

    // for events with a single vertex, do something with IP of
    // highest momentum track, as function of phi and eta.
    if( pvcontainer.size()==1 && tracks.size()>=10 ) {

      // now get all good long tracks from the best container:
      TrackVector goodlongtracks ;
      BOOST_FOREACH( const LHCb::Track* tr, alltracks )
        if( isLong(tr) && tr->chi2PerDoF()<m_maxLongTrackChisqPerDof &&
            tr->p()>m_minLongTrackMomentum)
          goodlongtracks.push_back( tr ) ;

      BOOST_FOREACH( const LHCb::Track* tr, goodlongtracks ) {
        const LHCb::State& firststate = tr->firstState() ;
        double dz  = pv->position().z() - firststate.z() ;
        double dx  = firststate.x() + dz * firststate.tx() - pv->position().x() ;
        double dy  = firststate.y() + dz * firststate.ty() - pv->position().y() ;
        Gaudi::XYZVector p3 = firststate.momentum() ;
        m_trackXIP->fill( dx ) ;
        m_trackYIP->fill( dy ) ;
        // apply a cut for the profiles
        if( std::abs(dx) < m_ipmaxprof && std::abs(dy) < m_ipmaxprof ) {
          double phi = p3.phi() ;
          double eta = p3.eta() ;
          m_trackXIPVsEta->fill(eta,dx) ;
          m_trackXIPVsPhi->fill(phi,dx) ;
          m_trackYIPVsEta->fill(eta,dy) ;
          m_trackYIPVsPhi->fill(phi,dy) ;
        }
      }

      if( goodlongtracks.size()>=2 ) {

        using namespace boost::lambda;
        std::sort(goodlongtracks.begin(), goodlongtracks.end(),
                  bind(&LHCb::State::pt,bind(&LHCb::Track::firstState,*_1)) <
                  bind(&LHCb::State::pt,bind(&LHCb::Track::firstState,*_2)) ) ;

        const LHCb::Track* firsttrack = goodlongtracks.back() ;
        goodlongtracks.pop_back() ;

        // now pick a 2nd track that makes the highest possible invariant mass with this one
        double highestmass2(0) ;
        const LHCb::Track* secondtrack(0) ;
        Gaudi::XYZVector firstp3 = firsttrack->firstState().momentum() ;
        for( TrackVector::const_iterator it = goodlongtracks.begin();
             it != goodlongtracks.end(); ++it) {
          Gaudi::XYZVector p3 = (*it)->firstState().momentum() ;
          double mass2= p3.r() * firstp3.r() - p3.Dot( firstp3 ) ;
          if(secondtrack==0 || highestmass2 < mass2 ) {
            highestmass2 = mass2 ;
            secondtrack = *it ;
          }
        }

        // recompute the vertex without these tracks
        TrackVector::iterator newend = tracks.end() ;
        newend = std::remove(tracks.begin(),newend,firsttrack) ;
        newend = std::remove(tracks.begin(),newend,secondtrack) ;
        tracks.erase(newend,tracks.end()) ;
        LHCb::RecVertex* restvertex  = m_vertexer->fit( tracks ) ;
        if( restvertex && firsttrack->nStates()!=0 ) {
          const LHCb::State& firststate = firsttrack->firstState() ;
          double dz  = restvertex->position().z() - firststate.z() ;
          double dx  = firststate.x() + dz * firststate.tx() - restvertex->position().x()  ;
          double dy  = firststate.y() + dz * firststate.ty() - restvertex->position().y() ;
          double nt  = std::sqrt( firststate.tx()*firststate.tx() + firststate.ty()*firststate.ty() ) ;
          // transverse and longitudinal impact parameter
          double iptrans = (dx * firststate.ty() - dy * firststate.tx())/nt ;
          double iplong  = (dx * firststate.tx() + dy * firststate.ty())/nt ;
          Gaudi::XYZVector p3 = firststate.momentum() ;
          double phi = p3.phi() ;
          double eta = p3.eta() ;

          m_fastTrackTransverseIP->fill(iptrans ) ;
          m_fastTrackLongitudinalIP->fill(iplong ) ;
          m_fastTrackXIP->fill( dx ) ;
          m_fastTrackYIP->fill( dy ) ;
          // apply a cut for the profiles
          if( std::abs(iptrans) < m_ipmaxprof && std::abs(iplong) < m_ipmaxprof ) {
            m_fastTrackTransverseIPVsEta->fill(eta,iptrans) ;
            m_fastTrackTransverseIPVsPhi->fill(phi,iptrans) ;
            m_fastTrackLongitudinalIPVsEta->fill(eta,iplong) ;
            m_fastTrackLongitudinalIPVsPhi->fill(phi,iplong) ;
          }
          if( std::abs(dx) < m_ipmaxprof && std::abs(dy) < m_ipmaxprof ) {
            m_fastTrackXIPVsEta->fill(eta,dx) ;
            m_fastTrackXIPVsPhi->fill(phi,dx) ;
            m_fastTrackYIPVsEta->fill(eta,dy) ;
            m_fastTrackYIPVsPhi->fill(phi,dy) ;
          }

          // The two-track cuts we only make for relatively heavy objects
          double mass = std::sqrt(highestmass2) ;
          m_twoprongMass->fill(mass / Gaudi::Units::GeV ) ;
          if( mass > 1*Gaudi::Units::GeV ) {
            // compute doca of two tracks
            Gaudi::XYZVector dx3 = firsttrack->firstState().position() - secondtrack->firstState().position() ;
            Gaudi::XYZVector n3  = firsttrack->firstState().slopes().Cross( secondtrack->firstState().slopes() ) ;
            double doca = dx3.Dot(n3) / n3.R() ;
            m_twoprongDoca->fill(doca) ;
            if( std::abs(doca) < m_twoprongDoca->axis().upperEdge() ) {
              m_twoprongDocaVsEta->fill(firstp3.eta(),doca) ;
              m_twoprongDocaVsPhi->fill(firstp3.phi(),doca) ;
            }
            // the easiest way to compute the pull is with a vertex fit
            LHCb::TwoProngVertex* twoprong = m_vertexer->fit(firsttrack->firstState(),secondtrack->firstState()) ;
            if(twoprong) {
              double pc = twoprong->p3().R() ;
              m_twoprongMomentum->fill( pc / Gaudi::Units::GeV ) ;
              m_twoprongDocaPull->fill(std::sqrt(twoprong->chi2()) * (doca>0 ? 1 : -1)) ;
              double chi2, decaylength,decaylengtherr ;
              m_vertexer->computeDecayLength( *twoprong, *restvertex, chi2, decaylength,decaylengtherr ) ;
              m_twoprongDecaylength->fill( decaylength ) ;
              m_twoprongDecaylengthSignificance->fill( decaylength/decaylengtherr ) ;
              m_twoprongIPChisquare->fill( chi2 / 2 ) ;
              m_twoprongCTau->fill( decaylength * mass / pc ) ;
              m_twoprongTau->fill( decaylength * mass / (pc * Gaudi::Units::c_light * Gaudi::Units::picosecond) ) ;
              delete twoprong ;
            }
          }
        }
        delete restvertex ;
      }
    }