]> git.uio.no Git - u/mrichter/AliRoot.git/blob - STEER/ESD/AliESDHeader.cxx
test
[u/mrichter/AliRoot.git] / STEER / ESD / AliESDHeader.cxx
1 /**************************************************************************
2  * Copyright(c) 1998-1999, ALICE Experiment at CERN, All rights reserved. *
3  *                                                                        *
4  * Author: The ALICE Off-line Project.                                    *
5  * Contributors are mentioned in the code where appropriate.              *
6  *                                                                        *
7  * Permission to use, copy, modify and distribute this software and its   *
8  * documentation strictly for non-commercial purposes is hereby granted   *
9  * without fee, provided that the above copyright notice appears in all   *
10  * copies and that both the copyright notice and this permission notice   *
11  * appear in the supporting documentation. The authors make no claims     *
12  * about the suitability of this software for any purpose. It is          *
13  * provided "as is" without express or implied warranty.                  *
14  **************************************************************************/
15
16 //-------------------------------------------------------------------------
17 //                      Implementation of   Class AliESDHeader
18 //   Header data
19 //   for the ESD   
20 //   Origin: Christian Klein-Boesing, CERN, Christian.Klein-Boesing@cern.ch 
21 //-------------------------------------------------------------------------
22
23 #include "AliESDHeader.h"
24 #include "AliTriggerScalersESD.h"
25 #include "AliTriggerScalersRecordESD.h"
26 #include "AliTriggerIR.h"
27 #include "AliTriggerConfiguration.h"
28 #include "AliLog.h" 
29
30 ClassImp(AliESDHeader)
31
32 //______________________________________________________________________________
33 AliESDHeader::AliESDHeader() :
34   AliVHeader(),
35   fTriggerMask(0),
36   fOrbitNumber(0),
37   fTimeStamp(0),
38   fEventType(0),
39   fEventSpecie(0),
40   fPeriodNumber(0),
41   fEventNumberInFile(0),
42   fBunchCrossNumber(0),
43   fTriggerCluster(0),
44   fL0TriggerInputs(0),
45   fL1TriggerInputs(0),
46   fL2TriggerInputs(0),
47   fTriggerScalers(),
48   fTriggerScalersDeltaEvent(),
49   fTriggerScalersDeltaRun(),
50   fTriggerInputsNames(kNTriggerInputs),
51   fCTPConfig(NULL),
52   fIRBufferArray(),
53   fIRInt2InteractionsMap(0),
54   fIRInt1InteractionsMap(0)
55 {
56   // default constructor
57
58   SetName("AliESDHeader");
59   for(Int_t i = 0; i<kNMaxIR ; i++) fIRArray[i] = 0;
60   fTriggerInputsNames.SetOwner(kTRUE);
61 }
62
63 AliESDHeader::~AliESDHeader() 
64 {
65   // destructor
66   for(Int_t i=0;i<kNMaxIR;i++)if(fIRArray[i])delete fIRArray[i];
67   delete fCTPConfig;
68
69   fIRBufferArray.Delete();
70 }
71
72
73 AliESDHeader::AliESDHeader(const AliESDHeader &header) :
74   AliVHeader(header),
75   fTriggerMask(header.fTriggerMask),
76   fOrbitNumber(header.fOrbitNumber),
77   fTimeStamp(header.fTimeStamp),
78   fEventType(header.fEventType),
79   fEventSpecie(header.fEventSpecie),
80   fPeriodNumber(header.fPeriodNumber),
81   fEventNumberInFile(header.fEventNumberInFile),
82   fBunchCrossNumber(header.fBunchCrossNumber),
83   fTriggerCluster(header.fTriggerCluster),
84   fL0TriggerInputs(header.fL0TriggerInputs),
85   fL1TriggerInputs(header.fL1TriggerInputs),
86   fL2TriggerInputs(header.fL2TriggerInputs),
87   fTriggerScalers(header.fTriggerScalers),
88   fTriggerScalersDeltaEvent(header.fTriggerScalersDeltaEvent),
89   fTriggerScalersDeltaRun(header.fTriggerScalersDeltaRun),
90   fTriggerInputsNames(TObjArray(kNTriggerInputs)),
91   fCTPConfig(header.fCTPConfig),
92   fIRBufferArray(),
93   fIRInt2InteractionsMap(header.fIRInt2InteractionsMap),
94   fIRInt1InteractionsMap(header.fIRInt1InteractionsMap)
95 {
96   // copy constructor
97   for(Int_t i = 0; i<kNMaxIR ; i++) {
98     if(header.fIRArray[i])fIRArray[i] = new AliTriggerIR(*header.fIRArray[i]);
99     else fIRArray[i]=0;
100   }
101   for(Int_t i = 0; i < kNTriggerInputs; i++) {
102     TNamed *str = (TNamed *)((header.fTriggerInputsNames).At(i));
103     if (str) fTriggerInputsNames.AddAt(new TNamed(*str),i);
104   }
105
106   for(Int_t i = 0; i < (header.fIRBufferArray).GetEntries(); ++i) {
107     AliTriggerIR *ir = (AliTriggerIR*)((header.fIRBufferArray).At(i));
108     if (ir) fIRBufferArray.Add(new AliTriggerIR(*ir));
109   }
110 }
111
112 AliESDHeader& AliESDHeader::operator=(const AliESDHeader &header)
113
114   // assigment operator
115   if(this!=&header) {
116     AliVHeader::operator=(header);
117     fTriggerMask = header.fTriggerMask;
118     fOrbitNumber = header.fOrbitNumber;
119     fTimeStamp = header.fTimeStamp;
120     fEventType = header.fEventType;
121     fEventSpecie = header.fEventSpecie;
122     fPeriodNumber = header.fPeriodNumber;
123     fEventNumberInFile = header.fEventNumberInFile;
124     fBunchCrossNumber = header.fBunchCrossNumber;
125     fTriggerCluster = header.fTriggerCluster;
126     fL0TriggerInputs = header.fL0TriggerInputs;
127     fL1TriggerInputs = header.fL1TriggerInputs;
128     fL2TriggerInputs = header.fL2TriggerInputs;
129     fTriggerScalers = header.fTriggerScalers;
130     fTriggerScalersDeltaEvent = header.fTriggerScalersDeltaEvent;
131     fTriggerScalersDeltaRun = header.fTriggerScalersDeltaRun;
132     fIRInt2InteractionsMap = header.fIRInt2InteractionsMap;
133     fIRInt1InteractionsMap = header.fIRInt1InteractionsMap;
134
135     delete fCTPConfig;
136     fCTPConfig = header.fCTPConfig;
137
138     fTriggerInputsNames.Clear();
139     for(Int_t i = 0; i < kNTriggerInputs; i++) {
140       TNamed *str = (TNamed *)((header.fTriggerInputsNames).At(i));
141       if (str) fTriggerInputsNames.AddAt(new TNamed(*str),i);
142     }
143     for(Int_t i = 0; i<kNMaxIR ; i++) {
144       delete fIRArray[i];
145        if(header.fIRArray[i])fIRArray[i] = new AliTriggerIR(*header.fIRArray[i]);
146        else fIRArray[i]=0;
147     }
148
149     fIRBufferArray.Delete();
150     for(Int_t i = 0; i < (header.fIRBufferArray).GetEntries(); ++i) {
151       AliTriggerIR *ir = (AliTriggerIR*)((header.fIRBufferArray).At(i));
152       if (ir) fIRBufferArray.Add(new AliTriggerIR(*ir));
153     }
154   }
155   return *this;
156 }
157
158 void AliESDHeader::Copy(TObject &obj) const 
159 {  
160   // this overwrites the virtual TOBject::Copy()
161   // to allow run time copying without casting
162   // in AliESDEvent
163
164   if(this==&obj)return;
165   AliESDHeader *robj = dynamic_cast<AliESDHeader*>(&obj);
166   if(!robj)return; // not an AliESDHeader
167   *robj = *this;
168
169 }
170 //______________________________________________________________________________
171 void AliESDHeader::Reset()
172 {
173   // reset all data members
174   fTriggerMask       = 0;
175   fOrbitNumber       = 0;
176   fTimeStamp         = 0;
177   fEventType         = 0;
178   fEventSpecie       = 0;
179   fPeriodNumber      = 0;
180   fEventNumberInFile = 0;
181   fBunchCrossNumber  = 0;
182   fTriggerCluster    = 0;
183   fL0TriggerInputs   = 0;
184   fL1TriggerInputs   = 0;
185   fL2TriggerInputs   = 0;
186   fTriggerScalers.Reset();
187   fTriggerScalersDeltaEvent.Reset();
188   fTriggerScalersDeltaRun.Reset();
189   fTriggerInputsNames.Clear();
190
191   fIRInt2InteractionsMap.ResetAllBits();
192   fIRInt1InteractionsMap.ResetAllBits();
193
194   delete fCTPConfig;
195   fCTPConfig = 0;
196   for(Int_t i=0;i<kNMaxIR;i++)if(fIRArray[i]){
197    delete fIRArray[i];
198    fIRArray[i]=0;
199   }
200
201   fIRBufferArray.Delete();
202 }
203 //______________________________________________________________________________
204 Bool_t AliESDHeader::AddTriggerIR(const AliTriggerIR* ir)
205 {
206   // Add an IR object into the array
207   // of IRs in the ESD header
208
209  fIRBufferArray.Add(new AliTriggerIR(*ir));
210
211  return kTRUE;
212 }
213 //______________________________________________________________________________
214 void AliESDHeader::Print(const Option_t *) const
215 {
216   // Print some data members
217   printf("Event # %d in file Bunch crossing # %d Orbit # %d Trigger %lld \n",
218          GetEventNumberInFile(),
219          GetBunchCrossNumber(),
220          GetOrbitNumber(),
221          GetTriggerMask());
222          printf("List of the active trigger inputs: ");
223          for(Int_t i = 0; i < kNTriggerInputs; i++) {
224            TNamed *str = (TNamed *)((fTriggerInputsNames).At(i));
225            if (str) printf("%s ",str->GetName());
226          }
227          printf("\n");
228 }
229
230 //______________________________________________________________________________
231 void AliESDHeader::SetActiveTriggerInputs(const char*name, Int_t index)
232 {
233   // Fill the active trigger inputs names
234   // into the corresponding fTriggerInputsNames (TObjArray of TNamed)
235   if (index >= kNTriggerInputs || index < 0) {
236     AliError(Form("Index (%d) is outside the allowed range (0,59)!",index));
237     return;
238   }
239
240   fTriggerInputsNames.AddAt(new TNamed(name,NULL),index);
241 }
242 //______________________________________________________________________________
243 const char* AliESDHeader::GetTriggerInputName(Int_t index, Int_t trglevel) const
244 {
245   // Get the trigger input name
246   // at the specified position in the trigger mask and trigger level (0,1,2)
247   TNamed *trginput = 0;
248   if (trglevel == 0) trginput = (TNamed *)fTriggerInputsNames.At(index);
249   if (trglevel == 1) trginput = (TNamed *)fTriggerInputsNames.At(index+24);  
250   if (trglevel == 2) trginput = (TNamed *)fTriggerInputsNames.At(index+48); 
251   if (trginput) return trginput->GetName();
252   else return "";
253 }
254 //______________________________________________________________________________
255 TString AliESDHeader::GetActiveTriggerInputs() const
256 {
257   // Returns the list with the names of the active trigger inputs
258   TString trginputs;
259   for(Int_t i = 0; i < kNTriggerInputs; i++) {
260     TNamed *str = (TNamed *)((fTriggerInputsNames).At(i));
261     if (str) {
262       trginputs += " ";
263       trginputs += str->GetName();
264       trginputs += " ";
265     }
266   }
267
268   return trginputs;
269 }
270 //______________________________________________________________________________
271 TString AliESDHeader::GetFiredTriggerInputs() const
272 {
273   // Returns the list with the names of the fired trigger inputs
274   TString trginputs;
275   for(Int_t i = 0; i < kNTriggerInputs; i++) {
276       TNamed *str = (TNamed *)((fTriggerInputsNames.At(i)));
277       if (i < 24 && (fL0TriggerInputs & (1 << i))) {
278         if (str) {
279           trginputs += " ";
280           trginputs += str->GetName();
281           trginputs += " ";
282         }
283       }
284       if (i >= 24 && i < 48 && (fL1TriggerInputs & (1 << (i-24)))) {
285         if (str) {
286           trginputs += " ";
287           trginputs += str->GetName();
288           trginputs += " ";
289         }
290       }
291       if (i >= 48 && (fL2TriggerInputs & (1 << (i-48)))) {
292         if (str) {
293           trginputs += " ";
294           trginputs += str->GetName();
295           trginputs += " ";
296         }
297       }
298
299   }
300   return trginputs;
301 }
302 //______________________________________________________________________________
303 Bool_t AliESDHeader::IsTriggerInputFired(const char *name) const
304 {
305   // Checks if the trigger input is fired 
306  
307   TNamed *trginput = (TNamed *)fTriggerInputsNames.FindObject(name);
308   if (!trginput) return kFALSE;
309
310   Int_t inputIndex = fTriggerInputsNames.IndexOf(trginput);
311   if (inputIndex < 0) return kFALSE;
312   
313   if (fL0TriggerInputs & (1 << inputIndex)) return kTRUE;
314   else if (fL1TriggerInputs & (1 << (inputIndex-24))) return kTRUE;
315   else if (fL2TriggerInputs & (1 << (inputIndex-48))) return kTRUE;
316   else return kFALSE;
317 }
318 //________________________________________________________________________________
319 Int_t  AliESDHeader::GetTriggerIREntries(Int_t int1, Int_t int2, Float_t deltaTime) const
320 {
321   // returns number of IR-s within time window deltaTime
322   // all possible combinations of int1 and int2 int1 - zdc bit, int2 v0 bit
323   //
324   const AliTriggerIR *IR;
325   // triggered event 
326   Int_t nIR = GetTriggerIREntries();
327   UInt_t orbit1 = GetOrbitNumber();
328   const Double_t ot=0.0889218; //orbit time msec
329   Float_t timediff; // time difference between orbits (msec)
330   //
331   Int_t nofIR;
332   nofIR=0;
333   // loop over IR-s
334     for(Int_t i=0;i<nIR;i++){//1
335       IR=GetTriggerIR(i);
336       //
337       UInt_t orbit2 = IR->GetOrbit();
338       timediff = (orbit2<=orbit1) ? (Float_t)((orbit1-orbit2))*ot : 
339         (Float_t)((16777215-orbit1+orbit2))*ot;
340       if (timediff>deltaTime) continue; //timediff outside time window
341       if((int1&int2) == -1){ //ignore both bits, just count IR-s within time window
342         nofIR++;
343         continue;
344       }
345       // now check if int1, int2 bits are set
346       UInt_t nw = IR->GetNWord();
347       Bool_t *bint1 = IR->GetInt1s();
348       Bool_t *bint2 = IR->GetInt2s();
349       //
350       Int_t flag1,flag2;
351       flag1=0;
352       flag2=0;
353       for(UInt_t j=0;j<nw;j++){//2
354         if(bint1[j]) flag1=1; // at least one int1 set
355         if(bint2[j]) flag2=1; //  at least one int2 set
356         //printf("IR %d, bint1 %d, bint2 %d\n",i,bint1[j],bint2[j]);
357       }//2
358       // checking combinations
359       //
360       
361       if((flag1*int1*flag2*int2)==1){// int1=1 & int2=1  
362           nofIR++;
363           continue;       
364       }
365       if(int1 == -1){// ignore int1
366         if(flag2&int2){// int2=1
367           nofIR++;
368           continue;
369         }
370         else if (!flag2&!int2){ //int2=0 
371           nofIR++;
372           continue;          
373         }
374       }
375       
376       if(int2 ==-1){//ignore int2
377         if(flag1&int1){//int1=1
378           nofIR++;
379           continue;  
380         }
381         else if(!flag1&!int1){ //int1=0
382           nofIR++;
383           continue;  
384         }
385       }
386       
387       if((flag1*int1)&!flag2&!int2){// int1=1, int2=0
388           nofIR++;
389           continue;  
390       }
391       
392       if((int2*flag2)&!int1&!flag1){// int1=0, int2=1
393           nofIR++;
394           continue;  
395       } 
396          
397       
398
399     }//1
400   
401     return nofIR;
402 }
403 //__________________________________________________________________________
404 TObjArray AliESDHeader::GetIRArray(Int_t int1, Int_t int2, Float_t deltaTime) const
405 {
406   //
407   // returns an array of IR-s within time window deltaTime
408   // all possible combinations of int1 and int2 int1 - zdc bit, int2 v0 bit
409   //
410   const AliTriggerIR *IR;
411   TObjArray arr;
412   // triggered event 
413   Int_t nIR = GetTriggerIREntries();
414   UInt_t orbit1 = GetOrbitNumber();
415   const Double_t ot=0.0889218; //orbit time msec
416   Float_t timediff; // time difference between orbits (msec)
417   //
418   // loop over IR-s
419     for(Int_t i=0;i<nIR;i++){//1
420       IR=GetTriggerIR(i);
421       //
422       UInt_t orbit2 = IR->GetOrbit();
423       timediff = (orbit2<=orbit1) ? (Float_t)((orbit1-orbit2))*ot : 
424         (Float_t)((16777215-orbit1+orbit2))*ot;
425       if (timediff>deltaTime) continue; //timediff outside time window
426       if((int1&int2) == -1){ //ignore both bits, just count IR-s within time window
427         arr.Add((AliTriggerIR*)IR); //add this IR
428         continue;
429       }
430       // now check if int1, int2 bits are set
431       UInt_t nw = IR->GetNWord();
432       Bool_t *bint1 = IR->GetInt1s();
433       Bool_t *bint2 = IR->GetInt2s();
434       //
435       Int_t flag1,flag2;
436       flag1=0;
437       flag2=0;
438       for(UInt_t j=0;j<nw;j++){//2
439         if(bint1[j]) flag1=1; // at least one int1 set
440         if(bint2[j]) flag2=1; //  at least one int2 set
441       }//2
442       // checking combinations
443       //
444       if((flag1*int1*flag2*int2)==1){// int1=1 & int2=1
445           arr.Add((AliTriggerIR*)IR); //add this IR
446           continue;       
447       }
448       if(int1 == -1){// ignore int1
449         if(flag2&int2){// int2=1
450           arr.Add((AliTriggerIR*)IR); //add this IR
451           continue;
452         }
453         else if (!flag2&!int2){ //int2=0 
454           arr.Add((AliTriggerIR*)IR); //add this IR
455           continue;          
456         }
457       }
458       if(int2 ==-1){//ignore int2
459         if(flag1&int1){//int1=1
460           arr.Add((AliTriggerIR*)IR); //add this IR
461           continue;  
462         }
463         else if(!flag1&!int1){ //int1=0
464           arr.Add((AliTriggerIR*)IR); //add this IR
465           continue;  
466         }
467       }
468       if ((flag1*int1)&!flag2&!int2){// int1=1, int2=0
469           arr.Add((AliTriggerIR*)IR); //add this IR
470           continue;  
471       }
472       if ((int2*flag2)&!int1&!flag1){// int1=0, int2=1
473           arr.Add((AliTriggerIR*)IR); //add this IR
474           continue;  
475       }      
476
477     }//1
478   
479   return arr;
480 }
481
482 //__________________________________________________________________________
483 void AliESDHeader::SetIRInteractionMap() const
484 {
485   //
486   // Function to compute the map of interations 
487   // within 0TVX (int2) or V0A&V0C (int1) and the Event Id 
488   // Note, the zero value is excluded
489   //
490   const AliTriggerIR *ir[5] = {GetTriggerIR(0),GetTriggerIR(1),GetTriggerIR(2),GetTriggerIR(3),GetTriggerIR(4)};
491
492   Long64_t orb = (Long64_t)GetOrbitNumber();
493   Long64_t bc = (Long64_t)GetBunchCrossNumber();
494   
495   Long64_t evId = orb*3564 + bc;
496
497   for(Int_t i = 0; i < 5; ++i) {
498     if (ir[i] == NULL || ir[i]->GetNWord() == 0) continue;
499     Long64_t irOrb = (Long64_t)ir[i]->GetOrbit();
500     Bool_t* int2 = ir[i]->GetInt2s();
501     Bool_t* int1 = ir[i]->GetInt1s();
502     UShort_t* bcs = ir[i]->GetBCs();
503     for(UInt_t nW = 0; nW < ir[i]->GetNWord(); ++nW) {
504       Long64_t intId = irOrb*3564 + (Long64_t)bcs[nW];
505       if (int2[nW] == kTRUE) {
506           Int_t item = (intId-evId);
507           Int_t bin = FindIRIntInteractionsBXMap(item);
508           if(bin>=0) {
509             fIRInt2InteractionsMap.SetBitNumber(bin,kTRUE);
510           }
511       }
512       if (int1[nW] == kTRUE) {
513           Int_t item = (intId-evId);
514           Int_t bin = FindIRIntInteractionsBXMap(item);
515           if(bin>=0) {
516             fIRInt1InteractionsMap.SetBitNumber(bin,kTRUE);
517           }
518       }
519     }
520   }
521
522   fIRInt2InteractionsMap.Compact();
523   fIRInt1InteractionsMap.Compact();
524 }
525
526 //__________________________________________________________________________
527 Int_t AliESDHeader::FindIRIntInteractionsBXMap(Int_t difference) const
528 {
529   //
530   // The mapping is of 181 bits, from -90 to +90
531   //
532   Int_t bin=-1;
533
534   if(difference<-90 || difference>90) return bin;
535   else { bin = 90 + difference; }
536   
537   return bin;
538 }
539
540 //__________________________________________________________________________
541 Int_t AliESDHeader::GetIRInt2ClosestInteractionMap() const
542 {
543   //
544   // Calculation of the closest interaction
545   //
546   SetIRInteractionMap();
547
548   Int_t firstNegative=100;
549   for(Int_t item=-1; item>=-90; item--) {
550     Int_t bin = FindIRIntInteractionsBXMap(item);
551     Bool_t isFired = fIRInt2InteractionsMap.TestBitNumber(bin);
552     if(isFired) {
553       firstNegative = item;
554       break;
555     }
556   }
557   Int_t firstPositive=100;
558   for(Int_t item=1; item<=90; item++) {
559     Int_t bin = FindIRIntInteractionsBXMap(item);
560     Bool_t isFired = fIRInt2InteractionsMap.TestBitNumber(bin);
561     if(isFired) {
562       firstPositive = item;
563       break;
564     }
565   }
566
567   Int_t closest = firstPositive < TMath::Abs(firstNegative) ? firstPositive : TMath::Abs(firstNegative);
568   if(firstPositive==100 && firstNegative==100) closest=0;
569   return closest;
570 }
571
572 //__________________________________________________________________________
573 Int_t AliESDHeader::GetIRInt1ClosestInteractionMap(Int_t gap) const
574 {
575   //
576   // Calculation of the closest interaction
577   // In case of VZERO (Int1) one has to introduce a gap
578   // in order to avoid false positivies from after-pulses
579
580   SetIRInteractionMap();
581
582   Int_t firstNegative=100;
583   for(Int_t item=-1; item>=-90; item--) {
584     Int_t bin = FindIRIntInteractionsBXMap(item);
585     Bool_t isFired = fIRInt1InteractionsMap.TestBitNumber(bin);
586     if(isFired) {
587       firstNegative = item;
588       break;
589     }
590   }
591   Int_t firstPositive=100;
592   for(Int_t item=1+gap; item<=90; item++) {
593     Int_t bin = FindIRIntInteractionsBXMap(item);
594     Bool_t isFired = fIRInt1InteractionsMap.TestBitNumber(bin);
595     if(isFired) {
596       firstPositive = item;
597       break;
598     }
599   }
600
601   Int_t closest = firstPositive < TMath::Abs(firstNegative) ? firstPositive : TMath::Abs(firstNegative);
602   if(firstPositive==100 && firstNegative==100) closest=0;
603   return closest;
604 }
605
606 //__________________________________________________________________________
607 Int_t  AliESDHeader::GetIRInt2LastInteractionMap() const
608 {
609   //
610   // Calculation of the last interaction
611   //
612   SetIRInteractionMap();
613
614   Int_t lastNegative=0;
615   for(Int_t item=-90; item<=-1; item++) {
616     Int_t bin = FindIRIntInteractionsBXMap(item);
617     Bool_t isFired = fIRInt2InteractionsMap.TestBitNumber(bin);
618     if(isFired) {
619       lastNegative = item;
620       break;
621     }
622   }
623   Int_t lastPositive=0;
624   for(Int_t item=90; item>=1; item--) {
625     Int_t bin = FindIRIntInteractionsBXMap(item);
626     Bool_t isFired = fIRInt2InteractionsMap.TestBitNumber(bin);
627     if(isFired) {
628       lastPositive = item;
629       break;
630     }
631   }
632
633   Int_t last = lastPositive > TMath::Abs(lastNegative) ? lastPositive : TMath::Abs(lastNegative);
634   return last;
635 }