T0 QA Task
[u/mrichter/AliRoot.git] / PWG2 / FORWARD / analysis2 / AliFMDEventInspector.cxx
CommitLineData
7984e5f7 1//
2// This class inspects the event
3//
4// Input:
5// - AliESDFMD object possibly corrected for sharing
6//
7// Output:
8// - A histogram of v_z of events with triggers.
9// - A histogram of v_z of events with vertex and triggers
10// - A histogram of trigger counters
11//
12// Note, that these are added to the master output list
13//
14// Corrections used:
15// - None
16//
8565b10b 17#include "AliFMDEventInspector.h"
18#include "AliLog.h"
19#include "AliESDEvent.h"
20#include "AliMultiplicity.h"
21#include "AliAnalysisManager.h"
22#include "AliInputEventHandler.h"
23#include "AliTriggerAnalysis.h"
24#include "AliPhysicsSelection.h"
25#include "AliAODForwardMult.h"
0bd4b00f 26#include "AliForwardUtil.h"
5e4d905e 27#include "AliCentrality.h"
8565b10b 28#include <TH1.h>
29#include <TList.h>
30#include <TDirectory.h>
0bd4b00f 31#include <TROOT.h>
32#include <iostream>
33#include <iomanip>
e1f47419 34
8565b10b 35//====================================================================
36AliFMDEventInspector::AliFMDEventInspector()
37 : TNamed(),
38 fHEventsTr(0),
39 fHEventsTrVtx(0),
40 fHTriggers(0),
0bd4b00f 41 fHType(0),
fe52e455 42 fHWords(0),
5e4d905e 43 fHCent(0),
8565b10b 44 fLowFluxCut(1000),
9d05ffeb 45 fMaxVzErr(0.2),
8565b10b 46 fList(0),
0bd4b00f 47 fEnergy(0),
48 fField(999),
49 fCollisionSystem(kUnknown),
8565b10b 50 fDebug(0)
51{
7984e5f7 52 //
53 // Constructor
54 //
8565b10b 55}
56
57//____________________________________________________________________
58AliFMDEventInspector::AliFMDEventInspector(const char* name)
59 : TNamed("fmdEventInspector", name),
60 fHEventsTr(0),
61 fHEventsTrVtx(0),
62 fHTriggers(0),
0bd4b00f 63 fHType(0),
fe52e455 64 fHWords(0),
5e4d905e 65 fHCent(0),
8565b10b 66 fLowFluxCut(1000),
9d05ffeb 67 fMaxVzErr(0.2),
8565b10b 68 fList(0),
0bd4b00f 69 fEnergy(0),
70 fField(999),
71 fCollisionSystem(kUnknown),
8565b10b 72 fDebug(0)
73{
7984e5f7 74 //
75 // Constructor
76 //
77 // Parameters:
78 // name Name of object
79 //
8565b10b 80}
81
82//____________________________________________________________________
83AliFMDEventInspector::AliFMDEventInspector(const AliFMDEventInspector& o)
84 : TNamed(o),
85 fHEventsTr(o.fHEventsTr),
86 fHEventsTrVtx(o.fHEventsTrVtx),
87 fHTriggers(o.fHTriggers),
0bd4b00f 88 fHType(o.fHType),
fe52e455 89 fHWords(o.fHWords),
5e4d905e 90 fHCent(o.fHCent),
6feacd76 91 fLowFluxCut(o.fLowFluxCut),
8565b10b 92 fMaxVzErr(o.fMaxVzErr),
93 fList(o.fList),
0bd4b00f 94 fEnergy(o.fEnergy),
95 fField(o.fField),
96 fCollisionSystem(o.fCollisionSystem),
8565b10b 97 fDebug(0)
98{
7984e5f7 99 //
100 // Copy constructor
101 //
102 // Parameters:
103 // o Object to copy from
104 //
8565b10b 105}
106
107//____________________________________________________________________
108AliFMDEventInspector::~AliFMDEventInspector()
109{
7984e5f7 110 //
111 // Destructor
112 //
8565b10b 113 if (fHEventsTr) delete fHEventsTr;
114 if (fHEventsTrVtx) delete fHEventsTrVtx;
115 if (fHTriggers) delete fHTriggers;
0bd4b00f 116 if (fHType) delete fHType;
fe52e455 117 if (fHWords) delete fHWords;
5e4d905e 118 if (fHCent) delete fHCent;
8565b10b 119 if (fList) delete fList;
120}
121//____________________________________________________________________
122AliFMDEventInspector&
123AliFMDEventInspector::operator=(const AliFMDEventInspector& o)
124{
7984e5f7 125 //
126 // Assignement operator
127 //
128 // Parameters:
129 // o Object to assign from
130 //
131 // Return:
132 // Reference to this object
133 //
8565b10b 134 TNamed::operator=(o);
135 fHEventsTr = o.fHEventsTr;
136 fHEventsTrVtx = o.fHEventsTrVtx;
137 fHTriggers = o.fHTriggers;
0bd4b00f 138 fHType = o.fHType;
fe52e455 139 fHWords = o.fHWords;
5e4d905e 140 fHCent = o.fHCent;
8565b10b 141 fLowFluxCut = o.fLowFluxCut;
142 fMaxVzErr = o.fMaxVzErr;
143 fDebug = o.fDebug;
144 fList = (o.fList ? new TList : 0);
0bd4b00f 145 fEnergy = o.fEnergy;
146 fField = o.fField;
147 fCollisionSystem = o.fCollisionSystem;
8565b10b 148 if (fList) {
149 fList->SetName(GetName());
150 if (fHEventsTr) fList->Add(fHEventsTr);
151 if (fHEventsTrVtx) fList->Add(fHEventsTrVtx);
152 if (fHTriggers) fList->Add(fHTriggers);
0bd4b00f 153 if (fHType) fList->Add(fHType);
fe52e455 154 if (fHWords) fList->Add(fHWords);
5e4d905e 155 if (fHCent) fList->Add(fHCent);
8565b10b 156 }
157 return *this;
158}
159
160//____________________________________________________________________
161Bool_t
fb3430ac 162AliFMDEventInspector::FetchHistograms(const TList* d,
8565b10b 163 TH1I*& hEventsTr,
164 TH1I*& hEventsTrVtx,
165 TH1I*& hTriggers) const
166{
7984e5f7 167 //
168 // Fetch our histograms from the passed list
169 //
170 // Parameters:
171 // d Input
172 // hEventsTr On return, pointer to histogram, or null
173 // hEventsTrVtx On return, pointer to histogram, or null
174 // hTriggers On return, pointer to histogram, or null
175 //
176 // Return:
177 // true on success, false otherwise
178 //
8565b10b 179 hEventsTr = 0;
180 hEventsTrVtx = 0;
181 hTriggers = 0;
182 TList* dd = dynamic_cast<TList*>(d->FindObject(GetName()));
183 if (!dd) return kFALSE;
184
185 hEventsTr = dynamic_cast<TH1I*>(dd->FindObject("nEventsTr"));
186 hEventsTrVtx = dynamic_cast<TH1I*>(dd->FindObject("nEventsTrVtx"));
187 hTriggers = dynamic_cast<TH1I*>(dd->FindObject("triggers"));
188
189 if (!hEventsTr || !hEventsTrVtx || !hTriggers) return kFALSE;
190 return kTRUE;
191}
192//____________________________________________________________________
193void
194AliFMDEventInspector::Init(const TAxis& vtxAxis)
195{
7984e5f7 196 //
197 // Initialize the object
198 //
199 // Parameters:
200 // vtxAxis Vertex axis in use
201 //
8565b10b 202 fHEventsTr = new TH1I("nEventsTr", "Number of events w/trigger",
203 vtxAxis.GetNbins(),
204 vtxAxis.GetXmin(),
205 vtxAxis.GetXmax());
206 fHEventsTr->SetXTitle("v_{z} [cm]");
207 fHEventsTr->SetYTitle("# of events");
208 fHEventsTr->SetFillColor(kRed+1);
209 fHEventsTr->SetFillStyle(3001);
210 fHEventsTr->SetDirectory(0);
211 // fHEventsTr->Sumw2();
212 fList->Add(fHEventsTr);
213
214 fHEventsTrVtx = new TH1I("nEventsTrVtx",
215 "Number of events w/trigger and vertex",
216 vtxAxis.GetNbins(),
217 vtxAxis.GetXmin(),
218 vtxAxis.GetXmax());
219 fHEventsTrVtx->SetXTitle("v_{z} [cm]");
220 fHEventsTrVtx->SetYTitle("# of events");
221 fHEventsTrVtx->SetFillColor(kBlue+1);
222 fHEventsTrVtx->SetFillStyle(3001);
223 fHEventsTrVtx->SetDirectory(0);
224 // fHEventsTrVtx->Sumw2();
225 fList->Add(fHEventsTrVtx);
226
227
228 fHTriggers = new TH1I("triggers", "Triggers", 10, 0, 10);
229 fHTriggers->SetFillColor(kRed+1);
230 fHTriggers->SetFillStyle(3001);
231 fHTriggers->SetStats(0);
232 fHTriggers->SetDirectory(0);
233 fHTriggers->GetXaxis()->SetBinLabel(kInel +1,"INEL");
234 fHTriggers->GetXaxis()->SetBinLabel(kInelGt0+1,"INEL>0");
235 fHTriggers->GetXaxis()->SetBinLabel(kNSD +1,"NSD");
236 fHTriggers->GetXaxis()->SetBinLabel(kEmpty +1,"Empty");
237 fHTriggers->GetXaxis()->SetBinLabel(kA +1,"A");
238 fHTriggers->GetXaxis()->SetBinLabel(kB +1,"B");
239 fHTriggers->GetXaxis()->SetBinLabel(kC +1,"C");
240 fHTriggers->GetXaxis()->SetBinLabel(kE +1,"E");
e58000b7 241 fHTriggers->GetXaxis()->SetBinLabel(kPileUp +1,"Pileup");
242 fHTriggers->GetXaxis()->SetBinLabel(kMCNSD +1,"nsd");
8565b10b 243 fList->Add(fHTriggers);
0bd4b00f 244
245 fHType = new TH1I("type", Form("Event type (cut: SPD mult>%d)",
246 fLowFluxCut), 2, -.5, 1.5);
247 fHType->SetFillColor(kRed+1);
248 fHType->SetFillStyle(3001);
249 fHType->SetStats(0);
250 fHType->SetDirectory(0);
251 fHType->GetXaxis()->SetBinLabel(1,"Low-flux");
252 fHType->GetXaxis()->SetBinLabel(2,"High-flux");
253 fList->Add(fHType);
fe52e455 254
255
256 fHWords = new TH1I("words", "Trigger words seen", 1, 0, 0);
257 fHWords->SetFillColor(kBlue+1);
258 fHWords->SetFillStyle(3001);
259 fHWords->SetStats(0);
260 fHWords->SetDirectory(0);
261 fHWords->SetBit(TH1::kCanRebin);
262 fList->Add(fHWords);
5e4d905e 263
264 fHCent = new TH1F("cent", "Centrality", 101, -1.5, 100.5);
265 fHCent->SetFillColor(kBlue+1);
266 fHCent->SetFillStyle(3001);
267 fHCent->SetStats(0);
268 fHCent->SetDirectory(0);
269 fHCent->SetXTitle("Centrality [%]");
270 fHCent->SetYTitle("Events");
271 fList->Add(fHCent);
8565b10b 272}
273
274//____________________________________________________________________
275void
276AliFMDEventInspector::DefineOutput(TList* dir)
277{
7984e5f7 278 //
279 // Define the output histograms. These are put in a sub list of the
280 // passed list. The histograms are merged before the parent task calls
281 // AliAnalysisTaskSE::Terminate
282 //
283 // dir Directory to add to
284 //
8565b10b 285 fList = new TList;
286 fList->SetName(GetName());
287 dir->Add(fList);
288}
289
290//____________________________________________________________________
291UInt_t
292AliFMDEventInspector::Process(const AliESDEvent* event,
293 UInt_t& triggers,
294 Bool_t& lowFlux,
0bd4b00f 295 UShort_t& ivz,
5e4d905e 296 Double_t& vz,
297 Double_t& cent)
8565b10b 298{
7984e5f7 299 //
300 // Process the event
301 //
302 // Parameters:
303 // event Input event
304 // triggers On return, the triggers fired
305 // lowFlux On return, true if the event is considered a low-flux
306 // event (according to the setting of fLowFluxCut)
307 // ivz On return, the found vertex bin (1-based). A zero
308 // means outside of the defined vertex range
309 // vz On return, the z position of the interaction
5e4d905e 310 // cent On return, the centrality - if not available < 0
7984e5f7 311 //
312 // Return:
313 // 0 (or kOk) on success, otherwise a bit mask of error codes
314 //
315
e1f47419 316 // --- Check that we have an event ---------------------------------
8565b10b 317 if (!event) {
318 AliWarning("No ESD event found for input event");
319 return kNoEvent;
320 }
321
e1f47419 322 // --- Read trigger information from the ESD and store in AOD object
8565b10b 323 if (!ReadTriggers(event, triggers)) {
0bd4b00f 324 if (fDebug > 2) {
325 AliWarning("Failed to read triggers from ESD"); }
8565b10b 326 return kNoTriggers;
327 }
328
e1f47419 329 // --- Check if this is a high-flux event --------------------------
8565b10b 330 const AliMultiplicity* testmult = event->GetMultiplicity();
331 if (!testmult) {
0bd4b00f 332 if (fDebug > 3) {
333 AliWarning("No central multiplicity object found"); }
8565b10b 334 }
e1f47419 335 else
336 lowFlux = testmult->GetNumberOfTracklets() < fLowFluxCut;
5e4d905e 337
0bd4b00f 338 fHType->Fill(lowFlux ? 0 : 1);
5e4d905e 339
e1f47419 340 // --- Read centrality information
e58000b7 341 cent = -10;
e1f47419 342 if (!ReadCentrality(event, cent)) {
343 if (fDebug > 3)
344 AliWarning("Failed to get centrality");
8565b10b 345 }
346
e1f47419 347 // --- Get the vertex information ----------------------------------
8565b10b 348 vz = 0;
349 Bool_t vzOk = ReadVertex(event, vz);
350
351 fHEventsTr->Fill(vz);
352 if (!vzOk) {
0bd4b00f 353 if (fDebug > 3) {
354 AliWarning("Failed to read vertex from ESD"); }
8565b10b 355 return kNoVertex;
356 }
357 fHEventsTrVtx->Fill(vz);
358
e1f47419 359 // --- Get the vertex bin ------------------------------------------
0bd4b00f 360 ivz = fHEventsTr->GetXaxis()->FindBin(vz);
361 if (ivz <= 0 || ivz > fHEventsTr->GetXaxis()->GetNbins()) {
362 if (fDebug > 3) {
8565b10b 363 AliWarning(Form("Vertex @ %f outside of range [%f,%f]",
364 vz, fHEventsTr->GetXaxis()->GetXmin(),
0bd4b00f 365 fHEventsTr->GetXaxis()->GetXmax())); }
366 ivz = 0;
8565b10b 367 return kBadVertex;
368 }
e58000b7 369
e1f47419 370 // --- Check the FMD ESD data --------------------------------------
371 if (!event->GetFMDData()) {
372 if (fDebug > 3) {
373 AliWarning("No FMD data found in ESD"); }
374 return kNoFMD;
375 }
376
e58000b7 377
8565b10b 378 return kOk;
379}
380
e1f47419 381//____________________________________________________________________
382Bool_t
383AliFMDEventInspector::ReadCentrality(const AliESDEvent* esd, Double_t& cent)
384{
385 //
386 // Read centrality from event
387 //
388 // Parameters:
389 // esd Event
390 // cent On return, the centrality or negative if not found
391 //
392 // Return:
393 // False on error, true otherwise
394 //
395 AliCentrality* centObj = const_cast<AliESDEvent*>(esd)->GetCentrality();
396 if (centObj) {
397 // AliInfo(Form("Got centrality object %p with quality %d",
398 // centObj, centObj->GetQuality()));
399 // centObj->Print();
16e7c989 400 cent = centObj->GetCentralityPercentile("V0M");
e1f47419 401 }
402 // AliInfo(Form("Centrality is %f", cent));
403 fHCent->Fill(cent);
404
405 return true;
406}
407
8565b10b 408//____________________________________________________________________
409Bool_t
410AliFMDEventInspector::ReadTriggers(const AliESDEvent* esd, UInt_t& triggers)
411{
7984e5f7 412 //
413 // Read the trigger information from the ESD event
414 //
415 // Parameters:
416 // esd ESD event
417 // triggers On return, contains the trigger bits
418 //
419 // Return:
420 // @c true on success, @c false otherwise
421 //
8565b10b 422 triggers = 0;
423
424 // Get the analysis manager - should always be there
425 AliAnalysisManager* am = AliAnalysisManager::GetAnalysisManager();
426 if (!am) {
427 AliWarning("No analysis manager defined!");
428 return kFALSE;
429 }
430
431 // Get the input handler - should always be there
432 AliInputEventHandler* ih =
433 static_cast<AliInputEventHandler*>(am->GetInputEventHandler());
434 if (!ih) {
435 AliWarning("No input handler");
436 return kFALSE;
437 }
e58000b7 438
e1f47419 439 // Check if this is a collision candidate (MB)
e333578d 440 // Note, that we should use the value cached in the input
441 // handler rather than calling IsCollisionCandiate directly
442 // on the AliPhysicsSelection obejct. If we called the latter
443 // then the AliPhysicsSelection object would overcount by a
444 // factor of 2! :-(
445 Bool_t inel = ih->IsEventSelected();
8565b10b 446 if (inel) {
447 triggers |= AliAODForwardMult::kInel;
448 fHTriggers->Fill(kInel+0.5);
449 }
450
e333578d 451 // If this is inel, see if we have a tracklet
8565b10b 452 if (inel) {
453 const AliMultiplicity* spdmult = esd->GetMultiplicity();
454 if (!spdmult) {
455 AliWarning("No SPD multiplicity");
456 }
457 else {
e333578d 458 // Check if we have one or more tracklets
459 // in the range -1 < eta < 1 to set the INEL>0
460 // trigger flag.
8565b10b 461 Int_t n = spdmult->GetNumberOfTracklets();
462 for (Int_t j = 0; j < n; j++) {
463 if(TMath::Abs(spdmult->GetEta(j)) < 1) {
464 triggers |= AliAODForwardMult::kInelGt0;
465 fHTriggers->Fill(kInelGt0+.5);
466 break;
467 }
468 }
469 }
470 }
471
472 // Analyse some trigger stuff
473 AliTriggerAnalysis ta;
474 if (ta.IsOfflineTriggerFired(esd, AliTriggerAnalysis::kNSD1)) {
475 triggers |= AliAODForwardMult::kNSD;
476 fHTriggers->Fill(kNSD+.5);
477 }
e58000b7 478 //Check pileup
479 Bool_t pileup = esd->IsPileupFromSPD(3,0.8);
480 if (pileup) {
481 triggers |= AliAODForwardMult::kPileUp;
482 fHTriggers->Fill(kPileUp+.5);
483 }
484
8565b10b 485 // Get trigger stuff
486 TString trigStr = esd->GetFiredTriggerClasses();
fe52e455 487 // AliWarning(Form("Fired trigger classes: %s", trigStr.Data()));
488 fHWords->Fill(trigStr.Data(), 1);
489#if 0
490 if (trigStr.Contains("MB1") || trigStr.Contains("MBBG3"))
491 triggers |= AliAOODForwardMult::kB;
492 if (trigStr.Contains("COTA"))
493 triggers |= AliAODForwardMult::kA;
494 if (trigStr.Contains("COTC"))
495 triggers |= AliAODForwardMult::kC;
496#endif
8565b10b 497 if (trigStr.Contains("CBEAMB-ABCE-NOPF-ALL")) {
498 triggers |= AliAODForwardMult::kEmpty;
499 fHTriggers->Fill(kEmpty+.5);
500 }
501
502 if (trigStr.Contains("CINT1A-ABCE-NOPF-ALL")) {
503 triggers |= AliAODForwardMult::kA;
504 fHTriggers->Fill(kA+.5);
505 }
506
507 if (trigStr.Contains("CINT1B-ABCE-NOPF-ALL")) {
508 triggers |= AliAODForwardMult::kB;
509 fHTriggers->Fill(kB+.5);
510 }
511
512
513 if (trigStr.Contains("CINT1C-ABCE-NOPF-ALL")) {
514 triggers |= AliAODForwardMult::kC;
515 fHTriggers->Fill(kC+.5);
516 }
517
518 if (trigStr.Contains("CINT1-E-NOPF-ALL")) {
519 triggers |= AliAODForwardMult::kE;
520 fHTriggers->Fill(kE+.5);
521 }
522
523 return kTRUE;
524}
525//____________________________________________________________________
526Bool_t
527AliFMDEventInspector::ReadVertex(const AliESDEvent* esd, Double_t& vz)
528{
7984e5f7 529 //
530 // Read the vertex information from the ESD event
531 //
532 // Parameters:
533 // esd ESD event
534 // vz On return, the vertex Z position
535 //
536 // Return:
537 // @c true on success, @c false otherwise
538 //
8565b10b 539 vz = 0;
540 // Get the vertex
541 const AliESDVertex* vertex = esd->GetPrimaryVertexSPD();
542 if (!vertex) {
0bd4b00f 543 if (fDebug > 2) {
544 AliWarning("No SPD vertex found in ESD"); }
8565b10b 545 return kFALSE;
546 }
547
548 // Check that enough tracklets contributed
549 if(vertex->GetNContributors() <= 0) {
0bd4b00f 550 if (fDebug > 2) {
8565b10b 551 AliWarning(Form("Number of contributors to vertex is %d<=0",
0bd4b00f 552 vertex->GetNContributors())); }
8565b10b 553 vz = 0;
554 return kFALSE;
555 }
556
557 // Check that the uncertainty isn't too large
558 if (vertex->GetZRes() > fMaxVzErr) {
0bd4b00f 559 if (fDebug > 2) {
8565b10b 560 AliWarning(Form("Uncertaintity in Z of vertex is too large %f > %f",
0bd4b00f 561 vertex->GetZRes(), fMaxVzErr)); }
8565b10b 562 return kFALSE;
563 }
564
565 // Get the z coordiante
566 vz = vertex->GetZ();
567 return kTRUE;
568}
0bd4b00f 569
570//____________________________________________________________________
571Bool_t
572AliFMDEventInspector::ReadRunDetails(const AliESDEvent* esd)
573{
7984e5f7 574 //
575 // Read the collision system, collision energy, and L3 field setting
576 // from the ESD
577 //
578 // Parameters:
579 // esd ESD to get information from
580 //
581 // Return:
582 // true on success, false
583 //
cc83fca2 584 // AliInfo(Form("Parameters from 1st ESD event: cms=%s, sNN=%f, field=%f",
585 // esd->GetBeamType(), 2*esd->GetBeamEnergy(),
586 // esd->GetMagneticField()));
0bd4b00f 587 fCollisionSystem =
588 AliForwardUtil::ParseCollisionSystem(esd->GetBeamType());
589 fEnergy =
590 AliForwardUtil::ParseCenterOfMassEnergy(fCollisionSystem,
591 2 * esd->GetBeamEnergy());
592 fField =
593 AliForwardUtil::ParseMagneticField(esd->GetMagneticField());
594
595 if (fCollisionSystem == AliForwardUtil::kUnknown ||
596 fEnergy <= 0 ||
597 TMath::Abs(fField) > 10)
598 return kFALSE;
599
600 return kTRUE;
601}
602
603//____________________________________________________________________
604void
605AliFMDEventInspector::Print(Option_t*) const
606{
7984e5f7 607 //
608 // Print information
609 //
610 // option Not used
611 //
0bd4b00f 612 char ind[gROOT->GetDirLevel()+1];
613 for (Int_t i = 0; i < gROOT->GetDirLevel(); i++) ind[i] = ' ';
614 ind[gROOT->GetDirLevel()] = '\0';
615 TString sNN(AliForwardUtil::CenterOfMassEnergyString(fEnergy));
616 sNN.Strip(TString::kBoth, '0');
617 sNN.ReplaceAll("GeV", " GeV");
618 TString field(AliForwardUtil::MagneticFieldString(fField));
619 field.ReplaceAll("p", "+");
620 field.ReplaceAll("m", "-");
621 field.ReplaceAll("kG", " kG");
622
e1f47419 623 std::cout << ind << ClassName() << ": " << GetName() << '\n'
0bd4b00f 624 << ind << " Low flux cut: " << fLowFluxCut << '\n'
625 << ind << " Max(delta v_z): " << fMaxVzErr << " cm\n"
626 << ind << " System: "
627 << AliForwardUtil::CollisionSystemString(fCollisionSystem) << '\n'
628 << ind << " CMS energy per nucleon: " << sNN << '\n'
629 << ind << " Field: " << field << std::endl;
630}
631
632
8565b10b 633//
634// EOF
635//
636