source: trunk/MagicSoft/Mars/mdata/MDataChain.cc@ 1615

Last change on this file since 1615 was 1574, checked in by tbretz, 22 years ago
*** empty log message ***
File size: 16.6 KB
Line 
1/* ======================================================================== *\
2!
3! *
4! * This file is part of MARS, the MAGIC Analysis and Reconstruction
5! * Software. It is distributed to you in the hope that it can be a useful
6! * and timesaving tool in analysing Data of imaging Cerenkov telescopes.
7! * It is distributed WITHOUT ANY WARRANTY.
8! *
9! * Permission to use, copy, modify and distribute this software and its
10! * documentation for any purpose is hereby granted without fee,
11! * provided that the above copyright notice appear in all copies and
12! * that both that copyright notice and this permission notice appear
13! * in supporting documentation. It is provided "as is" without express
14! * or implied warranty.
15! *
16!
17!
18! Author(s): Thomas Bretz 04/2002 <mailto:tbretz@astro.uni-wuerzburg.de>
19!
20! Copyright: MAGIC Software Development, 2000-2002
21!
22!
23\* ======================================================================== */
24
25/////////////////////////////////////////////////////////////////////////////
26//
27// MDataChain
28//
29// With this chain you can concatenate simple mathematical operations on
30// members of mars containers.
31//
32// In the constructor you can give rule, like
33// "HillasSource.fDist / MHillas.fLength"
34// Where MHillas/HillasSource is the name of the parameter container in
35// the parameter list and fDist/fLength is the name of the data members
36// in the containers. The result will be fDist divided by fLength.
37//
38// You can also use brackets:
39// "HillasDource.fDist / (MHillas.fLength + MHillas.fWidth)"
40//
41// The allowed operations are: +, -, *, /
42//
43// Warning: There is no priority rule build in. So better use brackets
44// to get correct results. The rule is parsed/evaluated from the left
45// to the right, which means:
46//
47// "MHillas.fWidth + MHillas.fLength / HillasSource.fDist"
48//
49// is parses as
50//
51// "(MHillas.fWidth + MHillas.fLength) / HillasSource.fDist"
52//
53// You can also use mathmatical operators, eg:
54// "5*log10(MMcEvt.fEnergy*MHillas.fSize)"
55//
56// The allowed operators are:
57// exp(x) e^x
58// log(x) natural logarithm of x
59// pow10(x) 10^x
60// log10(x) logarithm of x to base ten
61// cos(x) cosine of x
62// sin(x) sine of x
63// tan(x) tangent of x
64// cosh(x) hyperbolic cosine of x
65// sinh(x) hyperbolic sine of x
66// tanh(x) hyperbolic tangent of x
67// acos(x) arc cosine (inverse cosine) of x
68// asin(x) arc sine (inverse sine) of x
69// atan(x) arc tangent (inverse tangent) of x
70// sqrt(x) square root of x
71// abs(x) absolute value of x, |x|
72//
73//
74// FIXME: The possibility to use other objects inheriting from MData
75// is missing.
76// Maybe we can use gInterpreter->Calc("") for this.
77// gROOT->ProcessLineFast("line");
78//
79/////////////////////////////////////////////////////////////////////////////
80
81#include "MDataChain.h"
82
83#include <math.h> // fabs on Alpha
84#include <ctype.h> // isalnum, ...
85#include <stdlib.h> // strtod, ...
86
87#include "MLog.h"
88#include "MLogManip.h"
89
90#include "MDataList.h"
91#include "MDataValue.h"
92#include "MDataMember.h"
93#include "MDataElement.h"
94
95ClassImp(MDataChain);
96
97// --------------------------------------------------------------------------
98//
99// Constructor which takes a rule and a surrounding operator as argument
100//
101MDataChain::MDataChain(const char *rule, OperatorType_t op)
102 : fOperatorType(op)
103{
104 fName = "MDataChain";
105 fTitle = rule;
106
107 fMember=ParseString(rule, 1);
108}
109
110// --------------------------------------------------------------------------
111//
112// Default constructor
113//
114MDataChain::MDataChain()
115 : fMember(NULL), fOperatorType(kENoop)
116{
117}
118
119// --------------------------------------------------------------------------
120//
121// Constructor taking a rule as an argument. For more details see
122// class description
123//
124MDataChain::MDataChain(const char *rule, const char *name, const char *title)
125 : fOperatorType(kENoop)
126{
127 fName = name ? name : "MDataChain";
128 fTitle = title ? title : rule;
129
130 *fLog << inf << "Trying to resolve rule... " << flush;
131 if (!(fMember=ParseString(rule, 1)))
132 {
133 *fLog << err << dbginf << "Parsing '" << rule << "' failed." << endl;
134 return;
135 }
136 *fLog << inf << "found: " << flush;
137 fMember->Print();
138 *fLog << endl;
139}
140
141// --------------------------------------------------------------------------
142//
143// PreProcesses all members in the list
144//
145Bool_t MDataChain::PreProcess(const MParList *pList)
146{
147 return fMember ? fMember->PreProcess(pList) : kFALSE;
148}
149
150// --------------------------------------------------------------------------
151//
152// Checks whether at least one member has the ready-to-save flag.
153//
154Bool_t MDataChain::IsReadyToSave() const
155{
156 *fLog << all << "fM=" << fMember << "/" << (int)fMember->IsReadyToSave() << " " << endl;
157 return fMember ? fMember->IsReadyToSave() : kFALSE;
158}
159
160// --------------------------------------------------------------------------
161//
162// Destructor. Delete filters.
163//
164MDataChain::~MDataChain()
165{
166 if (fMember)
167 delete fMember;
168}
169
170// --------------------------------------------------------------------------
171//
172// Returns the number of alphanumeric characters (including '.')
173// in the given string
174//
175Int_t MDataChain::IsAlNum(TString txt)
176{
177 int l = txt.Length();
178 for (int i=0; i<l; i++)
179 {
180 if (!isalnum(txt[i]) && txt[i]!='.' && /*txt[i]!='['&&txt[i]!=']'&&*/
181 ((txt[i]!='-' && txt[i]!='+') || i!=0))
182 return i;
183 }
184
185 return l;
186}
187
188Int_t MDataChain::GetBracket(TString txt, char open, char close)
189{
190 Int_t first=1;
191 for (int cnt=0; first<txt.Length(); first++)
192 {
193 if (txt[first]==open)
194 cnt++;
195 if (txt[first]==close)
196 cnt--;
197 if (cnt==-1)
198 break;
199 }
200 return first;
201}
202
203// --------------------------------------------------------------------------
204//
205// Compare a given text with the known operators. If no operator match
206// kENoop is retunred, otherwise the corresponding OperatorType_t
207//
208MDataChain::OperatorType_t MDataChain::ParseOperator(TString txt) const
209{
210 txt.ToLower();
211
212 if (txt=="abs") return kEAbs;
213 if (txt=="fabs") return kEAbs;
214 if (txt=="log") return kELog;
215 if (txt=="log10") return kELog10;
216 if (txt=="sin") return kESin;
217 if (txt=="cos") return kECos;
218 if (txt=="tan") return kETan;
219 if (txt=="sinh") return kESinH;
220 if (txt=="cosh") return kECosH;
221 if (txt=="tanh") return kETanH;
222 if (txt=="asin") return kEASin;
223 if (txt=="acos") return kEACos;
224 if (txt=="atan") return kEATan;
225 if (txt=="sqrt") return kESqrt;
226 if (txt=="exp") return kEExp;
227 if (txt=="pow10") return kEPow10;
228 if (txt=="sgn") return kESgn;
229 if (txt[0]=='-') return kENegative;
230 if (txt[0]=='+') return kEPositive;
231
232 return kENoop;
233}
234
235// --------------------------------------------------------------------------
236//
237// Core of the data chain. Here the chain is constructed out of the rule.
238//
239MData *MDataChain::ParseString(TString txt, Int_t level)
240{
241 MData *member0=NULL;
242
243 char type=0;
244 int nlist = 0;
245
246 while (!txt.IsNull())
247 {
248 MData *newmember = NULL;
249
250 txt = txt.Strip(TString::kBoth);
251
252 switch (txt[0])
253 {
254 case '(':
255 {
256 //
257 // Search for the corresponding bracket
258 //
259 Int_t first=GetBracket(txt, '(', ')');
260
261 if (first==txt.Length())
262 {
263 *fLog << err << dbginf << "Syntax Error: ')' missing." << endl;
264 if (member0)
265 delete member0;
266 return NULL;
267 }
268
269 //
270 // Make a copy of the 'interieur' and delete the substringä
271 // including the brackets
272 //
273 TString sub = txt(1, first-1);
274 txt.Remove(0, first+1);
275
276 //
277 // Parse the substring
278 //
279 newmember = ParseString(sub, level+1);
280 if (!newmember)
281 {
282 *fLog << err << dbginf << "Parsing '" << sub << "' failed." << endl;
283 if (member0)
284 delete member0;
285 return NULL;
286 }
287 }
288 break;
289
290 case ')':
291 *fLog << err << dbginf << "Syntax Error: Too many ')'" << endl;
292 if (member0)
293 delete member0;
294 return NULL;
295
296 case '+':
297 case '-':
298 case '*':
299 case '/':
300 if (member0)
301 {
302 //
303 // Check for the type of the symbol
304 //
305 char is = txt[0];
306 txt.Remove(0, 1);
307
308 //
309 // If no filter is available or the available filter
310 // is of a different symbols we have to create a new
311 // data list with the new symbol
312 //
313 if (/*!member0->InheritsFrom(MDataMember::Class()) ||*/ type!=is)
314 {
315 MDataList *list = new MDataList(is);
316 list->SetName(Form("List_%c_%d", is, 10*level+nlist++));
317
318 list->SetOwner();
319 list->AddToList(member0);
320
321 member0 = list;
322
323 type = is;
324 }
325 continue;
326 }
327
328 if (txt[0]!='-' && txt[0]!='+')
329 {
330 *fLog << err << dbginf << "Syntax Error: First argument of symbol '";
331 *fLog << txt[0] << "' missing." << endl;
332 if (member0)
333 delete member0;
334 return NULL;
335 }
336
337 // FALLTHROU
338
339 case '0':
340 case '1':
341 case '2':
342 case '3':
343 case '4':
344 case '5':
345 case '6':
346 case '7':
347 case '8':
348 case '9':
349 if ((txt[0]!='-' && txt[0]!='+') || isdigit(txt[1]) || txt[1]=='.')
350 {
351 char *end;
352 Double_t num = strtod(txt.Data(), &end);
353 if (!end || txt.Data()==end)
354 {
355 *fLog << err << dbginf << "Error trying to convert '" << txt << "' to value." << endl;
356 if (member0)
357 delete member0;
358 return NULL;
359 }
360
361 txt.Remove(0, end-txt.Data());
362
363 newmember = new MDataValue(num);
364 break;
365 }
366
367 // FALLTHROUH
368
369 default:
370 int i = IsAlNum(txt);
371
372 if (i==0)
373 {
374 *fLog << err << dbginf << "Syntax Error: Name of data member missing in '" << txt << "'" << endl;
375 if (member0)
376 delete member0;
377 return NULL;
378 }
379
380 TString text = txt(0, i);
381
382 txt.Remove(0, i);
383 txt = txt.Strip(TString::kBoth);
384
385 if (!txt.IsNull() && txt[0]=='[')
386 {
387 Int_t first = GetBracket(txt, '[', ']');
388 TString op = txt(1, first-1);
389 txt.Remove(0, first+1);
390
391 newmember = new MDataElement(text, atoi(op));
392 break;
393 }
394 if ((txt.IsNull() || txt[0]!='(') && text[0]!='-' && text[0]!='+')
395 {
396 newmember = new MDataMember(text.Data());
397 break;
398 }
399
400 OperatorType_t op = ParseOperator(text);
401 if (op==kENoop)
402 {
403 *fLog << err << dbginf << "Syntax Error: Operator '" << text << "' unknown." << endl;
404 if (member0)
405 delete member0;
406 return NULL;
407 }
408
409 Int_t first = GetBracket(txt, '(', ')');
410 TString sub = op==kENegative || op==kEPositive ? text.Remove(0,1) + txt : txt(1, first-1);
411 txt.Remove(0, first+1);
412
413 newmember = new MDataChain(sub, op);
414 if (!newmember->IsValid())
415 {
416 *fLog << err << dbginf << "Syntax Error: Error parsing contents '" << sub << "' of operator " << text << endl;
417 delete newmember;
418 if (member0)
419 delete member0;
420 return NULL;
421 }
422 }
423
424 if (!member0)
425 {
426 member0 = newmember;
427 continue;
428 }
429
430 if (!member0->InheritsFrom(MDataList::Class()))
431 continue;
432
433 ((MDataList*)member0)->AddToList(newmember);
434 }
435
436 return member0;
437}
438
439// --------------------------------------------------------------------------
440//
441// Returns the value described by the rule
442//
443Double_t MDataChain::GetValue() const
444{
445 if (!fMember)
446 {
447 *fLog << warn << "MDataChain not valid." << endl;
448 return 0;
449 }
450
451 const Double_t val = fMember->GetValue();
452
453 switch (fOperatorType)
454 {
455 case kEAbs: return fabs(val);
456 case kELog: return log(val);
457 case kELog10: return log10(val);
458 case kESin: return sin(val);
459 case kECos: return cos(val);
460 case kETan: return tan(val);
461 case kESinH: return sinh(val);
462 case kECosH: return cosh(val);
463 case kETanH: return tanh(val);
464 case kEASin: return asin(val);
465 case kEACos: return acos(val);
466 case kEATan: return atan(val);
467 case kESqrt: return sqrt(val);
468 case kEExp: return exp(val);
469 case kEPow10: return pow(10, val);
470 case kESgn: return val<0 ? -1 : 1;
471 case kENegative: return -val;
472 case kEPositive: return val;
473 case kENoop: return val;
474 }
475
476 *fLog << warn << "No Case for " << fOperatorType << " available." << endl;
477
478 return 0;
479}
480
481 /*
482void MDataChain::Print(Option_t *opt) const
483{
484 *fLog << GetRule() << flush;
485 Bool_t bracket = fOperatorType!=kENoop && !fMember->InheritsFrom(MDataList::Class());
486
487 switch (fOperatorType)
488 {
489 case kEAbs: *fLog << "abs" << flush; break;
490 case kELog: *fLog << "log" << flush; break;
491 case kELog10: *fLog << "log10" << flush; break;
492 case kESin: *fLog << "sin" << flush; break;
493 case kECos: *fLog << "cos" << flush; break;
494 case kETan: *fLog << "tan" << flush; break;
495 case kESinH: *fLog << "sinh" << flush; break;
496 case kECosH: *fLog << "cosh" << flush; break;
497 case kETanH: *fLog << "tanh" << flush; break;
498 case kEASin: *fLog << "asin" << flush; break;
499 case kEACos: *fLog << "acos" << flush; break;
500 case kEATan: *fLog << "atan" << flush; break;
501 case kESqrt: *fLog << "sqrt" << flush; break;
502 case kEExp: *fLog << "exp" << flush; break;
503 case kEPow10: *fLog << "pow10" << flush; break;
504 case kESgn: *fLog << "sgn" << flush; break;
505 case kENegative: *fLog << "-" << flush; break;
506 case kEPositive: *fLog << "+" << flush; break;
507 case kENoop:
508 break;
509 }
510
511 if (bracket)
512 *fLog << "(" << flush;
513
514 fMember->Print();
515
516 if (bracket)
517 *fLog << ")" << flush;
518 }
519 */
520
521// --------------------------------------------------------------------------
522//
523// Builds a rule from all the chain members. This is a rule which could
524// be used to rebuild the chain.
525//
526TString MDataChain::GetRule() const
527{
528 if (!fMember)
529 return "<n/a>";
530
531 TString str;
532
533 Bool_t bracket = fOperatorType!=kENoop && !fMember->InheritsFrom(MDataList::Class());
534
535 switch (fOperatorType)
536 {
537 case kEAbs: str += "abs" ; break;
538 case kELog: str += "log" ; break;
539 case kELog10: str += "log10"; break;
540 case kESin: str += "sin" ; break;
541 case kECos: str += "cos" ; break;
542 case kETan: str += "tan" ; break;
543 case kESinH: str += "sinh" ; break;
544 case kECosH: str += "cosh" ; break;
545 case kETanH: str += "tanh" ; break;
546 case kEASin: str += "asin" ; break;
547 case kEACos: str += "acos" ; break;
548 case kEATan: str += "atan" ; break;
549 case kESqrt: str += "sqrt" ; break;
550 case kEExp: str += "exp" ; break;
551 case kEPow10: str += "pow10"; break;
552 case kESgn: str += "sgn" ; break;
553 case kENegative: str += "-" ; break;
554 case kEPositive: str += "+" ; break;
555 case kENoop:
556 break;
557 }
558
559 if (bracket)
560 str += "(";
561
562 str += fMember->GetRule();
563
564 if (bracket)
565 str += ")";
566
567 return str;
568}
569
570// --------------------------------------------------------------------------
571//
572// Return a comma seperated list of all data members used in the chain.
573// This is mainly used in MTask::AddToBranchList
574//
575TString MDataChain::GetDataMember() const
576{
577 return fMember->GetDataMember();
578}
Note: See TracBrowser for help on using the repository browser.