@@ -39,6 +39,7 @@ StepTHn::StepTHn() : mNBins(0),
3939 mNbinsCache(nullptr ),
4040 mLastVars(nullptr ),
4141 mLastBins(nullptr ),
42+ mLookup(nullptr ),
4243 mPrototype(nullptr )
4344{
4445 // Default constructor (for streaming)
@@ -55,6 +56,7 @@ StepTHn::StepTHn(const Char_t* name, const Char_t* title, const Int_t nSteps, co
5556 mNbinsCache(nullptr ),
5657 mLastVars(nullptr ),
5758 mLastBins(nullptr ),
59+ mLookup(nullptr ),
5860 mPrototype(nullptr )
5961{
6062 // Constructor
@@ -130,6 +132,7 @@ StepTHn::StepTHn(const StepTHn& c) : mNBins(c.mNBins),
130132 mNbinsCache(nullptr ),
131133 mLastVars(nullptr ),
132134 mLastBins(nullptr ),
135+ mLookup(nullptr ),
133136 mPrototype(nullptr )
134137{
135138 //
@@ -152,6 +155,7 @@ StepTHn::~StepTHn()
152155 delete[] mNbinsCache ;
153156 delete[] mLastVars ;
154157 delete[] mLastBins ;
158+ delete[] mLookup ;
155159 delete mPrototype ;
156160}
157161
@@ -392,13 +396,35 @@ void StepTHn::Fill(int iStep, int nParams, double positionAndWeight[])
392396 LOGF (fatal, " Fill called with invalid number of parameters (%d vs %d)" , mNVars , nParams);
393397 }
394398
395- // fill axis cache
399+ // fill axis cache and build lookup tables on first call
396400 if (!mAxisCache ) {
397401 mAxisCache = new TAxis*[mNVars ];
398402 mNbinsCache = new Int_t[mNVars ];
403+ mLookup = new AxisLookup[mNVars ];
399404 for (Int_t i = 0 ; i < mNVars ; i++) {
400405 mAxisCache [i] = mPrototype ->GetAxis (i);
401406 mNbinsCache [i] = mAxisCache [i]->GetNbins ();
407+
408+ // Build lookup table for this axis
409+ auto & lut = mLookup [i];
410+ lut.nBins = mNbinsCache [i];
411+ lut.edges = mAxisCache [i]->GetXbins ()->GetArray ();
412+ lut.xmin = mAxisCache [i]->GetXmin ();
413+ lut.xmax = mAxisCache [i]->GetXmax ();
414+
415+ if (lut.edges && lut.xmax > lut.xmin ) {
416+ // Variable-width binning: build slot->bin mapping
417+ Double_t slotWidth = (lut.xmax - lut.xmin ) / kLookupSize ;
418+ lut.invSlotWidth = 1.0 / slotWidth;
419+ for (Int_t s = 0 ; s < kLookupSize ; s++) {
420+ Double_t x = lut.xmin + (s + 0.5 ) * slotWidth;
421+ lut.table [s] = mAxisCache [i]->FindBin (x);
422+ }
423+ } else {
424+ // Uniform binning or degenerate axis: direct formula, no table needed
425+ lut.edges = nullptr ;
426+ lut.invSlotWidth = lut.xmax > lut.xmin ? lut.nBins / (lut.xmax - lut.xmin ) : 0 ;
427+ }
402428 }
403429
404430 mLastVars = new Double_t[mNVars ];
@@ -420,11 +446,54 @@ void StepTHn::Fill(int iStep, int nParams, double positionAndWeight[])
420446 if (mLastVars [i] == positionAndWeight[i]) {
421447 tmpBin = mLastBins [i];
422448 } else {
423- tmpBin = mAxisCache [i]->FindBin (positionAndWeight[i]);
449+ const auto & lut = mLookup [i];
450+ Double_t x = positionAndWeight[i];
451+
452+ if (!lut.edges ) {
453+ // Uniform binning: direct computation.
454+ // Use floor instead of truncation to match TAxis::FindBin rounding,
455+ // then verify against the exact edge to handle values that land
456+ // precisely on a bin boundary (where float arithmetic can round
457+ // either way).
458+ Double_t rawBin = (x - lut.xmin ) * lut.invSlotWidth ;
459+ tmpBin = 1 + static_cast <Int_t>(std::floor (rawBin));
460+ if (tmpBin >= 1 && tmpBin <= lut.nBins ) {
461+ // Compute the exact upper edge of this bin the same way TAxis does:
462+ // edge = xmin + tmpBin * binWidth
463+ // If x is at or above it, move to the next bin.
464+ Double_t binWidth = (lut.xmax - lut.xmin ) / lut.nBins ;
465+ Double_t upperEdge = lut.xmin + tmpBin * binWidth;
466+ if (x >= upperEdge && tmpBin < lut.nBins ) {
467+ tmpBin++;
468+ }
469+ }
470+ if (tmpBin < 1 ) {
471+ tmpBin = 0 ; // underflow
472+ } else if (tmpBin > lut.nBins ) {
473+ tmpBin = lut.nBins + 1 ; // overflow
474+ }
475+ } else {
476+ // Variable-width binning: lookup table + refine
477+ Int_t slot = static_cast <Int_t>((x - lut.xmin ) * lut.invSlotWidth );
478+ if (slot < 0 || slot >= kLookupSize ) {
479+ tmpBin = (slot < 0 ) ? 0 : lut.nBins + 1 ; // under/overflow
480+ } else {
481+ tmpBin = lut.table [slot];
482+ // Refine: the lookup gives an approximate bin, check edges
483+ // Move left if x is below the lower edge of tmpBin
484+ while (tmpBin > 1 && x < lut.edges [tmpBin - 1 ]) {
485+ tmpBin--;
486+ }
487+ // Move right if x is at or above the upper edge of tmpBin
488+ while (tmpBin < lut.nBins && x >= lut.edges [tmpBin]) {
489+ tmpBin++;
490+ }
491+ }
492+ }
493+
424494 mLastBins [i] = tmpBin;
425- mLastVars [i] = positionAndWeight[i] ;
495+ mLastVars [i] = x ;
426496 }
427- // Printf("%d", tmpBin);
428497
429498 // under/overflow not supported
430499 if (tmpBin < 1 || tmpBin > mNbinsCache [i]) {
@@ -433,27 +502,9 @@ void StepTHn::Fill(int iStep, int nParams, double positionAndWeight[])
433502
434503 // bins start from 0 here
435504 bin += tmpBin - 1 ;
436- // Printf("%lld", bin);
437- }
438-
439- if (!mValues [iStep]) {
440- mValues [iStep] = createArray ();
441- LOGF (info, " Created values container for step %d" , iStep);
442505 }
443506
444- if (weight != 1 .) {
445- // initialize with already filled entries (which have been filled with weight == 1), in this case mSumw2 := mValues
446- if (!mSumw2 [iStep]) {
447- mSumw2 [iStep] = createArray (mValues [iStep]);
448- LOGF (info, " Created sumw2 container for step %d" , iStep);
449- }
450- }
451-
452- // TODO probably slow; add StepTHnT::add ?
453- mValues [iStep]->SetAt (mValues [iStep]->GetAt (bin) + weight, bin);
454- if (mSumw2 [iStep]) {
455- mSumw2 [iStep]->SetAt (mSumw2 [iStep]->GetAt (bin) + weight * weight, bin);
456- }
507+ updateBin (iStep, bin, weight);
457508}
458509
459510template class StepTHnT <TArrayF>;
0 commit comments