std::set<PHG4VtxPoint*> SvtxVertexEval::all_truth_points(SvtxVertex* vertex) {

 if ((_do_cache) && (_cache_all_truth_points.find(vertex) !=
		     _cache_all_truth_points.end())) {
    return _cache_all_truth_points[vertex];
  }
  
  PHG4TruthInfoContainer* truthinfo = findNode::getClass<PHG4TruthInfoContainer>(_topNode,"G4TruthInfo");
  if (!truthinfo) {
    cerr << PHWHERE << " ERROR: Can't find G4TruthInfo" << endl;
    exit(-1);
  }

  std::set<PHG4VtxPoint*> points;
  
  std::set<PHG4Particle*> particles = all_truth_particles(vertex);
  for (std::set<PHG4Particle*>::iterator iter = particles.begin();
       iter != particles.end();
       ++iter) {
    PHG4Particle* particle = *iter;

    // only consider primary particles
    PHG4TruthInfoContainer::Map primarymap = truthinfo->GetPrimaryMap();
    if (primarymap.find(particle->get_track_id()) == primarymap.end()) continue;
        
    points.insert(truthinfo->GetPrimaryVtx(particle->get_vtx_id()));
  }

  if (_do_cache) _cache_all_truth_points.insert(make_pair(vertex,points));
  
  return points;
}
// overlap calculations
unsigned int SvtxVertexEval::get_ntracks_contribution(SvtxVertex* vertex, PHG4VtxPoint* truthpoint) {

  if ((_do_cache) && (_cache_get_ntracks_contribution.find(make_pair(vertex,truthpoint)) !=
		      _cache_get_ntracks_contribution.end())) {
    return _cache_get_ntracks_contribution[make_pair(vertex,truthpoint)];
  }
  
  SvtxTrackMap* trackmap = findNode::getClass<SvtxTrackMap>(_topNode,"SvtxTrackMap");
  if (!trackmap) {
    cerr << PHWHERE << " ERROR: Can't find SvtxTrackMap" << endl;
    exit(-1);
  }

  PHG4TruthInfoContainer* truthinfo = findNode::getClass<PHG4TruthInfoContainer>(_topNode,"G4TruthInfo");
  if (!truthinfo) {
    cerr << PHWHERE << " ERROR: Can't find G4TruthInfo" << endl;
    exit(-1);
  }
  
  unsigned int ntracks = 0;

  for (SvtxVertex::TrackIter iter = vertex->begin_tracks();
       iter != vertex->end_tracks();
       ++iter) {
    
    SvtxTrack* track = trackmap->get(*iter);
    PHG4Particle* particle = _trackeval.max_truth_particle_by_nclusters(track);

    // only consider primary particles
    PHG4TruthInfoContainer::Map primarymap = truthinfo->GetPrimaryMap();
    if (primarymap.find(particle->get_track_id()) == primarymap.end()) continue;

    PHG4VtxPoint* candidate = truthinfo->GetPrimaryVtx(particle->get_vtx_id());
    if (candidate->get_id() == truthpoint->get_id()) ++ntracks;
  }
  
  if (_do_cache) _cache_get_ntracks_contribution.insert(make_pair(make_pair(vertex,truthpoint),ntracks));
  
  return ntracks;
}
int MomentumEvaluator::process_event( PHCompositeNode *topNode )
{
	PHG4TruthInfoContainer* truthinfo = findNode::getClass<PHG4TruthInfoContainer>(topNode,"G4TruthInfo");

	PHG4HitContainer* g4hits = findNode::getClass<PHG4HitContainer>(topNode,"G4HIT_SVTX");
	if(g4hits == nullptr){cout<<"can't find PHG4HitContainer"<<endl;exit(1);}
	PHG4HitContainer::ConstRange g4range = g4hits->getHits();

	// set<int> trkids;
	map<int, pair<unsigned int,unsigned int> > trkids;

	for( PHG4HitContainer::ConstIterator iter = g4range.first; iter != g4range.second; ++iter )
	{
		PHG4Hit* hit = iter->second;

		int layer = hit->get_layer();
		float length = outer_z_length;
		if(((unsigned int)layer)<n_inner_layers){length=inner_z_length;}
		if(fabs(hit->get_z(0))>length){continue;}

		int trk_id = hit->get_trkid();
		if(trkids.find(trk_id) == trkids.end())
		{
			trkids[trk_id].first = 0;
			trkids[trk_id].second = 0;
		}
		if( hit->get_layer() < 32 )
		{
			trkids[trk_id].first = (trkids[trk_id].first | (1<<(hit->get_layer())));
		}
		else
		{
			trkids[trk_id].second = (trkids[trk_id].second | (1<<(hit->get_layer()-32)));
		}
		
		// cout<<"trk_id = "<<trk_id<<endl;
		// cout<<"layer = "<<hit->get_layer()<<endl;
		// cout<<"nlayer = "<<__builtin_popcount(trkids[trk_id].first)+__builtin_popcount(trkids[trk_id].second)<<endl<<endl;
		// trkids.insert(trk_id);
	}


	SvtxTrackMap* trackmap = findNode::getClass<SvtxTrackMap>(topNode,"SvtxTrackMap");

	PHG4VtxPoint *gvertex = truthinfo->GetPrimaryVtx( truthinfo->GetPrimaryVertexIndex() );
	float gvx = gvertex->get_x();
	float gvy = gvertex->get_y();
	float gvz = gvertex->get_z();

	RecursiveMomentumContainer true_sorted( -20., 20., -20., 20., -20., 20., 10 );

	// PHG4TruthInfoContainer::Map primarymap = truthinfo->GetPrimaryMap();
	PHG4TruthInfoContainer::Map primarymap = truthinfo->GetMap();
   for(PHG4TruthInfoContainer::Iterator iter = primarymap.begin();iter != primarymap.end();++iter)
   {
   	PHG4Particle *particle = iter->second;

   	float vx = truthinfo->GetVtx(particle->get_vtx_id())->get_x();
   	float vy = truthinfo->GetVtx(particle->get_vtx_id())->get_y();
   	float vz = truthinfo->GetVtx(particle->get_vtx_id())->get_z();
   	
   	TrivialTrack track( particle->get_px(), particle->get_py(), particle->get_pz(), vx-gvx, vy-gvy, vz-gvz );

   	if( ( (track.px * track.px) + (track.py * track.py) ) < (0.1*0.1) ){continue;}

   	if( trkids.find(particle->get_track_id()) == trkids.end() )
   	{
   		continue;
   	}

   	// cout<<"trk, nhits = "<<particle->get_track_id()<<" "<<__builtin_popcount(trkids[particle->get_track_id()].first)+__builtin_popcount(trkids[particle->get_track_id()].second)<<endl;

   	if( __builtin_popcount(trkids[particle->get_track_id()].first)+__builtin_popcount(trkids[particle->get_track_id()].second) < (int)n_required_layers )
   	{
   		continue;
   	}

   	true_sorted.insert( track );
   }


   RecursiveMomentumContainer reco_sorted( -20., 20., -20., 20., -20., 20., 10 );
   for(SvtxTrackMap::Iter iter = trackmap->begin();iter != trackmap->end();++iter)
   {
   	SvtxTrack* track = iter->second;

   	TrivialTrack ttrack( track->get_px(), track->get_py(), track->get_pz(), track->get_x()-gvx, track->get_y()-gvy, track->get_z()-gvz, track->get_quality() );
   	reco_sorted.insert(ttrack);
   }


   TrivialTrack* t_track = true_sorted.begin();
   vector<TrivialTrack*> pointer_list;
   while(t_track != nullptr)
   {
   	pointer_list.clear();

   	float pt = sqrt((t_track->px * t_track->px) + (t_track->py * t_track->py));
   	float pt_diff = pt*pt_search_scale;
   	float px_lo = t_track->px - pt_diff;
   	float px_hi = t_track->px + pt_diff;
   	float py_lo = t_track->py - pt_diff;
   	float py_hi = t_track->py + pt_diff;
   	float pz_diff = fabs( t_track->pz )*pz_search_scale;
   	float pz_lo = t_track->pz - pz_diff;
   	float pz_hi = t_track->pz + pz_diff;

   	reco_sorted.append_list( pointer_list, px_lo,px_hi, py_lo,py_hi, pz_lo,pz_hi );

   	if(pointer_list.size() > 0)
   	{
   		float mom_true = sqrt(pt*pt + (t_track->pz)*(t_track->pz));
   		float best_ind = 0;
   		float mom_reco = sqrt( (pointer_list[0]->px)*(pointer_list[0]->px) + (pointer_list[0]->py)*(pointer_list[0]->py) + (pointer_list[0]->pz)*(pointer_list[0]->pz) );
   		float best_mom = mom_reco;
   		for(unsigned int i=1;i<pointer_list.size();++i)
   		{
   			mom_reco = sqrt( (pointer_list[i]->px)*(pointer_list[i]->px) + (pointer_list[i]->py)*(pointer_list[i]->py) + (pointer_list[i]->pz)*(pointer_list[i]->pz) );
   			if( fabs( mom_true - mom_reco ) < fabs( mom_true - best_mom )  )
   			{
   				best_mom = mom_reco;
   				best_ind = i;
   			}
   		}
   		
   		float ntp_data[14] = { (float) event_counter, t_track->px, t_track->py, t_track->pz, t_track->dcax, t_track->dcay, t_track->dcaz, pointer_list[best_ind]->px, pointer_list[best_ind]->py, pointer_list[best_ind]->pz, pointer_list[best_ind]->dcax, pointer_list[best_ind]->dcay, pointer_list[best_ind]->dcaz, pointer_list[best_ind]->quality };
   		ntp_true->Fill(ntp_data);
   	}
   	else
   	{
   		float ntp_data[14] = { (float) event_counter, t_track->px, t_track->py, t_track->pz, t_track->dcax, t_track->dcay, t_track->dcaz, -9999.,-9999.,-9999.,-9999.,-9999.,-9999., -9999. };
   		ntp_true->Fill(ntp_data);
   	}

   	t_track = true_sorted.next();
   }

   TrivialTrack* r_track = reco_sorted.begin();
   while(r_track != nullptr)
   {
   	pointer_list.clear();

   	float pt = sqrt((r_track->px * r_track->px) + (r_track->py * r_track->py));
   	float pt_diff = pt*pt_search_scale;
   	float px_lo = r_track->px - pt_diff;
   	float px_hi = r_track->px + pt_diff;
   	float py_lo = r_track->py - pt_diff;
   	float py_hi = r_track->py + pt_diff;
   	float pz_diff = fabs( r_track->pz )*pz_search_scale;
   	float pz_lo = r_track->pz - pz_diff;
   	float pz_hi = r_track->pz + pz_diff;

   	true_sorted.append_list( pointer_list, px_lo,px_hi, py_lo,py_hi, pz_lo,pz_hi );

   	if(pointer_list.size() > 0)
   	{
   		float mom_reco = sqrt(pt*pt + (r_track->pz)*(r_track->pz));
   		float best_ind = 0;
   		float mom_true = sqrt( (pointer_list[0]->px)*(pointer_list[0]->px) + (pointer_list[0]->py)*(pointer_list[0]->py) + (pointer_list[0]->pz)*(pointer_list[0]->pz) );
   		float best_mom = mom_true;
   		for(unsigned int i=1;i<pointer_list.size();++i)
   		{
   			mom_true = sqrt( (pointer_list[i]->px)*(pointer_list[i]->px) + (pointer_list[i]->py)*(pointer_list[i]->py) + (pointer_list[i]->pz)*(pointer_list[i]->pz) );
   			if( fabs( mom_reco - mom_true ) < fabs( mom_reco - best_mom )  )
   			{
   				best_mom = mom_true;
   				best_ind = i;
   			}
   		}
   		
   		float ntp_data[14] = { (float) event_counter, r_track->px, r_track->py, r_track->pz, r_track->dcax, r_track->dcay, r_track->dcaz, pointer_list[best_ind]->px, pointer_list[best_ind]->py, pointer_list[best_ind]->pz, pointer_list[best_ind]->dcax, pointer_list[best_ind]->dcay, pointer_list[best_ind]->dcaz, r_track->quality };
   		ntp_reco->Fill(ntp_data);
   	}
   	else
   	{
   		float ntp_data[14] = { (float) event_counter, r_track->px, r_track->py, r_track->pz, r_track->dcax, r_track->dcay, r_track->dcaz, -9999.,-9999.,-9999.,-9999.,-9999.,-9999., r_track->quality };
   		ntp_reco->Fill(ntp_data);
   	}

   	r_track = reco_sorted.next();
   }


   event_counter += 1;
   return Fun4AllReturnCodes::EVENT_OK;
}