    // @(#)root/tree:$Name:  $:$Id: TTreeIndex.cxx,v 1.5 2004/07/09 16:19:28 brun Exp $
    // Author: Rene Brun   05/07/2004

    /*************************************************************************
     * Copyright (C) 1995-2004, Rene Brun and Fons Rademakers.               *
     * All rights reserved.                                                  *
     *                                                                       *
     * For the licensing terms see $ROOTSYS/LICENSE.                         *
     * For the list of contributors see $ROOTSYS/README/CREDITS.             *
     *************************************************************************/

    //////////////////////////////////////////////////////////////////////////
    //                                                                      //
    // A Tree Index with majorname and minorname.                           //
    //                                                                      //
    //////////////////////////////////////////////////////////////////////////

    #include "TTreeIndex.h"
    #include "TTree.h"

    ClassImp(TTreeIndex)

    //______________________________________________________________________________
     TTreeIndex::TTreeIndex(): TVirtualIndex()
    {
    // Default constructor for TTreeIndex

       fTree               = 0;
       fN                  = 0;
       fIndexValues        = 0;
       fIndex              = 0;
       fMajorFormula       = 0;
       fMinorFormula       = 0;
       fMajorFormulaParent = 0;
       fMinorFormulaParent = 0;
    }

    //______________________________________________________________________________
     TTreeIndex::TTreeIndex(const TTree *T, const char *majorname, const char *minorname)
               : TVirtualIndex()
    {
       // Normal constructor for TTreeIndex
       //
       // Build an index table using the leaves of Tree T with  major & minor names
       // The index is built with the expressions given in "majorname" and "minorname".
       //
       // a Long64_t array fIndexValues is built with:
       //    major = the value of majorname converted to an integer
       //    minor = the value of minorname converted to an integer
       //    fIndexValues[i] = major<<31 + minor
       // This array is sorted. The sorted fIndex[i] contains the serial number
       // in the Tree corresponding to the pair "major,minor" in fIndexvalues[i].
       //
       //  Once the index is computed, one can retrieve one entry via
       //    T->GetEntryWithIndex(majornumber, minornumber)
       // Example:
       //  tree.BuildIndex("Run","Event"); //creates an index using leaves Run and Event
       //  tree.GetEntryWithIndex(1234,56789); //reads entry corresponding to
       //                                        Run=1234 and Event=56789
       //
       // Note that majorname and minorname may be expressions using original
       // Tree variables eg: "run-90000", "event +3*xx". However the result
       // must be integer.
       // In case an expression is specified, the equivalent expression must be computed
       // when calling GetEntryWithIndex.
       //
       // To build an index with only majorname, specify minorname="0" (default)
       //
       //    TreeIndex and Friend Trees
       //    ---------------------------
       // Assuming a parent Tree T and a friend Tree TF, the following cases are supported:
       // CASE 1: T->GetEntry(entry) is called
       //         In this case, the serial number entry is used to retrieve
       //         the data in both Trees.
       // CASE 2: T->GetEntry(entry) is called, TF has a TreeIndex
       //         the expressions given in major/minorname of TF are used
       //         to compute the value pair major,minor with the data in T.
       //         TF->GetEntry(major,minor) is then called (tricky case!)
       // CASE 3: T->GetEntry(major,minor) is called.
       //         It is assumed that both T and TF have a TreeIndex built using
       //         the same major and minor name.
       //
       //    Saving the TreeIndex
       //    --------------------
       // Once the index is built, it can be saved with the TTree object
       // with tree.Write(); (if the file has been open in "update" mode).
       //
       // The most convenient place to create the index is at the end of
       // the filling process just before saving the Tree header.
       // If a previous index was computed, it is redefined by this new call.
       //
       // Note that this function can also be applied to a TChain.
       //
       // The return value is the number of entries in the Index (< 0 indicates failure)
       //
       // It is possible to play with different TreeIndex in the same Tree.
       // see comments in TTree::SetTreeIndex.

       fTree               = (TTree*)T;
       fN                  = 0;
       fIndexValues        = 0;
       fIndex              = 0;
       fMajorFormula       = 0;
       fMinorFormula       = 0;
       fMajorFormulaParent = 0;
       fMinorFormulaParent = 0;
       fMajorName          = majorname;
       fMinorName          = minorname;
       if (!T) return;
       fN = (Long64_t)T->GetEntries();
       if (fN <= 0) {
          MakeZombie();
          Error("TreeIndex","Cannot build a TreeIndex with a Tree having no entries");
          return;
       }

       GetMajorFormula();
       GetMinorFormula();
       if (!fMajorFormula || !fMinorFormula) {
          MakeZombie();
          Error("TreeIndex","Cannot build the index with major=%s, minor=%s",fMajorName.Data(), fMinorName.Data());
          return;
       }
       if ((fMajorFormula->GetNdim() != 1) || (fMinorFormula->GetNdim() != 1)) {
          MakeZombie();
          Error("TreeIndex","Cannot build the index with major=%s, minor=%s",fMajorName.Data(), fMinorName.Data());
          return;
       }
       if ((fMajorFormula->GetMultiplicity() != 0) || (fMinorFormula->GetMultiplicity() != 0)) {
          MakeZombie();
          Error("TreeIndex","Cannot build the index with major=%s, minor=%s that cannot be arrays",fMajorName.Data(), fMinorName.Data());
          return;
       }

       Long64_t *w = new Long64_t[fN];
       Long64_t i;
       for (i=0;i<fN;i++) {
          GetEntry(i);
          Double_t majord = fMajorFormula->EvalInstance();
          Double_t minord = fMinorFormula->EvalInstance();
          Long64_t majorv = (Long64_t)majord;
          Long64_t minorv = (Long64_t)minord;
          w[i]  = majorv<<31;
          w[i] += minorv;
       }
       fIndex = new Long64_t[fN];
       TMath::Sort(fN,w,fIndex,0);
       fIndexValues = new Long64_t[fN];
       for (i=0;i<fN;i++) {
          fIndexValues[i] = w[fIndex[i]];
       }

       delete [] w;
    }

    //______________________________________________________________________________
     TTreeIndex::~TTreeIndex()
    {
       if (fTree && fTree->GetTreeIndex() == this) fTree->SetTreeIndex(0);
       delete [] fIndexValues;      fIndexValues = 0;
       delete [] fIndex;            fIndex = 0;
       delete fMajorFormula;        fMajorFormula  = 0;
       delete fMinorFormula;        fMinorFormula  = 0;
       delete fMajorFormulaParent;  fMajorFormulaParent = 0;
       delete fMinorFormulaParent;  fMinorFormulaParent = 0;
    }

    //______________________________________________________________________________
     Int_t TTreeIndex::GetEntry(Long64_t entry) {
       // Read in memory the branches referenced in major and minorname

       if (!fTree) return 0;
       TLeaf *leaf;
       Int_t i;
       Int_t nbytes = 0;
       for (i=0;i<10;i++) {
          if (!fMajorFormula) break;
          leaf = fMajorFormula->GetLeaf(i);
          if (!leaf) break;
          nbytes += leaf->GetBranch()->GetEntry(entry);
       }
       for (i=0;i<10;i++) {
          if (!fMinorFormula) break;
          leaf = fMinorFormula->GetLeaf(i);
          if (!leaf) break;
          nbytes += leaf->GetBranch()->GetEntry(entry);
       }
       return nbytes;
    }

    //______________________________________________________________________________
     Int_t TTreeIndex::GetEntryNumberFriend(const TTree *T)
    {
    // returns the entry number in this friend Tree corresponding to entry in
    // the master Tree T.
    // In case this friend Tree and T do not share an index with the same
    // major and minor name, the entry serial number in the friend tree
    // and in the master Tree are assumed to be the same

       if (!T) return -3;
       GetMajorFormulaParent(T);
       GetMinorFormulaParent(T);
       if (!fMajorFormulaParent || !fMinorFormulaParent) return -1;
       if (!fMajorFormulaParent->GetNdim() || !fMinorFormulaParent->GetNdim()) {
          // The Tree Index in the friend has a pair majorname,minorname
          // not available in the parent Tree T.
          // if the friend Tree has less entries than the parent, this is an error
          Int_t pentry = T->GetReadEntry();
          if (pentry >= (Int_t)fTree->GetEntries()) return -2;
          // otherwise we ignore the Tree Index and return the entry number
          // in the parent Tree.
          return pentry;
       }

       // majorname, minorname exist in the parent Tree
       // we find the current values pair majorv,minorv in the parent Tree
       Double_t majord = fMajorFormulaParent->EvalInstance();
       Double_t minord = fMinorFormulaParent->EvalInstance();
       Long64_t majorv = (Long64_t)majord;
       Long64_t minorv = (Long64_t)minord;
       // we check if this pair exist in the index.
       // if yes, we return the corresponding entry number
       // if not the function returns -1
       return fTree->GetEntryNumberWithIndex(majorv,minorv);
    }

    //______________________________________________________________________________
     Long64_t TTreeIndex::GetEntryNumberWithBestIndex(Int_t major, Int_t minor) const
    {
    // Return entry number corresponding to major and minor number
    // Note that this function returns only the entry number, not the data
    // To read the data corresponding to an entry number, use TTree::GetEntryWithIndex
    // the BuildIndex function has created a table of Double_t* of sorted values
    // corresponding to val = major + minor*1e-9;
    // The function performs binary search in this sorted table.
    // If it finds a pair that maches val, it returns directly the
    // index in the table.
    // If an entry corresponding to major and minor is not found, the function
    // returns the index of the major,minor pair immediatly lower than the
    // requested value, ie it will return -1 if the pair is lower than
    // the first entry in the index.
    //
    // See also GetEntryNumberWithIndex

       if (fN == 0) return -1;
       Long64_t value = Long64_t(major)<<31;
       value += minor;
       Int_t i = TMath::BinarySearch(fN, fIndexValues, value);
       if (i < 0) return -1;
       return fIndex[i];
    }

    //______________________________________________________________________________
     Long64_t TTreeIndex::GetEntryNumberWithIndex(Int_t major, Int_t minor) const
    {
    // Return entry number corresponding to major and minor number
    // Note that this function returns only the entry number, not the data
    // To read the data corresponding to an entry number, use TTree::GetEntryWithIndex
    // the BuildIndex function has created a table of Double_t* of sorted values
    // corresponding to val = major + minor*1e-9;
    // The function performs binary search in this sorted table.
    // If it finds a pair that maches val, it returns directly the
    // index in the table, otherwise it returns -1.
    //
    // See also GetEntryNumberWithBestIndex

       if (fN == 0) return -1;
       Long64_t value = Long64_t(major)<<31;
       value += minor;
       Int_t i = TMath::BinarySearch(fN, fIndexValues, value);
       if (i < 0) return -1;
       if (TMath::Abs(fIndexValues[i] - value) > 1.e-10) return -1;
       return fIndex[i];
    }

    //______________________________________________________________________________
     TTreeFormula *TTreeIndex::GetMajorFormula()
    {
       // return a pointer to the TreeFormula corresponding to the majorname

       if (!fMajorFormula) fMajorFormula = new TTreeFormula("Major",fMajorName.Data(),fTree);
       return fMajorFormula;
    }

    //______________________________________________________________________________
     TTreeFormula *TTreeIndex::GetMinorFormula()
    {
       // return a pointer to the TreeFormula corresponding to the minorname

       if (!fMinorFormula) fMinorFormula = new TTreeFormula("Minor",fMinorName.Data(),fTree);
       return fMinorFormula;
    }

    //______________________________________________________________________________
     TTreeFormula *TTreeIndex::GetMajorFormulaParent(const TTree *T)
    {
       // return a pointer to the TreeFormula corresponding to the majorname in parent tree T

       if (!fMajorFormulaParent) fMajorFormulaParent = new TTreeFormula("MajorP",fMajorName.Data(),(TTree*)T);
       return fMajorFormulaParent;
    }

    //______________________________________________________________________________
     TTreeFormula *TTreeIndex::GetMinorFormulaParent(const TTree *T)
    {
       // return a pointer to the TreeFormula corresponding to the minorname in parent tree T

       if (!fMinorFormulaParent) fMinorFormulaParent = new TTreeFormula("MinorP",fMinorName.Data(),(TTree*)T);
       return fMinorFormulaParent;
    }

    //______________________________________________________________________________
     void TTreeIndex::Print(Option_t * option) const
    {
       // print the table with : serial number, majorname, minorname
       // if option = "10" print only the first 10 entries
       // if option = "100" print only the first 100 entries
       // if option = "1000" print only the first 1000 entries

       TString opt = option;
       Long64_t n = fN;
       if (opt.Contains("10"))   n = 10;
       if (opt.Contains("100"))  n = 100;
       if (opt.Contains("1000")) n = 1000;

       printf("\n**********************************************\n");
       printf("*    Index of Tree: %s/%s\n",fTree->GetName(),fTree->GetTitle());
       printf("**********************************************\n");
       printf("%8s : %16s : %16s\n","serial",fMajorName.Data(),fMinorName.Data());
       printf("**********************************************\n");
       for (Long64_t i=0;i<n;i++) {
          Long64_t minor = fIndexValues[i] & 0xffff;
          Long64_t major = fIndexValues[i]>>31;
          printf("%8lld :         %8lld :         %8lld\n",i,major,minor);
       }
    }

    //______________________________________________________________________________
     void TTreeIndex::Streamer(TBuffer &R__b)
    {
       // Stream an object of class TTreeIndex.
       // Note that this Streamer should be changed to an automatic Streamer
       // once TStreamerInfo supports an index of type Long64_t

       UInt_t R__s, R__c;
       if (R__b.IsReading()) {
          Version_t R__v = R__b.ReadVersion(&R__s, &R__c); if (R__v) { }
          TVirtualIndex::Streamer(R__b);
          fMajorName.Streamer(R__b);
          fMinorName.Streamer(R__b);
          R__b >> fN;
          fIndexValues = new Long64_t[fN];
          R__b.ReadFastArray(fIndexValues,fN);
          fIndex      = new Long64_t[fN];
          R__b.ReadFastArray(fIndex,fN);
          R__b.CheckByteCount(R__s, R__c, TTreeIndex::IsA());
       } else {
          R__c = R__b.WriteVersion(TTreeIndex::IsA(), kTRUE);
          TVirtualIndex::Streamer(R__b);
          fMajorName.Streamer(R__b);
          fMinorName.Streamer(R__b);
          R__b << fN;
          R__b.WriteFastArray(fIndexValues, fN);
          R__b.WriteFastArray(fIndex, fN);
          R__b.SetByteCount(R__c, kTRUE);
       }
    }

    //______________________________________________________________________________
     void TTreeIndex::SetTree(const TTree *T)
    {
       // this function is called by TChain::LoadTree and TTreePlayer::UpdateFormulaLeaves
       // when a new Tree is loaded.
       // Because Trees in a TChain may have a different list of leaves, one
       // must update the leaves numbers in the TTreeFormula used by the TreeIndex.

       fTree = (TTree*)T;
       if (fMajorFormula)       {fMajorFormula->SetTree(fTree);        fMajorFormula->UpdateFormulaLeaves();}
       if (fMinorFormula)       {fMinorFormula->SetTree(fTree);        fMinorFormula->UpdateFormulaLeaves();}
       if (fMajorFormulaParent) {fMajorFormulaParent->SetTree(fTree);  fMajorFormulaParent->UpdateFormulaLeaves();}
       if (fMinorFormulaParent) {fMinorFormulaParent->SetTree(fTree);  fMinorFormulaParent->UpdateFormulaLeaves();}
    }