Added AddTrackParams() method for convenience + some comments
[u/mrichter/AliRoot.git] / ANALYSIS / AliTagAnalysis.cxx
1 /**************************************************************************
2  * Author: Panos Christakoglou.                                           *
3  * Contributors are mentioned in the code where appropriate.              *
4  *                                                                        *
5  * Permission to use, copy, modify and distribute this software and its   *
6  * documentation strictly for non-commercial purposes is hereby granted   *
7  * without fee, provided that the above copyright notice appears in all   *
8  * copies and that both the copyright notice and this permission notice   *
9  * appear in the supporting documentation. The authors make no claims     *
10  * about the suitability of this software for any purpose. It is          *
11  * provided "as is" without express or implied warranty.                  *
12  **************************************************************************/
13
14 /* $Id$ */
15
16 //-----------------------------------------------------------------
17 //           AliTagAnalysis class
18 //   This is the class to deal with the tag analysis
19 //   Origin: Panos Christakoglou, UOA-CERN, Panos.Christakoglou@cern.ch
20 //-----------------------------------------------------------------
21
22 //ROOT
23 #include <Riostream.h>
24 #include <TSystem.h>
25 #include <TChain.h>
26 #include <TFile.h>
27 #include <TEventList.h>
28 #include <TEntryList.h>
29 #include <TTreeFormula.h>
30
31 //ROOT-AliEn
32 #include <TGridResult.h>
33
34 #include "AliLog.h"
35
36 #include "AliRunTag.h"
37 #include "AliEventTag.h"
38 #include "AliTagAnalysis.h"
39 #include "AliEventTagCuts.h"
40 #include "AliDetectorTagCuts.h"
41 #include "AliLHCTagCuts.h"
42 #include "AliRunTagCuts.h"
43 #include "AliXMLCollection.h"
44
45 class TTree;
46
47 ClassImp(AliTagAnalysis)
48
49 //___________________________________________________________________________
50 AliTagAnalysis::AliTagAnalysis(): 
51   TObject(),
52   ftagresult(0x0),
53   fTagDirName(),
54   fChain(0x0),
55   fAnalysisType(),
56   fGlobalList(0) {
57   //Default constructor for a AliTagAnalysis
58 }
59
60 //___________________________________________________________________________
61 AliTagAnalysis::AliTagAnalysis(const char* type): 
62   TObject(),
63   ftagresult(0x0),
64   fTagDirName(),
65   fChain(0x0),
66   fAnalysisType(type),
67   fGlobalList(0) {
68   //constructor for a AliTagAnalysis
69 }
70
71 //___________________________________________________________________________
72 AliTagAnalysis::~AliTagAnalysis() {
73   //Default destructor for a AliTagAnalysis
74   if(ftagresult) delete ftagresult;
75   if(fChain) delete fChain;
76   if(fGlobalList) delete fGlobalList;
77 }
78
79 //___________________________________________________________________________
80 Bool_t  AliTagAnalysis::AddTagsFile(const char *alienUrl) {
81   // Add a single tags file to the chain
82
83   Bool_t rv = kTRUE ;
84
85   if (! fChain) fChain = new TChain("T");
86
87   TFile *f = TFile::Open(alienUrl,"READ");
88   fChain->Add(alienUrl);
89   AliInfo(Form("Chained tag files: %d ",fChain->GetEntries()));
90   delete f;
91
92   if (fChain->GetEntries() == 0 )
93     rv = kFALSE ;
94
95   return rv ;
96 }
97
98 //___________________________________________________________________________
99 void AliTagAnalysis::ChainLocalTags(const char *dirname) {
100   //Searches the entries of the provided direcory
101   //Chains the tags that are stored locally
102   fTagDirName = dirname;
103   TString fTagFilename;
104   
105   if (! fChain)  fChain = new TChain("T");
106   const char * tagPattern = 0x0;
107   if(fAnalysisType == "ESD") tagPattern = "ESD.tag.root";
108   else if(fAnalysisType == "AOD") tagPattern = "AOD.tag.root";
109   else AliFatal("Only ESD and AOD type is implemented!!!");
110
111   // Open the working directory
112   void * dirp = gSystem->OpenDirectory(fTagDirName);
113   const char * name = 0x0;
114   // Add all files matching *pattern* to the chain
115   while((name = gSystem->GetDirEntry(dirp))) {
116     if (strstr(name,tagPattern)) { 
117       fTagFilename = fTagDirName;
118       fTagFilename += "/";
119       fTagFilename += name;
120                 
121       fChain->Add(fTagFilename);  
122     }//pattern check
123   }//directory loop
124   AliInfo(Form("Chained tag files: %d ",fChain->GetEntries()));
125 }
126
127
128 //___________________________________________________________________________
129 void AliTagAnalysis::ChainGridTags(TGridResult *res) {
130   //Loops overs the entries of the TGridResult
131   //Chains the tags that are stored in the GRID
132   ftagresult = res;
133   Int_t nEntries = ftagresult->GetEntries();
134  
135   if (! fChain)  fChain = new TChain("T");
136
137   TString gridname = "alien://";
138   TString alienUrl;
139  
140   for(Int_t i = 0; i < nEntries; i++) {
141     alienUrl = ftagresult->GetKey(i,"turl");
142     fChain->Add(alienUrl);
143   }//grid result loop  
144 }
145
146
147 //___________________________________________________________________________
148 TChain *AliTagAnalysis::QueryTags(AliRunTagCuts *runTagCuts, 
149                                   AliLHCTagCuts *lhcTagCuts, 
150                                   AliDetectorTagCuts *detTagCuts, 
151                                   AliEventTagCuts *evTagCuts) {
152   //Queries the tag chain using the defined 
153   //event tag cuts from the AliEventTagCuts object
154   //and returns a TChain along with the associated TEventList
155   AliInfo(Form("Querying the tags........"));
156
157   TString fAliceFile;
158   if(fAnalysisType == "ESD") fAliceFile = "esdTree";
159   else if(fAnalysisType == "AOD") fAliceFile = "aodTree";
160   else AliFatal("Only ESD and AOD type is implemented!!!");
161
162   //ESD file chain
163   TChain *fESDchain = new TChain(fAliceFile.Data());
164   //global entry list
165   fGlobalList = new TEntryList();
166   
167   //Defining tag objects
168   AliRunTag   *tag     = new AliRunTag;
169   AliEventTag *evTag   = new AliEventTag;
170   fChain->SetBranchAddress("AliTAG",&tag);
171
172   TString guid = 0;
173   TString turl = 0;
174   TString path = 0;
175
176   Int_t iAccepted = 0;
177   for(Int_t iTagFiles = 0; iTagFiles < fChain->GetEntries(); iTagFiles++) {
178     fChain->GetEntry(iTagFiles);
179     if(runTagCuts->IsAccepted(tag)) {
180       if(lhcTagCuts->IsAccepted(tag->GetLHCTag())) {
181         if(detTagCuts->IsAccepted(tag->GetDetectorTags())) {
182           TEntryList *fLocalList = new TEntryList();
183           Int_t iEvents = tag->GetNEvents();
184           const TClonesArray *tagList = tag->GetEventTags();
185           for(Int_t i = 0; i < iEvents; i++) {
186             evTag = (AliEventTag *) tagList->At(i);
187             guid = evTag->GetGUID(); 
188             turl = evTag->GetTURL(); 
189             path = evTag->GetPath();
190             fLocalList->SetTreeName(fAliceFile.Data());
191             if(turl!="") fLocalList->SetFileName(turl.Data());
192             else fLocalList->SetFileName(path.Data());
193             if(evTagCuts->IsAccepted(evTag)) fLocalList->Enter(i);
194           }//event loop
195           if(path != "") fESDchain->AddFile(path);
196           else if(turl != "") fESDchain->AddFile(turl);
197           fGlobalList->Add(fLocalList);
198           iAccepted += fLocalList->GetN();
199         }//detector tag cuts
200       }//lhc tag cuts
201     }//run tags cut
202     tag->Clear();
203   }//tag file loop
204   AliInfo(Form("Accepted events: %d",iAccepted));
205   fESDchain->SetEntryList(fGlobalList,"ne");
206    
207   return fESDchain;
208 }
209
210 //___________________________________________________________________________
211 TChain *AliTagAnalysis::QueryTags(const char *fRunCut, 
212                                   const char *fLHCCut, 
213                                   const char *fDetectorCut, 
214                                   const char *fEventCut) {       
215   //Queries the tag chain using the defined      
216   //event tag cuts from the AliEventTagCuts object       
217   //and returns a TChain along with the associated TEventList    
218   AliInfo(Form("Querying the tags........"));    
219   
220   TString fAliceFile;
221   if(fAnalysisType == "ESD") fAliceFile = "esdTree";
222   else if(fAnalysisType == "AOD") fAliceFile = "aodTree";
223   else AliFatal("Only ESD and AOD type is implemented!!!");
224
225   //ESD file chain
226   TChain *fESDchain = new TChain(fAliceFile.Data());
227   //global entry list
228   fGlobalList = new TEntryList();
229   
230   //Defining tag objects         
231   AliRunTag *tag = new AliRunTag;        
232   AliEventTag *evTag = new AliEventTag;          
233   fChain->SetBranchAddress("AliTAG",&tag);       
234   
235   TString guid = 0;      
236   TString turl = 0;      
237   TString path = 0;      
238   
239   TTreeFormula *fRunFormula = new TTreeFormula("fRun",fRunCut,fChain);   
240   TTreeFormula *fLHCFormula = new TTreeFormula("fLHC",fLHCCut,fChain);   
241   TTreeFormula *fDetectorFormula = new TTreeFormula("fDetector",fDetectorCut,fChain);
242   TTreeFormula *fEventFormula = new TTreeFormula("fEvent",fEventCut,fChain);
243   
244   Int_t current = -1;    
245   Int_t iAccepted = 0;   
246   for(Int_t iTagFiles = 0; iTagFiles < fChain->GetEntries(); iTagFiles++) {
247     fChain->GetEntry(iTagFiles);         
248     if (current != fChain->GetTreeNumber()) {    
249       fRunFormula->UpdateFormulaLeaves();        
250       fLHCFormula->UpdateFormulaLeaves();        
251       fDetectorFormula->UpdateFormulaLeaves();   
252       fEventFormula->UpdateFormulaLeaves();      
253       current = fChain->GetTreeNumber();         
254     }    
255     if(fRunFormula->EvalInstance(iTagFiles) == 1) {      
256       if(fLHCFormula->EvalInstance(iTagFiles) == 1) {    
257         if(fDetectorFormula->EvalInstance(iTagFiles) == 1) {     
258           TEntryList *fLocalList = new TEntryList();
259           Int_t iEvents = fEventFormula->GetNdata();     
260           const TClonesArray *tagList = tag->GetEventTags();     
261           for(Int_t i = 0; i < iEvents; i++) {   
262             evTag = (AliEventTag *) tagList->At(i);      
263             guid = evTag->GetGUID();     
264             turl = evTag->GetTURL();     
265             path = evTag->GetPath();     
266             fLocalList->SetTreeName(fAliceFile.Data());
267             fLocalList->SetFileName(turl.Data());
268             if(fEventFormula->EvalInstance(i) == 1) fLocalList->Enter(i);
269           }//event loop          
270           iAccepted += fLocalList->GetN();       
271           
272           if(path != "") fESDchain->AddFile(path);       
273           else if(turl != "") fESDchain->AddFile(turl);          
274           fGlobalList->Add(fLocalList);
275           iAccepted += fLocalList->GetN();
276         }//detector tag cuts
277       }//lhc tag cuts
278     }//run tag cut       
279   }//tag file loop       
280   AliInfo(Form("Accepted events: %d",iAccepted));        
281   fESDchain->SetEntryList(fGlobalList,"ne");     
282   
283   return fESDchain;      
284 }
285
286 //___________________________________________________________________________
287 Bool_t AliTagAnalysis::CreateXMLCollection(const char* name, 
288                                            AliRunTagCuts *runTagCuts, 
289                                            AliLHCTagCuts *lhcTagCuts, 
290                                            AliDetectorTagCuts *detTagCuts, 
291                                            AliEventTagCuts *evTagCuts) {
292   //Queries the tag chain using the defined 
293   //event tag cuts from the AliEventTagCuts object
294   //and returns a XML collection
295   AliInfo(Form("Creating the collection........"));
296
297   AliXMLCollection *collection = new AliXMLCollection();
298   collection->SetCollectionName(name);
299   collection->WriteHeader();
300
301   TString guid = 0x0;
302   TString turl = 0x0;
303   TString lfn = 0x0;
304   
305   //Defining tag objects
306   AliRunTag *tag = new AliRunTag;
307   AliEventTag *evTag = new AliEventTag;
308   fChain->SetBranchAddress("AliTAG",&tag);
309
310   for(Int_t iTagFiles = 0; iTagFiles < fChain->GetEntries(); iTagFiles++) {
311     //Event list
312     TEntryList *fList = new TEntryList();
313     fChain->GetEntry(iTagFiles);
314     if(runTagCuts->IsAccepted(tag)) {
315       if(lhcTagCuts->IsAccepted(tag->GetLHCTag())) {
316         if(detTagCuts->IsAccepted(tag->GetDetectorTags())) {
317           Int_t iEvents = tag->GetNEvents();
318           const TClonesArray *tagList = tag->GetEventTags();
319           for(Int_t i = 0; i < iEvents; i++) {
320             evTag = (AliEventTag *) tagList->At(i);
321             guid = evTag->GetGUID(); 
322             turl = evTag->GetTURL(); 
323             lfn = turl(8,turl.Length());
324             if(evTagCuts->IsAccepted(evTag)) fList->Enter(i);
325           }//event loop
326           collection->WriteBody(iTagFiles+1,guid,lfn,turl,fList);
327         }//detector tag cuts
328       }//lhc tag cuts 
329     }//run tag cuts
330     tag->Clear();
331   }//tag file loop
332   collection->Export();
333
334   return kTRUE;
335 }
336
337 //___________________________________________________________________________
338 Bool_t AliTagAnalysis::CreateXMLCollection(const char* name, 
339                                            const char *fRunCut, 
340                                            const char *fLHCCut, 
341                                            const char *fDetectorCut, 
342                                            const char *fEventCut) {
343   //Queries the tag chain using the defined 
344   //event tag cuts from the AliEventTagCuts object
345   //and returns a XML collection
346   AliInfo(Form("Creating the collection........"));
347
348   AliXMLCollection *collection = new AliXMLCollection();
349   collection->SetCollectionName(name);
350   collection->WriteHeader();
351
352   TString guid = 0x0;
353   TString turl = 0x0;
354   TString lfn = 0x0;
355   
356   //Defining tag objects
357   AliRunTag *tag = new AliRunTag;
358   AliEventTag *evTag = new AliEventTag;
359   fChain->SetBranchAddress("AliTAG",&tag);
360
361   TTreeFormula *fRunFormula = new TTreeFormula("fRun",fRunCut,fChain);
362   TTreeFormula *fLHCFormula = new TTreeFormula("fLHC",fLHCCut,fChain);   
363   TTreeFormula *fDetectorFormula = new TTreeFormula("fDetector",fDetectorCut,fChain);
364   TTreeFormula *fEventFormula = new TTreeFormula("fEvent",fEventCut,fChain);
365
366   Int_t current = -1;
367   for(Int_t iTagFiles = 0; iTagFiles < fChain->GetEntries(); iTagFiles++) {
368     //Event list
369     TEntryList *fList = new TEntryList();
370     fChain->GetEntry(iTagFiles);
371     if (current != fChain->GetTreeNumber()) {
372       fRunFormula->UpdateFormulaLeaves();
373       fLHCFormula->UpdateFormulaLeaves();
374       fDetectorFormula->UpdateFormulaLeaves();
375       fEventFormula->UpdateFormulaLeaves();
376       current = fChain->GetTreeNumber();
377     }
378     if(fRunFormula->EvalInstance(iTagFiles) == 1) {
379       if(fLHCFormula->EvalInstance(iTagFiles) == 1) {    
380         if(fDetectorFormula->EvalInstance(iTagFiles) == 1) {     
381           Int_t iEvents = fEventFormula->GetNdata();
382           const TClonesArray *tagList = tag->GetEventTags();
383           for(Int_t i = 0; i < iEvents; i++) {
384             evTag = (AliEventTag *) tagList->At(i);
385             guid = evTag->GetGUID(); 
386             turl = evTag->GetTURL(); 
387             lfn = turl(8,turl.Length());
388             if(fEventFormula->EvalInstance(i) == 1) fList->Enter(i);
389           }//event loop
390           collection->WriteBody(iTagFiles+1,guid,lfn,turl,fList);
391         }//detector tag cuts
392       }//lhc tag cuts 
393     }//run tag cuts
394   }//tag file loop
395   collection->Export();
396
397   return kTRUE;
398 }
399
400 //___________________________________________________________________________
401 Bool_t AliTagAnalysis::CreateAsciiCollection(const char* name, 
402                                              AliRunTagCuts *runTagCuts, 
403                                              AliLHCTagCuts *lhcTagCuts, 
404                                              AliDetectorTagCuts *detTagCuts, 
405                                              AliEventTagCuts *evTagCuts) {
406   //Queries the tag chain using the defined 
407   //event tag cuts from the AliEventTagCuts object
408   //and returns a XML collection
409   AliInfo(Form("Creating the collection........"));
410
411   ofstream fout;
412   fout.open(name);
413
414   TString guid = 0x0;
415   TString turl = 0x0;
416   TString lfn = 0x0;
417
418   TString line0 = 0;
419
420   //Defining tag objects
421   AliRunTag *tag = new AliRunTag;
422   AliEventTag *evTag = new AliEventTag;
423   fChain->SetBranchAddress("AliTAG",&tag);
424
425   for(Int_t iTagFiles = 0; iTagFiles < fChain->GetEntries(); iTagFiles++) {
426     //Event list
427     TEntryList *fList = new TEntryList();
428     fChain->GetEntry(iTagFiles);
429     if(runTagCuts->IsAccepted(tag)) {
430       if(lhcTagCuts->IsAccepted(tag->GetLHCTag())) {
431         if(detTagCuts->IsAccepted(tag->GetDetectorTags())) {
432           Int_t iEvents = tag->GetNEvents();
433           const TClonesArray *tagList = tag->GetEventTags();
434           for(Int_t i = 0; i < iEvents; i++) {
435             evTag = (AliEventTag *) tagList->At(i);
436             guid = evTag->GetGUID(); 
437             turl = evTag->GetTURL(); 
438             lfn = turl(8,turl.Length());
439             if(evTagCuts->IsAccepted(evTag)) fList->Enter(i);
440           }//event loop
441           line0 = guid; line0 += " "; line0 += turl; line0 += " ";
442           for(Int_t i = 0; i < fList->GetN(); i++) {
443             line0 += fList->GetEntry(i); 
444             line0 += " ";
445           }  
446           fout<<line0<<"\n";
447         }//detector tag cuts
448       }//lhc tag cuts 
449     }//run tag cuts
450     tag->Clear();
451   }//tag file loop
452
453   fout.close();
454
455   return kTRUE;
456 }
457
458 //___________________________________________________________________________
459 Bool_t AliTagAnalysis::CreateAsciiCollection(const char* name, 
460                                              const char *fRunCut, 
461                                              const char *fLHCCut, 
462                                              const char *fDetectorCut, 
463                                              const char *fEventCut) {
464   //Queries the tag chain using the defined 
465   //event tag cuts from the AliEventTagCuts object
466   //and returns a XML collection
467   AliInfo(Form("Creating the collection........"));
468
469   ofstream fout;
470   fout.open(name);
471
472   TString guid = 0x0;
473   TString turl = 0x0;
474   TString lfn = 0x0;
475
476   TString line0 = 0;
477   
478   //Defining tag objects
479   AliRunTag *tag = new AliRunTag;
480   AliEventTag *evTag = new AliEventTag;
481   fChain->SetBranchAddress("AliTAG",&tag);
482
483   TTreeFormula *fRunFormula = new TTreeFormula("fRun",fRunCut,fChain);
484   TTreeFormula *fLHCFormula = new TTreeFormula("fLHC",fLHCCut,fChain);   
485   TTreeFormula *fDetectorFormula = new TTreeFormula("fDetector",fDetectorCut,fChain);
486   TTreeFormula *fEventFormula = new TTreeFormula("fEvent",fEventCut,fChain);
487
488   Int_t current = -1;
489   for(Int_t iTagFiles = 0; iTagFiles < fChain->GetEntries(); iTagFiles++) {
490     //Event list
491     TEntryList *fList = new TEntryList();
492     fChain->GetEntry(iTagFiles);
493     if (current != fChain->GetTreeNumber()) {
494       fRunFormula->UpdateFormulaLeaves();
495       fLHCFormula->UpdateFormulaLeaves();
496       fDetectorFormula->UpdateFormulaLeaves();
497       fEventFormula->UpdateFormulaLeaves();
498       current = fChain->GetTreeNumber();
499     }
500     if(fRunFormula->EvalInstance(iTagFiles) == 1) {
501       if(fLHCFormula->EvalInstance(iTagFiles) == 1) {    
502         if(fDetectorFormula->EvalInstance(iTagFiles) == 1) {     
503           Int_t iEvents = fEventFormula->GetNdata();
504           const TClonesArray *tagList = tag->GetEventTags();
505           for(Int_t i = 0; i < iEvents; i++) {
506             evTag = (AliEventTag *) tagList->At(i);
507             guid = evTag->GetGUID(); 
508             turl = evTag->GetTURL(); 
509             lfn = turl(8,turl.Length());
510             if(fEventFormula->EvalInstance(i) == 1) fList->Enter(i);
511           }//event loop
512           line0 = guid; line0 += " "; line0 += turl; line0 += " ";
513           for(Int_t i = 0; i < fList->GetN(); i++) {
514             line0 += fList->GetEntry(i); 
515             line0 += " ";
516           }  
517           fout<<line0<<"\n";
518         }//detector tag cuts
519       }//lhc tag cuts 
520     }//run tag cuts
521   }//tag file loop
522
523   fout.close();
524
525   return kTRUE;
526 }
527
528 //___________________________________________________________________________
529 TChain *AliTagAnalysis::GetInputChain(const char* system, const char *wn) {
530   //returns the chain+event list - used in batch sessions
531   // this function will be removed once the new root 
532   // improvements are committed
533   TString fsystem = system;
534   Int_t iAccepted = 0;
535
536   TChain *fAnalysisChain = 0;
537   if(fAnalysisType == "ESD") fAnalysisChain = new TChain("esdTree");
538   else if(fAnalysisType == "AOD") fAnalysisChain = new TChain("aodTree");
539   else AliFatal("Only ESD and AOD type is implemented!!!");
540   
541   //Event list
542   TEventList *fEventList = new TEventList();
543   AliXMLCollection *collection = AliXMLCollection::Open(wn);
544
545   collection->Reset();
546   while (collection->Next()) {
547     AliInfo(Form("Adding: %s",collection->GetTURL("")));
548     fAnalysisChain->Add(collection->GetTURL(""));
549     TEntryList *list = (TEntryList *)collection->GetEventList("");
550     for(Int_t i = 0; i < list->GetN(); i++) fEventList->Enter(iAccepted+list->GetEntry(i));
551
552     if(fsystem == "pp") iAccepted += 100;
553     else if(fsystem == "PbPb") iAccepted += 1;
554   }
555
556   fAnalysisChain->SetEventList(fEventList);
557   
558   AliInfo(Form("Number of selected events: %d",fEventList->GetN()));
559
560   return fAnalysisChain;
561 }
562
563 //___________________________________________________________________________
564 TChain *AliTagAnalysis::GetChainFromCollection(const char* collectionname, 
565                                                const char* treename) {
566   //returns the TChain+TEntryList object- used in batch sessions
567   TString fAliceFile = treename;
568   Int_t iAccepted = 0;
569   TChain *fAnalysisChain = 0;
570   if(fAliceFile == "esdTree") fAnalysisChain = new TChain("esdTree");
571   else if(fAliceFile == "aodTree") fAnalysisChain = new TChain("aodTree");
572   else AliFatal("Inconsistent tree name - use esdTree or aodTree!");
573
574   //Event list
575   fGlobalList = new TEntryList();
576   AliXMLCollection *collection = AliXMLCollection::Open(collectionname);
577
578   collection->Reset();
579   while (collection->Next()) {
580     AliInfo(Form("Adding: %s",collection->GetTURL("")));
581     fAnalysisChain->Add(collection->GetTURL(""));
582     TEntryList *list = (TEntryList *)collection->GetEventList("");
583     list->SetTreeName(fAliceFile.Data());
584     list->SetFileName(collection->GetTURL(""));
585     fGlobalList->Add(list);
586     iAccepted += list->GetN();
587   }
588
589   fAnalysisChain->SetEntryList(fGlobalList,"ne");
590   
591   AliInfo(Form("Number of selected events: %d",iAccepted));
592
593   return fAnalysisChain;
594 }