]>
Commit | Line | Data |
---|---|---|
a19e2543 | 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 | // | |
18 | // MUON Raw Data generator and reader in ALICE-MUON | |
19 | // This class version 3 (further details could be found in Alice-note) | |
20 | // | |
21 | // Implemented non-constant buspatch numbers for tracking | |
22 | // with correct DDL id (first guess) | |
23 | // (Ch. Finck, dec 2005) | |
24 | // | |
25 | // Digits2Raw: | |
26 | // Generates raw data for MUON tracker and finally for trigger | |
27 | // Using real mapping (inverse) for tracker | |
28 | // For trigger there is no mapping (mapping could be found in AliMUONTriggerCircuit) | |
29 | // Ch. Finck july 04 | |
30 | // | |
31 | // Raw2Digits: | |
32 | // Using real mapping for tracker | |
33 | // Indranil Das (Adapted for runloader: Ch. Finck) july 05 | |
34 | // | |
35 | //////////////////////////////////// | |
36 | ||
37 | #include <fstream> | |
38 | #include <string> | |
39 | ||
40 | #include <TClonesArray.h> | |
41 | ||
42 | #include "AliLoader.h" | |
43 | #include "AliBitPacking.h" | |
44 | #include "AliRawReader.h" | |
45 | #include "AliLog.h" | |
46 | #include "AliRun.h" | |
47 | ||
48 | #include "AliMUON.h" | |
49 | #include "AliMUONRawReader.h" | |
50 | #include "AliMUONDigit.h" | |
51 | ||
52 | #include "AliMUONConstants.h" | |
53 | #include "AliMUONData.h" | |
54 | ||
55 | #include "AliMUONSubEventTracker.h" | |
56 | #include "AliMUONSubEventTrigger.h" | |
57 | #include "AliMUONDDLTracker.h" | |
58 | #include "AliMUONDDLTrigger.h" | |
59 | ||
60 | #include "AliMUONLocalTrigger.h" | |
61 | #include "AliMUONGlobalTrigger.h" | |
62 | ||
63 | #include "AliMUONGeometrySegmentation.h" | |
64 | #include "AliMUONGeometryModule.h" | |
65 | #include "AliMUONGeometryStore.h" | |
66 | #include "AliMpSegFactory.h" | |
67 | #include "AliMpPlaneType.h" | |
68 | #include "AliMpVSegmentation.h" | |
69 | #include "AliMpHelper.h" | |
70 | #include "AliMpPad.h" | |
71 | ||
72 | ||
73 | ClassImp(AliMUONRawReader) // Class implementation in ROOT context | |
74 | //__________________________________________________________________________ | |
75 | AliMUONRawReader::AliMUONRawReader(AliLoader* loader, AliMUONData* data) | |
76 | : TObject() | |
77 | { | |
78 | // Standard Constructor | |
79 | ||
80 | // initialize loader's | |
81 | fLoader = loader; | |
82 | ||
83 | // initialize segmentation factory | |
84 | fSegFactory = new AliMpSegFactory(); | |
85 | ||
86 | // initialize container | |
87 | fMUONData = data; | |
88 | ||
89 | // ddl pointer | |
90 | fDDLTracker = new AliMUONDDLTracker(); | |
91 | fDDLTrigger = new AliMUONDDLTrigger(); | |
92 | ||
93 | fBusPatchManager = new AliMpBusPatch(); | |
94 | fBusPatchManager->ReadBusPatchFile(); | |
95 | ||
96 | } | |
97 | ||
98 | //__________________________________________________________________________ | |
99 | AliMUONRawReader::AliMUONRawReader() | |
100 | : TObject(), | |
101 | fMUONData(0), | |
102 | fLoader(0), | |
103 | fSegFactory(0), | |
104 | fDDLTracker(0), | |
105 | fDDLTrigger(0) | |
106 | { | |
107 | // Default Constructor | |
108 | ||
109 | } | |
110 | ||
111 | //_______________________________________________________________________ | |
112 | AliMUONRawReader::AliMUONRawReader (const AliMUONRawReader& rhs) | |
113 | : TObject(rhs) | |
114 | { | |
115 | // Protected copy constructor | |
116 | ||
117 | AliFatal("Not implemented."); | |
118 | } | |
119 | ||
120 | //_______________________________________________________________________ | |
121 | AliMUONRawReader & | |
122 | AliMUONRawReader::operator=(const AliMUONRawReader& rhs) | |
123 | { | |
124 | // Protected assignement operator | |
125 | ||
126 | if (this == &rhs) return *this; | |
127 | ||
128 | AliFatal("Not implemented."); | |
129 | ||
130 | return *this; | |
131 | } | |
132 | ||
133 | //__________________________________________________________________________ | |
134 | AliMUONRawReader::~AliMUONRawReader(void) | |
135 | { | |
136 | if (fSegFactory) | |
137 | fSegFactory->DeleteSegmentations(); | |
138 | delete fSegFactory; | |
139 | ||
140 | if (fDDLTracker) | |
141 | delete fDDLTracker; | |
142 | if (fDDLTrigger) | |
143 | delete fDDLTrigger; | |
144 | ||
145 | fBusPatchManager->Delete(); | |
146 | ||
147 | return; | |
148 | } | |
149 | ||
150 | //____________________________________________________________________ | |
151 | Int_t AliMUONRawReader::Raw2Digits(AliRawReader* rawReader) | |
152 | { | |
153 | ||
154 | // generate digits | |
155 | ReadTrackerDDL(rawReader); | |
156 | ||
157 | // generate trigger | |
158 | ReadTriggerDDL(rawReader); | |
159 | ||
160 | return kTRUE; | |
161 | ||
162 | } | |
163 | ||
164 | //____________________________________________________________________ | |
165 | Int_t AliMUONRawReader::ReadTrackerDDL(AliRawReader* rawReader) | |
166 | { | |
167 | // reading tracker DDL | |
168 | // filling the TClonesArray in MUONData | |
169 | // | |
170 | ||
171 | AliMUONSubEventTracker* subEventTracker = new AliMUONSubEventTracker(); | |
172 | AliMUONDigit* digit = new AliMUONDigit(); | |
173 | ||
174 | ||
175 | //Read Header Size of DDL,Block,DSP and BusPatch. | |
176 | ||
177 | Int_t ddlHeaderSize = fDDLTracker->GetHeaderSize(); | |
178 | Int_t blockHeaderSize = fDDLTracker->GetBlkHeaderLength(); | |
179 | Int_t dspHeaderSize = fDDLTracker->GetDspHeaderLength(); | |
180 | Int_t buspatchHeaderSize = subEventTracker->GetHeaderLength(); | |
181 | ||
182 | Int_t totalDDLSize, totalBlockSize, totalDspSize , totalBusPatchSize, dataSize; | |
183 | ||
184 | ||
185 | Int_t iBusPerDSP[5];//number of bus patches per DSP | |
186 | Int_t iDspMax; //number max of DSP per block | |
187 | ||
188 | // minimum data size (only header's) | |
189 | Int_t blankDDLSize; | |
190 | Int_t blankBlockSize; | |
191 | Int_t blankDspSize; | |
192 | ||
193 | for(Int_t iDDL = 0; iDDL < 20; iDDL++) { // DDL loop | |
194 | ||
195 | AliDebug(3, Form("Chamber %d\n", iDDL/2 +1 )); | |
196 | ||
197 | // getting DSP info | |
198 | fBusPatchManager->GetDspInfo(iDDL/2, iDspMax, iBusPerDSP); | |
199 | ||
200 | // Each DDL is made with 2 Blocks each of which consists of 5 DSP's at most and each of DSP has at most 5 buspatches. | |
201 | // This information is used to calculate the size of headers (DDL,Block and DSP) which has no interesting data. | |
202 | blankDDLSize = ddlHeaderSize + 2*blockHeaderSize + 2*iDspMax*dspHeaderSize; | |
203 | blankBlockSize = blockHeaderSize + iDspMax*dspHeaderSize; | |
204 | ||
205 | for (Int_t i = 0; i < iDspMax; i++) { | |
206 | blankDDLSize += 2*iBusPerDSP[i]*buspatchHeaderSize; | |
207 | blankBlockSize += iBusPerDSP[i]*buspatchHeaderSize; | |
208 | } | |
209 | ||
210 | rawReader->Select(0X9, iDDL, iDDL); //Select the DDL file to be read | |
211 | ||
212 | rawReader->ReadHeader(); | |
213 | ||
214 | totalDDLSize = (rawReader->GetDataSize() + sizeof(AliRawDataHeader))/4; // 4 is multiplied to convert byte 2 words | |
215 | ||
216 | if(totalDDLSize > blankDDLSize) { // Compare the DDL header with an empty DDL header size to read the file | |
217 | ||
218 | Int_t totalDataWord = rawReader->GetDataSize()/4 ; | |
219 | UInt_t *buffer = new UInt_t[totalDataWord]; | |
220 | for(Int_t i = 0; i < totalDataWord; i++) { | |
221 | UInt_t& temp = buffer[i]; | |
222 | rawReader->ReadNextInt(temp); // takes the whole result into buffer variable for future analysis | |
223 | } | |
224 | ||
225 | // elex info | |
226 | Int_t buspatchId; | |
227 | UChar_t channelId; | |
228 | UShort_t manuId; | |
229 | Char_t parity; | |
230 | UShort_t charge; | |
231 | ||
232 | // indexes | |
233 | Int_t indexDsp; | |
234 | Int_t indexBusPatch; | |
235 | Int_t index = 0; | |
236 | ||
237 | for(Int_t iBlock = 0; iBlock < 2 ;iBlock++){ // loop over 2 blocks | |
238 | totalBlockSize = buffer[index]; | |
239 | ||
240 | if(totalBlockSize > blankBlockSize) { // compare block header | |
241 | index += blockHeaderSize; | |
242 | ||
243 | for(Int_t iDsp = 0; iDsp < iDspMax ;iDsp++){ //DSP loop | |
244 | ||
245 | totalDspSize = buffer[index]; | |
246 | indexDsp = index; | |
247 | ||
248 | blankDspSize = dspHeaderSize + iBusPerDSP[iDsp]*buspatchHeaderSize; // no data just header | |
249 | ||
250 | if(totalDspSize > blankDspSize) { // Compare DSP Header | |
251 | index += dspHeaderSize; | |
252 | ||
253 | for(Int_t iBusPatch = 0; iBusPatch < iBusPerDSP[iDsp]; iBusPatch++) { | |
254 | ||
255 | totalBusPatchSize = buffer[index]; | |
256 | buspatchId = buffer[index+2]; | |
257 | indexBusPatch = index; | |
258 | ||
259 | if(totalBusPatchSize > buspatchHeaderSize) { //Check Buspatch header | |
260 | ||
261 | index += buspatchHeaderSize; | |
262 | dataSize = totalBusPatchSize - buspatchHeaderSize; | |
263 | ||
264 | if(dataSize>0) { // check data present | |
265 | ||
266 | for(Int_t iData = 0; iData < dataSize; iData++) { | |
267 | ||
268 | subEventTracker->SetData(buffer[index++],iData); //Set to extract data | |
269 | // digits info | |
270 | parity = subEventTracker->GetParity(iData); // test later for parity | |
271 | manuId = subEventTracker->GetManuId(iData); | |
272 | channelId = subEventTracker->GetChannelId(iData); | |
273 | charge = subEventTracker->GetCharge(iData); | |
274 | // set charge | |
275 | digit->SetSignal(charge); | |
276 | ||
277 | Int_t error = GetMapping(buspatchId,manuId,channelId,digit); // Get Back the hits at pads | |
278 | if (error) continue; | |
279 | ||
280 | // debugging | |
281 | if (AliLog::GetGlobalDebugLevel() == 3) { | |
282 | Int_t padX = digit->PadX(); | |
283 | Int_t padY = digit->PadY(); | |
284 | Int_t iCath = digit->Cathode(); | |
285 | Int_t idDE = digit->DetElemId(); | |
286 | ||
287 | AliDebug(1,Form("output IdDE %d busPatchid %d PadX %d PadY %d iCath %d \n", | |
288 | idDE, buspatchId, padX, padY, iCath)); | |
289 | ||
290 | AliDebug(3,Form("idDE %d Padx %d Pady %d, Cath %d, charge %d",idDE, padX, padY, iCath, charge)); | |
291 | } | |
292 | ||
293 | // fill digits | |
294 | fMUONData->AddDigit(iDDL/2, *digit); | |
295 | ||
296 | } // data loop | |
297 | } // dataSize test | |
298 | } // testing buspatch | |
299 | ||
300 | index = indexBusPatch + totalBusPatchSize; | |
301 | ||
302 | } //buspatch loop | |
303 | ||
304 | } // dsp test | |
305 | ||
306 | index = indexDsp + totalDspSize; | |
307 | ||
308 | } // dsp loop | |
309 | ||
310 | } //block test | |
311 | ||
312 | index = totalBlockSize; | |
313 | ||
314 | } //block loop | |
315 | ||
316 | delete[] buffer; | |
317 | } //loop checking the header size of DDL | |
318 | ||
319 | //delete rawReader; | |
320 | } // DDL loop | |
321 | ||
322 | ||
323 | delete subEventTracker; | |
324 | delete digit; | |
325 | ||
326 | return kTRUE; | |
327 | } | |
328 | ||
329 | //____________________________________________________________________ | |
330 | Int_t AliMUONRawReader::GetMapping(Int_t busPatchId, UShort_t manuId, | |
331 | UChar_t channelId, AliMUONDigit* digit ) | |
332 | { | |
333 | ||
334 | // mapping for tracker | |
335 | ||
336 | // getting DE from buspatch | |
337 | Int_t idDE = fBusPatchManager->GetDEfromBus(busPatchId); | |
338 | AliDebug(3,Form("idDE: %d busPatchId %d\n", idDE, busPatchId)); | |
339 | ||
340 | // segmentation | |
341 | Int_t iCath; | |
342 | Int_t iCath1 = 0; | |
343 | Int_t iCath2 = 1; | |
344 | ||
345 | /* | |
346 | AliMpPlaneType plane; | |
347 | ||
348 | if (manuId > 1000) { // again tmp solution (ChF) (+1000 for Non-Bending plane | |
349 | plane = kNonBendingPlane; | |
350 | } else { | |
351 | plane = kBendingPlane; | |
352 | } | |
353 | */ | |
354 | ||
355 | if (idDE < 500) { // should use GetDirection somehow (ChF) | |
356 | if ( ((idDE % 100) % 2) != 0 ) { | |
357 | iCath1 = 1; | |
358 | iCath2 = 0; | |
359 | } | |
360 | } | |
361 | ||
362 | iCath = (manuId > 1000) ? iCath2 : iCath1; | |
363 | ||
364 | if (manuId > 1000) manuId -= 1000; // back to normal manuId | |
365 | ||
366 | // Could the above logic be simplified ??? | |
367 | //AliMpVSegmentation* seg = AliMUONSegmentationManager::Segmentation(idDE, plane); | |
368 | AliMpVSegmentation* seg = fSegFactory->CreateMpSegmentation(idDE, iCath); | |
369 | AliMpPad pad = seg->PadByLocation(AliMpIntPair(manuId,(Int_t)channelId),kTRUE); | |
370 | ||
371 | if(!pad.IsValid()){ | |
372 | AliWarning(Form("No pad for idDE: %d, busPatchId %d, manuId: %d, channelId: %d\n", | |
373 | idDE, busPatchId, manuId, channelId)); | |
374 | return kTRUE; | |
375 | } // return error | |
376 | ||
377 | // Getting padX | |
378 | Int_t padX = pad.GetIndices().GetFirst(); | |
379 | ||
380 | // Getting padY | |
381 | Int_t padY = pad.GetIndices().GetSecond(); | |
382 | ||
383 | if (idDE >= 500) { // Since in AliMpSlat pads begin at (0,0) | |
384 | padX++; // while in AliMUONSt345Seg. they begin at (1,1) | |
385 | padY++; | |
386 | } | |
387 | // storing into digits | |
388 | digit->SetPadX(padX); | |
389 | digit->SetPadY(padY); | |
390 | digit->SetCathode(iCath); | |
391 | digit->SetDetElemId(idDE); | |
392 | ||
393 | AliDebug(3,Form("idDE: %d, busPatchId %d, manuId: %d, channelId: %d, padx: %d pady %d\n", | |
394 | idDE, busPatchId, manuId, channelId, padX, padY)); | |
395 | return kFALSE; | |
396 | } | |
397 | ||
398 | //____________________________________________________________________ | |
399 | Int_t AliMUONRawReader::ReadTriggerDDL(AliRawReader* rawReader) | |
400 | { | |
401 | ||
402 | // reading DDL for trigger | |
403 | ||
404 | AliMUONSubEventTrigger* subEventTrigger = new AliMUONSubEventTrigger(); | |
405 | AliMUONGlobalTrigger* globalTrigger = 0x0; | |
406 | AliMUONLocalTrigger* localTrigger = new AliMUONLocalTrigger(); | |
407 | ||
408 | ||
409 | //Int_t ddlHeaderSize = fDDLTrigger->GetHeaderSize(); | |
410 | // we dont need this, as size of ddl data is same for triger and no trigger | |
411 | ||
412 | Int_t ddlEnhanceHeaderSize = fDDLTrigger->GetHeaderLength(); | |
413 | Int_t regHeaderLength = subEventTrigger->GetRegHeaderLength() ; | |
414 | ||
415 | Int_t loCircuit, loStripX, loDev, loStripY, loLpt, loHpt; | |
416 | Char_t loDecision; | |
417 | ||
418 | UShort_t x1Pattern, x2Pattern, x3Pattern, x4Pattern; | |
419 | UShort_t y1Pattern, y2Pattern, y3Pattern, y4Pattern; | |
420 | ||
421 | ||
422 | // loop over the two ddl's | |
423 | for(Int_t iDDL = 0; iDDL < 2; iDDL++) { //DDL loop | |
424 | ||
425 | rawReader->Select(0XA,iDDL,iDDL); //Select the DDL file to be read | |
426 | ||
427 | rawReader->ReadHeader(); | |
428 | ||
429 | Int_t totalDataWord = rawReader->GetDataSize()/4 ; | |
430 | UInt_t *buffer = new UInt_t[totalDataWord]; | |
431 | for(Int_t i=0;i<totalDataWord;i++){ | |
432 | UInt_t& temp = buffer[i]; | |
433 | rawReader->ReadNextInt(temp); // takes the whole result into buffer variable for future analysis | |
434 | } | |
435 | ||
436 | // rawReader->ReadNext((UChar_t*)buffer, totalDataWord); // method is protected ???? | |
437 | ||
438 | Int_t index = 0; | |
439 | ||
440 | // fill DDL header informations | |
441 | memcpy(fDDLTrigger->GetEnhancedHeader(), &buffer[index], ddlEnhanceHeaderSize*4); | |
442 | ||
443 | // fill global trigger information | |
444 | globalTrigger = GetGlobalTriggerPattern(fDDLTrigger->GetGlobalOuput()); | |
445 | fMUONData->AddGlobalTrigger(*globalTrigger); | |
446 | ||
447 | index += ddlEnhanceHeaderSize; | |
448 | ||
449 | // 8 regional boards | |
450 | for (Int_t iReg = 0; iReg < 8; iReg++) { //loop over regeonal card | |
451 | ||
452 | ||
453 | subEventTrigger->SetRegWord(buffer[index]); //read regional data | |
454 | ||
455 | index += regHeaderLength; | |
456 | ||
457 | // 16 local cards per regional board | |
458 | for (Int_t iLoc = 0; iLoc < 16; iLoc++) { //loop over local card | |
459 | ||
460 | Int_t iLocIndex = index; | |
461 | ||
462 | // 5 word trigger information | |
463 | for(Int_t iData = 0; iData < 5 ;iData++ ){ | |
464 | subEventTrigger->SetLocalData(buffer[index++],5*iLoc+iData); //read local data | |
465 | } | |
466 | ||
467 | if(buffer[iLocIndex] > 0) { | |
468 | ||
469 | loCircuit = (Int_t)subEventTrigger->GetLocalId(iLoc)+ 16*iReg + 128*iDDL; | |
470 | loStripX = (Int_t)subEventTrigger->GetXPos(iLoc); | |
471 | loStripY = (Int_t)subEventTrigger->GetYPos(iLoc); | |
472 | loDev = (Int_t)subEventTrigger->GetXDev(iLoc); | |
473 | ||
474 | // fill local trigger | |
475 | localTrigger->SetLoCircuit(loCircuit); | |
476 | localTrigger->SetLoStripX(loStripX ); | |
477 | localTrigger->SetLoStripY(loStripY); | |
478 | localTrigger->SetLoDev(loDev); | |
479 | ||
480 | loDecision = subEventTrigger->GetLocalDec(iLoc); | |
481 | loLpt = loDecision & 0x3; | |
482 | loHpt = (loDecision >> 2) & 0x3; | |
483 | ||
484 | // fill local trigger | |
485 | localTrigger->SetLoLpt(loLpt); | |
486 | localTrigger->SetLoHpt(loHpt); | |
487 | ||
488 | //getting pattern from subvent | |
489 | x1Pattern = subEventTrigger->GetX1(iLoc); | |
490 | x2Pattern = subEventTrigger->GetX2(iLoc); | |
491 | x3Pattern = subEventTrigger->GetX3(iLoc); | |
492 | x4Pattern = subEventTrigger->GetX4(iLoc); | |
493 | ||
494 | y1Pattern = subEventTrigger->GetY1(iLoc); | |
495 | y2Pattern = subEventTrigger->GetY2(iLoc); | |
496 | y3Pattern = subEventTrigger->GetY3(iLoc); | |
497 | y4Pattern = subEventTrigger->GetY4(iLoc); | |
498 | ||
499 | // fill local trigger | |
500 | localTrigger->SetX1Pattern(x1Pattern); | |
501 | localTrigger->SetX2Pattern(x2Pattern); | |
502 | localTrigger->SetX3Pattern(x3Pattern); | |
503 | localTrigger->SetX4Pattern(x4Pattern); | |
504 | ||
505 | localTrigger->SetY1Pattern(y1Pattern); | |
506 | localTrigger->SetY2Pattern(y2Pattern); | |
507 | localTrigger->SetY3Pattern(y3Pattern); | |
508 | localTrigger->SetY4Pattern(y4Pattern); | |
509 | fMUONData->AddLocalTrigger(*localTrigger); | |
510 | ||
511 | } | |
512 | ||
513 | } // local card loop | |
514 | ||
515 | } // regional card loop | |
516 | ||
517 | delete [] buffer; | |
518 | } // DDL loop | |
519 | ||
520 | delete subEventTrigger; | |
521 | delete globalTrigger; | |
522 | delete localTrigger; | |
523 | ||
524 | return kTRUE; | |
525 | ||
526 | } | |
527 | //____________________________________________________________________ | |
528 | AliMUONGlobalTrigger* AliMUONRawReader::GetGlobalTriggerPattern(Int_t gloTrigPat) const | |
529 | { | |
530 | // global trigger pattern calculation | |
531 | ||
532 | Int_t globalSinglePlus[3]; // tot num of single plus | |
533 | Int_t globalSingleMinus[3]; // tot num of single minus | |
534 | Int_t globalSingleUndef[3]; // tot num of single undefined | |
535 | Int_t globalPairUnlike[3]; // tot num of unlike-sign pairs | |
536 | Int_t globalPairLike[3]; // tot num of like-sign pairs | |
537 | ||
538 | ||
539 | for (Int_t i = 0; i < 3; i++) { | |
540 | globalSinglePlus[i] = gloTrigPat & (0x1 << i); | |
541 | globalSingleMinus[i] = gloTrigPat & (0x1 << i+3); | |
542 | globalSingleUndef[i] = gloTrigPat & (0x1 << i+6); | |
543 | globalPairUnlike[i] = gloTrigPat & (0x1 << i+9); | |
544 | globalPairLike[i] = gloTrigPat & (0x1 << i+12); | |
545 | } | |
546 | ||
547 | return (new AliMUONGlobalTrigger(globalSinglePlus, globalSingleMinus, | |
548 | globalSingleUndef, globalPairUnlike, | |
549 | globalPairLike)); | |
550 | ||
551 | } | |
552 |