diff --git a/Reconstruction/Tracking/CMakeLists.txt b/Reconstruction/Tracking/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..3335b022e55e3dc1b442575d37c998c798ec33e9
--- /dev/null
+++ b/Reconstruction/Tracking/CMakeLists.txt
@@ -0,0 +1,27 @@
+gaudi_subdir(Tracking v0r0)
+
+find_package(GEAR REQUIRED)
+find_package(GSL REQUIRED ) 
+find_package(LCIO REQUIRED ) 
+find_package(EDM4HEP REQUIRED ) 
+find_package(DD4hep COMPONENTS DDCore DDRec REQUIRED)
+
+
+gaudi_depends_on_subdirs(
+    Service/GearSvc
+    Service/EventSeeder
+    Service/TrackSystemSvc
+)
+
+set(Tracking_srcs
+    src/Clupatra/*.cpp
+)
+
+# Modules
+gaudi_add_module(Tracking ${Tracking_srcs}
+    INCLUDE_DIRS GaudiKernel gear ${GSL_INCLUDE_DIRS} ${LCIO_INCLUDE_DIRS}
+    LINK_LIBRARIES GaudiAlgLib GaudiKernel ${GEAR_LIBRARIES} ${GSL_LIBRARIES} ${LCIO_LIBRARIES} TrackSystemSvcLib
+      -Wl,--no-as-needed
+     EDM4HEP::edm4hep EDM4HEP::edm4hepDict
+     -Wl,--as-needed
+)
diff --git a/Reconstruction/Tracking/src/Clupatra/ClupatraAlg.cpp b/Reconstruction/Tracking/src/Clupatra/ClupatraAlg.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c048c09816dc2e85f628db1294cefbbb845f3bee
--- /dev/null
+++ b/Reconstruction/Tracking/src/Clupatra/ClupatraAlg.cpp
@@ -0,0 +1,1377 @@
+#include "ClupatraAlg.h"
+
+#include "clupatra_new.h"
+
+#include <time.h>
+#include <vector>
+#include <map>
+#include <set>
+#include <algorithm>
+#include <math.h>
+#include <cmath>
+#include <memory>
+#include <float.h>
+
+//---- MarlinUtil
+
+//---- LCIO ---
+#include "IMPL/LCCollectionVec.h"
+#include "EVENT/SimTrackerHit.h"
+#include "UTIL/Operators.h"
+#include "UTIL/LCTOOLS.h"
+#include "UTIL/CellIDDecoder.h"
+#include "UTIL/ILDConf.h"
+#include "UTIL/LCIterator.h"
+
+//-------gsl -----
+#include "gsl/gsl_randist.h"
+#include "gsl/gsl_cdf.h"
+
+
+//---- GEAR ----
+#include "gear/GEAR.h"
+#include "gear/TPCParameters.h"
+#include "gear/ZPlanarParameters.h"
+#include "gear/ZPlanarLayerLayout.h"
+#include "gear/PadRowLayout2D.h"
+#include "gear/BField.h"
+
+
+#include "TrackSystemSvc/IMarlinTrack.h"
+#include "TrackSystemSvc/IMarlinTrkSystem.h"
+#include "TrackSystemSvc/MarlinTrkUtils.h"
+
+#include "RuntimeMap.h"
+
+#include "edm4hep/TrackerHitCollection.h"
+#include "edm4hep/TrackCollection.h"
+// #include "edm4hep/TrackerHitPlane.h"
+
+
+using namespace edm4hep ;
+using namespace MarlinTrk ;
+
+using namespace clupatra_new ;
+
+/*
+   namespace edm4hep::TrackState {
+   const int AtIP = 0;
+   const int AtFirstHit = 1;
+   const int AtLastHit = 2;
+   const int AtCalorimeter = 3;
+   };
+   */
+
+RuntimeMap<edm4hep::ConstTrack, clupatra_new::TrackInfoStruct*> TrackInfo_of_edm4hepTrack;
+RuntimeMap<edm4hep::ConstTrack, MarlinTrk::IMarlinTrack*> MarTrk_of_edm4hepTrack;
+RuntimeMap<MarlinTrk::IMarlinTrack*, clupatra_new::CluTrack*> CluTrk_of_MarTrack;
+RuntimeMap<edm4hep::ConstTrackerHit, clupatra_new::Hit*> GHitof;
+RuntimeMap<clupatra_new::CluTrack*, MarlinTrk::IMarlinTrack*> MarTrkof;
+
+gear::GearMgr* gearMgr; 
+#define WRITE_PICKED_DEBUG_TRACKS false
+
+//----------------------------------------------------------------
+
+template <class In, class Pred> In find_smallest(In first, In last, Pred p, double& d){
+
+	In res =  last ;
+
+	double min = DBL_MAX ;
+
+	while( first != last ){
+
+		double val = p( *first) ;
+
+		if(  val < min ){
+
+			res = first ;
+			min = val ;
+		}
+
+		++first ;
+	}
+
+	d = min ;
+
+	// FIXME: Mingrui ignore the debug
+
+	return res ;
+}
+//----------------------------------------------------------------
+struct Distance3D2{
+	gear::Vector3D _pos ;
+	Distance3D2( const gear::Vector3D& pos) : _pos( pos ) {}
+	template <class T>
+		double operator()( const T* t) {
+			gear::Vector3D p( t->getPosition() ) ;
+			return ( p - _pos ).r2() ;
+
+		}
+};
+
+//----------------------------------------------------------------
+struct StripDistance2{
+	gear::Vector3D _pos ;
+	StripDistance2( const gear::Vector3D& pos) : _pos( pos ) {}
+
+	double operator()( const TrackerHit* t) {
+
+/*
+		gear::Vector3D p(t->getPosition()[0], t->getPosition()[1], t->getPosition()[2]) ;
+
+		const TrackerHitPlane* h = (const TrackerHitPlane*) t ;
+
+		gear::Vector3D v( 1. , h->getU()[1] ,  h->getU()[0] , gear::Vector3D::spherical ) ;
+
+		double d = ( p - _pos ).dot(v)  ;
+
+		return d*d ;
+*/
+return 1;
+	}
+};
+
+//----------------------------------------------------------------
+struct MeanAbsZOfTrack{
+	double operator()( ConstTrack t){
+		double z = 0 ;
+		int hitCount = 0 ;
+		/*
+		   const TrackerHitVec& hV = t->getTrackerHits() ;
+		   for(unsigned i=0, N = hV.size() ; i<N ; ++i){
+		   z += hV[i]->getPosition()[2]  ;
+		   ++hitCount ;
+		   }
+		   */
+		for (auto iter = t.trackerHits_begin(); iter != t.trackerHits_end(); iter++) {
+			z += iter->getPosition()[2];
+			++hitCount;
+		}
+		return ( hitCount>0  ?  std::abs(  z )  / hitCount  : 0 ) ;
+	}
+};
+
+
+DECLARE_COMPONENT( ClupatraAlg )
+
+ClupatraAlg::ClupatraAlg(const std::string& name, ISvcLocator* svcLoc) : GaudiAlgorithm(name, svcLoc), _trksystem(0), _gearTPC(0) {
+
+		// _description = "ClupatraProcessor : nearest neighbour clustering seeded pattern recognition" ;
+
+		// Input Collections
+		// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+		declareProperty("TPCHitCollection", _TPCHitCollectionHandle, "handler of the tpc hit input collections");
+
+		// Output Collections
+		// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+		declareProperty("ClupatraTracks", _ClupatraTrackCollectionHandle, "handler of the collection with final TPC tracks");
+		declareProperty("ClupatraTrackSegments", _ClupatraTrackSegmentCollectionHandle, "handler of the collection that has the individual track segments");
+
+	}
+
+void ClupatraAlg::printParameters() {
+
+  debug() << "Print parameters:" << endmsg;
+  debug() << _distCut << endmsg;
+  debug() << _cosAlphaCut << endmsg;
+  debug() << _nLoop << endmsg;
+  debug() << _minCluSize << endmsg;
+  debug() << _duplicatePadRowFraction << endmsg; 
+  debug() << _dChi2Max << endmsg;
+  debug() << _chi2Cut << endmsg;
+  debug() << _maxStep << endmsg;
+  debug() << _pickUpSiHits << endmsg;
+  debug() << _createDebugCollections << endmsg;
+  debug() << _padRowRange << endmsg;
+  debug() <<  _nZBins << endmsg;
+  debug() << _minLayerFractionWithMultiplicity << endmsg;
+  debug() << _minLayerNumberWithMultiplicity << endmsg;
+  debug() << _trackStartsInnerDist << endmsg;
+  debug() << _trackEndsOuterCentralDist << endmsg;
+  debug() << _trackEndsOuterForwardDist << endmsg;
+  debug() <<  _trackIsCurlerOmega << endmsg;
+
+}
+
+
+StatusCode ClupatraAlg::initialize() {
+
+	// Usually a good idea to
+	printParameters() ;
+
+	auto _trackSystemSvc = service<ITrackSystemSvc>("TrackSystemSvc");
+	if ( !_trackSystemSvc ) {
+		error() << "Fail to find TrackSystemSvc ..." << endmsg;
+	}
+	// _trksystem =  MarlinTrk::Factory::createMarlinTrkSystem( "KalTest" , marlin::Global::GEAR , "" ) ;
+
+	_trksystem = _trackSystemSvc->getTrackSystem();
+	// FIXME Mingrui Important fix it later
+	// _trksystem->setOption( MarlinTrk::IMarlinTrkSystem::CFG::useQMS,        true ) ;
+	_trksystem->setOption( MarlinTrk::IMarlinTrkSystem::CFG::useQMS,        false ) ;
+	_trksystem->setOption( MarlinTrk::IMarlinTrkSystem::CFG::usedEdx,       true) ;
+	_trksystem->setOption( MarlinTrk::IMarlinTrkSystem::CFG::useSmoothing,  false) ;
+	_trksystem->init() ;
+
+	_nRun = 0 ;
+	_nEvt = 0 ;
+
+        tree = new TTree("Tuple", "Particle Tree");
+        tree->Branch("omega", &omega, "omega/D");
+        tree->Branch("totalCandidates", &totalCandidates, "totalCandidates/I");
+        tree->Branch("eventNumber", &_nEvt, "eventNumber/I");
+
+	return GaudiAlgorithm::initialize();
+
+}
+
+
+StatusCode ClupatraAlg::execute() {
+
+	TrackInfo_of_edm4hepTrack.init();
+	MarTrk_of_edm4hepTrack.init();
+	CluTrk_of_MarTrack.init();
+	MarTrkof.init();
+	GHitof.init();
+
+
+	debug() << "Clupatra Algorithm started" << endmsg;
+
+	//  clock_t start =  clock() ;
+	Timer timer ;
+	unsigned t_init       = timer.registerTimer(" initialization      " ) ;
+	unsigned t_seedtracks = timer.registerTimer(" extend seed tracks  " ) ;
+	unsigned t_recluster  = timer.registerTimer(" recluster leftovers " ) ;
+	unsigned t_split      = timer.registerTimer(" split clusters      " ) ;
+	unsigned t_finalfit   = timer.registerTimer(" final refit         " ) ;
+	unsigned t_merge      = timer.registerTimer(" merge segments      " ) ;
+	unsigned t_pickup     = timer.registerTimer(" pick up Si hits     " ) ;
+
+	timer.start() ;
+
+	// the clupa wrapper hits that hold pointers to PLCIO hits plus some additional parameters
+	// create them in a vector for convenient memeory mgmt
+	std::vector<ClupaHit> clupaHits ;
+
+	// on top of the clupahits we need the tiny wrappers for clustering - they are created on the heap
+	// and we put them in a vector of pointers that takes ownership (i.e. deletes them at the end)
+	HitVec nncluHits ;
+	nncluHits.setOwner( true ) ;
+
+
+	// this is the final list of cluster tracks
+	Clusterer::cluster_list cluList ;
+	cluList.setOwner() ;
+
+
+	PLCIOTrackConverter converter ;
+
+	// Gear can be put as global
+	auto _gear = service<IGearSvc>("GearSvc");
+	gearMgr = _gear->getGearMgr();
+
+	_gearTPC = &(gearMgr->getTPCParameters());
+	_bfield = gearMgr->getBField().at( gear::Vector3D(0.,0.0,0.) ).z() ;
+
+	// Support for more than one module
+	// The ternary operator is used to make the trick with the static variable which
+	// is supposed to be calculated only once, also for performance reason 
+	static const unsigned int maxTPCLayers = (gearMgr->getDetectorName() == "LPTPC" ) ?
+		_gearTPC->getModule(0).getNRows() + _gearTPC->getModule(2).getNRows() + _gearTPC->getModule(5).getNRows() :  // LCTPC
+		_gearTPC->getModule(0).getNRows(); // ILD
+
+	double driftLength = _gearTPC->getMaxDriftLength() ;
+	ZIndex zIndex( -driftLength , driftLength , _nZBins  ) ;
+
+
+	const edm4hep::TrackerHitCollection* col = nullptr;
+        debug() << "col" << endmsg;
+
+	try{   col = _TPCHitCollectionHandle.get();
+
+	} catch(...) {
+		// FIXME: Mingrui fix the output message
+		// streamlog_out( WARNING ) <<  " input collection not in event : " << _colName << "   - nothing to do  !!! " << std::endl ;
+	}
+
+	//===============================================================================================
+	//   create clupa and clustering hits for every edm4hep hit
+	//===============================================================================================
+	if (col == nullptr) {
+                return StatusCode::SUCCESS;
+        }
+
+
+	int nHit = col->size() ;
+
+	clupaHits.resize( nHit ) ;       // creates clupa hits (w/ default c'tor)
+	nncluHits.reserve( nHit ) ;
+
+
+	// FIXME: Mingrui fix the output message
+	debug()  << "  create clupatra TPC hits, n = " << nHit << endmsg ;
+
+	// Mingrui!: Copy the items in col to col_copy to avoid losing the pointer.
+	for(int i=0 ; i < nHit ; ++i ) {
+
+		//------
+		ConstTrackerHit th(col->at(i));
+		// debug() << i << " " << th->getCellID0() << " " << th->getCellID1() << endmsg;
+		if ( fabs(th.getPosition()[2]) > driftLength ) continue;
+
+		ClupaHit* ch  = & clupaHits[i] ;
+
+		Hit* gh =  new Hit( ch ) ;
+
+		nncluHits.push_back( gh ) ;
+
+		//-------
+		// FIXME: Here we should have a resolution
+		GHitof(th) = gh ;  // assign the clupa hit to the LCIO hit for memory mgmt
+
+		ch->edm4hepHit = th ;
+
+		ch->pos = gear::Vector3D(th.getPosition()[0], th.getPosition()[1], th.getPosition()[2]) ;
+
+		//  int padIndex = padLayout.getNearestPad( ch->pos.rho() , ch->pos.phi() ) ;
+		//    ch->layer = padLayout.getRowNumber( padIndex ) ;
+		// May cause a problem
+		ch->layer = getLayer( th );
+
+		// FIXME: Mingrui Debug
+		// streamlog_out( DEBUG ) << "  ch->layer = idDec( th )[ ILDCellID0::layer ] = " <<  ch->layer << " - CellID0 " << th->getCellID0() << std::endl ;
+
+		ch->zIndex = zIndex( &th ) ;
+
+		//ch->phiIndex = ....
+
+	}
+        debug() << "Get hits for clustering" << endmsg;
+
+	//---------------------------------------------------------------------------------------------------------
+
+	std::sort( nncluHits.begin(), nncluHits.end() , ZSort() ) ;
+
+	//---------------------------------------------------------------------------------------------------------
+
+	HitListVector hitsInLayer( maxTPCLayers ) ;
+	addToHitListVector(  nncluHits.begin(), nncluHits.end() , hitsInLayer  ) ;
+
+	// debug() << "  added  " <<  nncluHits.size()  << "  tp hitsInLayer - > size " <<  hitsInLayer.size() << std::endl ;
+
+	//---------------------------------------------------------------------------------------------------------
+
+	//===============================================================================================
+	//   create output collections  ( some optional )
+	//===============================================================================================
+
+	// FIXME: Mingrui how to create the correct output collection?
+
+	// const bool writeSeedCluster        = _createDebugCollections ;
+	// const bool writeCluTrackSegments   = _createDebugCollections ;
+	// const bool writeLeftoverClusters   = _createDebugCollections ;
+	// const bool writeQualityTracks      = _createDebugCollections ;
+	// const bool writeDebugTracks      =  WRITE_PICKED_DEBUG_TRACKS ;
+
+	static const bool copyTrackSegments = false ;
+
+	/*
+	 * FIXME Mingrui remove these collections for now
+	 LCCollectionVec* seedCol =  ( writeSeedCluster        ?  newTrkCol( "ClupatraSeedCluster"          , evt )  :   0   )  ;
+	 LCCollectionVec* cluCol  =  ( writeCluTrackSegments   ?  newTrkCol( "ClupatraInitialTrackSegments" , evt )  :   0   )  ;
+	 LCCollectionVec* locCol  =  ( writeCluTrackSegments   ?  newTrkCol( "ClupatraLeftoverClusters"     , evt )  :   0   )  ;
+
+	 LCCollectionVec* incSegCol  = ( writeCluTrackSegments   ?  newTrkCol( "ClupatraIncompleteSegments"   , evt , true )  :   0   )  ;
+	 LCCollectionVec* curSegCol  = ( writeCluTrackSegments   ?  newTrkCol( "ClupatraCurlerSegments"       , evt , true )  :   0   )  ;
+	 LCCollectionVec* finSegCol  = ( writeCluTrackSegments   ?  newTrkCol( "ClupatraFinalTrackSegments"   , evt , true )  :   0   )  ;
+
+	//LCCollectionVec* goodCol  = ( writeQualityTracks ?  newTrkCol( "ClupatraGoodQualityTracks" , evt ,true )  :   0   )  ;
+	//LCCollectionVec* fairCol  = ( writeQualityTracks ?  newTrkCol( "ClupatraFairQualityTracks" , evt ,true )  :   0   )  ;
+	LCCollectionVec* poorCol  = ( writeQualityTracks ?  newTrkCol( "ClupatraPoorQualityTracks" , evt , true )  :   0   )  ;
+
+	LCCollectionVec* debugCol=  ( writeDebugTracks ?  newTrkCol( "ClupatraDebugTracks" , evt , false )  :   0   )  ;
+	if( WRITE_PICKED_DEBUG_TRACKS )
+	DebugTracks::setCol( debugCol , this ) ;
+
+	LCCollectionVec* outerCol  = ( _createDebugCollections ?  newTrkCol( "ClupatraOuterSegments" , evt ,true )  :   0   )  ;
+	LCCollectionVec* innerCol  = ( _createDebugCollections ?  newTrkCol( "ClupatraInnerSegments" , evt ,true )  :   0   )  ;
+	LCCollectionVec* middleCol = ( _createDebugCollections ?  newTrkCol( "ClupatraMiddleSegments" , evt ,true )  :   0   )  ;
+	*/
+
+
+
+	TrackCollection* outCol =  _ClupatraTrackCollectionHandle.createAndPut();
+	TrackCollection* tsCol  =  _ClupatraTrackSegmentCollectionHandle.createAndPut();
+        std::vector<ClupaPlcioConstTrack*> outCol_tmp;
+        std::vector<ClupaPlcioConstTrack*> tsCol_tmp;
+
+	//---------------------------------------------------------------------------------------------------------
+
+	timer.time(t_init ) ;
+
+	//===============================================================================================
+	// first main step of clupatra:
+	//   * cluster in pad row range - starting from the outside - to find clean cluster segments
+	//   * extend the track segments with best matching hits, based on extrapolation to next layer(s)
+	//   * add the hits and apply a Kalman filter step ( track segement is always best estimate )
+	//   * repeat in backward direction ( after smoothing back, to get a reasonable track
+	//     state for extrapolating backwards )
+	//===============================================================================================
+
+	Clusterer nncl ;
+
+	int outerRow = 0 ;
+
+	nnclu::PtrVector<MarlinTrk::IMarlinTrack> seedTrks ;
+	seedTrks.setOwner() ; // memory mgmt - will delete MarlinTrks at the end
+
+	IMarlinTrkFitter fitter( _trksystem ) ;
+
+
+	debug() << "===============================================================================================\n"
+		<< "   first step of Clupatra algorithm: find seeds with NN-clustering  in " <<  _nLoop << " loops - max dist = " << _distCut <<" \n"
+		<< "===============================================================================================\n"  << endmsg;
+
+	// ---- introduce a loop over increasing distance cuts for finding the tracks seeds
+	//      -> should fix (some of) the problems seen @ 3 TeV with extremely boosted jets
+	//
+	double dcut =  _distCut / _nLoop ;
+	for(int nloop=1 ; nloop <= _nLoop ; ++nloop) {
+
+		HitDistance dist( nloop * dcut , _cosAlphaCut ) ;
+
+		outerRow = maxTPCLayers - 1 ;
+
+		while( outerRow >= _minCluSize ) {
+
+			HitVec hits ;
+			hits.reserve( nHit ) ;
+
+			// add all hits in pad row range to hits
+			for(int iRow = outerRow ; iRow > ( outerRow - _padRowRange) ; --iRow ) {
+
+				if( iRow > -1 ) {
+
+					// debug() << "  copy " <<  hitsInLayer[ iRow ].size() << " hits for row " << iRow << endmsg ;
+
+					std::copy( hitsInLayer[ iRow ].begin() , hitsInLayer[ iRow ].end() , std::back_inserter( hits )  ) ;
+				}
+			}
+
+			//-----  cluster in given pad row range  -----------------------------
+			Clusterer::cluster_list sclu ;
+			sclu.setOwner() ;
+
+			// FIXME Mingrui
+			//streamlog_out( DEBUG2 ) << "   call cluster_sorted with " <<  hits.size() << " hits " << std::endl ;
+
+			nncl.cluster_sorted( hits.begin(), hits.end() , std::back_inserter( sclu ), dist , _minCluSize ) ;
+
+			const static int merge_seeds = true ;
+
+			if( merge_seeds ) { //-----------------------------------------------------------------------
+
+				// sometimes we have split seed clusters as one link is just above the cut
+				// -> recluster in all hits of small clusters with 1.2 * cut
+				float _smallClusterPadRowFraction = 0.9  ;
+				float _cutIncrease = 1.2 ;
+				// fixme: could make parameters ....
+
+				HitVec seedhits ;
+				Clusterer::cluster_list smallclu ;
+				smallclu.setOwner() ;
+				split_list( sclu, std::back_inserter(smallclu),  ClusterSize(  int( _padRowRange * _smallClusterPadRowFraction) ) ) ;
+				for( Clusterer::cluster_list::iterator sci=smallclu.begin(), end= smallclu.end() ; sci!=end; ++sci ){
+					for( Clusterer::cluster_type::iterator ci=(*sci)->begin(), end= (*sci)->end() ; ci!=end;++ci ){
+						seedhits.push_back( *ci ) ;
+					}
+				}
+				// free hits from bad clusters
+				std::for_each( smallclu.begin(), smallclu.end(), std::mem_fun( &CluTrack::freeElements ) ) ;
+
+				HitDistance distLarge( nloop * dcut * _cutIncrease ) ;
+
+				nncl.cluster_sorted( seedhits.begin(), seedhits.end() , std::back_inserter( sclu ), distLarge , _minCluSize ) ;
+
+			} //------------------------------------------------------------------------------------------
+
+			// FIXME Mingrui
+			// debug()  << "     found " <<  sclu.size() << "  clusters " << std::endl ;
+
+			// try to split up clusters according to multiplicity
+			int layerWithMultiplicity = _padRowRange - 2  ; // fixme: make parameter
+			split_multiplicity( sclu , layerWithMultiplicity , 10 ) ;
+
+
+			// remove clusters whith too many duplicate hits per pad row
+			Clusterer::cluster_list bclu ;    // bad clusters
+			bclu.setOwner() ;
+			split_list( sclu, std::back_inserter(bclu),  DuplicatePadRows( maxTPCLayers, _duplicatePadRowFraction  ) ) ;
+			// free hits from bad clusters
+			std::for_each( bclu.begin(), bclu.end(), std::mem_fun( &CluTrack::freeElements ) ) ;
+
+
+			// ---- now we also need to remove the hits from good cluster seeds from the hitsInLayers:
+			for( Clusterer::cluster_list::iterator sci=sclu.begin(), end= sclu.end() ; sci!=end; ++sci ){
+				for( Clusterer::cluster_type::iterator ci=(*sci)->begin(), end= (*sci)->end() ; ci!=end;++ci ){
+
+					// this is not cheap ...
+					hitsInLayer[ (*ci)->first->layer ].remove( *ci )  ;
+				}
+			}
+
+			// now we have 'clean' seed clusters
+			// Write debug collection with seed clusters:
+			// convert the clusters into tracks and write the resulting tracks into the existing debug collection seedCol.
+			// The conversion is performed by the STL transform() function, the insertion to the end of the
+			// debug track collection is done by creating an STL back_inserter iterator on the LCCollectionVector seedCol
+			/* Debug FIXME Mingrui
+			   if( writeSeedCluster ) {
+			   std::transform( sclu.begin(), sclu.end(), std::back_inserter( *seedCol ) , converter ) ;
+			   }
+			   */
+
+			//      std::transform( sclu.begin(), sclu.end(), std::back_inserter( seedTrks) , fitter ) ;
+			// reduce memory footprint: deal with one KalTest track at a time and delete it, when done
+
+			for( Clusterer::cluster_list::iterator icv = sclu.begin(), end =sclu.end()  ; icv != end ; ++ icv ) {
+				// icv = *cluster_list type
+
+				int nHitsAdded = 0 ;
+
+				// debug() <<  " call fitter for seed cluster with " << (*icv)->size() << " hits " << endmsg;
+				// int counter = 0;
+				// for( Clusterer::cluster_type::iterator ci=(*icv)->begin(), end= (*icv)->end() ; ci!=end; ++ci ) {
+				// 	debug() << counter++ << " " <<  *((*ci)->first->edm4hepHit) << " \nlayer " << (*ci)->first->layer << endmsg;
+				// }
+
+
+				MarlinTrk::IMarlinTrack* mTrk = fitter( *icv ) ;
+
+                // std::vector<std::pair<edm4hep::ConstTrackerHit, double> > hitsInFit ;
+                // mTrk->getHitsInFit( hitsInFit ) ;
+                // for (auto hit : hitsInFit) std::cout << hit.first << std::endl;
+
+				nHitsAdded += addHitsAndFilter( *icv , hitsInLayer , _dChi2Max, _chi2Cut , _maxStep , zIndex ) ;
+
+				static const bool backward = true ;
+				nHitsAdded += addHitsAndFilter( *icv , hitsInLayer , _dChi2Max, _chi2Cut , _maxStep , zIndex, backward ) ;
+				// in order to use smooth for backward extrapolation call with   _trksystem  - does not work well...
+				// nHitsAdded += addHitsAndFilter( *icv , hitsInLayer , _dChi2Max, _chi2Cut , _maxStep , zIndex, backward , _trksystem ) ;
+
+
+				// drop seed clusters with no hits added - but not in the very forward region...
+				// debug() << "Goes here" << endmsg;
+				if( nHitsAdded < 1  &&  outerRow >   2*_padRowRange  ){  //FIXME: make parameter ?
+
+					ConstTrack edm4hepTrk( converter( *icv ) ) ;
+					// debug() << "Goes goes here" << endmsg;
+
+					// FIXME Mingrui
+					//streamlog_out( DEBUG2) << "=============  poor seed cluster - no hits added - started from row " <<  outerRow << "\n"
+					//<< *edm4hepTrk << std::endl ;
+
+
+					for( Clusterer::cluster_type::iterator ci=(*icv)->begin(), end= (*icv)->end() ; ci!=end; ++ci ) {
+						hitsInLayer[ (*ci)->first->layer ].push_back( *ci )   ;
+					}
+					(*icv)->freeElements() ;
+					(*icv)->clear() ;
+				}
+
+				/*
+				   if( writeCluTrackSegments )  //  ---- store track segments from the first main step  -----
+				   cluCol->addElement(  converter( *icv ) );
+				   Debug FIXME Mingrui
+				   */
+				// reset the pointer to the KalTest track - as we are done with this track
+				MarTrkof(*icv) = 0;
+
+				delete mTrk ;
+			}
+
+			// merge the good clusters to final list
+			cluList.merge( sclu ) ;
+
+			outerRow -= _padRowRange ;
+
+		} //while outerRow > padRowRange
+
+	} // nloop
+
+
+	timer.time( t_seedtracks ) ;
+
+	timer.time( t_recluster ) ;
+
+	//===============================================================================================
+	//  do a global reclustering in leftover hits
+	//===============================================================================================
+	debug() << "do global reclustering" << endmsg;
+	static const int do_global_reclustering = true ;
+	if( do_global_reclustering ) {
+
+		outerRow = maxTPCLayers - 1 ;
+
+		int padRangeRecluster = 50 ; // FIXME: make parameter
+		// define an inner cylinder where we exclude hits from re-clustering:
+		double zMaxInnerHits   = driftLength * .67 ;   // FIXME: make parameter
+		double rhoMaxInnerHits = _gearTPC->getPlaneExtent()[0] + (  _gearTPC->getPlaneExtent()[1] - _gearTPC->getPlaneExtent()[0] ) * .67 ;// FIXME: make parameter
+
+
+		while( outerRow > 0 ) {
+
+
+			Clusterer::cluster_list loclu ; // leftover clusters
+			loclu.setOwner() ;
+
+			HitVec hits ;
+
+			int  minRow = ( ( outerRow - padRangeRecluster ) > -1 ?  ( outerRow - padRangeRecluster ) : -1 ) ;
+
+			// add all hits in pad row range to hits
+			for(int iRow = outerRow ; iRow > minRow ; --iRow ) {
+
+
+				for( HitList::iterator hlIt=hitsInLayer[ iRow ].begin() , end = hitsInLayer[ iRow ].end() ; hlIt != end ; ++hlIt ) {
+
+					if( std::abs( (*hlIt)->first->pos.z() ) > zMaxInnerHits  ||  (*hlIt)->first->pos.rho() >  rhoMaxInnerHits ) {
+						hits.push_back( *hlIt ) ;
+					}
+				}
+			}
+
+
+			HitDistance distSmall( _distCut ) ;
+			nncl.cluster( hits.begin(), hits.end() , std::back_inserter( loclu ),  distSmall , _minCluSize ) ;
+
+			// Write debug collection using STL transform() function on the clusters
+			/* Debug FIXME Mingrui
+			   if( writeLeftoverClusters )
+			   std::transform( loclu.begin(), loclu.end(), std::back_inserter( *locCol ) , converter ) ;
+			   */
+
+
+			// timer.time( t_recluster ) ;
+
+			//===============================================================================================
+			//  now we split the clusters based on their hit multiplicities
+			//===============================================================================================
+
+
+			//    _dChi2Max = 5. * _dChi2Max ; //FIXME !!!!!!!!!
+
+			for( Clusterer::cluster_list::iterator it= loclu.begin(), end= loclu.end() ; it != end ; ++it ){
+
+				CluTrack* clu = *it ;
+
+				// FIXME: Mingrui
+
+				std::vector<int> mult(8) ;
+				// get hit multiplicities up to 6 ( 7 means 7 or higher )
+				getHitMultiplicities( clu , mult ) ;
+
+				// FIXME Mingrui
+				// streamlog_out(  DEBUG3 ) << " **** left over cluster with hit multiplicities: \n" ;
+				for( unsigned i=0,n=mult.size() ; i<n ; ++i) {
+					// FIXME Mingrui
+					// streamlog_out(  DEBUG3 ) << "     m["<<i<<"] = " <<  mult[i] << "\n"  ;
+				}
+
+
+				if( float( mult[5]) / mult[0]  >= _minLayerFractionWithMultiplicity &&  mult[5] >  _minLayerNumberWithMultiplicity ) {
+
+					Clusterer::cluster_list reclu ; // reclustered leftover clusters
+					reclu.setOwner() ;
+
+					create_n_clusters( *clu , reclu , 5 ) ;
+
+					std::transform( reclu.begin(), reclu.end(), std::back_inserter( seedTrks) , fitter ) ;
+
+					for( Clusterer::cluster_list::iterator ir= reclu.begin(), end= reclu.end() ; ir != end ; ++ir ){
+
+						// FIXME Mingrui
+						// streamlog_out( DEBUG5 ) << " extending mult-5 clustre  of length " << (*ir)->size() << std::endl ;
+
+						addHitsAndFilter( *ir , hitsInLayer , _dChi2Max, _chi2Cut , _maxStep , zIndex) ;
+						static const bool backward = true ;
+						addHitsAndFilter( *ir , hitsInLayer , _dChi2Max, _chi2Cut , _maxStep , zIndex, backward ) ;
+					}
+
+					cluList.merge( reclu ) ;
+				}
+
+				else if( float( mult[4]) / mult[0]  >= _minLayerFractionWithMultiplicity &&  mult[4] >  _minLayerNumberWithMultiplicity ) {
+
+					Clusterer::cluster_list reclu ; // reclustered leftover clusters
+					reclu.setOwner() ;
+
+					create_n_clusters( *clu , reclu , 4 ) ;
+
+					std::transform( reclu.begin(), reclu.end(), std::back_inserter( seedTrks) , fitter ) ;
+
+					for( Clusterer::cluster_list::iterator ir= reclu.begin(), end= reclu.end() ; ir != end ; ++ir ){
+
+
+						addHitsAndFilter( *ir , hitsInLayer , _dChi2Max, _chi2Cut , _maxStep , zIndex) ;
+						static const bool backward = true ;
+						addHitsAndFilter( *ir , hitsInLayer , _dChi2Max, _chi2Cut , _maxStep , zIndex, backward ) ;
+					}
+
+					cluList.merge( reclu ) ;
+				}
+
+				else if( float( mult[3]) / mult[0]  >= _minLayerFractionWithMultiplicity &&  mult[3] >  _minLayerNumberWithMultiplicity ) {
+
+					Clusterer::cluster_list reclu ; // reclustered leftover clusters
+					reclu.setOwner() ;
+
+					create_three_clusters( *clu , reclu ) ;
+
+					std::transform( reclu.begin(), reclu.end(), std::back_inserter( seedTrks) , fitter ) ;
+
+					for( Clusterer::cluster_list::iterator ir= reclu.begin(), end= reclu.end() ; ir != end ; ++ir ){
+
+						// FIXME Mingrui
+						// streamlog_out( DEBUG5 ) << " extending triplet clustre  of length " << (*ir)->size() << std::endl ;
+
+						addHitsAndFilter( *ir , hitsInLayer , _dChi2Max, _chi2Cut , _maxStep , zIndex) ;
+						static const bool backward = true ;
+						addHitsAndFilter( *ir , hitsInLayer , _dChi2Max, _chi2Cut , _maxStep , zIndex, backward ) ;
+					}
+
+					cluList.merge( reclu ) ;
+				}
+
+				else if( float( mult[2]) / mult[0]  >= _minLayerFractionWithMultiplicity &&  mult[2] >  _minLayerNumberWithMultiplicity ) {
+
+					Clusterer::cluster_list reclu ; // reclustered leftover clusters
+					reclu.setOwner() ;
+
+					create_two_clusters( *clu , reclu ) ;
+
+					std::transform( reclu.begin(), reclu.end(), std::back_inserter( seedTrks) , fitter ) ;
+
+					for( Clusterer::cluster_list::iterator ir= reclu.begin(), end= reclu.end() ; ir != end ; ++ir ){
+
+						// FIXME Mingrui
+						// streamlog_out( DEBUG5 ) << " extending doublet clustre  of length " << (*ir)->size() << std::endl ;
+
+						addHitsAndFilter( *ir , hitsInLayer , _dChi2Max, _chi2Cut , _maxStep , zIndex) ;
+						static const bool backward = true ;
+						addHitsAndFilter( *ir , hitsInLayer , _dChi2Max, _chi2Cut , _maxStep , zIndex, backward ) ;
+					}
+
+					cluList.merge( reclu ) ;
+
+				}
+				else if( float( mult[1]) / mult[0]  >= _minLayerFractionWithMultiplicity &&  mult[1] >  _minLayerNumberWithMultiplicity ) {
+
+
+					seedTrks.push_back( fitter( *it )  );
+
+					addHitsAndFilter( *it , hitsInLayer , _dChi2Max, _chi2Cut , _maxStep , zIndex) ;
+					static const bool backward = true ;
+					addHitsAndFilter( *it , hitsInLayer , _dChi2Max, _chi2Cut , _maxStep , zIndex, backward ) ;
+
+					cluList.push_back( *it ) ;
+
+					it = loclu.erase( it ) ;
+					--it ; // erase returns iterator to next element
+
+				} else {
+
+					//  discard cluster and free hits
+					clu->freeElements() ;
+				}
+
+			}
+
+
+			outerRow -=  padRangeRecluster ;
+
+		}
+	}
+
+	//=======================================================================================================================
+	//  try again to gobble up hits at the ends ....   - does not work right now, as there are no fits  for the clusters....
+	//=======================================================================================================================
+
+
+	timer.time( t_split ) ;
+
+	//===============================================================================================
+	//  now refit the tracks
+	//===============================================================================================
+
+	// FIXME Mingrui
+	debug()  << " ===========    refitting final " << cluList.size() << " track segments  "   << endmsg ;
+
+	//---- refit cluster tracks individually to save memory ( KalTest tracks have ~1MByte each)
+
+	IMarlinTrkFitter fit(_trksystem,  _dChi2Max) ; // fixme: do we need a different chi2 max here ????
+
+	for( Clusterer::cluster_list::iterator icv = cluList.begin() , end = cluList.end() ; icv != end ; ++ icv ) {
+
+		if( (*icv)->empty() )
+			continue ;
+
+		MarlinTrk::IMarlinTrack* trk = fit( *icv ) ;
+		trk->smooth() ;
+		edm4hep::ConstTrack edm4hepTrk = converter( *icv ) ;
+		tsCol_tmp.push_back( new ClupaPlcioConstTrack(edm4hepTrk) ) ;
+		MarTrk_of_edm4hepTrack(edm4hepTrk) = 0 ;
+		delete trk ;
+	}
+
+	timer.time( t_finalfit) ;
+
+	//===============================================================================================
+	//   optionally create collections of used and unused TPC hits
+	//===============================================================================================
+
+
+	//===============================================================================================
+	//  compute some track parameters for possible merging
+	//===============================================================================================
+
+	typedef nnclu::NNClusterer<ClupaPlcioConstTrack> TrackClusterer ;
+	TrackClusterer nntrkclu ;
+	MakePLCIOElement<ClupaPlcioConstTrack> trkMakeElement ;
+
+	for( int i=0,N=tsCol_tmp.size() ;  i<N ; ++i ) {
+
+		ConstTrack track = tsCol_tmp.at(i)->edm4hepTrack;
+		computeTrackInfo(track) ;
+	}
+
+	//===============================================================================================
+	//  merge split segements
+	//===============================================================================================
+
+
+	static const int merge_split_segments = true ;
+
+	if( merge_split_segments ) {
+
+		for(unsigned l=0 ; l < 2 ; ++l ) { // do this twice ....
+
+			int nMax  =  tsCol_tmp.size()   ;
+
+			TrackClusterer::element_vector incSegVec ;
+			incSegVec.setOwner() ;
+			incSegVec.reserve( nMax  ) ;
+			TrackClusterer::cluster_vector incSegCluVec ;
+			incSegCluVec.setOwner() ;
+
+			for( int i=0,N=tsCol_tmp.size() ;  i<N ; ++i ){
+
+				edm4hep::ConstTrack trk = tsCol_tmp.at(i)->edm4hepTrack;
+
+				const TrackInfoStruct* ti = TrackInfo_of_edm4hepTrack(trk);
+
+				bool isIncompleteSegment =   !ti->isCurler  && ( !ti->startsInner || ( !ti->isCentral && !ti->isForward )  ) ;
+
+				std::bitset<32> type = trk.getType() ;
+
+				if( isIncompleteSegment  && ! type[ lcio::ILDTrackTypeBit::SEGMENT ]){
+
+					incSegVec.push_back(  trkMakeElement( tsCol_tmp[i] )  ) ;
+
+					// FIXME Mingrui Debug part
+					/*
+					   if( writeCluTrackSegments )  incSegCol->addElement( trk ) ;
+					   */
+				}
+			}
+
+
+			TrackSegmentMerger trkMerge( _dChi2Max , _trksystem ,  _bfield  ) ;
+
+			nntrkclu.cluster( incSegVec.begin() , incSegVec.end() , std::back_inserter( incSegCluVec ), trkMerge , 2  ) ;
+
+			// FIXME: Mingrui
+			// streamlog_out( DEBUG4 ) << " ===== merged track segments - # cluster: " << incSegCluVec.size()
+			//  << " from " << incSegVec.size() << " incomplete track segments "    << "  ============================== " << std::endl ;
+
+			for(  TrackClusterer::cluster_vector::iterator it= incSegCluVec.begin() ; it != incSegCluVec.end() ; ++it) {
+
+				// FIXME: Mingrui
+				// streamlog_out( DEBUG4 ) <<  edm4hep::header<edm4hep::Track>() << std::endl ;
+
+				TrackClusterer::cluster_type*  incSegClu = *it ;
+
+				std::vector<edm4hep::ConstTrack> mergedTrk ;
+
+				// vector to collect hits from segments
+				//      std::vector< TrackerHit* >  hits ;
+				// hits.reserve( 1024 ) ;
+				// IMPL::TrackImpl* track = new  IMPL::TrackImpl ;
+				// tsCol->addElement( track ) ;
+
+				CluTrack hits ;
+
+				for( TrackClusterer::cluster_type::iterator itC = incSegClu->begin() ; itC != incSegClu->end() ; ++ itC ){
+
+					//streamlog_out( DEBUG3 ) << lcshort(  (*itC)->first ) << std::endl ;
+
+					edm4hep::ConstTrack trk = (*itC)->first->edm4hepTrack ;
+
+					mergedTrk.push_back( trk ) ;
+
+					//	std::copy( trk->getTrackerHits().begin() , trk->getTrackerHits().end() , std::back_inserter( hits ) ) ;
+
+					/*
+					   for( edm4hep::TrackerHitVec::const_iterator it = trk->getTrackerHits().begin() , END =  trk->getTrackerHits().end() ; it != END ; ++ it ){
+					   hits.addElement( GHitof(*it) )  ;
+					   }
+					   */
+					for (auto it = trk.trackerHits_begin(); it != trk.trackerHits_end(); it++) {
+						ConstTrackerHit hit = *it;
+						hits.addElement( GHitof(hit) );
+					}
+
+					// flag the segments so they can be ignored for final list
+					// FIXME I need to set type
+					// trk->setType( lcio::ILDTrackTypeBit::SEGMENT ) ;
+
+					// add old segments to new track
+					//	track->addTrack( trk ) ;
+				}
+
+				// MarlinTrk::IMarlinTrack* mTrk = _trksystem->createTrack();
+				// EVENT::FloatVec icov( 15 ) ;
+				// icov[ 0] = 1e2 ;
+				// icov[ 2] = 1e2 ;
+				// icov[ 5] = 1e2 ;
+				// icov[ 9] = 1e2 ;
+				// icov[14] = 1e2 ;
+				// int result = createFinalisedLCIOTrack( mTrk, hits, track, !MarlinTrk::IMarlinTrack::backward, icov, _bfield,  _dChi2Max ) ;
+				// //int result = createFinalisedLCIOTrack( mTrk, hits, track, ! MarlinTrk::IMarlinTrack::backward, icov, _bfield,  _dChi2Max ) ;
+				// // ???
+
+				MarlinTrk::IMarlinTrack* mTrk = fit( &hits ) ;
+				mTrk->smooth() ;
+				edm4hep::ConstTrack track = converter( &hits ) ;
+				tsCol_tmp.push_back( new ClupaPlcioConstTrack(track) ) ;
+				MarTrk_of_edm4hepTrack(track) = 0 ;
+				delete mTrk ;
+				computeTrackInfo( track ) ;
+
+				// FIXME: Mingrui
+				// streamlog_out( DEBUG4 ) << "   ******  created new track : " << " : " << lcshort( (Track*) track )  << std::endl ;
+
+			}
+		}// loop over l
+	}
+	//===============================================================================================
+	//  merge curler segments
+	//===============================================================================================
+
+
+	static const int merge_curler_segments = true ;
+
+	if( merge_curler_segments ) {
+
+		int nMax  =  tsCol_tmp.size()   ;
+
+		TrackClusterer::element_vector curSegVec ;
+		curSegVec.setOwner() ;
+		curSegVec.reserve( nMax  ) ;
+		TrackClusterer::cluster_vector curSegCluVec ;
+		curSegCluVec.setOwner() ;
+
+
+		for( int i=tsCol_tmp.size()-1 ;  i>=0 ; --i ){
+
+			ConstTrack trk = tsCol_tmp.at(i)->edm4hepTrack;
+
+			std::bitset<32> type = trk.getType() ;
+
+			if( type[ lcio::ILDTrackTypeBit::SEGMENT ] )
+				continue ;   // ignore previously merged track segments
+
+			const TrackInfoStruct* ti = TrackInfo_of_edm4hepTrack(trk) ;
+
+			bool isCompleteTrack =   ti && !ti->isCurler  && ( ti->startsInner &&  (  ti->isCentral || ti->isForward ) );
+
+			if( !isCompleteTrack ){
+
+				curSegVec.push_back(  trkMakeElement( tsCol_tmp[i] )  ) ;
+
+				// FIXME
+				// if( writeCluTrackSegments )  curSegCol->push_back( trk ) ;
+
+			} else {   // ... is not a curler ->  add a copy to the final tracks collection
+
+
+				if( copyTrackSegments) {
+
+					outCol_tmp.push_back( new ClupaPlcioConstTrack(trk)  ) ;
+
+				}else{
+
+					outCol_tmp.push_back( new ClupaPlcioConstTrack(trk) ) ;
+
+					// FIXME !!! Possibly different with the LCIO version
+					// tsCol-> Do not remove it at the Beginning
+					// Very very important
+					// tsCol->removeElementAt( i ) ;
+				}
+
+				// FIXME Mingrui remove
+				// if( writeCluTrackSegments )  finSegCol->push_back( trk ) ;
+			}
+		}
+
+		//======================================================================================================
+
+
+		TrackCircleDistance trkMerge( 0.1 ) ;
+
+		nntrkclu.cluster( curSegVec.begin() , curSegVec.end() , std::back_inserter( curSegCluVec ), trkMerge , 2  ) ;
+
+		// FIXME: Mingrui
+		debug() << " ===== merged tracks - # cluster: " << curSegCluVec.size()
+			<< " from " << tsCol_tmp.size() << " track segments "    << "  ============================== " << endmsg ;
+
+for (auto t : tsCol_tmp)
+// std::cout << t->edm4hepTrack << std::endl;
+
+// debug() << "Finish this line" << endmsg;
+
+		for(  TrackClusterer::cluster_vector::iterator it= curSegCluVec.begin() ; it != curSegCluVec.end() ; ++it) {
+
+			// FIXME: Mingrui
+			// streamlog_out( DEBUG4 ) <<  edm4hep::header<Track>() << std::endl ;
+
+			TrackClusterer::cluster_type*  curSegClu = *it ;
+
+			std::list<ConstTrack> mergedTrk ;
+
+			for( TrackClusterer::cluster_type::iterator itC = curSegClu->begin() ; itC != curSegClu->end() ; ++ itC ){
+
+				// FIXME: Mingrui
+				// streamlog_out( DEBUG4 ) << lcshort(  (*itC)->first ) << std::endl ;
+
+				mergedTrk.push_back( (*itC)->first->edm4hepTrack ) ;
+			}
+
+
+			mergedTrk.sort( TrackZSort() ) ;
+
+			//================================================================================
+
+
+			if( copyTrackSegments) {
+
+				// ====== create a new LCIO track for the merged cluster ...
+				// edm4hep::Track trk;
+				edm4hep::Track trk; 
+
+				trk.setType( lcio::ILDDetID::TPC ) ;
+
+				// == and copy all the hits
+				unsigned hitCount = 0 ;
+				for( std::list<ConstTrack>::iterator itML = mergedTrk.begin() ; itML != mergedTrk.end() ; ++ itML ){
+
+					for (auto itHit = (*itML).trackerHits_begin(); itHit != (*itML).trackerHits_end(); itHit++) {
+						trk.addToTrackerHits(*itHit);
+					}
+					hitCount  += (*itML).trackerHits_size()  ;
+
+					// add a pointer to the original track segment
+					trk.addToTracks(*itML) ;
+				}
+
+				// take track states from first and last track :
+				ConstTrack firstTrk = mergedTrk.front() ;
+				ConstTrack lastTrk  = mergedTrk.back() ;
+
+				/* !!!!!!!!!!!!!! critical important FIXME should wait zoujiaheng
+				   edm4hep::TrackState ts;
+				   ts = firstTrk->getTrackState( lcio::TrackState::AtIP  ) ;
+				   if( ts ) trk->addTrackState( new TrackState( *ts )  ) ;
+
+				   ts = firstTrk->getTrackState( lcio::TrackState::AtFirstHit  ) ;
+				   if( ts ) 	trk->addTrackState( new TrackState( *ts )  ) ;
+
+				   ts = lastTrk->getTrackState( lcio::TrackState::AtLastHit  ) ;
+				   if( ts ) trk->addTrackState( new TrackState( *ts )  ) ;
+
+				   ts = lastTrk->getTrackState( lcio::TrackState::AtCalorimeter  ) ;
+				   if( ts ) trk->addTrackState( new TrackState( *ts )  ) ;
+				   */
+				if (hasTrackStateAt(firstTrk, lcio::TrackState::AtIP )) trk.addToTrackStates(getTrackStateAt(firstTrk, lcio::TrackState::AtIP));
+				if (hasTrackStateAt(firstTrk, lcio::TrackState::AtFirstHit )) trk.addToTrackStates(getTrackStateAt(firstTrk, lcio::TrackState::AtFirstHit));
+				if (hasTrackStateAt(lastTrk, lcio::TrackState::AtLastHit )) trk.addToTrackStates(getTrackStateAt(lastTrk, lcio::TrackState::AtLastHit));
+				if (hasTrackStateAt(lastTrk, lcio::TrackState::AtCalorimeter )) trk.addToTrackStates(getTrackStateAt(lastTrk, lcio::TrackState::AtCalorimeter));
+
+
+				MarTrk_of_edm4hepTrack(trk) = MarTrk_of_edm4hepTrack(firstTrk) ;
+
+				// FIXME: Mingrui Maybe this info not needed
+				//int hitsInFit  =  firstTrk->getSubdetectorHitNumbers()[ 2 * lcio::ILDDetID::TPC - 1 ] ;
+				trk.setChi2(     firstTrk.getChi2()     ) ;
+				trk.setNdf(      firstTrk.getNdf()      ) ;
+				trk.setDEdx(     firstTrk.getDEdx()     ) ;
+				trk.setDEdxError(firstTrk.getDEdxError()) ;
+
+				/* FIXME: Mingrui Maybe this subdetectorHitNumbers is not needed?
+				   trk->subdetectorHitNumbers().resize( 2 * lcio::ILDDetID::ETD ) ;
+				   trk->subdetectorHitNumbers()[ 2 * lcio::ILDDetID::TPC - 2 ] =  hitsInFit ;
+				   trk->subdetectorHitNumbers()[ 2 * lcio::ILDDetID::TPC - 1 ] =  hitCount ;
+				   */
+
+				double RMin = 0.0;
+				if (hasTrackStateAt(trk, lcio::TrackState::AtFirstHit  ) ) {
+					TrackState ts = getTrackStateAt(trk, lcio::TrackState::AtFirstHit  ) ;
+					RMin = sqrt( ts.referencePoint[0] * ts.referencePoint[0]
+							+ ts.referencePoint[1] * ts.referencePoint[1] );
+
+				}
+				/*
+				   double RMin = ( ts ?
+				   sqrt( ts->referencePoint[0] * ts->referencePoint[0]
+				   + ts->referencePoint[1] * ts->referencePoint[1] )
+				   :  0.0 ) ;
+				   */
+				trk.setRadiusOfInnermostHit( RMin  ) ;
+
+
+
+				// streamlog_out( DEBUG2 ) << "   create new merged track from bestTrack parameters - ptr to MarlinTrk : " << trk->ext<MarTrk>()
+				//  << "   with subdetector hit numbers  : " <<  trk->subdetectorHitNumbers()[0 ] << " , " <<  trk->subdetectorHitNumbers()[1]
+				//  << std::endl ;
+
+
+				outCol_tmp.push_back( new ClupaPlcioConstTrack(trk) )  ;
+
+			} else { //==========================
+
+				// we move the first segment to the final list and keep pointers to the other segments
+
+				std::list<ConstTrack>::iterator itML = mergedTrk.begin() ;
+
+				ConstTrack trk = (ConstTrack) *itML++ ;
+
+				for(  ; itML != mergedTrk.end() ; ++itML ){
+
+					// add a pointer to the original track segment
+					// FIXME I need to add sub track
+					// trk->addTrack(**itML) ;
+				}
+
+				outCol_tmp.push_back( new ClupaPlcioConstTrack(trk) ) ;
+
+				//remove from segment collection:
+				for( int i=0,N=tsCol_tmp.size() ; i<N ; ++i) {
+					if( ( tsCol_tmp.at(i)->edm4hepTrack ) == trk ){
+						// FIXME Do not remove
+						// tsCol_tmp.removeElementAt( i ) ;
+						break ;
+					}
+				}
+
+			}//================================================================================
+
+		}
+		//---------------------------------------------------------------------------------------------
+		// // add all tracks that have not been merged :
+
+// debug() << "Still works here" << endmsg;
+		for( TrackClusterer::element_vector::iterator it = curSegVec.begin(); it != curSegVec.end() ;++it){
+
+                                // std::cout << "Can I get the track? 1" << std::endl;
+			if( (*it)->second == 0 ){
+
+                                // std::cout << "Can I get the track? 2" << std::endl;
+                                // std::cout << (*it)->first->edm4hepTrack;
+				ConstTrack trk = (*it)->first->edm4hepTrack ;
+                                // std::cout << "Can I get the track? 3" << std::endl;
+                                // std::cout << trk << std::endl;
+
+				if( copyTrackSegments) {
+
+					ConstTrack t =  trk;
+
+					TrackInfo_of_edm4hepTrack(t) = 0 ; // set extension to 0 to prevent double free ...
+
+					MarTrk_of_edm4hepTrack(t) = 0 ; // dynamic_cast<Track*>( (*it)->first )->ext<MarTrk>() ;
+
+					// FIXME Mingrui debug
+					// streamlog_out( DEBUG2 ) << "   create new track from existing LCIO track  - ptr to MarlinTrk : " << t->ext<MarTrk>()  << std::endl ;
+
+					outCol_tmp.push_back( new ClupaPlcioConstTrack(t) ) ;
+
+				} else {
+					outCol_tmp.push_back( new ClupaPlcioConstTrack(trk) ) ;
+
+					//remove from segment collection:
+					for( int i=0,N=tsCol_tmp.size() ; i<N ; ++i) {
+						if( ( tsCol_tmp.at(i)->edm4hepTrack ) == trk ){
+							// FIXME Do not remove
+							// tsCol->removeElementAt( i ) ;
+							break ;
+						}
+					}
+				}
+
+
+			}
+		}
+	}
+
+	timer.time( t_merge ) ;
+
+// debug() << "Finish merging works here" << endmsg;
+
+	//---------------------------------------------------------------------------------------------------------
+	//    pick up hits from Si trackers
+	//---------------------------------------------------------------------------------------------------------
+
+	/*
+	   if( _pickUpSiHits ){
+	   pickUpSiTrackerHits(outCol) ;
+	   }
+	   */
+	//---------------------------------------------------------------------------------------------------------
+
+	timer.time( t_pickup ) ;
+
+        totalCandidates = outCol_tmp.size();
+        for (auto t : outCol_tmp) {
+            // std::cout << "Final track: omega = " << getOmega(t->edm4hepTrack) << std::endl;
+            // omega = getOmega(t->edm4hepTrack);
+            // tree->Fill();
+            outCol->push_back(t->edm4hepTrack);
+        }
+        for (auto t : outCol_tmp) {
+            delete t;
+        }
+        for (auto t : tsCol_tmp) {
+            delete t;
+        }
+
+	_nEvt++ ;
+
+	return StatusCode::SUCCESS;
+}
+
+// ####### 001
+/*************************************************************************************************/
+
+void ClupatraAlg::computeTrackInfo(  edm4hep::ConstTrack lTrk  ){
+
+	if( ! TrackInfo_of_edm4hepTrack(lTrk) )
+		TrackInfo_of_edm4hepTrack(lTrk) = new TrackInfoStruct ;
+
+	float r_inner = _gearTPC->getPlaneExtent()[0] ;
+	float r_outer = _gearTPC->getPlaneExtent()[1] ;
+	float driftLength = _gearTPC->getMaxDriftLength() ;
+
+	// compute z-extend of this track segment
+	// auto hv = lTrk->getTrackerHits() ;
+
+	float zMin =  1e99 ;
+	float zMax = -1e99 ;
+	float zAvg =  0. ;
+
+	if( lTrk.trackerHits_size() >  1 ) {
+		zMin = lTrk.getTrackerHits(            0  ).getPosition()[2] ;
+		zMax = lTrk.getTrackerHits( lTrk.trackerHits_size() -1  ).getPosition()[2] ;
+		zAvg = ( zMax + zMin ) / 2. ;
+	}
+
+	if( zMin > zMax ){ // swap
+		float d = zMax ;
+		zMax = zMin ;
+		zMin = d  ;
+	}
+
+
+	// protect against bad tracks
+	// if(  tsF == 0 ) return ;
+	// if(  tsL == 0 ) return ;
+	if (! hasTrackStateAt(lTrk, lcio::TrackState::AtFirstHit)) return; // StatusCode::FAILURE;
+	if (! hasTrackStateAt(lTrk, lcio::TrackState::AtFirstHit)) return; // StatueCode::FAILURE;
+
+	edm4hep::TrackState tsF = getTrackStateAt(lTrk, lcio::TrackState::AtFirstHit  ) ;
+	edm4hep::TrackState tsL = getTrackStateAt(lTrk, lcio::TrackState::AtLastHit  ) ;
+
+	gear::Vector3D fhPos(tsF.referencePoint[0], tsF.referencePoint[1], tsF.referencePoint[2]) ;
+	gear::Vector3D lhPos(tsL.referencePoint[0], tsL.referencePoint[1], tsL.referencePoint[2]) ;
+
+
+	TrackInfoStruct* ti = TrackInfo_of_edm4hepTrack(lTrk);
+
+	ti->startsInner =  std::abs( fhPos.rho() - r_inner )     <  _trackStartsInnerDist ;        // first hit close to inner field cage
+	ti->isCentral   =  std::abs( lhPos.rho() - r_outer )     <  _trackEndsOuterCentralDist ;   // last hit close to outer field cage
+	ti->isForward   =  driftLength - std::abs( lhPos.z() )   <  _trackEndsOuterForwardDist  ;  // last hitclose to endcap
+	ti->isCurler    =  std::abs( tsF.omega )           >  _trackIsCurlerOmega  ;         // curler segment ( r <~ 1m )
+
+	ti->zMin = zMin ;
+	ti->zMax = zMax ;
+	ti->zAvg = zAvg ;
+
+}
+
+
+/*************************************************************************************************/
+// void ClupatraAlg::check() {
+
+
+// check that all Clupatra tracks actually have the four canonical track states set:
+
+// FIXME debug
+// streamlog_out( DEBUG5 ) <<   " ------------------- check()  called " << std::endl ;
+
+//  for(  LCIterator<Track> it(   evt, _segmentsOutColName  ) ; EVENT::Track* trk = it.next()  ; ) {
+/* FIXME debug
+   for(  LCIterator<Track> it(   evt, _outColName  ) ; EVENT::Track* trk = it.next()  ; ) {
+
+   const EVENT::TrackState* ts0 = trk->getTrackState( edm4hep::TrackState::AtIP ) ;
+   const EVENT::TrackState* ts1 = trk->getTrackState( edm4hep::TrackState::AtFirstHit ) ;
+   const EVENT::TrackState* ts2 = trk->getTrackState( edm4hep::TrackState::AtLastHit ) ;
+   const EVENT::TrackState* ts3 = trk->getTrackState( edm4hep::TrackState::AtCalorimeter ) ;
+
+
+//    streamlog_out( DEBUG2 ) <<  lcshort( trk ) <<  ", " << ts0 <<  ", " << ts1 <<  ", " << ts2 <<  ", " << ts3  << std::endl ;
+// FIXME debug
+// streamlog_out( DEBUG3 ) <<  " -- " << ts0 <<  ", " << ts1 <<  ", " << ts2 <<  ", " << ts3  << std::endl ;
+
+if( ! ts0 || ! ts1 || ! ts2 || ! ts3 )
+
+// FIXME debug
+// streamlog_out( ERROR ) <<  " clupatra track w/ missing track state : " <<  lcshort( trk )
+// <<  "  ts0-ts3 : " << ts0 <<  ", " << ts1 <<  ", " << ts2 <<  ", " << ts3  << std::endl ;
+
+
+}
+
+streamlog_out( DEBUG5 ) <<   " ------------------- check()  done " << std::endl ;
+*/
+
+/*************************************************************************************************/
+// }
+
+
+
+//====================================================================================================
+
+StatusCode ClupatraAlg::finalize(){
+        tree->SaveAs("Tuple.root");
+
+	/*
+	 * FIXME debug
+	 streamlog_out( MESSAGE )  << "ClupatraProcessor::end()  " << name()
+	 << " processed " << _nEvt << " events in " << _nRun << " runs "
+	 << std::endl ;
+	 */
+	return StatusCode::SUCCESS;
+
+}
+
+
+//====================================================================================================
diff --git a/Reconstruction/Tracking/src/Clupatra/ClupatraAlg.h b/Reconstruction/Tracking/src/Clupatra/ClupatraAlg.h
new file mode 100644
index 0000000000000000000000000000000000000000..80c10b64fb45156095a2ab13eec8a26783081266
--- /dev/null
+++ b/Reconstruction/Tracking/src/Clupatra/ClupatraAlg.h
@@ -0,0 +1,174 @@
+#ifndef ClupatraProcessor_h
+#define ClupatraProcessor_h 1
+
+#include "assert.h"
+#include "FWCore/DataHandle.h"
+#include "GearSvc/IGearSvc.h"
+#include "TrackSystemSvc/ITrackSystemSvc.h"
+
+#include "GaudiAlg/GaudiAlgorithm.h"
+#include "edm4hep/Track.h"
+#include "edm4hep/TrackerHitConst.h"
+#include "edm4hep/TrackCollection.h"
+#include <string>
+
+#include "gear/TPCModule.h"
+#include "RuntimeMap.h"
+#include "clupatra_new.h"
+#include "TrackingHelper.h"
+
+#include "TrackSystemSvc/MarlinTrkUtils.h"
+#include "TrackSystemSvc/HelixTrack.h"
+#include "TrackSystemSvc/HelixFit.h"
+#include "TrackSystemSvc/IMarlinTrack.h"
+
+namespace MarlinTrk{
+  class IMarlinTrkSystem ;
+}
+
+namespace gear{
+  class TPCParameters ;
+  class PadRowLayout2D ;
+}
+
+/** ClupatraAlgorithm : nearest neighbour clustering seeded pattern recognition using Kalman-Filter for extrapolation and
+ *  hit search.
+ *
+ *   @parameter TPCHitCollection         Name of the tpc hit input collections
+ *   @parameter OutputCollection         Name of the output collection with final TPC tracks
+ *   @parameter SegmentCollectionName    Name of the output collection that has the individual track segments
+ *   @parameter CreateDebugCollections   optionally create some debug collection with intermediate track segments and used and unused hits
+ *
+ *   @parameter DistanceCut              Cut for distance between hits in mm for the seed finding
+ *   @parameter Chi2Cut                  the maximum chi2-distance for which a hit is considered for merging
+ *   @parameter CosAlphaCut              Cut for max.angle between hits in consecutive layers for seed finding - NB value should be smaller than 1 - default is 0.9999999
+ *   @parameter MaxDeltaChi2             the maximum delta Chi2 after filtering for which a hit is added to a track segement
+ *
+ *   @parameter DuplicatePadRowFraction  allowed fraction of hits in same pad row per track
+ *   @parameter NLoopForSeeding          number of seed finding loops - every loop increases the distance cut by DistanceCut/NLoopForSeeding
+ *   @parameter NumberOfZBins            number of bins in z over total length of TPC - hits from different z bins are nver merged
+ *   @parameter PadRowRange              number of pad rows used in initial seed clustering
+ *
+ *   @parameter MaxStepWithoutHit                 the maximum number of layers without finding a hit before hit search search is stopped
+ *   @parameter MinLayerFractionWithMultiplicity  minimum fraction of layers that have a given multiplicity, when forcing a cluster into sub clusters
+ *   @parameter MinLayerNumberWithMultiplicity    minimum number of layers that have a given multiplicity, when forcing a cluster into sub clusters
+ *   @parameter MinimumClusterSize                minimum number of hits per cluster
+ *
+ *   @parameter TrackEndsOuterCentralDist maximum radial distance [mm] from outer field cage of last hit, such that the track is considered to end at the end
+ *   @parameter TrackEndsOuterForwardDist maximum distance in z [mm] from endplate of last hit, such that the track is considered to end at the end
+ *   @parameter TrackIsCurlerOmega        minimum curvature omega of a track segment for being considered a curler
+ *   @parameter TrackStartsInnerDist      maximum radial distance [mm] from inner field cage of first hit, such that the track is considered to start at the beginning
+ *
+ *   @parameter EnergyLossOn             Use Energy Loss in Fit
+ *   @parameter MultipleScatteringOn     Use MultipleScattering in Fit
+ *   @parameter SmoothOn                 Smooth All Mesurement Sites in Fit
+ *
+ *   @parameter pickUpSiHits             try to pick up hits from Si-trackers
+ *   @parameter SITHitCollection         name of the SIT hit collections - used to extend TPC tracks if (pickUpSiHits==true)
+ *   @parameter VXDHitCollection         name of the VXD hit collections - used to extend TPC tracks if (pickUpSiHits==true)
+ *
+ *   @parameter Verbosity               verbosity level of this processor ("DEBUG0-4,MESSAGE0-4,WARNING0-4,ERROR0-4,SILENT")
+ *
+ * @author F.Gaede, DESY, 2011/2012
+ * @version $Id: ClupatraProcessor.h 4488 2013-04-05 12:03:59Z volynets $
+ */
+class ClupatraAlg : public GaudiAlgorithm {
+    // friend class AlgFactory<ClupatraAlg>;
+ public:
+
+
+  ClupatraAlg(const std::string& name, ISvcLocator* svcLoc) ;
+
+  /** Called at the begin of the job before anything is read.
+   * Use to initialize the processor, e.g. book histograms.
+   */
+  virtual StatusCode initialize() ;
+
+  /** Called for every run.
+   */
+  // virtual void processRunHeader( LCRunHeader* run ) ;
+
+  /** Called for every event - the working horse.
+   */
+  virtual StatusCode execute() ;
+
+  // virtual void check( LCEvent * evt ) ;
+
+  /** Called after data processing for clean up.
+   */
+  virtual StatusCode finalize() ;
+
+
+ protected:
+
+  /** helper method to compute a few track segment parameters (start and end points, z spread,...)
+   */
+  void computeTrackInfo(edm4hep::ConstTrack lTrk) ;
+
+
+  StatusCode pickUpSiTrackerHits(edm4hep::TrackCollection* trackCol) ;
+  void printParameters();
+
+  /** Input collection name.
+   */
+  std::string _colName ;
+  std::string _vxdColName ;
+  std::string _sitColName ;
+  std::string _outColName ;
+  std::string  _segmentsOutColName ;
+
+  Gaudi::Property<float> _distCut{this, "DistanceCut", 40.0}; // Cut for distance between hits in mm for the seed finding
+  Gaudi::Property<float> _cosAlphaCut{this, "CosAlphaCut", 0.9999999}; // Cut for max.angle between hits in consecutive layers for seed finding - NB value should be smaller than 1 - default is 0.9999999 !!!
+  Gaudi::Property<int> _nLoop{this, "NLoopForSeeding", 4}; // number of seed finding loops - every loop increases the distance cut by DistanceCut/NLoopForSeeding
+  Gaudi::Property<int> _minCluSize{this, "MinimumClusterSize", 6}; // minimum number of hits per cluster
+  Gaudi::Property<float> _duplicatePadRowFraction{this, "DuplicatePadRowFraction", 0.1}; // allowed fraction of hits in same pad row per track
+  Gaudi::Property<float> _dChi2Max{this, "MaxDeltaChi2", 35.}; // the maximum delta Chi2  after filtering for which a hit is added to a track segement
+  Gaudi::Property<float> _chi2Cut{this, "Chi2Cut", 100.}; // the maximum chi2-distance for which a hit is considered for merging
+  Gaudi::Property<int> _maxStep{this, "MaxStepWithoutHit", 6}; // the maximum number of layers without finding a hit before hit search search is stopped
+  Gaudi::Property<bool> _pickUpSiHits{this, "pickUpSiHits", false};  // "try to pick up hits from Si-trackers"
+  Gaudi::Property<bool> _createDebugCollections{this, "CreateDebugCollections", false}; // optionally create some debug collection with intermediate track segments and used and unused hits
+  Gaudi::Property<int> _padRowRange{this, "PadRowRange", 15}; // "number of pad rows used in initial seed clustering"
+  Gaudi::Property<int> _nZBins{this, "NumberOfZBins", 150}; // "number of bins in z over total length of TPC - hits from different z bins are nver merged"
+  Gaudi::Property<float> _minLayerFractionWithMultiplicity{this, "MinLayerFractionWithMultiplicity" , 0.5}; // "minimum fraction of layers that have a given multiplicity, when forcing a cluster into sub clusters"
+  Gaudi::Property<float> _minLayerNumberWithMultiplicity{this, "MinLayerNumberWithMultiplicity", 3}; // "minimum number of layers that have a given multiplicity, when forcing a cluster into sub clusters"
+  Gaudi::Property<float> _trackStartsInnerDist{this, "TrackStartsInnerDist", (float) 25.}; // "maximum radial distance [mm] from inner field cage of first hit, such that the track is considered to start at the beginning "
+  Gaudi::Property<float> _trackEndsOuterCentralDist{this, "TrackEndsOuterCentralDist", 25.}; // "maximum radial distance [mm] from outer field cage of last hit, such that the track is considered to end at the end " 
+  Gaudi::Property<float> _trackEndsOuterForwardDist{this, "TrackEndsOuterForwardDist" , 40.}; // "maximum distance in z [mm] from endplate of last hit, such that the track is considered to end at the end " ,
+  Gaudi::Property<float> _trackIsCurlerOmega{this, "TrackIsCurlerOmega", 0.001}; // "minimum curvature omega of a track segment for being considered a curler"
+
+
+
+  DataHandle<edm4hep::TrackerHitCollection> _TPCHitCollectionHandle{"TPCTrackerHits", Gaudi::DataHandle::Reader, this};
+  DataHandle<edm4hep::TrackerHitCollection> _SITHitCollectionHandle{"SIDTrackerHits", Gaudi::DataHandle::Reader, this};
+  DataHandle<edm4hep::TrackerHitCollection> _VTXHitCollectionHandle{"VTXTrackerHits", Gaudi::DataHandle::Reader, this};
+
+  DataHandle<edm4hep::TrackCollection> _ClupatraTrackCollectionHandle{"ClupatraTracks", Gaudi::DataHandle::Writer, this};
+  DataHandle<edm4hep::TrackCollection> _ClupatraTrackSegmentCollectionHandle{"ClupatraSegmentTracks", Gaudi::DataHandle::Writer, this};
+
+  float _bfield ;
+
+  bool _MSOn ;
+  bool _ElossOn ;
+  bool _SmoothOn ;
+
+
+  int _nRun ;
+  int _nEvt ;
+
+  MarlinTrk::IMarlinTrkSystem* _trksystem ;
+
+  const gear::TPCParameters*  _gearTPC ;
+
+  TTree *tree;
+  double omega;
+  double pt;
+  int totalCandidates;
+
+//   NNClusterer* _clusterer ;
+
+} ;
+
+#endif
+
+
+
diff --git a/Reconstruction/Tracking/src/Clupatra/NNClusterer.h b/Reconstruction/Tracking/src/Clupatra/NNClusterer.h
new file mode 100644
index 0000000000000000000000000000000000000000..2055d99c6fafbd1c55fc0618527de507300f6e36
--- /dev/null
+++ b/Reconstruction/Tracking/src/Clupatra/NNClusterer.h
@@ -0,0 +1,386 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+#ifndef NNClusterer_h
+#define NNClusterer_h 1
+
+#include <list>
+#include <vector>
+
+// #include "LCRTRelations.h"
+
+/** Nearest neighbour type clusering for arbitrary types.
+ *
+ *  @author F.Gaede (DESY)
+ *  @version $Id: NNClusterer.h 4052 2012-09-12 09:56:04Z gaede $
+ */
+namespace nnclu {
+
+   /** fast check if integer is in a given range [Min,Max] */
+  template <unsigned Min,unsigned Max >
+  inline bool inRange( int i){   return ( (unsigned int) ( i - Min )  <= (unsigned int) ( Max - Min ) ); }
+
+  /** fast check if integer is not in a given range [Min,Max] */
+  template <unsigned Min,unsigned Max >
+  inline bool notInRange( int i){   return ( (unsigned int) ( i - Min )  > (unsigned int) ( Max - Min ) ); }
+
+
+  // forward declaration:
+  template <class U>
+  class Cluster ;
+
+
+  /** Wrapper class for elements that are clustered, holding a pointer to the actual
+   *  object "->first"  and a pointer to the cluster this obejct belongs to "->second".
+   *
+   *  @see Cluster
+   *  @author F.Gaede (DESY)
+   *  @version $Id: NNClusterer.h 4052 2012-09-12 09:56:04Z gaede $
+   */
+  template <class T>
+  class Element : public  std::pair< T*, Cluster<T>* >{
+
+    typedef T value_type ;
+    typedef Cluster<T> cluster_type ;
+
+  public:
+
+    /** Default c'tor takes a pointer to the original element type object. The optional index can be used to
+     *  code nearest neighbour bins, e.g. in z-coordinate to speed up the clustering process.
+     */
+    Element(T* element, int index0 = 0 ) : Index0( index0 )   {
+      Pair::first = element ;
+      Pair::second = 0 ;
+    }
+
+    /** C'tor that also takes a pointer to the cluster this element belongs to - in case seed elements/clusters are used.
+     */
+    Element(T* element ,  Cluster<T>* cl , int index0 = 0) : Index0( index0 ) {
+      Pair::first =  element ;
+      Pair::second = cl ;
+    }
+
+    /** Index that can be used to code nearest neighbour bins, e.g. in z-coordinate
+     *  to speed up the clustering process.
+     */
+    int Index0 ;
+
+  protected:
+    typedef std::pair< T*, Cluster<T>* > Pair ;
+
+    /** Don't allow default c'tor w/o element */
+    Element() ;
+  } ;
+
+
+  /** Helper class that creates an Elements for an objects of type T.
+   */
+  template <class T>
+  struct MakeElement{
+    Element<T>  operator()( T* t) { return new Element<T>( t ) ;  }
+  } ;
+
+
+  /** Extension of std::vector that allows to take ownership of objects pointed to and
+   *  delete these when going out of scope.
+   */
+  template <class T>
+  class PtrVector : public std::vector<T*> {
+    typedef std::vector<T*> vec ;
+    bool _isOwner ;
+  public:
+    PtrVector() : _isOwner( false ) {}
+    ~PtrVector() {
+      if( _isOwner )
+        for( typename vec::iterator i = vec::begin(),end = vec::end(); i != end ; delete *i++ ) ; //++i ) delete *i ;
+    }
+    void setOwner( bool val=true ) { _isOwner = val ; }
+  };
+
+
+  /** Extension of std::list that allows to take ownership of objects pointed to and
+   *  delete these when going out of scope.
+   */
+  template <class T>
+  class PtrList : public std::list<T*> {
+    typedef std::list<T*> vec ;
+    bool _isOwner ;
+  public:
+    PtrList() : _isOwner( false ) {}
+    ~PtrList() {
+      if( _isOwner )
+        for( typename vec::iterator i = vec::begin(),end = vec::end(); i != end ; delete *i++ ) ; //++i ) delete *i ;
+    }
+    void setOwner( bool val=true ) { _isOwner = val ; }
+  };
+
+
+
+  /** Templated class for generic clusters  of Elements that are clustered with
+   *  an NN-like clustering algorithm. Effectively this is just a list of elements.
+   *
+   *  @see Element
+   *  @author F.Gaede (DESY)
+   *  @version $Id: NNClusterer.h 4052 2012-09-12 09:56:04Z gaede $
+   */
+  template <class T >
+  class Cluster : public std::list< Element<T> * >{ //, public lcrtrel::LCRTRelations {
+
+  public :
+    typedef Element<T> element_type ;
+    typedef std::list< Element<T> * > base ;
+
+    int ID ; //DEBUG
+
+    Cluster() : ID(0) {}
+
+    /** C'tor that takes the first element */
+    Cluster( Element<T>* element)  {
+      static int SID=0 ;  //DEBUG
+      ID = SID++ ;      //DEBUG
+      addElement( element ) ;
+    }
+
+    /** Add a element to this cluster - updates the element's pointer to cluster */
+    void addElement( Element<T>* element ) {
+
+      element->second = this ;
+      base::push_back( element ) ;
+    }
+
+    // /** Remove all elements from the cluster and reset the cluster association, i.e. elements can be
+    //  *  used for another clustering procedure.
+    //  */
+    // template <class Out>
+    // void takeElements(Out result){
+    //   typename Cluster<T>::iterator it = this->begin() ;
+    //   while( it !=  this->end() ){
+    //     (*it)->second = 0 ;
+    //     result++ = *it ;
+    //     it = this->erase(it) ;
+    //   }
+    // }
+
+
+    /** Free all elements, ie. reset their cluster association - the elements are still in the list !.
+     */
+    void freeElements(){
+
+      for( typename Cluster<T>::iterator it = this->begin(), end = this->end() ; it != end ; it++ ){
+        (*it)->second = 0 ;
+      }
+
+      // typename Cluster<T>::iterator it = this->begin() ;
+      // while( it !=  this->end() ){
+      //   (*it)->second = 0 ;
+      //   it = this->erase(it) ;
+      // }
+    }
+
+    /** Merges all elements from the other cluster cl into this cluster */
+    void mergeClusters( Cluster<T>* cl ) {
+
+      for( typename Cluster<T>::iterator it = cl->begin(), end = cl->end() ; it != end ; it++ ){
+        (*it)->second = this  ;
+      }
+      this->merge( *cl ) ;
+    }
+
+    /** D'tor frees all remaining elements that still belong to this cluster */
+    ~Cluster()  {
+
+      //  typename Cluster<T>::iterator it = this->begin() ;
+      //  while( it !=  this->end()  )
+
+      for( typename Cluster<T>::iterator it = this->begin() , end =  this->end()  ;  it != end ; ++it ){
+
+        typename Cluster<T>::value_type h = *it ;
+
+        if( h != 0 && h->second == this )
+          h->second = 0 ;
+
+      }
+    }
+  } ;
+
+
+  template <class T>
+  /** Main class for a nearest neighbour type clustering.
+   *
+   *  @author F.Gaede (DESY)
+   *  @version $Id: NNClusterer.h 4052 2012-09-12 09:56:04Z gaede $
+   */
+  class NNClusterer{
+
+  public:
+    typedef T value_type ;
+    typedef Cluster<T> cluster_type ;
+    typedef Element<T> element_type ;
+    typedef PtrVector< element_type >    element_vector ;
+    typedef PtrVector< cluster_type >    cluster_vector ;
+    typedef PtrList< element_type >      element_list ;
+    typedef PtrList< cluster_type >      cluster_list ;
+
+    /** Simple nearest neighbour (NN) clustering algorithm. Users have to provide an input iterator of
+     *  Element objects and an output iterator for the clusters found. The predicate has to have
+     *  a method with the following signature: bool operator()( const Element<T>*, const Element<T>*).
+     *  All pairs of elements for which this method returns 'true' will be merged into one output cluster
+     *  - all other pairs of elements will be in different clusters.
+     */
+
+    template <class In, class Out, class Pred >
+    void cluster( In first, In last, Out result, Pred& pred , const unsigned minSize=1) {
+
+      cluster_vector tmp ;
+      tmp.reserve( 1024 ) ;
+
+      while( first != last ) {
+
+        for( In other = first+1 ;   other != last ; other ++ ) {
+
+          if( pred( (*first) , (*other) ) ) {
+
+            if( (*first)->second == 0 && (*other)->second == 0 ) {  // no cluster exists
+
+              cluster_type* cl = new cluster_type( (*first) ) ;
+
+              cl->addElement( (*other) ) ;
+
+              tmp.push_back( cl ) ;
+
+            }
+            else if( (*first)->second != 0 && (*other)->second != 0 ) { // two clusters
+
+              if(  (*first)->second != (*other)->second )  // don't call merge on identical clusters
+                (*first)->second->mergeClusters( (*other)->second ) ;
+
+            } else {  // one cluster exists
+
+              if( (*first)->second != 0 ) {
+
+                (*first)->second->addElement( (*other)  ) ;
+
+              } else {
+
+                (*other)->second->addElement( (*first)  ) ;
+              }
+            }
+
+          }
+        }
+        ++first ;
+      }
+
+      // remove empty clusters
+      for( typename cluster_vector::iterator i = tmp.begin(); i !=  tmp.end() ; i++ ){
+
+        if( (*i)->size() > minSize-1 ) {
+
+          result++ = *i ;
+        }
+        else {
+
+          delete *i ;
+        }
+      }
+    }
+
+
+    /** Same as above - but requires the elements to be sorted in index0 (only compare neighbouring bins in index0). */
+
+    template <class In, class Out, class Pred >
+    void cluster_sorted( In first, In last, Out result, Pred& pred , const unsigned minSize=1) {
+
+
+      cluster_vector tmp ;
+      tmp.reserve( 1024 ) ;
+
+      while( first != last ) {
+
+        for( In other = first+1 ;   other != last ; other ++ ) {
+
+          // if the elements are sorted we can skip the rest of the inner loop
+          if( notInRange<-1,1>(   (*first)->Index0 - (*other)->Index0  )   )
+            break ;
+
+          if( pred( (*first) , (*other) ) ) {
+
+            if( (*first)->second == 0 && (*other)->second == 0 ) {  // no cluster exists
+
+              cluster_type* cl = new cluster_type( (*first) ) ;
+
+              cl->addElement( (*other) ) ;
+
+              tmp.push_back( cl ) ;
+
+            }
+            else if( (*first)->second != 0 && (*other)->second != 0 ) { // two clusters
+
+              if(  (*first)->second != (*other)->second )  // don't call merge on identical clusters
+                (*first)->second->mergeClusters( (*other)->second ) ;
+
+            } else {  // one cluster exists
+
+              if( (*first)->second != 0 ) {
+
+                (*first)->second->addElement( (*other)  ) ;
+
+              } else {
+
+                (*other)->second->addElement( (*first)  ) ;
+              }
+            }
+
+          }
+        }
+        ++first ;
+      }
+
+      // remove empty clusters
+      for( typename cluster_vector::iterator i = tmp.begin(); i !=  tmp.end() ; i++ ){
+
+        if( (*i)->size() > minSize-1 ) {
+
+          result++ = *i ;
+        }
+        else {
+
+          delete *i ;
+        }
+      }
+    }
+
+  };
+  //-----------------------------------------------------------------------------------------------------------------------
+
+
+
+  /**Splits a list into two based on a predicate. The new list will
+   * hold all elements for which Pred is true which are in turn removed
+   * from the original list.
+   */
+  template <class List, class Out, class Pred >
+  void split_list( List& list, Out result, Pred pred) {
+
+    typename List::iterator it = list.begin() ;
+
+    while(  it != list.end() ){
+
+      if( pred( *it ) ){
+
+        result++ = *it ;
+        it = list.erase( it ) ;
+
+      } else
+        ++it ;
+    }
+  }
+
+
+
+
+  //-----------------------------------------------------------------------------------------------------------------------
+
+} // namespace nnclu
+
+#endif
+
+
diff --git a/Reconstruction/Tracking/src/Clupatra/RuntimeMap.h b/Reconstruction/Tracking/src/Clupatra/RuntimeMap.h
new file mode 100644
index 0000000000000000000000000000000000000000..2b0b14a2ed1b0edcbcd75e427fa942f6d1f19853
--- /dev/null
+++ b/Reconstruction/Tracking/src/Clupatra/RuntimeMap.h
@@ -0,0 +1,16 @@
+#ifndef CLUPATRA_RUNTIMEMAP_H
+#define CLUPATRA_RUNTIMEMAP_H
+#include <map>
+
+template<class U, class V>
+class RuntimeMap {
+    std::map<U, V> data;
+    public:
+    V& operator()(const U& u) {
+        return data[u];
+    }
+    void init() {
+        data.clear();
+    }
+};
+#endif
diff --git a/Reconstruction/Tracking/src/Clupatra/TrackingHelper.h b/Reconstruction/Tracking/src/Clupatra/TrackingHelper.h
new file mode 100644
index 0000000000000000000000000000000000000000..2a16aa8250daccc87c7cd320ea18d90a4e8ed7b1
--- /dev/null
+++ b/Reconstruction/Tracking/src/Clupatra/TrackingHelper.h
@@ -0,0 +1,62 @@
+#ifndef CLUPATRA_TRACKINGHELPER
+#define CLUPATRA_TRACKINGHELPER
+
+#include "edm4hep/TrackState.h"
+#include "edm4hep/Track.h"
+#include "UTIL/BitField64.h"
+#include "UTIL/CellIDDecoder.h"
+#include "UTIL/ILDConf.h"
+#include "lcio.h"
+
+inline bool hasTrackStateAt(edm4hep::ConstTrack track, int location) {
+    for (auto it = track.trackStates_begin(); it != track.trackStates_end(); it++) {
+        if (it->location == location) {
+            return true;
+        }
+    }
+    return false;
+}
+
+inline edm4hep::TrackState getTrackStateAt(edm4hep::ConstTrack track, int location) {
+    for (auto it = track.trackStates_begin(); it != track.trackStates_end(); it++) {
+        if (it->location == location) {
+            return *it;
+        }
+    }
+    return edm4hep::TrackState();
+}
+
+inline float getTanLambda(const edm4hep::ConstTrack &track) {
+    return track.getTrackStates(0).tanLambda;
+}
+inline float getOmega(const edm4hep::ConstTrack &track) {
+    return track.getTrackStates(0).omega;
+}
+inline float getD0(const edm4hep::ConstTrack &track) {
+    return track.getTrackStates(0).D0;
+}
+inline float getZ0(const edm4hep::ConstTrack &track) {
+    return track.getTrackStates(0).Z0;
+}
+inline float getPhi(const edm4hep::ConstTrack &track) {
+    return track.getTrackStates(0).phi;
+}
+
+
+inline int getLayer(const edm4hep::TrackerHit hit) {
+    UTIL::BitField64* _encoder = new UTIL::BitField64(lcio::ILDCellID0::encoder_string);
+    _encoder->setValue(hit.getCellID());
+    int layer = (*_encoder)[lcio::ILDCellID0::layer];
+    delete _encoder;
+    return layer;
+}
+
+inline int getLayer(const edm4hep::ConstTrackerHit hit) {
+    UTIL::BitField64* _encoder = new UTIL::BitField64(lcio::ILDCellID0::encoder_string);
+    _encoder->setValue(hit.getCellID());
+    int layer = (*_encoder)[lcio::ILDCellID0::layer];
+    delete _encoder;
+    return layer;
+}
+
+#endif
diff --git a/Reconstruction/Tracking/src/Clupatra/clupatra_new.cpp b/Reconstruction/Tracking/src/Clupatra/clupatra_new.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..479b981c5803b1112232161cd991eeeaab2194bb
--- /dev/null
+++ b/Reconstruction/Tracking/src/Clupatra/clupatra_new.cpp
@@ -0,0 +1,1513 @@
+#include "clupatra_new.h"
+#include <set>
+#include <vector>
+
+#include <UTIL/BitField64.h>
+#include <UTIL/ILDConf.h>
+#include <UTIL/BitSet32.h>
+
+///---- GEAR ----
+#include "gear/GEAR.h"
+#include "gear/TPCParameters.h"
+#include "gear/TPCModule.h"
+
+#include "gear/BField.h"
+
+#include "IMPL/TrackerHitImpl.h"
+#include "IMPL/TrackStateImpl.h"
+
+#include "FWCore/DataHandle.h"
+#include "GaudiAlg/GaudiAlgorithm.h"
+#include "GearSvc/IGearSvc.h"
+using namespace MarlinTrk ;
+
+namespace lcio{
+	// bits 0-15 are reserved !?
+	const int  ILDTrackTypeBit::SEGMENT   = 16  ;
+	const int  ILDTrackTypeBit::COMPOSITE = 17  ;
+}
+
+extern gear::GearMgr* gearMgr; // = _gear->getGearMgr();
+extern RuntimeMap<clupatra_new::CluTrack*, MarlinTrk::IMarlinTrack*> MarTrkof;
+extern RuntimeMap<edm4hep::ConstTrack, MarlinTrk::IMarlinTrack*> MarTrk_of_edm4hepTrack;
+extern RuntimeMap<edm4hep::ConstTrack, clupatra_new::TrackInfoStruct*> TrackInfo_of_edm4hepTrack;
+
+namespace clupatra_new{
+
+	bool TrackZSort::operator()( edm4hep::ConstTrack l, edm4hep::ConstTrack r){
+		return (  std::abs( TrackInfo_of_edm4hepTrack(l)->zAvg )   <   std::abs( TrackInfo_of_edm4hepTrack(r)->zAvg )  ) ;
+	}
+
+	void ComputeTrackerInfo::operator()( edm4hep::ConstTrack o )
+	{
+		edm4hep::ConstTrack lTrk  = o ;
+
+		// compute z-extend of this track segment
+		// const edm4hep::TrackerHitVec& hv = lTrk->getTrackerHits() ;
+
+		float zMin =  1e99 ;
+		float zMax = -1e99 ;
+		float zAvg =  0. ;
+
+		if( lTrk.trackerHits_size() >  1 ) {
+			zMin = lTrk.getTrackerHits(            0  ).getPosition()[2] ;
+			zMax = lTrk.getTrackerHits( lTrk.trackerHits_size() -1  ).getPosition()[2] ;
+			zAvg = ( zMax + zMin ) / 2. ;
+		}
+
+		if( zMin > zMax ){ // swap
+			float d = zMax ;
+			zMax = zMin ;
+			zMin = d  ;
+		}
+
+		if( ! TrackInfo_of_edm4hepTrack(lTrk) )
+			TrackInfo_of_edm4hepTrack(lTrk) =  new TrackInfoStruct ;
+
+		TrackInfo_of_edm4hepTrack(lTrk)->zMin = zMin ;
+		TrackInfo_of_edm4hepTrack(lTrk)->zMax = zMax ;
+		TrackInfo_of_edm4hepTrack(lTrk)->zAvg = zAvg ;
+
+	}
+	bool TrackCircleDistance:: operator()( nnclu::Element<ClupaPlcioConstTrack>* h0, nnclu::Element<ClupaPlcioConstTrack>* h1){
+
+		edm4hep::ConstTrack trk0 = h0->first->edm4hepTrack ;
+		edm4hep::ConstTrack trk1 = h1->first->edm4hepTrack ;
+
+		const TrackInfoStruct* ti0 =  TrackInfo_of_edm4hepTrack(trk0);
+		const TrackInfoStruct* ti1 =  TrackInfo_of_edm4hepTrack(trk1);
+
+
+		/* FIXME debug Mingrui
+		   streamlog_out( DEBUG2 ) << "TrackCircleDistance::operator() : " <<  trk0->id() << " <-> "  << trk1->id()
+		   << "  (  ti0->zAvg > ti1->zAvg ) = " << (  ti0->zAvg > ti1->zAvg )
+		   << std::endl ;
+		   */
+
+
+		// don't allow  overlaps in z !!!!
+		float epsilon = 0. ;
+		if(  ti0->zAvg > ti1->zAvg ){
+
+			if( ti1->zMax > ( ti0->zMin + epsilon )  )
+				return false ;
+
+		} else {
+
+			if( ti0->zMax > ( ti1->zMin + epsilon ) )
+				return false ;
+
+		}
+
+		double tl0 = std::abs( getTanLambda(trk0) ) ;
+		double tl1 = std::abs( getTanLambda(trk1) ) ;
+
+		if( getTanLambda(trk0) * getTanLambda(trk1)   < 0. )
+			return false ; // require the same sign
+
+
+		double dtl = 2. * std::abs( tl0 - tl1 ) / ( tl0 + tl1 ) ;
+
+		/* FIXME debug Mingrui
+		   streamlog_out( DEBUG2 ) << "TrackCircleDistance::operator() : " <<  trk0->id() << " <-> "  << trk1->id()
+		   << " dtl : " << dtl << "   std::abs( tl0 + tl1 ) = " <<  std::abs( tl0 + tl1 )
+		   << " (  dtl > 2.  * _dCut  &&  std::abs( tl0 + tl1 ) > 1.e-2  ) = " << (  dtl > 2.  * _dCut  &&  std::abs( tl0 + tl1 ) > 1.e-2  )
+		   << std::endl ;
+		   */
+
+		if(  dtl > 2.  * _dCut  &&  std::abs( tl0 + tl1 ) > 1.e-2  )
+			return false ;
+		// for very steep tracks (tanL < 0.001 ) tanL might differ largely for curlers due to multiple scattering
+
+		double r0 = 1. / getOmega(trk0)   ;
+		double r1 = 1. / getOmega(trk1)  ;
+
+		double r0abs = std::abs( r0 ) ;
+		double r1abs = std::abs( r1 ) ;
+
+
+		double d0 = getD0(trk0) ;
+		double d1 = getD0(trk1) ;
+
+		// double z0 = trk0->getZ0() ;
+		// double z1 = trk1->getZ0() ;
+
+		double rIP = 20. ;
+
+		// streamlog_out( DEBUG2 ) << "TrackCircleDistance::operator() : " <<  trk0->id() << " <-> "  << trk1->id()
+		//  			      << " (  std::abs( d0 ) < rIP &&  std::abs( z0 ) < rIP  && std::abs( d1 ) < rIP &&  std::abs( z1 ) < rIP     ) "
+		//  			      << (  std::abs( d0 ) < rIP &&  std::abs( z0 ) < rIP  && std::abs( d1 ) < rIP &&  std::abs( z1 ) < rIP     )
+		//  			      << std::endl ;
+		// // // don't merge tracks that come from an area of 20 mm around the IP
+		// if(  std::abs( d0 ) < rIP &&  std::abs( z0 ) < rIP  &&
+		//  	   std::abs( d1 ) < rIP &&  std::abs( z1 ) < rIP     )
+		// 	return false ;
+
+
+		edm4hep::TrackState ts0 =  getTrackStateAt(trk0, lcio::TrackState::AtFirstHit ) ;
+		edm4hep::TrackState ts1 =  getTrackStateAt(trk1, lcio::TrackState::AtFirstHit ) ;
+
+		/*
+		   double z0 = ( ts0 ? ts0->referencePoint()[2]  : 0. ) ;
+		   double z1 = ( ts1 ? ts1->referencePoint()[2]  : 0. ) ;
+		   */
+		// FIXME Mingrui since it is no pointer:
+		double z0 = ts0.referencePoint[2];
+		double z1 = ts1.referencePoint[2];
+
+
+		/* FIXME debug Mingrui
+		   streamlog_out( DEBUG2 ) << "TrackCircleDistance::operator() : " <<  trk0->id() << " <-> "  << trk1->id()
+		   << " (  std::abs( z0 ) < rIP  &&  std::abs( z1 ) < rIP     ) "
+		   << ( std::abs( z0 ) < rIP  &&  std::abs( z1 ) < rIP     )
+		   << std::endl ;
+		   */
+
+		if(  std::abs( z0 ) < rIP  && std::abs( z1 ) < rIP     )
+			return false ;
+
+
+
+		double p0 = getPhi(trk0) ;
+		double p1 = getPhi(trk1) ;
+
+		double x0 = ( r0 - d0 ) * sin( p0 ) ;
+		double x1 = ( r1 - d1 ) * sin( p1 ) ;
+
+		double y0 = ( d0 - r0 ) * cos( p0 ) ;
+		double y1 = ( d1 - r1 ) * cos( p1 ) ;
+
+		double dr = 2. * std::abs( r0abs - r1abs )  / (r0abs + r1abs )  ;
+
+		double distMS = sqrt ( ( x0 - x1 ) * ( x0 - x1 ) + ( y0 - y1 ) * ( y0 - y1 )  ) ;
+
+		/* FIXME debug Mingrui
+		   streamlog_out( DEBUG2 ) << "TrackCircleDistance:: operator() : " <<  trk0->id() << " <-> "  << trk1->id()
+		   << "( dr < _dCut * std::abs( r0 )  &&  distMS < _dCut * std::abs( r0 )  ) "
+		   << ( dr < _dCut * std::abs( r0 )  &&  distMS < _dCut * std::abs( r0 )  )
+		   <<  " dr : " << dr
+		   <<  " _dCut * std::abs( r0 ) " << _dCut * std::abs( r0 )
+		   << " distMS :" << distMS
+		   << std::endl ;
+		   */
+
+		//      return ( dr < DRMAX && distMS < _dCut * std::abs( r0 )  ) ;
+		return ( dr < _dCut * std::abs( r0 )  &&  distMS < _dCut * std::abs( r0 )  ) ;
+
+	}
+
+	/** helper class to compute the chisquared of two points in rho and z coordinate */
+	struct Chi2_RPhi_Z_Hit{
+		//    double operator()( const TrackerHit* h, const gear::Vector3D& v1) {
+		double operator()( const ClupaHit* h, const edm4hep::Vector3d& v1_t) {
+
+			gear::Vector3D v1(v1_t[0], v1_t[1], v1_t[2]);
+			//      gear::Vector3D v0( h->getPosition()[0] ,  h->getPosition()[1] ,  h->getPosition()[2] ) ;
+			const gear::Vector3D& v0 = h->pos ;
+
+			double sigsr =  h->edm4hepHit.getCovMatrix()[0] + h->edm4hepHit.getCovMatrix()[2]  ;
+			double sigsz =  h->edm4hepHit.getCovMatrix()[5] ;
+			// double sigsr =  0.01 ;
+			// double sigsz =  0.1 ;
+
+			double dPhi = std::abs(  v0.phi() - v1.phi() )  ;
+			if( dPhi > M_PI )
+				dPhi = 2.* M_PI - dPhi ;
+
+			double dRPhi =  dPhi *  v0.rho() ;
+
+			double dZ = v0.z() - v1.z() ;
+
+			return  dRPhi * dRPhi / sigsr + dZ * dZ / sigsz  ;
+		}
+	};
+
+	//-------------------------------------------------------------------------------
+
+	struct CDot{
+		CDot( int i , int j , double d) : I(i) , J(j) , Dot( d ) {}
+		int I ;
+		int J ;
+		double Dot ;
+	};
+
+	bool sort_CDot( const CDot& c0 ,const CDot& c1 ){
+		return c0.Dot > c1.Dot ;
+	}
+
+	//-------------------------------------------------------------------------------
+
+
+	int addHitsAndFilter( CluTrack* clu, HitListVector& hLV , double dChi2Max, double chi2Cut, unsigned maxStep, ZIndex& zIndex, bool backward,
+			MarlinTrk::IMarlinTrkSystem* trkSys ) {
+
+
+		int nHitsAdded = 0 ;
+
+		const double bfield = gearMgr->getBField().at( gear::Vector3D(0.,0.0,0.) ).z() ;
+
+		// Support for more than one module
+		static const gear::TPCParameters*  gearTPC = &(gearMgr->getTPCParameters());
+		// The ternary operator is used to make the trick with the static variable which
+		// is supposed to be calculated only once, also for performance reason
+		static const int maxTPCLayerID  = (gearMgr->getDetectorName() == "LPTPC" ) ?
+			gearTPC->getModule(0).getNRows() + gearTPC->getModule(2).getNRows() + gearTPC->getModule(5).getNRows() - 1 : // LCTPC
+			gearTPC->getModule(0).getNRows() - 1 ; // ILD
+
+		clu->sort( LayerSortIn() ) ;
+
+		int layer =  ( backward ?  clu->front()->first->layer : clu->back()->first->layer   ) ;
+
+
+		// std::cout <<  " ======================  addHitsAndFilter():  - layer " << layer << "  backward: " << backward << std::endl;
+		// std::cerr <<  " ======================  addHitsAndFilter():  - layer " << layer << "  backward: " << backward << std::endl;
+
+
+		if( layer <= 0  || layer >=  maxTPCLayerID   )
+			return  nHitsAdded ;
+
+
+		Chi2_RPhi_Z_Hit ch2rzh ;
+
+		IMarlinTrack* trk =  MarTrkof(clu);
+// {
+// edm4hep::TrackState ts;
+// double chi2; int ndf;
+// trk->getTrackState( ts, chi2,  ndf ) ;
+// std::cout << "Omega of this part is: " << ts.omega << " " << chi2 << " " << ndf << std::endl;
+// }
+
+		if(  trk == 0 ){
+			// streamlog_out( DEBUG3 ) <<  "  addHitsAndFilter called with null pointer to MarlinTrk  - won't do anything " << std::endl ;
+			return  nHitsAdded;
+		}
+
+		unsigned step = 0 ;
+
+		UTIL::BitField64 encoder( UTIL::ILDCellID0::encoder_string ) ;
+		encoder[UTIL::ILDCellID0::subdet] = UTIL::ILDDetID::TPC ;
+
+		edm4hep::ConstTrackerHit firstHit; // =  0 ;
+                firstHit.unlink();
+
+		IMarlinTrack* bwTrk = 0 ;
+
+		if( trkSys && backward  ) { //==================== only active if called with _trkSystem pointer ============================
+                        // std::cout << "It is backward" << std::endl;
+
+			// need to go back in cluster until 4th hit from the start
+			CluTrack::iterator it =  clu->begin() , end = clu->end() ;
+			int i=0 ;
+			for(     ; it++ != end && i< 3 ;  ++i  ) ;
+
+			if( it == end ) --it ;
+
+			// std::cout <<  " ---- addHitsAndFilter : will smooth back to " << i <<"th  hit - size of clu " << clu->size() << std::endl ;
+
+			if( !  (*it)->first ){
+
+				// std::cout << " ---- addHitsAndFilter : null pointer at i-th hit : i = " << i << std::endl ;
+				return nHitsAdded ;
+			}
+
+
+			firstHit = (*it)->first->edm4hepHit ;
+
+			int smoothed  = trk->smooth( firstHit ) ;
+
+			double chi2 ;
+			int ndf  ;
+			edm4hep::TrackState ts ;
+			trk->getTrackState( firstHit , ts, chi2,  ndf ) ;
+
+			// std::cout <<  "  -- addHitsAndFilter(): smoothed track segment : " <<  MarlinTrk::errorCode( smoothed )
+			//  <<  " using track state : " <<   ts
+			//  <<  " ---- chi2: " << chi2
+			//  <<  "  ndf " << ndf
+			//  << std::endl ;
+
+
+
+			//      std::auto_ptr<MarlinTrk::IMarlinTrack> mTrk( _trksystem->createTrack()  ) ;
+			bwTrk = trkSys->createTrack()  ;
+
+			//need to add a dummy hit to the track
+			bwTrk->addHit(  firstHit  ) ; // use the hit we smoothed back to
+
+			// note: backward here is forward in the MarlinTrk sense, ie. in direction of energy loss
+			bwTrk->initialise( ts ,  bfield ,  MarlinTrk::IMarlinTrack::forward ) ;
+
+		}
+
+
+		IMarlinTrack* theTrk  = ( bwTrk ? bwTrk : trk )  ;
+
+
+		while( step < maxStep + 1 ) {
+
+			layer += ( backward ?  +1 : -1 )  ;
+
+			if( layer < 0  || layer >  maxTPCLayerID   )
+				break ;
+
+
+			encoder[ UTIL::ILDCellID0::layer ]  = layer ;
+
+			// gear::Vector3D xv ;
+			edm4hep::Vector3d xv;
+
+			bool hitAdded = false ;
+
+			int layerID = encoder.lowWord() ;
+			int elementID = 0 ;
+
+			//      int mode = ( backward ? IMarlinTrack::modeBackward : IMarlinTrack::modeForward  )  ;
+			//      int mode = ( backward ? IMarlinTrack::modeForward : IMarlinTrack::modeClosest )  ;
+			int mode =  IMarlinTrack::modeClosest ;
+
+
+			int intersects = -1  ;
+
+			if( firstHit.isAvailable() )  { // FIXME Mingrui:: Important!! need to check whether firstHit is OK or not.
+// std::cout << "available" << std::endl;
+// std::cout << layerID << std::endl;
+// std::cout << firstHit << std::endl;
+// std::cout << xv << std::endl;
+// std::cout << elementID << std::endl;
+// std::cout << mode << std::endl;
+			intersects  = theTrk->intersectionWithLayer( layerID, firstHit, xv, elementID , mode )   ;
+
+			} else {
+
+			 	intersects  = theTrk->intersectionWithLayer( layerID, xv, elementID , mode )  ;
+			}
+
+
+
+			   // std::cout <<  "  -- addHitsAndFilter(): looked for intersection - "
+			   // <<  "  Step : " << step
+			   // <<  "  at layer: "   << layer
+			   // <<  "   intersects: " << MarlinTrk::errorCode( intersects )
+			   // <<  "  next xing point : " <<  xv  ;
+
+
+			if( intersects == IMarlinTrack::success ) { // found a crossing point
+
+				int zIndCP = zIndex.index( xv[2] ) ;
+
+				HitList& hLL = hLV.at( layer ) ;
+
+				double ch2Min = 1.e99 ;
+				Hit* bestHit = 0 ;
+
+				// streamlog_out( DEBUG3 ) <<  "      -- number of hits on layer " << layer << " : " << hLL.size() << std::endl ;
+
+				for( HitList::const_iterator ih = hLL.begin(), end = hLL.end() ; ih != end ; ++ih ){
+
+					// if the z indices differ by more than one we can continue
+					if( nnclu::notInRange<-1,1>(  (*ih)->first->zIndex - zIndCP ) )
+						continue ;
+
+					double ch2 = ch2rzh( (*ih)->first , xv )  ;
+
+					if( ch2 < ch2Min ){
+
+						ch2Min = ch2 ;
+						bestHit = (*ih) ;
+					}
+				}//-------------------------------------------------------------------
+
+				if( bestHit != 0 ){
+
+					// streamlog_out( DEBUG3 ) <<   " ************ bestHit "  << bestHit
+					// <<   " pos : " <<   (bestHit ? bestHit->first->pos :  gear::Vector3D() )
+					// <<   " chi2: " <<  ch2Min
+					// <<   " chi2Cut: " <<  chi2Cut <<   std::endl ;
+
+					const gear::Vector3D&  hPos = bestHit->first->pos  ;
+
+					if( ch2Min  < chi2Cut ) {
+
+						double deltaChi = 0. ;
+
+						edm4hep::ConstTrackerHit ht = bestHit->first->edm4hepHit;
+						int addHit =  theTrk->addAndFit(ht , deltaChi, dChi2Max )  ;
+
+
+
+						// streamlog_out( DEBUG3 ) <<   " *****       assigning left over hit : " << errorCode( addHit )  //<< hPos << " <-> " << xv
+						/*
+						   <<   " dist: " <<  (  hPos - xv ).r()
+						   <<   " chi2: " <<  ch2Min
+						   <<   "  hit errors :  rphi=" <<  sqrt( bestHit->first->edm4hepHit->getCovMatrix()[0]
+						   + bestHit->first->edm4hepHit->getCovMatrix()[2] )
+						   <<	 "  z= " <<  sqrt( bestHit->first->edm4hepHit->getCovMatrix()[5] )
+						   <<   "  ----- deltaChi = " << deltaChi
+						   << std::endl ;
+						   */
+
+
+						if( addHit  == IMarlinTrack::success ) {
+
+							hitAdded = true ;
+
+							hLL.remove(  bestHit ) ;
+							clu->addElement( bestHit ) ;
+
+							// firstHit = 0 ; // after we added a hit, the next intersection search should use this last hit...
+							firstHit.unlink();
+
+							++nHitsAdded ;
+
+							// streamlog_out( DEBUG ) <<   " ---- track state filtered with new hit ! ------- " << std::endl ;
+						}
+					} // chi2Cut
+				} // bestHit
+
+
+			} else {  // no intersection found
+
+				// we stop searching here
+				step = maxStep + 1 ;
+			}
+
+			// reset the step or increment it
+			step = (  hitAdded  ?  1 :  step + 1 ) ;
+
+		} // while step < maxStep
+
+
+		if( bwTrk ) delete bwTrk ;
+
+		return nHitsAdded ;
+
+	}
+
+	//------------------------------------------------------------------------------------------------------------
+
+	bool addHitAndFilter( int detectorID, int layer, CluTrack* clu, HitListVector& hLV , double dChi2Max, double chi2Cut) {
+
+		Chi2_RPhi_Z_Hit ch2rzh ;
+
+		IMarlinTrack* trk =  MarTrkof(clu) ;
+
+		UTIL::BitField64 encoder( UTIL::ILDCellID0::encoder_string ) ;
+
+		encoder[ UTIL::ILDCellID0::subdet ] = detectorID ;
+		encoder[ UTIL::ILDCellID0::layer  ] = layer ;
+
+		int layerID = encoder.lowWord() ;
+
+		// gear::Vector3D xv ;
+		edm4hep::Vector3d xv ;
+
+		int elementID = -1 ;
+
+		int intersects = trk->intersectionWithLayer( layerID, xv, elementID , IMarlinTrack::modeClosest  ) ;
+
+		//  IMarlinTrack::modeBackward , IMarlinTrack::modeForward
+
+		/*
+		   streamlog_out( DEBUG2 ) <<  "  ============ addHitAndFilter(): looked for intersection - "
+		   <<  "  detector : " <<  detectorID
+		   <<  "  at layer: "   << layer
+		   <<  "  intersects: " << MarlinTrk::errorCode( intersects )
+		   <<  "  next xing point : " <<  xv  ;
+		   */
+
+		bool hitAdded = false ;
+
+		if( intersects == IMarlinTrack::success ) { // found a crossing point
+
+			//FIXME: this is copied from ClupatraProcessor
+			static const int ZBins = 160 ;
+			ZIndex zIndex( -2750. , 2750. ,ZBins  ) ;
+			int zIndCP = zIndex.index( xv[2] ) ;
+
+			HitList& hLL = hLV.at( layer ) ;
+
+			double ch2Min = 1.e99 ;
+			Hit* bestHit = 0 ;
+
+			for( HitList::const_iterator ih = hLL.begin(), end = hLL.end() ; ih != end ; ++ih ){
+
+				// if the z indices differ by more than one we can continue
+				if( nnclu::notInRange<-1,1>(  (*ih)->first->zIndex - zIndCP ) )
+					continue ;
+
+				double ch2 = ch2rzh( (*ih)->first , xv )  ;
+
+				if( ch2 < ch2Min ){
+
+					ch2Min = ch2 ;
+					bestHit = (*ih) ;
+				}
+			}//-------------------------------------------------------------------
+
+
+			// streamlog_out( DEBUG2 ) <<   " ************ bestHit "  << bestHit
+			// <<   " pos : " <<   (bestHit ? bestHit->first->pos :  gear::Vector3D() )
+			// <<   " chi2: " <<  ch2Min
+			// <<   " chi2Cut: " <<  chi2Cut <<   std::endl ;
+
+			if( bestHit != 0 ){
+
+				const gear::Vector3D&  hPos = bestHit->first->pos  ;
+
+				if( ch2Min  < chi2Cut ) {
+
+					double deltaChi = 0. ;
+					edm4hep::ConstTrackerHit bh = bestHit->first->edm4hepHit;
+					int addHit = trk->addAndFit( bh, deltaChi, dChi2Max ) ;
+
+
+
+					// streamlog_out( DEBUG2 ) <<   " *****       assigning left over hit : " << errorCode( addHit )  //<< hPos << " <-> " << xv
+					/*
+					   <<   " dist: " <<  (  hPos - xv ).r()
+					   <<   " chi2: " <<  ch2Min
+					   <<   "  hit errors :  rphi=" <<  sqrt( bestHit->first->edm4hepHit->getCovMatrix()[0]
+					   + bestHit->first->edm4hepHit->getCovMatrix()[2] )
+					   <<	 "  z= " <<  sqrt( bestHit->first->edm4hepHit->getCovMatrix()[5] )
+					   << std::endl ;
+					   */
+
+
+
+
+
+					if( addHit  == IMarlinTrack::success ) {
+
+						hitAdded = true ;
+
+						hLL.remove(  bestHit ) ;
+						clu->addElement( bestHit ) ;
+
+
+						// streamlog_out( DEBUG2 ) <<   " ---- track state filtered with new hit ! ------- " << std::endl ;
+					}
+				} // chi2Cut
+			} // bestHit
+
+		} // intersection found
+
+		return hitAdded ;
+	}
+
+
+	//------------------------------------------------------------------------------------------------------------
+
+	void getHitMultiplicities( CluTrack* clu, std::vector<int>& mult ){
+
+		// int static maxTPCLayerID = marlin::Global::GEAR->getTPCParameters().getPadLayout().getNRows() - 1 ;
+		// HitListVector hLV( maxTPCLayerID )  ;
+		// addToHitListVector( clu->begin(), clu->end(),  hLV ) ;
+
+		std::map<int, unsigned > hm ;
+
+		for( CluTrack::iterator it=clu->begin(), end =clu->end() ;   it != end ; ++ it ){
+
+			++hm[ (*it)->first->layer ] ;
+		}
+
+		unsigned maxN = mult.size() - 1 ;
+
+		//    for( unsigned i=0 ; i < hLV.size() ; ++i ){
+		//      unsigned m =  hLV[i].size() ;
+
+		for( std::map<int, unsigned>::iterator it= hm.begin() , end = hm.end() ; it != end ; ++ it ){
+
+			unsigned m = ( it->second < maxN ?  it->second  :  maxN   ) ;
+
+			if( m == 0 )
+				continue ;
+
+			++mult[m] ;
+
+			++mult[0] ;
+		}
+	}
+
+	//-------------------------------------------------------------------------------------------------------------------------
+	void split_multiplicity( Clusterer::cluster_list& cluList, int layerWithMultiplicity , int N) {
+
+		for( Clusterer::cluster_list::iterator it= cluList.begin(), end= cluList.end() ; it != end ; ++it ){
+
+			CluTrack* clu = *it ;
+
+			std::vector<int> mult( N + 2 ) ;
+			// get hit multiplicities up to N ( N+1 means N+1 or higher )
+			getHitMultiplicities( clu , mult ) ;
+
+
+			// streamlog_out(  DEBUG2 ) << " **** split_multiplicity -  hit multiplicities: \n" ;
+
+			/*
+			   for( unsigned i=0,n=mult.size() ; i<n ; ++i) {
+			   streamlog_out(  DEBUG2 ) << "     m["<<i<<"] = " <<  mult[i] << "\n"  ;
+			   }
+			   */
+
+
+			if(  mult[1] >= layerWithMultiplicity )
+				continue ;
+
+			bool split_cluster = false  ;
+
+			for( int m=2 ; m<N ; ++m){
+
+				if( m == 2 && mult[m] >= layerWithMultiplicity ){
+
+					// streamlog_out(  DEBUG3 ) << " **** split_multiplicity - create_two_clusters \n" ;
+
+					create_two_clusters( *clu , cluList  ) ;
+
+					split_cluster = true  ;
+				}
+				else if( m == 3 && mult[m] >= layerWithMultiplicity ){
+
+					// streamlog_out(  DEBUG3 ) << " **** split_multiplicity - create_three_clusters \n" ;
+
+					create_three_clusters( *clu , cluList  ) ;
+
+					split_cluster = true  ;
+				}
+				else if(  mult[m] >= layerWithMultiplicity ){
+
+					// streamlog_out(  DEBUG3 ) << " **** split_multiplicity - create_n_clusters \n" ;
+
+					create_n_clusters( *clu ,cluList , m  ) ;
+
+					split_cluster = true  ;
+				}
+
+			}
+			if( split_cluster ){
+
+				clu->clear() ;
+
+				it = cluList.erase( it ) ;
+				--it ; // erase returns iterator to next element
+			}
+
+
+		}
+	}
+
+	//-------------------------------------------------------------------------------------------------------------------------
+
+	void create_n_clusters( Clusterer::cluster_type& hV, Clusterer::cluster_list& cluVec , unsigned n ) {
+
+		if( n < 4 ){
+
+			// streamlog_out( ERROR ) <<  " create_n_clusters called for n = " << n << std::endl ;
+			return ;
+		}
+
+		// copy of the algorithm for creating three clusters (minimize angle between hits as seen from IP)
+
+		hV.freeElements() ;
+
+		// Support for more than one module
+		//auto _gear = service<IGearSvc>("GearSvc");
+
+		static const gear::TPCParameters*  gearTPC = &(gearMgr->getTPCParameters());
+		// The ternary operator is used to make the trick with the static variable which
+		// is supposed to be calculated only once, also for performance reason
+		static const int tpcNRow =
+			(gearMgr->getDetectorName() == "LPTPC" ) ?
+			gearTPC->getModule(0).getNRows() + gearTPC->getModule(2).getNRows() + gearTPC->getModule(5).getNRows() : // LCTPC
+			gearTPC->getModule(0).getNRows() ; // ILD
+
+		HitListVector hitsInLayer( tpcNRow )  ;
+		addToHitListVector(  hV.begin(), hV.end(), hitsInLayer ) ;
+
+		std::vector< CluTrack*> clu(n)  ;
+
+		std::vector< gear::Vector3D > lastp(n) ;
+
+		for(unsigned i=0; i<n; ++i){
+
+			clu[i] = new CluTrack ;
+
+			cluVec.push_back( clu[i] ) ;
+		}
+
+		for( int l=tpcNRow-1 ; l >= 0 ; --l){
+
+			HitList& hL = hitsInLayer[ l ] ;
+
+			// streamlog_out(  DEBUG ) << " create_n_clusters  --- layer " << l  <<  " size: " << hL.size() << std::endl ;
+
+			if( hL.size() != n ){  // ignore layers with different hit numbers
+
+				continue ;
+			}
+
+			HitList::iterator iH = hL.begin() ;
+
+			std::vector<Hit*> h(n) ;
+			std::vector< gear::Vector3D>  p(n) ;
+
+
+			// streamlog_out(  DEBUG2 ) << " create_n_clusters  ---  layer " << l << std::endl ;
+
+			for(unsigned i=0; i<n; ++i){
+
+				h[i] = *iH++ ;
+
+				p[i] = h[i]->first->pos ;
+
+				// streamlog_out(  DEBUG2 ) << "              h"<< i << ": " << p[i] << std::endl ;
+			}
+
+			// streamlog_out(  DEBUG2 ) << " ---------------------------- " << std::endl ;
+
+
+			if( clu[0]->empty() ){ // first hit tuple
+
+				// streamlog_out(  DEBUG ) << " create_n_clusters  --- initialize clusters " << std::endl ;
+
+				for(unsigned i=0; i<n; ++i){
+
+					clu[i]->addElement( h[i] ) ;
+
+					lastp[i] = ( 1. / p[i].r() ) * p[i] ;
+				}
+
+				continue ;   //----------  that's all for the first layer w/ hits
+			}
+
+
+			// unit vector in direction of current hits
+			std::vector< gear::Vector3D > pu(n) ;
+
+			for(unsigned i=0; i<n; ++i){
+
+				pu[i] = ( 1. / p[i].r() ) * p[i] ;
+			}
+
+			std::list< CDot > cDot ;
+
+			// create list of dot products between last hit in cluster and current hit
+			// cos( angle between  hits as seen from IP )
+			for( unsigned i = 0 ; i < n ; ++i ){
+				for( unsigned j = 0 ; j < n ; ++j ){
+
+					cDot.push_back( CDot( i , j , lastp[i].dot( pu[ j ] ) )) ;
+				}
+			}
+
+			// sort dot products in descending order
+			cDot.sort( sort_CDot ) ;
+
+			// assign hits to clusters with largest dot product ( smallest angle )
+
+			std::set<int> cluIdx ;
+			std::set<int> hitIdx ;
+
+			for( std::list<CDot>::iterator it = cDot.begin() ; it != cDot.end() ; ++ it ) {
+				/*
+				   streamlog_out(  DEBUG2 ) << " I : " << it->I
+				   << " J : " << it->J
+				   << " d : " << it->Dot
+				   << std::endl ;
+				   */
+				unsigned i =  it->I ;
+				unsigned j =  it->J ;
+
+				// ignore clusters or hits that hvae already been assigned
+				if( cluIdx.find( i ) == cluIdx.end()  && hitIdx.find( j ) == hitIdx.end() ){
+
+					cluIdx.insert( i ) ;
+					hitIdx.insert( j ) ;
+
+					clu[ i ]->addElement( h[ j ] ) ;
+
+					// cluDir[i] = - ( p[j] - lastp[i] ) ;
+					// cluDir[i] =  (  1. / cluDir[i].r() ) * cluDir[i]  ;
+
+					lastp[i] =  p[j] ;
+
+					/*
+					   streamlog_out(  DEBUG2 ) << " **** adding to cluster : " << it->I
+					   << " hit  : " << it->J
+					   << " d : " << it->Dot
+					   << std::endl ;
+					   */
+				}
+
+			}
+		}
+
+
+		// streamlog_out(  DEBUG ) << " create_three_clusters  --- clu[0] " << clu[0]->size()
+		// <<  " clu[1] " << clu[1]->size()
+		// <<  " clu[2] " << clu[2]->size()
+		// << std::endl ;
+
+
+
+
+		return ;
+	}
+
+
+	//======================================================================================================================
+
+	void create_three_clusters( Clusterer::cluster_type& hV, Clusterer::cluster_list& cluVec ) {
+
+		hV.freeElements() ;
+
+		// auto _gear = service<IGearSvc>("GearSvc");
+		// Support for more than one module
+		static const gear::TPCParameters*  gearTPC = &(gearMgr->getTPCParameters());
+		// The ternary operator is used to make the trick with the static variable which
+		// is supposed to be calculated only once, also for performance reason
+		static const int tpcNRow =
+			(gearMgr->getDetectorName() == "LPTPC" ) ?
+			gearTPC->getModule(0).getNRows() + gearTPC->getModule(2).getNRows() + gearTPC->getModule(5).getNRows() : // LCTPC
+			gearTPC->getModule(0).getNRows() ; // ILD
+
+		HitListVector hitsInLayer( tpcNRow )  ;
+		addToHitListVector(  hV.begin(), hV.end(), hitsInLayer ) ;
+
+		CluTrack* clu[3] ;
+
+		gear::Vector3D lastp[3] ;
+		//    gear::Vector3D cluDir[3] ;
+
+		clu[0] = new CluTrack ;
+		clu[1] = new CluTrack ;
+		clu[2] = new CluTrack ;
+
+		cluVec.push_back( clu[0] ) ;
+		cluVec.push_back( clu[1] ) ;
+		cluVec.push_back( clu[2] ) ;
+
+
+		for( int l=tpcNRow-1 ; l >= 0 ; --l){
+
+			HitList& hL = hitsInLayer[ l ] ;
+
+			// streamlog_out(  DEBUG ) << " create_three_clusters  --- layer " << l  <<  " size: " << hL.size() << std::endl ;
+
+			if( hL.size() != 3 ){
+
+				continue ;
+			}
+
+			HitList::iterator iH = hL.begin() ;
+
+			Hit* h[3] ;
+			h[0] = *iH++ ;
+			h[1] = *iH++   ;
+			h[2] = *iH   ;
+
+			gear::Vector3D p[3] ;
+			p[0] = h[0]->first->pos ;
+			p[1] = h[1]->first->pos ;
+			p[2] = h[2]->first->pos ;
+
+
+			/*
+			   streamlog_out(  DEBUG ) << " create_three_clusters  ---  layer " << l
+			   << "  h0 : " << p[0]
+			   << "  h1 : " << p[1]
+			   << "  h2 : " << p[2] ;
+			   */
+			//			       << std::endl ;
+
+			if( clu[0]->empty() ){ // first hit triplet
+
+				// streamlog_out(  DEBUG ) << " create_three_clusters  --- initialize clusters " << std::endl ;
+
+				clu[0]->addElement( h[0] ) ;
+				clu[1]->addElement( h[1] ) ;
+				clu[2]->addElement( h[2] ) ;
+
+				// lastp[0] =  p[0] ;
+				// lastp[1] =  p[1] ;
+				// lastp[2] =  p[2] ;
+
+				lastp[0] = ( 1. / p[0].r() ) * p[0] ;
+				lastp[1] = ( 1. / p[1].r() ) * p[1] ;
+				lastp[2] = ( 1. / p[2].r() ) * p[2] ;
+
+				continue ;
+			}
+
+
+			// unit vector in direction of current hits
+			gear::Vector3D pu[3] ;
+			pu[0] = ( 1. / p[0].r() ) * p[0] ;
+			pu[1] = ( 1. / p[1].r() ) * p[1] ;
+			pu[2] = ( 1. / p[2].r() ) * p[2] ;
+
+
+			std::list< CDot > cDot ;
+
+			// create list of dot products between last hit in cluster and current hit
+			// cos( angle between  hits as seen from IP )
+			for( int i = 0 ; i < 3 ; ++i ){
+				for( int j = 0 ; j < 3 ; ++j ){
+
+					// // unit vector in direction of last hit and
+					// gear::Vector3D pu = - ( p[0] - lastp[0] ) ;
+					// pu[1] = - ( p[1] - lastp[1] ) ;
+					// pu[2] = - ( p[2] - lastp[2] ) ;
+					// pu[0] =  (  1. / pu[0].r() ) * pu[0]  ;
+					// pu[1] =  (  1. / pu[1].r() ) * pu[1]  ;
+					// pu[2] =  (  1. / pu[2].r() ) * pu[2]  ;
+					// cDot.push_back( CDot( i , j , cluDir[i].dot( pu[ j ] ) )) ;
+
+					cDot.push_back( CDot( i , j , lastp[i].dot( pu[ j ] ) )) ;
+
+				}
+			}
+
+			// sort dot products in descending order
+			cDot.sort( sort_CDot ) ;
+
+			// assign hits to clusters with largest dot product ( smallest angle )
+
+			std::set<int> cluIdx ;
+			std::set<int> hitIdx ;
+
+			for( std::list<CDot>::iterator it = cDot.begin() ; it != cDot.end() ; ++ it ) {
+
+				/*
+				   streamlog_out(  DEBUG ) << " I : " << it->I
+				   << " J : " << it->J
+				   << " d : " << it->Dot
+				   << std::endl ;
+				   */
+
+				int i =  it->I ;
+				int j =  it->J ;
+
+				// ignore clusters or hits that hvae already been assigned
+				if( cluIdx.find( i ) == cluIdx.end()  && hitIdx.find( j ) == hitIdx.end() ){
+
+					cluIdx.insert( i ) ;
+					hitIdx.insert( j ) ;
+
+					clu[ i ]->addElement( h[ j ] ) ;
+
+					// cluDir[i] = - ( p[j] - lastp[i] ) ;
+					// cluDir[i] =  (  1. / cluDir[i].r() ) * cluDir[i]  ;
+
+					lastp[i] =  p[j] ;
+
+					/*
+					   streamlog_out(  DEBUG ) << " **** adding to cluster : " << it->I
+					   << " hit  : " << it->J
+					   << " d : " << it->Dot
+					   << std::endl ;
+					   */
+				}
+
+			}
+
+			// lastp[0] =  pu[0] ;
+			// lastp[1] =  pu[1] ;
+			// lastp[2] =  pu[2] ;
+			// lastp[0] =  p[0] ;
+			// lastp[1] =  p[1] ;
+			// lastp[2] =  p[2] ;
+
+
+		}
+
+
+		// streamlog_out(  DEBUG ) << " create_three_clusters  --- clu[0] " << clu[0]->size()
+		//<<  " clu[1] " << clu[1]->size()
+		//<<  " clu[2] " << clu[2]->size()
+		//<< std::endl ;
+
+
+
+
+		return ;
+	}
+	//-----------------------------------------------------------------
+
+	void create_two_clusters( Clusterer::cluster_type& clu, Clusterer::cluster_list& cluVec ) {
+
+
+		clu.freeElements() ;
+
+		// streamlog_out(  DEBUG ) << " create_two_clusters  --- called ! - size :  " << clu.size()  << std::endl ;
+		// auto _gear = service<IGearSvc>("GearSvc");
+
+
+		// Support for more than one module
+		static const gear::TPCParameters*  gearTPC = &(gearMgr->getTPCParameters());
+		// The ternary operator is used to make the trick with the static variable which
+		// is supposed to be calculated only once, also for performance reason
+		static const int tpcNRow =
+			(gearMgr->getDetectorName() == "LPTPC" ) ?
+			gearTPC->getModule(0).getNRows() + gearTPC->getModule(2).getNRows() + gearTPC->getModule(5).getNRows() : // LCTPC
+			gearTPC->getModule(0).getNRows() ; // ILD
+
+		HitListVector hitsInLayer( tpcNRow )  ;
+
+		addToHitListVector(  clu.begin(), clu.end(), hitsInLayer ) ;
+
+
+		CluTrack* clu0 = new CluTrack ;
+		CluTrack* clu1 = new CluTrack ;
+
+		cluVec.push_back( clu0 ) ;
+		cluVec.push_back( clu1 ) ;
+
+
+		gear::Vector3D lastDiffVec(0.,0.,0.) ;
+
+		for( int l=tpcNRow-1 ; l >= 0 ; --l){
+
+			HitList& hL = hitsInLayer[ l ] ;
+
+			// streamlog_out(  DEBUG ) << " create_two_clusters  --- layer " << l  <<  " size: " << hL.size() << std::endl ;
+
+			if( hL.size() != 2 ){
+
+				// if there is not exactly two hits in the layer, we simply copy the unassigned hits to the leftover hit vector
+				// std::copy( hL.begin(),  hL.end() , std::back_inserter( hV ) ) ;
+				continue ;
+			}
+
+			HitList::iterator iH = hL.begin() ;
+
+			Hit* h0 = *iH++ ;
+			Hit* h1 = *iH   ;
+
+			gear::Vector3D& p0 = h0->first->pos ;
+			gear::Vector3D& p1 = h1->first->pos ;
+
+
+			// streamlog_out(  DEBUG ) << " create_two_clusters  ---  layer " << l
+			//<< "  h0 : " << p0
+			// << "  h1 : " << p1 ;
+			//			       << std::endl ;
+
+			if( clu0->empty() ){ // first hit pair
+
+				// streamlog_out(  DEBUG ) << " create_two_clusters  --- initialize clusters " << std::endl ;
+
+				clu0->addElement( h0 ) ;
+				clu1->addElement( h1 ) ;
+
+				lastDiffVec = p1 - p0 ;
+
+				continue ;
+			}
+
+			gear::Vector3D d = p1 - p0 ;
+
+			float s0 =  ( lastDiffVec + d ).r() ;
+			float s1 =  ( lastDiffVec - d ).r() ;
+
+			if( s0 > s1 ){  // same orientation, i.e. h0 in this layer belongs to h0 in first layer
+
+				// streamlog_out(  DEBUG ) << " create_two_clusters  ---   same orientation " << std::endl ;
+				clu0->addElement( h0 ) ;
+				clu1->addElement( h1 ) ;
+
+			} else{                // oposite orientation, i.e. h1 in this layer belongs to h0 in first layer
+
+				// streamlog_out(  DEBUG2 ) << " create_two_clusters  ---  oposite orientation " << std::endl ;
+				clu0->addElement( h1 ) ;
+				clu1->addElement( h0 ) ;
+			}
+
+		}
+
+		// clu.freeHits() ;
+
+		// streamlog_out(  DEBUG1 ) << " create_two_clusters  --- clu0 " << clu0->size()
+		//  <<  " clu1 " << clu1->size() << std::endl ;
+
+		// return std::make_pair( clu0 , clu1 ) ;
+
+		return ;
+	}
+
+	//-------------------------------------------------------------------------------------------------------------------------
+
+
+	// /** Find the nearest hits in the previous and next layers - (at most maxStep layers appart - NOT YET).
+	//  */
+	// void  findNearestHits( CluTrack& clu,  int maxLayerID, int maxStep){
+
+	//   HitListVector hitsInLayer( maxLayerID )  ;
+	//   addToHitListVector(  clu.begin(), clu.end(), hitsInLayer ) ;
+
+	//   for(CluTrack::iterator it= clu.begin() ; it != clu.end() ; ++it ){
+
+	//     Hit* hit0 = *it ;
+	//     gear::Vector3D& pos0 =  hit0->first->pos ;
+
+	//     int layer = hit0->first->layerID ;
+
+	//     int l=0 ;
+	//     if( (l=layer+1) <  maxLayerID ) {
+
+	// 	HitList& hL = hitsInLayer[ l ] ;
+
+	// 	double minDist2 = 1.e99 ;
+	// 	Hit*   bestHit = 0 ;
+
+	// 	for( HitList::iterator iH = hL.begin() ; iH != hL.end() ; ++iH ) {
+
+	// 	  Hit* hit1 = *iH ;
+	// 	  gear::Vector3D& pos1 = hit1->first->pos ;
+
+	// 	  double dist2 = ( pos0 - pos1 ).r2() ;
+
+	// 	  if( dist2 < minDist2 ){
+	// 	    minDist2 = dist2 ;
+	// 	    bestHit = hit1 ;
+	// 	  }
+	// 	}
+	// 	if( bestHit ){
+
+	// 	  hit0->first->nNNHit =  bestHit->first ;
+	// 	  hit0->first->nDist  =  minDist2 ;
+	// 	}
+	//     }
+
+	//     if( (l=layer-1) > 0  ) {
+
+	// 	HitList& hL = hitsInLayer[ l ] ;
+
+	// 	double minDist2 = 1.e99 ;
+	// 	Hit* bestHit = 0 ;
+
+	// 	for( HitList::iterator iH = hL.begin() ; iH != hL.end() ; ++iH ) {
+
+	// 	  Hit* hit1 = *iH ;
+	// 	  gear::Vector3D&  pos1 =  hit1->first->pos ;
+
+	// 	  double dist2 = ( pos0 - pos1 ).r2() ;
+
+	// 	  if( dist2 < minDist2 ){
+	// 	    minDist2 = dist2 ;
+	// 	    bestHit = hit1 ;
+	// 	  }
+	// 	}
+	// 	if( bestHit ){
+
+	// 	  hit0->first->pNNHit =  bestHit->first ;
+	// 	  hit0->first->pDist  =  minDist2 ;
+	// 	}
+	//     }
+
+	//   }
+	// }
+
+
+	//-------------------------------------------------------------------------------------------------------------------------
+
+	MarlinTrk::IMarlinTrack* IMarlinTrkFitter::operator() (CluTrack* clu) {
+
+		bool isFirstFit = true ;
+		double maxChi2  =  _maxChi2Increment   ;
+
+start:
+
+		MarlinTrk::IMarlinTrack* trk = _ts->createTrack();
+
+		//if( clu->empty()  ){
+		if( clu->size() < 3  ){
+
+			// streamlog_out( ERROR ) << " IMarlinTrkFitter::operator() : cannot fit cluster track with less than 3 hits ! " << std::endl ;
+
+			return trk ;
+		}
+
+		MarTrkof(clu) = trk ;
+
+		clu->sort( LayerSortOut() ) ;
+
+		// need to reverse the order for incomming track segments (curlers)
+		// assume particle comes from IP
+		Hit* hf = clu->front() ;
+		Hit* hb = clu->back() ;
+
+		bool reverse_order =   ( std::abs( hf->first->pos.z() ) > std::abs( hb->first->pos.z()) + 3. ) ;
+
+		unsigned nHit = 0 ;
+
+		if( reverse_order ){
+                        // std::cout << "It is true order" << std::endl;
+			for( CluTrack::reverse_iterator it=clu->rbegin() ; it != clu->rend() ; ++it){
+				edm4hep::ConstTrackerHit ph = (*it)->first->edm4hepHit;
+				trk->addHit(ph) ;
+				++nHit ;
+				// std::cout  <<  "   hit  added  " <<  (*it)->first->edm4hepHit   << std::endl ;
+			}
+
+			trk->initialise( MarlinTrk::IMarlinTrack::forward ) ;
+
+		} else {
+
+                        // std::cout << "It is reverse order" << std::endl;
+			for( CluTrack::iterator it=clu->begin() ; it != clu->end() ; ++it){
+				edm4hep::ConstTrackerHit ph = (*it)->first->edm4hepHit;
+				trk->addHit(ph) ;
+				++nHit ;
+				// std::cout <<  "   hit  added  "<<  (*it)->first->edm4hepHit   << std::endl ;
+			}
+
+			trk->initialise( MarlinTrk::IMarlinTrack::backward ) ;
+		}
+
+		int code = trk->fit(  maxChi2  ) ;
+                // for (auto hit : hitsInFit) std::cout << hit.first << std::endl;
+
+		if( code != MarlinTrk::IMarlinTrack::success ){
+
+			std::cout << "  >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> IMarlinTrkFitter :  problem fitting track "
+		          << " error code : " << MarlinTrk::errorCode( code )
+		          << std::endl ;
+
+		}
+
+
+                std::vector<std::pair<edm4hep::ConstTrackerHit, double> > hitsInFit ;
+                trk->getHitsInFit( hitsInFit ) ;
+		//----- if the fit did not fail but has a small number of hits used,
+		//      we try again one more time with a larger max-chi2-increment
+		if( isFirstFit  && ( 1.*hitsInFit.size()) / (1.*nHit )  <  0.2  ) {  // fixme: parameter
+
+			isFirstFit = false ;
+
+			maxChi2 =  2. * _maxChi2Increment  ;
+
+			// streamlog_out( DEBUG4 ) << "  >>>>>>  IMarlinTrkFitter :  small number of hits used in fit " << hitsInFit.size() << "/" << nHit << " = "
+			// << ( 1.*hitsInFit.size()) / (1.*nHit )  << " refit with larger max chi2 increment:  " << maxChi2 <<  std::endl ;
+			delete trk ;
+
+			goto start ;   // ;-)
+		}
+		//----------------------------------------------------------------------
+
+
+		return trk;
+	}
+
+
+	//---------------------------------------------------------------------------------------------------------------------------
+
+	edm4hep::ConstTrack PLCIOTrackConverter::operator() (CluTrack* c) {
+
+		static lcio::BitField64 encoder( lcio::ILDCellID0::encoder_string ) ;
+
+		edm4hep::Track trk;
+
+		trk.setType( lcio::ILDDetID::TPC ) ;
+
+		double e = 0.0 ;
+		int nHit = 0 ;
+		for( CluTrack::iterator hi = c->begin(); hi != c->end() ; hi++) {
+
+			// reset outliers (not used in fit)  bit
+			//      IMPL::TrackerHitImpl* thi = dynamic_cast<IMPL::TrackerHitImpl*> (  (*hi)->first->edm4hepHit )  ;
+			//      thi->setQualityBit( UTIL::ILDTrkHitQualityBit::USED_IN_FIT , 0 )  ;
+
+			trk.addToTrackerHits(  (*hi)->first->edm4hepHit ) ;
+			e += (*hi)->first->edm4hepHit.getEDep() ;
+			nHit++ ;
+		}
+
+		MarlinTrk::IMarlinTrack* mtrk = MarTrkof(c);
+
+		MarTrk_of_edm4hepTrack(trk) = mtrk ;
+
+		trk.setDEdx( ( nHit ? e/nHit : -1. )  ) ;
+
+		/* FIXME Mingrui
+		   trk->subdetectorHitNumbers().resize( 2 * lcio::ILDDetID::ETD ) ;
+
+		   trk->subdetectorHitNumbers()[ 2*lcio::ILDDetID::TPC - 2 ] =  0 ;
+		   trk->subdetectorHitNumbers()[ 2*lcio::ILDDetID::TPC - 1 ] =  nHit ;
+		   */
+
+		RuntimeMap<edm4hep::ConstTrackerHit, int> DChi2_of_hit;
+
+		if( mtrk != 0 && ! c->empty() ){
+
+
+			std::vector<std::pair<edm4hep::ConstTrackerHit, double> > hitsInFit ;
+			mtrk->getHitsInFit( hitsInFit ) ;
+                        // for (auto hit : hitsInFit) std::cout << hit.second << std::endl;
+			// FIXME Mingrui
+			// trk->subdetectorHitNumbers()[ 2*lcio::ILDDetID::TPC - 2 ] =  hitsInFit.size() ;
+
+			if( ! hitsInFit.empty() ){
+
+				// store the delta chi2 for the given hit
+				for(unsigned i=0, N=hitsInFit.size() ; i<N ; ++i){
+
+					DChi2_of_hit(hitsInFit[i].first) = hitsInFit[i].second ;
+
+					//IMPL::TrackerHitImpl* thi = dynamic_cast<IMPL::TrackerHitImpl*> ( hitsInFit[i].first ) ;
+					//thi->setQualityBit( UTIL::ILDTrkHitQualityBit::USED_IN_FIT , 1 )  ;
+				}
+
+				edm4hep::TrackState tsIP;
+				edm4hep::TrackState tsFH;
+				edm4hep::TrackState tsLH;
+				edm4hep::TrackState tsCA;
+
+				tsIP.location = lcio::TrackState::AtIP;
+				tsFH.location = lcio::TrackState::AtFirstHit;
+				tsLH.location = lcio::TrackState::AtLastHit;
+				tsCA.location = lcio::TrackState::AtCalorimeter;
+
+				double chi2 ;
+				int ndf  ;
+				int code ;
+
+				// Hit* hf = c->front() ;
+				// Hit* hb = c->back() ;
+				// bool reverse_order =   ( std::abs( hf->first->pos.z() ) > std::abs( hb->first->pos.z()) + 3. ) ;
+				// lcio::TrackerHit* fHit =  ( reverse_order ?  hb->first->lcioHit  :  hf->first->lcioHit ) ;
+				// lcio::TrackerHit* lHit =  ( reverse_order ?  hf->first->lcioHit  :  hb->first->lcioHit ) ;
+
+				edm4hep::ConstTrackerHit fHit = (hitsInFit.back().first);
+				edm4hep::ConstTrackerHit lHit = (hitsInFit.front().first);
+
+
+
+				//order of hits in fit is reversed wrt time  (we fit inwards)
+
+				// ======= get TrackState at first hit  ========================
+
+				code = mtrk->getTrackState( fHit, tsFH, chi2, ndf ) ;
+
+				if( code != MarlinTrk::IMarlinTrack::success ){
+
+					   std::cout << "  >>>>>>>>>>> PLCIOTrackConverter :  could not get TrackState at first Hit !!?? "
+					   << " error code : " << MarlinTrk::errorCode( code )
+					   << std::endl ;
+				}
+
+				// ======= get TrackState at last hit  ========================
+
+#define use_fit_at_last_hit 0
+
+#if use_fit_at_last_hit
+				code = mtrk->getTrackState( lHit, tsLH, chi2, ndf ) ;
+#else     // get the track state at the last hit by propagating from the last(first) constrained fit position (a la MarlinTrkUtils)
+				edm4hep::ConstTrackerHit last_constrained_hit;
+				mtrk->getTrackerHitAtPositiveNDF( last_constrained_hit );
+// std::cout << "the hit" << std::endl;
+// std::cout << lHit.getCellID0() << std::endl;
+// std::cout << last_constrained_hit << std::endl;
+				code = mtrk->smooth() ;
+// std::cout << "Smooth success" << std::endl;
+				// gear::Vector3D last_hit_pos( lHit->getPosition()[0], lHit->getPosition()[1], lHit->getPosition()[2] );
+// std::cout << "lHit = " << lHit << std::endl;
+
+// std::cout << "----------------------------------------------------------" << std::endl;
+// std::cout << "Position" << lHit.getPosition() << std::endl;
+// std::cout << "----------------------------------------------------------" << std::endl;
+
+
+				edm4hep::Vector3d last_hit_pos( lHit.getPosition() );
+// std::cout << "lHit = " << lHit << std::endl;
+				code = mtrk->propagate( last_hit_pos, last_constrained_hit, tsLH, chi2, ndf);
+
+// std::cout << "Propagate success" << std::endl;
+
+#endif
+
+// std::cout << "We are here" << std::endl; 
+				if( code != MarlinTrk::IMarlinTrack::success ){
+
+					std::cout << "  >>>>>>>>>>> PLCIOTrackConverter :  could not get TrackState at last Hit !!?? " << std::endl ;
+				}
+
+				// ======= get TrackState at calo face  ========================
+				//
+				encoder.reset() ;
+				encoder[ lcio::ILDCellID0::subdet ] =  lcio::ILDDetID::ECAL ;
+				encoder[ lcio::ILDCellID0::layer  ] =  0  ;
+				encoder[ lcio::ILDCellID0::side   ] =  lcio::ILDDetID::barrel;
+				int layerID  = encoder.lowWord() ;
+				int sensorID = -1 ;
+
+#if use_fit_at_last_hit
+				code = mtrk->propagateToLayer( layerID , lHit, tsCA, chi2, ndf, sensorID, MarlinTrk::IMarlinTrack::modeClosest ) ;
+#else     // get the track state at the calorimter from a propagating from the last(first) constrained fit position
+				code = mtrk->propagateToLayer( layerID , last_constrained_hit, tsCA, chi2, ndf, sensorID, MarlinTrk::IMarlinTrack::modeClosest ) ;
+#endif
+
+// std::cout << "We are here 2" << std::endl; 
+				if( code ==  MarlinTrk::IMarlinTrack::no_intersection ){
+
+					encoder[ lcio::ILDCellID0::side   ] = ( lHit.getPosition()[2] > 0.  ?   lcio::ILDDetID::fwd  :  lcio::ILDDetID::bwd  ) ;
+					layerID = encoder.lowWord() ;
+
+#if use_fit_at_last_hit
+					code = mtrk->propagateToLayer( layerID , lHit, tsCA, chi2, ndf, sensorID, MarlinTrk::IMarlinTrack::modeClosest ) ;
+#else     // get the track state at the calorimter from a propagating from the last(first) constrained fit position
+					code = mtrk->propagateToLayer( layerID , last_constrained_hit, tsCA, chi2, ndf, sensorID, MarlinTrk::IMarlinTrack::modeClosest ) ;
+#endif
+
+				}
+				if ( code !=MarlinTrk::IMarlinTrack::success ) {
+
+					// streamlog_out( DEBUG6 ) << "  >>>>>>>>>>> PLCIOTrackConverter :  could not get TrackState at calo face !!?? " << std::endl ;
+				}
+
+				// ======= get TrackState at IP ========================
+
+// std::cout << "We are here 3" << std::endl; 
+				// const gear::Vector3D ipv( 0.,0.,0. );
+				const double tmp_ipv[] = {0, 0, 0};
+				const edm4hep::Vector3d ipv(tmp_ipv);
+
+				// fg: propagate is quite slow  and might not really be needed for the TPC
+
+				code = ( UsePropagate ?   mtrk->propagate( ipv, fHit, tsIP, chi2, ndf ) :  mtrk->extrapolate( ipv, tsIP, chi2, ndf ) ) ;
+
+				if( code != MarlinTrk::IMarlinTrack::success ){
+
+					std::cout << "  >>>>>>>>>>> PLCIOTrackConverter :  could not extrapolate TrackState to IP !!?? " << std::endl ;
+				}
+
+				trk.addToTrackStates( tsIP ) ;
+				trk.addToTrackStates( tsFH ) ;
+				trk.addToTrackStates( tsLH ) ;
+				trk.addToTrackStates( tsCA ) ;
+
+// std::cout << "We are here 3" << std::endl; 
+				double RMin = sqrt( tsFH.referencePoint[0] * tsFH.referencePoint[0]
+						+ tsFH.referencePoint[1] * tsFH.referencePoint[1] ) ;
+
+				trk.setRadiusOfInnermostHit( RMin  ) ;
+
+				trk.setChi2( chi2 ) ;
+				trk.setNdf( ndf ) ;
+
+			} else {
+
+				std::cout << "  >>>>>>>>>>> PLCIOTrackConverter::operator()  -  hitsInFitEmpty ! - nHits " << nHit << std::endl ;
+			}
+
+		} else {
+
+			// this is not an error  for debug collections that just consist of track hits (no fit )
+			// streamlog_out( ERROR ) << "  >>>>>>>>>>> LCIOTrackConverter::operator() (CluTrack* c)  :  "
+			// 			     << " no MarlinTrk::IMarlinTrack* found for cluster !!?? " << std::endl ;
+		}
+
+
+		return trk;
+	}
+
+
+	}//namespace
diff --git a/Reconstruction/Tracking/src/Clupatra/clupatra_new.h b/Reconstruction/Tracking/src/Clupatra/clupatra_new.h
new file mode 100644
index 0000000000000000000000000000000000000000..5e5c9529ec48547d4ea960c7c811314664340017
--- /dev/null
+++ b/Reconstruction/Tracking/src/Clupatra/clupatra_new.h
@@ -0,0 +1,598 @@
+#ifndef clupatra_new_h
+#define clupatra_new_h
+
+#include "RuntimeMap.h"
+#include "TrackingHelper.h"
+
+#include <cmath>
+#include <algorithm>
+#include <time.h>
+#include <math.h>
+#include <sstream>
+#include <memory>
+#include "assert.h"
+
+#include "NNClusterer.h"
+
+#include "lcio.h"
+#include "EVENT/TrackerHit.h"
+#include "IMPL/TrackImpl.h"
+#include "UTIL/Operators.h"
+#include "UTIL/CellIDDecoder.h"
+#include "UTIL/ILDConf.h"
+
+#include "gear/GEAR.h"
+
+#include "edm4hep/TrackState.h"
+
+#include "TrackSystemSvc/IMarlinTrack.h"
+#include "TrackSystemSvc/IMarlinTrkSystem.h"
+
+// ----- include for verbosity dependend logging ---------
+// #include "marlin/VerbosityLevels.h"
+
+
+/** Helper structs that should go to LCIo to make extraction of layer (and subdetector etc easier )
+*/
+namespace lcio{
+	struct ILDDecoder : public CellIDDecoder<TrackerHit>{
+		ILDDecoder() :  lcio::CellIDDecoder<TrackerHit>( lcio::ILDCellID0::encoder_string ) {}
+	} ;
+	static ILDDecoder ILD_cellID ;
+
+	struct ILDTrackTypeBit{
+		static const int SEGMENT ;
+		static const int COMPOSITE ;
+	} ;
+}
+
+namespace clupatra_new{
+
+	/** Small wrapper extension of the LCIO Hit
+	*/
+	struct ClupaHit {
+
+		ClupaHit() :layer(-1),
+		zIndex(-1),
+		phiIndex(-1),
+		edm4hepHit(0),
+		pos(0.,0.,0.) {}
+		int layer ;
+		int zIndex ;
+		int phiIndex ;
+		edm4hep::ConstTrackerHit edm4hepHit ;
+		gear::Vector3D pos ;
+
+	};
+
+	//  inline lcio::TrackerHit* lcioHit( const ClupaHit* h) { return h->lcioHit ; }
+
+
+	//------------------ typedefs for elements and clusters ---------
+
+	typedef nnclu::NNClusterer< ClupaHit > Clusterer ;
+
+	typedef Clusterer::element_type Hit ;
+	typedef Clusterer::cluster_type CluTrack ;
+
+	typedef Clusterer::element_vector HitVec ;
+	typedef Clusterer::cluster_vector CluTrackVec ;
+
+	typedef std::list<Hit*>        HitList ;
+	typedef std::vector< HitList > HitListVector ;
+
+	struct ClupaPlcioConstTrack {
+		edm4hep::ConstTrack edm4hepTrack ;
+                ClupaPlcioConstTrack(edm4hep::ConstTrack edm4hepTrack) : edm4hepTrack(edm4hepTrack) {}
+	};
+
+	// typedef GenericHitVec<ClupaHit>      GHitVec ;
+	// typedef GenericClusterVec<ClupaHit>  GClusterVec ;
+
+	//------------------------------------------------------------------------------------------
+
+	// struct GHit : lcrtrel::LCExtension<GHit, Hit > {} ;
+
+	//------------------------------------------------------------------------------------------
+
+	// struct MarTrk : lcrtrel::LCExtension<MarTrk, MarlinTrk::IMarlinTrack> {} ;
+
+	//------------------------------------------------------------------------------------------
+
+	// struct DChi2 : lcrtrel::LCFloatExtension<DChi2> {} ;
+
+	//----------------------------------------------------------------
+
+	/** Simple predicate class for computing an index from N bins of the z-coordinate of LCObjects
+	 *  that have a float/double* getPostion() method.
+	 */
+	class ZIndex{
+		public:
+			/** C'tor takes zmin and zmax - NB index can be negative and larger than N */
+			ZIndex( float zmin , float zmax , int n ) : _zmin( zmin ), _zmax( zmax ) , _N (n) {}
+
+			template <class T>
+				inline int operator() (T* hit) {
+
+					return (int) std::floor( ( hit->getPosition()[2] - _zmin ) / ( _zmax - _zmin ) * _N ) ;
+				}
+
+			inline int index( double z) {  return  (int) std::floor( ( z - _zmin ) / ( _zmax - _zmin ) * _N ) ;  }
+
+		protected:
+			ZIndex() {} ;
+			float _zmin ;
+			float _zmax ;
+			int _N ;
+	} ;
+
+	//------------------------------------------------------------------------------------------
+
+	struct ZSort {
+		inline bool operator()( const Hit* l, const Hit* r) {
+			return ( l->first->pos.z() < r->first->pos.z() );
+		}
+	};
+
+
+	//------------------------------------------------------------------------------------------
+
+	struct PtSort {  // sort tracks wtr to pt - largest first
+		inline bool operator()( const edm4hep::ConstTrack l, const edm4hep::ConstTrack r) {
+			return ( std::abs( getOmega(l) ) < std::abs( getOmega(r) )  );  // pt ~ 1./omega
+		}
+	};
+
+	//------------------------------------------------------------------------------------------
+
+
+	/** Add the elements (Hits) from (First,Last) to the HitListVector - vector needs to be initialized, e.g. with
+	 *  hLV.resize( MAX_LAYER_INDEX )
+	 */
+	template <class First, class Last>
+		void addToHitListVector( First first, Last last, HitListVector& hLV ){
+
+			while( first != last ){
+
+				// FIXME debug Mingrui
+				//streamlog_out( DEBUG ) << "  add hit << "  <<   *first << " to layer " <<  (*first)->first->layer  << std::endl ;
+
+				hLV[ (*first)->first->layer ].push_back( *first )  ;
+				++first ;
+			}
+		}
+
+	//------------------------------------------------------------------------------------------
+
+	/** Predicate class for 'distance' of NN clustering. */
+	class HitDistance{
+		public:
+
+			HitDistance(float dCut, float caCut = -1.0 ) : _dCutSquared( dCut*dCut ) , _caCut( caCut ) {}
+
+			/** Merge condition: true if distance  is less than dCut */
+			inline bool operator()( Hit* h0, Hit* h1){
+
+				if( std::abs( h0->Index0 - h1->Index0 ) > 1 ) return false ;
+
+				if( h0->first->layer == h1->first->layer )
+					return false ;
+
+				if(  _caCut > 0.  && std::abs( h0->first->layer - h1->first->layer ) == 1 ){
+
+					gear::Vector3D& p0 =  h0->first->pos   ;
+					gear::Vector3D& p1 =  h1->first->pos   ;
+
+					double cosAlpha = p0.dot( p1 ) / p0.r() / p1.r()  ;
+
+					// merge hits that seem to come from stiff track from the IP
+					//fixme: make parameter
+					if( cosAlpha > _caCut ) return true ;
+				}
+
+				return ( h0->first->pos - h1->first->pos).r2()  < _dCutSquared ;
+			}
+		protected:
+			HitDistance() ;
+			float _dCutSquared, _caCut  ;
+	} ;
+
+
+	// /** Predicate class for 'distance' of NN clustering. */
+
+	// struct HitDistance{  float _dCutSquared ;
+	//   HitDistance(float dCut) : _dCutSquared( dCut*dCut ) {}
+
+	//   inline bool operator()( Hit* h0, Hit* h1){
+
+	//     if( h0->first->layer == h1->first->layer )
+	// 	return false ;
+
+	//     return ( h0->first->pos - h1->first->pos).r2()
+	// 	< _dCutSquared ;
+	//   }
+	// } ;
+
+
+
+	//-----------------------------------------------
+
+	struct PLCIOTrackConverter{
+
+		bool UsePropagate ;
+		PLCIOTrackConverter() : UsePropagate(false ) {}
+
+		edm4hep::ConstTrack operator() (CluTrack* c) ;
+
+	} ;
+
+	//------------------------------------------------------------------------------------------
+	/** Predicate class for identifying small clusters. */
+	struct ClusterSize {
+		ClusterSize( unsigned n) : _n(n) {}
+		bool operator()(const CluTrack* cl) const {   return cl->size() < _n ;  }
+		unsigned _n ;
+	};
+
+	//------------------------------------------------------------------------------------------
+	/** Predicate class for identifying clusters with duplicate pad rows - returns true
+	 *  if the fraction of duplicate hits is larger than 'fraction'.
+	 */
+	struct DuplicatePadRows{
+
+		unsigned _N ;
+		float _f ;
+		DuplicatePadRows(unsigned nPadRows, float fraction) : _N( nPadRows), _f( fraction )  {}
+
+		bool operator()(const CluTrack* cl) const {
+
+			// check for duplicate layer numbers
+			std::vector<int> hLayer( _N )  ;
+			typedef CluTrack::const_iterator IT ;
+
+			unsigned nHit = 0 ;
+			for(IT it=cl->begin() ; it != cl->end() ; ++it ) {
+				++ hLayer[ (*it)->first->layer]   ;
+				++ nHit ;
+			}
+			unsigned nDuplicate = 0 ;
+			for(unsigned i=0 ; i < _N ; ++i ) {
+				if( hLayer[i] > 1 )
+					nDuplicate += hLayer[i] ;
+			}
+			return double(nDuplicate)/nHit > _f ;
+		}
+	};
+
+	//------------------------------------------------------------------------------------------
+
+	// helper for sorting cluster wrt layerID
+	struct LayerSortOut{
+		bool operator()( const Hit* l, const Hit* r) { return l->first->layer < r->first->layer ; }
+	} ;
+	struct LayerSortIn{
+		bool operator()( const Hit* l, const Hit* r) { return l->first->layer > r->first->layer ; }
+	} ;
+
+
+
+
+	//------------------------------------------------------------------------------------------
+
+	struct IMarlinTrkFitter{
+
+		MarlinTrk::IMarlinTrkSystem* _ts ;
+		double _maxChi2Increment ;
+
+		IMarlinTrkFitter(MarlinTrk::IMarlinTrkSystem* ts, double maxChi2Increment=DBL_MAX ) :
+			_ts( ts ) ,
+			_maxChi2Increment(maxChi2Increment) {}
+
+
+		MarlinTrk::IMarlinTrack* operator() (CluTrack* clu) ;
+	};
+
+	//-------------------------------------------------------------------------------------
+
+	/** Try to add hits from hLV (hit lists per layer) to the cluster. The cluster needs to have a fitted KalTrack associated to it.
+	 *  Hits are added if the resulting delta Chi2 is less than dChiMax - a maxStep is the maximum number of steps (layers) w/o
+	 *  successfully merging a hit.
+	 */
+	int addHitsAndFilter( CluTrack* clu, HitListVector& hLV , double dChiMax, double chi2Cut, unsigned maxStep, ZIndex& zIndex,  bool backward=false,
+			MarlinTrk::IMarlinTrkSystem* trkSys=0) ;
+	//------------------------------------------------------------------------------------------
+
+	/** Try to add a hit from the given HitList in layer of subdetector to the track.
+	 *  A hit is added if the resulting delta Chi2 is less than dChiMax.
+	 */
+	bool addHitAndFilter( int detectorID, int layer, CluTrack* clu, HitListVector& hLV , double dChiMax, double chi2Cut) ;
+
+	//------------------------------------------------------------------------------------------
+	/** Split up clusters that have a hit multiplicity of 2,3,4,...,N in at least layersWithMultiplicity.
+	*/
+	void split_multiplicity( Clusterer::cluster_list& cluList, int layersWithMultiplicity , int N=5) ;
+
+	//------------------------------------------------------------------------------------------
+	/** Returns the number of rows where cluster clu has i hits in mult[i] for i=1,2,3,4,.... -
+	 *  mult[0] counts all rows that have hits
+	 */
+	void getHitMultiplicities( CluTrack* clu, std::vector<int>& mult ) ;
+
+	//------------------------------------------------------------------------------------------
+
+	/** Split the cluster into two clusters.
+	*/
+	void create_two_clusters( Clusterer::cluster_type& clu, Clusterer::cluster_list& cluVec ) ;
+
+	//------------------------------------------------------------------------------------------
+
+	/** Split the cluster into three clusters.
+	*/
+	void create_three_clusters( Clusterer::cluster_type& clu, Clusterer::cluster_list& cluVec ) ;
+
+
+	/** Split the cluster into N clusters.
+	*/
+	void create_n_clusters( Clusterer::cluster_type& clu, Clusterer::cluster_list& cluVec ,  unsigned n ) ;
+
+
+
+	//=======================================================================================
+
+	struct TrackInfoStruct{
+		TrackInfoStruct() : zMin(0.), zAvg(0.), zMax(0.), startsInner(false), isCentral(false), isForward(false), isCurler(false) {}
+		float zMin ;
+		float zAvg ;
+		float zMax ;
+		bool startsInner ;
+		bool isCentral   ;
+		bool isForward   ;
+		bool isCurler    ;
+	} ;
+	// struct TrackInfo : lcrtrel::LCOwnedExtension<TrackInfo, TrackInfoStruct> {} ;
+	typedef TrackInfoStruct TrackInfo;
+	/** Helper class to compute track segment properties.
+	*/
+	struct ComputeTrackerInfo{
+		void operator()( edm4hep::ConstTrack o );
+	};
+
+	//=======================================================================================
+
+	/** helper class for merging split track segments */
+
+	class TrackSegmentMerger{
+
+		public:
+			/** C'tor takes merge distance */
+			TrackSegmentMerger(float chi2Max,  MarlinTrk::IMarlinTrkSystem* trksystem, float b) : _chi2Max( chi2Max ) , _trksystem( trksystem), _b(b) {}
+
+			float _chi2Max ;
+			MarlinTrk::IMarlinTrkSystem* _trksystem ;
+			float _b ;
+
+			/** Merge condition: ... */
+			inline bool operator()( nnclu::Element<ClupaPlcioConstTrack>* h0, nnclu::Element<ClupaPlcioConstTrack>* h1){
+
+				edm4hep::ConstTrack trk0 = h0->first->edm4hepTrack ;
+				edm4hep::ConstTrack trk1 = h1->first->edm4hepTrack ;
+
+
+				// protect against merging multiple segments (and thus complete tracks)
+				if(  h0->second || h1->second )
+					return false ;
+
+				// const TrackInfoStruct* ti0 =  trk0->ext<TrackInfo>() ;
+				// const TrackInfoStruct* ti1 =  trk1->ext<TrackInfo>() ;
+
+				// const lcio::TrackState* tsF0 = trk0->getTrackState( lcio::TrackState::AtFirstHit  ) ;
+				// const lcio::TrackState* tsL0 = trk0->getTrackState( lcio::TrackState::AtLastHit  ) ;
+
+				// const lcio::TrackState* tsF1 = trk1->getTrackState( lcio::TrackState::AtFirstHit  ) ;
+				// const lcio::TrackState* tsL1 = trk1->getTrackState( lcio::TrackState::AtLastHit  ) ;
+
+				// --- get three hits per track : first last midddle
+
+				unsigned nhit0 = trk0.trackerHits_size() ;
+				unsigned nhit1 = trk1.trackerHits_size() ;
+
+				edm4hep::ConstTrackerHit thf0 = trk0.getTrackerHits( 0 ) ;
+				edm4hep::ConstTrackerHit thf1 = trk1.getTrackerHits( 0 ) ;
+
+				edm4hep::ConstTrackerHit thl0 = trk0.getTrackerHits( nhit0 - 1 ) ;
+				edm4hep::ConstTrackerHit thl1 = trk1.getTrackerHits( nhit1 - 1 ) ;
+
+				// lcio::TrackerHit* thm1 = trk1->getTrackerHits()[ nhit1 / 2 ] ;
+				// lcio::TrackerHit* thm0 = trk0->getTrackerHits()[ nhit0 / 2 ] ;
+
+				// int lthf0 = ILD_cellID(  thf0 )[ lcio::ILDCellID0::layer ] ;
+				// int lthf1 = ILD_cellID(  thf1 )[ lcio::ILDCellID0::layer ] ;
+
+				// int lthl0 = ILD_cellID(  thl0 )[ lcio::ILDCellID0::layer ] ;
+				// int lthl1 = ILD_cellID(  thl1 )[ lcio::ILDCellID0::layer ] ;
+				int lthf0 = getLayer(thf0);
+				int lthf1 = getLayer(thf1);
+				int lthl0 = getLayer(thl0);
+				int lthl1 = getLayer(thl1);
+
+				//      if( lthf0 <= lthl1 && lthf1 <= lthl0 )   return false ;
+
+				// allow the track segements to overlap slightly  - FIXME: make a parameter ...
+				const int overlapRows = 4 ;
+				if( lthf0 + overlapRows <= lthl1 && lthf1  + overlapRows  <= lthl0 )   return false ;
+
+				// now we take the larger segment and see if we can add the three hits from the other segment...
+
+				edm4hep::ConstTrack trk = ( nhit0 > nhit1 ? trk0 :  trk1 ) ;
+				edm4hep::ConstTrack oth = ( nhit0 > nhit1 ? trk1 :  trk0 ) ;
+
+				bool  outward = ( nhit0 > nhit1  ?  lthl0 <= lthf1 + overlapRows :  lthl1 <= lthf0 + overlapRows ) ;
+
+				unsigned n = oth.trackerHits_size() ;
+
+				edm4hep::ConstTrackerHit th0 =  ( outward ? oth.getTrackerHits(0) : oth.getTrackerHits(n-1) ) ;
+				edm4hep::ConstTrackerHit th1 =              (oth.getTrackerHits(n/2) );
+				edm4hep::ConstTrackerHit th2 =  ( outward ? oth.getTrackerHits(n-1) : oth.getTrackerHits(0) );
+
+
+				// track state at last hit migyt be rubish....
+				//      const lcio::TrackState* ts = ( outward ? trk->getTrackState( lcio::TrackState::AtLastHit  ) : trk->getTrackState( lcio::TrackState::AtFirstHit  ) ) ;
+				const edm4hep::TrackState ts = getTrackStateAt(trk, lcio::TrackState::AtFirstHit  )  ;
+
+
+
+				// FIXME Mingrui debug
+				// streamlog_out( DEBUG3 ) << " *******  TrackSegmentMerger : will extrapolate track " << ( outward ? " outwards\t" : " inwards\t" )
+				//<<  lcio::lcshort( trk  ) << "     vs:  [" <<   std::hex << oth->id() << std::dec << "]"  << std::endl ;
+
+				// if( trk->id() == 0x0004534d &&  oth->id() == 0x000454a6 ){
+				// 	streamlog_out( DEBUG3 )  << " &&&&&&&&&&&&&& Track 1 : \n" << *trk
+				// 				 << " &&&&&&&&&&&&&& Track 2 : \n" << *oth
+				// 				 <<  std::endl ;
+				// }
+
+				std::auto_ptr<MarlinTrk::IMarlinTrack> mTrk( _trksystem->createTrack()  ) ;
+
+				int nHit = trk.trackerHits_size() ;
+
+				if( nHit == 0 /*|| ts ==0*/ ) // FIXME Mingrui Since it is no pointer
+					return false ;
+
+				// float initial_chi2 = trk->getChi2() ;
+				// float initial_ndf  = trk->getNdf() ;
+
+				// FIXME Mingrui debug
+				// streamlog_out( DEBUG3  )  << "               -- extrapolate TrackState : " << lcshort( ts )    << std::endl ;
+
+				edm4hep::ConstTrackerHit ht = trk.getTrackerHits(0); 
+				//need to add a dummy hit to the track
+				mTrk->addHit( ht ) ;  // is this the right hit ??????????
+
+				mTrk->initialise( ts ,  _b ,  MarlinTrk::IMarlinTrack::backward ) ;
+
+				double deltaChi ;
+				int addHit = 0 ;
+
+				//-----   now try to add the three hits : ----------------
+				addHit = mTrk->addAndFit(  th0 , deltaChi, _chi2Max ) ;
+
+				/*
+				 * FIXME Mingrui debug
+				 streamlog_out( DEBUG3 ) << "    ****  adding first hit : " <<  gear::Vector3D( th0->getPosition() )
+				 << "         added : " << MarlinTrk::errorCode( addHit )
+				 << "         deltaChi2: " << deltaChi
+				 << std::endl ;
+				 */
+
+				if( addHit !=  MarlinTrk::IMarlinTrack::success ) return false ;
+
+				//---------------------
+				addHit = mTrk->addAndFit(  th1 , deltaChi, _chi2Max ) ;
+
+				/* FIXME Mingrui debug
+				   streamlog_out( DEBUG3 ) << "    ****  adding second hit : " <<  gear::Vector3D( th1->getPosition() )
+				   << "         added : " << MarlinTrk::errorCode( addHit )
+				   << "         deltaChi2: " << deltaChi
+				   << std::endl ;
+				   */
+
+				if( addHit !=  MarlinTrk::IMarlinTrack::success ) return false ;
+
+				//--------------------
+				addHit = mTrk->addAndFit(  th2 , deltaChi, _chi2Max ) ;
+
+				/* FIXME Mingrui debug
+				   streamlog_out( DEBUG3 ) << "    ****  adding third hit : " <<  gear::Vector3D( th2->getPosition() )
+				   << "         added : " << MarlinTrk::errorCode( addHit )
+				   << "         deltaChi2: " << deltaChi
+				   << std::endl ;
+				   */
+
+				if( addHit !=  MarlinTrk::IMarlinTrack::success ) return false ;
+
+
+
+				////////////
+				return true ;
+				///////////
+			}
+
+	};
+	//=======================================================================================
+
+	/** helper class for merging track segments, based on circle (and tan lambda) */
+
+	class TrackCircleDistance{
+
+		public:
+			/** C'tor takes merge distance */
+			TrackCircleDistance(float dCut) : _dCutSquared( dCut*dCut ) , _dCut(dCut){}
+
+			/** Merge condition: ... */
+			bool operator() ( nnclu::Element<ClupaPlcioConstTrack>* h0, nnclu::Element<ClupaPlcioConstTrack>* h1);
+  protected:
+    float _dCutSquared ;
+    float _dCut ;
+			//=======================================================================================
+	};
+
+	struct TrackZSort {  // sort tracks wtr to abs(z_average )
+		bool operator()( edm4hep::ConstTrack l, edm4hep::ConstTrack r);
+	};
+
+
+	//=======================================================================================
+
+	/** Helper class that creates an Elements for an LCOjects of type T.
+	*/
+	template <class T>
+		struct MakePLCIOElement{
+			nnclu::Element<T>*  operator()( T* o) { return new nnclu::Element<T>(o) ;  }
+		} ;
+
+
+	//=======================================================================================
+
+	class Timer{
+		public:
+			Timer(){
+				_clocks.reserve( 100 ) ;
+				_names.reserve( 100 ) ;
+
+				_clocks.push_back(0) ;
+				_names.push_back(  "start"  ) ;
+			}
+			unsigned registerTimer( const std::string& name ){
+				_clocks.push_back(0) ;
+				_names.push_back( name ) ;
+				return _clocks.size() - 1 ;
+			}
+
+			void time(unsigned index){
+				_clocks[ index ] = clock() ;
+			}
+			void start() { time(0) ; }
+
+
+			std::string toString(){
+
+				std::stringstream s ;
+
+				s << " ============= Timer ================================ "  << std::endl ;
+				unsigned N=_clocks.size() ;
+				for( unsigned i=1 ;  i < N ; ++i){
+					s << "    " << _names[i] << " : " <<  double(_clocks[i] - _clocks[i-1] ) / double(CLOCKS_PER_SEC) << std::endl ;
+				}
+				s << "         Total  : " <<  double(_clocks[N-1] - _clocks[0] ) / double(CLOCKS_PER_SEC) << std::endl ;
+				s << " ==================================================== "  << std::endl ;
+
+				return s.str() ;
+			}
+		protected:
+			std::vector< clock_t> _clocks ;
+			std::vector< std::string > _names ;
+	};
+
+
+}
+#endif