Logo ROOT  
Reference Guide
TFormula.cxx
Go to the documentation of this file.
1 // @(#)root/hist:$Id$
2 // Author: Maciej Zimnoch 30/09/2013
3 
4 /*************************************************************************
5  * Copyright (C) 1995-2013, Rene Brun and Fons Rademakers. *
6  * All rights reserved. *
7  * *
8  * For the licensing terms see $ROOTSYS/LICENSE. *
9  * For the list of contributors see $ROOTSYS/README/CREDITS. *
10  *************************************************************************/
11 
12 #include "TROOT.h"
13 #include "TBuffer.h"
14 #include "TMethod.h"
15 #include "TMath.h"
16 #include "TF1.h"
17 #include "TMethodCall.h"
18 #include <TBenchmark.h>
19 #include "TError.h"
20 #include "TInterpreter.h"
21 #include "TInterpreterValue.h"
22 #include "TFormula.h"
23 #include "TRegexp.h"
24 #include <array>
25 #include <cassert>
26 #include <iostream>
27 #include <unordered_map>
28 #include <functional>
29 #include <set>
30 
31 using namespace std;
32 
33 #ifdef WIN32
34 #pragma optimize("",off)
35 #endif
36 #include "v5/TFormula.h"
37 
39 
40 /** \class TFormula TFormula.h "inc/TFormula.h"
41  \ingroup Hist
42  The Formula class
43 
44  This is a new version of the TFormula class based on Cling.
45  This class is not 100% backward compatible with the old TFormula class, which is still available in ROOT as
46  `ROOT::v5::TFormula`. Some of the TFormula member functions available in version 5, such as
47  `Analyze` and `AnalyzeFunction` are not available in the new TFormula.
48  On the other hand formula expressions which were valid in version 5 are still valid in TFormula version 6
49 
50  This class has been implemented during Google Summer of Code 2013 by Maciej Zimnoch.
51 
52  ### Example of valid expressions:
53 
54  - `sin(x)/x`
55  - `[0]*sin(x) + [1]*exp(-[2]*x)`
56  - `x + y**2`
57  - `x^2 + y^2`
58  - `[0]*pow([1],4)`
59  - `2*pi*sqrt(x/y)`
60  - `gaus(0)*expo(3) + ypol3(5)*x`
61  - `gausn(0)*expo(3) + ypol3(5)*x`
62  - `gaus(x, [0..2]) + expo(y, [3..4])`
63 
64  In the last examples above:
65 
66  - `gaus(0)` is a substitute for `[0]*exp(-0.5*((x-[1])/[2])**2)`
67  and (0) means start numbering parameters at 0
68  - `gausn(0)` is a substitute for `[0]*exp(-0.5*((x-[1])/[2])**2)/(sqrt(2*pi)*[2]))`
69  and (0) means start numbering parameters at 0
70  - `expo(3)` is a substitute for `exp([3]+[4]*x)`
71  - `pol3(5)` is a substitute for `par[5]+par[6]*x+par[7]*x**2+par[8]*x**3`
72  (`PolN` stands for Polynomial of degree N)
73  - `gaus(x, [0..2])` is a more explicit way of writing `gaus(0)`
74  - `expo(y, [3..4])` is a substitute for `exp([3]+[4]*y)`
75 
76  `TMath` functions can be part of the expression, eg:
77 
78  - `TMath::Landau(x)*sin(x)`
79  - `TMath::Erf(x)`
80 
81  Formula may contain constants, eg:
82 
83  - `sqrt2`
84  - `e`
85  - `pi`
86  - `ln10`
87  - `infinity`
88 
89  and more.
90 
91  Formulas may also contain other user-defined ROOT functions defined with a
92  TFormula, eg, where `f1` is defined on one x-dimension and 2 parameters:
93 
94  - `f1(x, [omega], [phi])`
95  - `f1([0..1])`
96  - `f1([1], [0])`
97  - `f1(y)`
98 
99  To replace only parameter names, the dimension variable can be dropped.
100  Alternatively, to change only the dimension variable, the parameters can be
101  dropped. Note that if a parameter is dropped or keeps its old name, its old
102  value will be copied to the new function. The syntax used in the examples
103  above also applies to the predefined parametrized functions like `gaus` and
104  `expo`.
105 
106  Comparisons operators are also supported `(&amp;&amp;, ||, ==, &lt;=, &gt;=, !)`
107 
108  Examples:
109 
110  `sin(x*(x&lt;0.5 || x&gt;1))`
111 
112  If the result of a comparison is TRUE, the result is 1, otherwise 0.
113 
114  Already predefined names can be given. For example, if the formula
115 
116  `TFormula old("old",sin(x*(x&lt;0.5 || x&gt;1)))`
117 
118  one can assign a name to the formula. By default the name of the object = title = formula itself.
119 
120  `TFormula new("new","x*old")`
121 
122  is equivalent to:
123 
124  `TFormula new("new","x*sin(x*(x&lt;0.5 || x&gt;1))")`
125 
126  The class supports unlimited number of variables and parameters.
127  By default the names which can be used for the variables are `x,y,z,t` or
128  `x[0],x[1],x[2],x[3],....x[N]` for N-dimensional formulas.
129 
130  This class is not anymore the base class for the function classes `TF1`, but it has now
131  a data member of TF1 which can be accessed via `TF1::GetFormula`.
132 
133  ### An expanded note on variables and parameters
134 
135  In a TFormula, a variable is a defined by a name `x`, `y`, `z` or `t` or an
136  index like `x[0]`, `x[1]`, `x[2]`; that is `x[N]` where N is an integer.
137 
138  ```
139  TFormula("", "x[0] * x[1] + 10")
140  ```
141 
142  Parameters are similar and can take any name. It is specified using brackets
143  e.g. `[expected_mass]` or `[0]`.
144 
145  ```
146  TFormula("", "exp([expected_mass])-1")
147  ```
148 
149  Variables and parameters can be combined in the same TFormula. Here we consider
150  a very simple case where we have an exponential decay after some time t and a
151  number of events with timestamps for which we want to evaluate this function.
152 
153  ```
154  TFormula tf ("", "[0]*exp(-[1]*t)");
155  tf.SetParameter(0, 1);
156  tf.SetParameter(1, 0.5);
157 
158  for (auto & event : events) {
159  tf.Eval(event.t);
160  }
161  ```
162 
163  The distinction between variables and parameters arose from the TFormula's
164  application in fitting. There parameters are fitted to the data provided
165  through variables. In other applications this distinction can go away.
166 
167  Parameter values can be provided dynamically using `TFormula::EvalPar`
168  instead of `TFormula::Eval`. In this way parameters can be used identically
169  to variables. See below for an example that uses only parameters to model a
170  function.
171 
172  ```
173  Int_t params[2] = {1, 2}; // {vel_x, vel_y}
174  TFormula tf ("", "[vel_x]/sqrt(([vel_x + vel_y])**2)");
175 
176  tf.EvalPar(nullptr, params);
177  ```
178 
179  ### A note on operators
180 
181  All operators of C/C++ are allowed in a TFormula with a few caveats.
182 
183  The operators `|`, `&`, `%` can be used but will raise an error if used in
184  conjunction with a variable or a parameter. Variables and parameters are treated
185  as doubles internally for which these operators are not defined.
186  This means the following command will run successfully
187  ```root -l -q -e TFormula("", "x+(10%3)").Eval(0)```
188  but not
189  ```root -l -q -e TFormula("", "x%10").Eval(0)```.
190 
191  The operator `^` is defined to mean exponentiation instead of the C/C++
192  interpretaion xor. `**` is added, also meaning exponentiation.
193 
194  The operators `++` and `@` are added, and are shorthand for the a linear
195  function. That means the expression `x@2` will be expanded to
196  ```[n]*x + [n+1]*2``` where n is the first previously unused parameter number.
197 
198  \class TFormulaFunction
199  Helper class for TFormula
200 
201  \class TFormulaVariable
202  Another helper class for TFormula
203 
204  \class TFormulaParamOrder
205  Functor defining the parameter order
206 */
207 
208 // prefix used for function name passed to Cling
209 static const TString gNamePrefix = "TFormula__";
210 
211 // static map of function pointers and expressions
212 //static std::unordered_map<std::string, TInterpreter::CallFuncIFacePtr_t::Generic_t> gClingFunctions = std::unordered_map<TString, TInterpreter::CallFuncIFacePtr_t::Generic_t>();
213 static std::unordered_map<std::string, void *> gClingFunctions = std::unordered_map<std::string, void * >();
214 
215 static void R__v5TFormulaUpdater(Int_t nobjects, TObject **from, TObject **to)
216 {
217  auto **fromv5 = (ROOT::v5::TFormula **)from;
218  auto **target = (TFormula **)to;
219 
220  for (int i = 0; i < nobjects; ++i) {
221  if (fromv5[i] && target[i]) {
222  TFormula fnew(fromv5[i]->GetName(), fromv5[i]->GetExpFormula());
223  *(target[i]) = fnew;
224  target[i]->SetParameters(fromv5[i]->GetParameters());
225  }
226  }
227 }
228 
229 using TFormulaUpdater_t = void (*)(Int_t nobjects, TObject **from, TObject **to);
231 
233 
234 ////////////////////////////////////////////////////////////////////////////////
236 {
237  // operator ":" must be handled separately
238  const static std::set<char> ops {'+','^','-','/','*','<','>','|','&','!','=','?','%'};
239  return ops.end() != ops.find(c);
240 }
241 
242 ////////////////////////////////////////////////////////////////////////////////
244 {
245  // Note that square brackets do not count as brackets here!!!
246  char brackets[] = { ')','(','{','}'};
247  Int_t bracketsLen = sizeof(brackets)/sizeof(char);
248  for(Int_t i = 0; i < bracketsLen; ++i)
249  if(brackets[i] == c)
250  return true;
251  return false;
252 }
253 
254 ////////////////////////////////////////////////////////////////////////////////
256 {
257  return !IsBracket(c) && !IsOperator(c) && c != ',' && c != ' ';
258 }
259 
260 ////////////////////////////////////////////////////////////////////////////////
262 {
263  return name == "x" || name == "z" || name == "y" || name == "t";
264 }
265 
266 ////////////////////////////////////////////////////////////////////////////////
268 {
269  // check if the character at position i is part of a scientific notation
270  if ( (formula[i] == 'e' || formula[i] == 'E') && (i > 0 && i < formula.Length()-1) ) {
271  // handle cases: 2e+3 2e-3 2e3 and 2.e+3
272  if ( (isdigit(formula[i-1]) || formula[i-1] == '.') && ( isdigit(formula[i+1]) || formula[i+1] == '+' || formula[i+1] == '-' ) )
273  return true;
274  }
275  return false;
276 }
277 
278 ////////////////////////////////////////////////////////////////////////////////
279 Bool_t TFormula::IsHexadecimal(const TString & formula, int i)
280 {
281  // check if the character at position i is part of a scientific notation
282  if ( (formula[i] == 'x' || formula[i] == 'X') && (i > 0 && i < formula.Length()-1) && formula[i-1] == '0') {
283  if (isdigit(formula[i+1]) )
284  return true;
285  static char hex_values[12] = { 'a','A', 'b','B','c','C','d','D','e','E','f','F'};
286  for (int jjj = 0; jjj < 12; ++jjj) {
287  if (formula[i+1] == hex_values[jjj])
288  return true;
289  }
290  }
291  // else
292  // return false;
293  // // handle cases: 2e+3 2e-3 2e3 and 2.e+3
294  // if ( (isdigit(formula[i-1]) || formula[i-1] == '.') && ( isdigit(formula[i+1]) || formula[i+1] == '+' || formula[i+1] == '-' ) )
295  // return true;
296  // }
297  return false;
298 }
299 ////////////////////////////////////////////////////////////////////////////
300 // check is given position is in a parameter name i.e. within "[ ]"
301 ////
302 Bool_t TFormula::IsAParameterName(const TString & formula, int pos) {
303 
304  Bool_t foundOpenParenthesis = false;
305  if (pos == 0 || pos == formula.Length()-1) return false;
306  for (int i = pos-1; i >=0; i--) {
307  if (formula[i] == ']' ) return false;
308  if (formula[i] == '[' ) {
309  foundOpenParenthesis = true;
310  break;
311  }
312  }
313  if (!foundOpenParenthesis ) return false;
314 
315  // search after the position
316  for (int i = pos+1; i < formula.Length(); i++) {
317  if (formula[i] == ']' ) return true;
318  }
319  return false;
320 }
321 
322 
323 ////////////////////////////////////////////////////////////////////////////////
324 bool TFormulaParamOrder::operator() (const TString& a, const TString& b) const {
325  // implement comparison used to set parameter orders in TFormula
326  // want p2 to be before p10
327 
328  // Returns true if (a < b), meaning a comes before b, and false if (a >= b)
329 
330  TRegexp numericPattern("p?[0-9]+");
331  Ssiz_t len; // buffer to store length of regex match
332 
333  int patternStart = numericPattern.Index(a, &len);
334  bool aNumeric = (patternStart == 0 && len == a.Length());
335 
336  patternStart = numericPattern.Index(b, &len);
337  bool bNumeric = (patternStart == 0 && len == b.Length());
338 
339  if (aNumeric && !bNumeric)
340  return true; // assume a (numeric) is always before b (not numeric)
341  else if (!aNumeric && bNumeric)
342  return false; // b comes before a
343  else if (!aNumeric && !bNumeric)
344  return a < b;
345  else {
346  int aInt = (a[0] == 'p') ? TString(a(1, a.Length())).Atoi() : a.Atoi();
347  int bInt = (b[0] == 'p') ? TString(b(1, b.Length())).Atoi() : b.Atoi();
348  return aInt < bInt;
349  }
350 
351 }
352 
353 ////////////////////////////////////////////////////////////////////////////////
354 void TFormula::ReplaceAllNames(TString &formula, map<TString, TString> &substitutions)
355 {
356  /// Apply the name substitutions to the formula, doing all replacements in one pass
357 
358  for (int i = 0; i < formula.Length(); i++) {
359  // start of name
360  // (a little subtle, since we want to match names like "{V0}" and "[0]")
361  if (isalpha(formula[i]) || formula[i] == '{' || formula[i] == '[') {
362  int j; // index to end of name
363  for (j = i + 1;
364  j < formula.Length() && (IsFunctionNameChar(formula[j]) // square brackets are function name chars
365  || (formula[i] == '{' && formula[j] == '}'));
366  j++)
367  ;
368  TString name = (TString)formula(i, j - i);
369 
370  // std::cout << "Looking for name: " << name << std::endl;
371 
372  // if we find the name, do the substitution
373  if (substitutions.find(name) != substitutions.end()) {
374  formula.Replace(i, name.Length(), "(" + substitutions[name] + ")");
375  i += substitutions[name].Length() + 2 - 1; // +2 for parentheses
376  // std::cout << "made substitution: " << name << " to " << substitutions[name] << std::endl;
377  } else if (isalpha(formula[i])) {
378  // if formula[i] is alpha, can skip to end of candidate name, otherwise, we'll just
379  // move one character ahead and try again
380  i += name.Length() - 1;
381  }
382  }
383  }
384 }
385 
386 ////////////////////////////////////////////////////////////////////////////////
388 {
389  fName = "";
390  fTitle = "";
391  fClingInput = "";
392  fReadyToExecute = false;
393  fClingInitialized = false;
394  fAllParametersSetted = false;
395  fMethod = 0;
396  fNdim = 0;
397  fNpar = 0;
398  fNumber = 0;
399  fClingName = "";
400  fFormula = "";
401  fLambdaPtr = nullptr;
402 }
403 
404 ////////////////////////////////////////////////////////////////////////////////
405 static bool IsReservedName(const char* name){
406  if (strlen(name)!=1) return false;
407  for (auto const & specialName : {"x","y","z","t"}){
408  if (strcmp(name,specialName)==0) return true;
409  }
410  return false;
411 }
412 
413 ////////////////////////////////////////////////////////////////////////////////
415 {
416 
417  // N.B. a memory leak may happen if user set bit after constructing the object,
418  // Setting of bit should be done only internally
419  if (!TestBit(TFormula::kNotGlobal) && gROOT ) {
421  gROOT->GetListOfFunctions()->Remove(this);
422  }
423 
424  if (fMethod) {
425  fMethod->Delete();
426  }
427  int nLinParts = fLinearParts.size();
428  if (nLinParts > 0) {
429  for (int i = 0; i < nLinParts; ++i) delete fLinearParts[i];
430  }
431 }
432 
433 ////////////////////////////////////////////////////////////////////////////////
434 TFormula::TFormula(const char *name, const char *formula, bool addToGlobList, bool vectorize) :
435  TNamed(name,formula),
436  fClingInput(formula),fFormula(formula)
437 {
438  fReadyToExecute = false;
439  fClingInitialized = false;
440  fMethod = 0;
441  fNdim = 0;
442  fNpar = 0;
443  fNumber = 0;
444  fMethod = 0;
445  fLambdaPtr = nullptr;
446  fVectorized = vectorize;
447 #ifndef R__HAS_VECCORE
448  fVectorized = false;
449 #endif
450 
451  FillDefaults();
452 
453 
454  if (addToGlobList && gROOT) {
455  TFormula *old = 0;
457  old = dynamic_cast<TFormula*> ( gROOT->GetListOfFunctions()->FindObject(name) );
458  if (old)
459  gROOT->GetListOfFunctions()->Remove(old);
460  if (IsReservedName(name))
461  Error("TFormula","The name %s is reserved as a TFormula variable name.\n",name);
462  else
463  gROOT->GetListOfFunctions()->Add(this);
464  }
465  SetBit(kNotGlobal,!addToGlobList);
466 
467  //fName = gNamePrefix + name; // is this needed
468 
469  // do not process null formulas.
470  if (!fFormula.IsNull() ) {
472 
474  }
475 
476 }
477 
478 ////////////////////////////////////////////////////////////////////////////////
479 /// Constructor from a full compile-able C++ expression
480 
481 TFormula::TFormula(const char *name, const char *formula, int ndim, int npar, bool addToGlobList) :
482  TNamed(name,formula),
483  fClingInput(formula),fFormula(formula)
484 {
485  fReadyToExecute = false;
486  fClingInitialized = false;
487  fNpar = 0;
488  fMethod = nullptr;
489  fNumber = 0;
490  fLambdaPtr = nullptr;
491  fFuncPtr = nullptr;
492  fGradFuncPtr = nullptr;
493 
494 
495  fNdim = ndim;
496  for (int i = 0; i < npar; ++i) {
497  DoAddParameter(TString::Format("p%d",i), 0, false);
498  }
499  fAllParametersSetted = true;
500  assert (fNpar == npar);
501 
502  bool ret = InitLambdaExpression(formula);
503 
504  if (ret) {
505 
507 
508  fReadyToExecute = true;
509 
510  if (addToGlobList && gROOT) {
511  TFormula *old = 0;
513  old = dynamic_cast<TFormula*> ( gROOT->GetListOfFunctions()->FindObject(name) );
514  if (old)
515  gROOT->GetListOfFunctions()->Remove(old);
516  if (IsReservedName(name))
517  Error("TFormula","The name %s is reserved as a TFormula variable name.\n",name);
518  else
519  gROOT->GetListOfFunctions()->Add(this);
520  }
521  SetBit(kNotGlobal,!addToGlobList);
522  }
523  else
524  Error("TFormula","Syntax error in building the lambda expression %s", formula );
525 }
526 
527 ////////////////////////////////////////////////////////////////////////////////
528 TFormula::TFormula(const TFormula &formula) :
529  TNamed(formula.GetName(),formula.GetTitle()), fMethod(nullptr)
530 {
531  formula.Copy(*this);
532 
533  if (!TestBit(TFormula::kNotGlobal) && gROOT ) {
535  TFormula *old = (TFormula*)gROOT->GetListOfFunctions()->FindObject(formula.GetName());
536  if (old)
537  gROOT->GetListOfFunctions()->Remove(old);
538 
539  if (IsReservedName(formula.GetName())) {
540  Error("TFormula","The name %s is reserved as a TFormula variable name.\n",formula.GetName());
541  } else
542  gROOT->GetListOfFunctions()->Add(this);
543  }
544 
545 }
546 
547 ////////////////////////////////////////////////////////////////////////////////
548 /// = operator.
549 
551 {
552 
553  if (this != &rhs) {
554  rhs.Copy(*this);
555  }
556  return *this;
557 }
558 
559 ////////////////////////////////////////////////////////////////////////////////
560 Bool_t TFormula::InitLambdaExpression(const char * formula) {
561 
562  std::string lambdaExpression = formula;
563 
564  // check if formula exist already in the map
565  {
567 
568  auto funcit = gClingFunctions.find(lambdaExpression);
569  if (funcit != gClingFunctions.end() ) {
570  fLambdaPtr = funcit->second;
571  fClingInitialized = true;
572  return true;
573  }
574  }
575 
576  // to be sure the interpreter is initialized
577  ROOT::GetROOT();
579 
580  // set the cling name using hash of the static formulae map
581  auto hasher = gClingFunctions.hash_function();
582  TString lambdaName = TString::Format("lambda__id%zu", hasher(lambdaExpression) );
583 
584  //lambdaExpression = TString::Format("[&](double * x, double *){ return %s ;}",formula);
585  //TString lambdaName = TString::Format("mylambda_%s",GetName() );
586  TString lineExpr = TString::Format("std::function<double(double*,double*)> %s = %s ;",lambdaName.Data(), lambdaExpression.c_str() );
587  gInterpreter->ProcessLine(lineExpr);
588  fLambdaPtr = (void*) gInterpreter->ProcessLine(TString(lambdaName)+TString(";")); // add ; to avoid printing
589  if (fLambdaPtr != nullptr) {
591  gClingFunctions.insert ( std::make_pair ( lambdaExpression, fLambdaPtr) );
592  fClingInitialized = true;
593  return true;
594  }
595  fClingInitialized = false;
596  return false;
597 }
598 
599 ////////////////////////////////////////////////////////////////////////////////
600 /// Compile the given expression with Cling
601 /// backward compatibility method to be used in combination with the empty constructor
602 /// if no expression is given , the current stored formula (retrieved with GetExpFormula()) or the title is used.
603 /// return 0 if the formula compilation is successful
604 
605 Int_t TFormula::Compile(const char *expression)
606 {
607  TString formula = expression;
608  if (formula.IsNull() ) {
609  formula = fFormula;
610  if (formula.IsNull() ) formula = GetTitle();
611  }
612 
613  if (formula.IsNull() ) return -1;
614 
615  // do not re-process if it was done before
616  if (IsValid() && formula == fFormula ) return 0;
617 
618  // clear if a formula was already existing
619  if (!fFormula.IsNull() ) Clear();
620 
621  fFormula = formula;
622 
623  if (TestBit(TFormula::kLambda) ) {
624  bool ret = InitLambdaExpression(fFormula);
625  return (ret) ? 0 : 1;
626  }
627 
628  if (fVars.empty() ) FillDefaults();
629  // prepare the formula for Cling
630  //printf("compile: processing formula %s\n",fFormula.Data() );
632  // pass formula in CLing
633  bool ret = PrepareFormula(fFormula);
634 
635  return (ret) ? 0 : 1;
636 }
637 
638 ////////////////////////////////////////////////////////////////////////////////
639 void TFormula::Copy(TObject &obj) const
640 {
641  TNamed::Copy(obj);
642  // need to copy also cling parameters
643  TFormula & fnew = dynamic_cast<TFormula&>(obj);
644 
647 
648  fnew.fFuncs = fFuncs;
649  fnew.fVars = fVars;
650  fnew.fParams = fParams;
651  fnew.fConsts = fConsts;
653  fnew.fFormula = fFormula;
654  fnew.fNdim = fNdim;
655  fnew.fNpar = fNpar;
656  fnew.fNumber = fNumber;
657  fnew.fVectorized = fVectorized;
659  // copy Linear parts (it is a vector of TFormula pointers) needs to be copied one by one
660  // looping at all the elements
661  // delete first previous elements
662  int nLinParts = fnew.fLinearParts.size();
663  if (nLinParts > 0) {
664  for (int i = 0; i < nLinParts; ++i) delete fnew.fLinearParts[i];
665  fnew.fLinearParts.clear();
666  }
667  // old size that needs to be copied
668  nLinParts = fLinearParts.size();
669  if (nLinParts > 0) {
670  fnew.fLinearParts.reserve(nLinParts);
671  for (int i = 0; i < nLinParts; ++i) {
672  TFormula * linearNew = new TFormula();
673  TFormula * linearOld = (TFormula*) fLinearParts[i];
674  if (linearOld) {
675  linearOld->Copy(*linearNew);
676  fnew.fLinearParts.push_back(linearNew);
677  }
678  else
679  Warning("Copy","Function %s - expr %s has a dummy linear part %d",GetName(),GetExpFormula().Data(),i);
680  }
681  }
682 
683  fnew.fClingInput = fClingInput;
687  fnew.fClingName = fClingName;
690 
691  // case of function based on a C++ expression (lambda's) which is ready to be compiled
693 
694  bool ret = fnew.InitLambdaExpression(fnew.fFormula);
695  if (ret) {
697  fnew.fReadyToExecute = true;
698  }
699  else {
700  Error("TFormula","Syntax error in building the lambda expression %s", fFormula.Data() );
701  fnew.fReadyToExecute = false;
702  }
703  }
704  else if (fMethod) {
705  if (fnew.fMethod) delete fnew.fMethod;
706  // use copy-constructor of TMethodCall
707  TMethodCall *m = new TMethodCall(*fMethod);
708  fnew.fMethod = m;
709  }
710 
711  if (fGradMethod) {
712  // use copy-constructor of TMethodCall
714  fnew.fGradMethod.reset(m);
715  }
716 
717  fnew.fFuncPtr = fFuncPtr;
719  fnew.fGradFuncPtr = fGradFuncPtr;
720 
721 }
722 
723 ////////////////////////////////////////////////////////////////////////////////
724 /// Clear the formula setting expression to empty and reset the variables and
725 /// parameters containers.
726 
728 {
729  fNdim = 0;
730  fNpar = 0;
731  fNumber = 0;
732  fFormula = "";
733  fClingName = "";
734 
735 
736  if(fMethod) fMethod->Delete();
737  fMethod = nullptr;
738 
739  fClingVariables.clear();
740  fClingParameters.clear();
741  fReadyToExecute = false;
742  fClingInitialized = false;
743  fAllParametersSetted = false;
744  fFuncs.clear();
745  fVars.clear();
746  fParams.clear();
747  fConsts.clear();
748  fFunctionsShortcuts.clear();
749 
750  // delete linear parts
751  int nLinParts = fLinearParts.size();
752  if (nLinParts > 0) {
753  for (int i = 0; i < nLinParts; ++i) delete fLinearParts[i];
754  }
755  fLinearParts.clear();
756 
757 }
758 
759 // Returns nullptr on failure.
760 static std::unique_ptr<TMethodCall>
761 prepareMethod(bool HasParameters, bool HasVariables, const char* FuncName,
762  bool IsVectorized, bool IsGradient = false) {
763  std::unique_ptr<TMethodCall> Method = std::unique_ptr<TMethodCall>(new TMethodCall());
764 
765  TString prototypeArguments = "";
766  if (HasVariables || HasParameters) {
767  if (IsVectorized)
768  prototypeArguments.Append("ROOT::Double_v*");
769  else
770  prototypeArguments.Append("Double_t*");
771  }
772  auto AddDoublePtrParam = [&prototypeArguments] () {
773  prototypeArguments.Append(",");
774  prototypeArguments.Append("Double_t*");
775  };
776  if (HasParameters)
777  AddDoublePtrParam();
778 
779  // We need an extra Double_t* for the gradient return result.
780  if (IsGradient)
781  AddDoublePtrParam();
782 
783  // Initialize the method call using real function name (cling name) defined
784  // by ProcessFormula
785  Method->InitWithPrototype(FuncName, prototypeArguments);
786  if (!Method->IsValid()) {
787  Error("prepareMethod",
788  "Can't compile function %s prototype with arguments %s", FuncName,
789  prototypeArguments.Data());
790  return nullptr;
791  }
792 
793  return Method;
794 }
795 
798  if (!Method) return nullptr;
799  CallFunc_t *callfunc = Method->GetCallFunc();
800 
801  if (!gCling->CallFunc_IsValid(callfunc)) {
802  Error("prepareFuncPtr", "Callfunc retuned from Cling is not valid");
803  return nullptr;
804  }
805 
807  = gCling->CallFunc_IFacePtr(callfunc).fGeneric;
808  if (!Result) {
809  Error("prepareFuncPtr", "Compiled function pointer is null");
810  return nullptr;
811  }
812  return Result;
813 }
814 
815 ////////////////////////////////////////////////////////////////////////////////
816 /// Sets TMethodCall to function inside Cling environment.
817 /// TFormula uses it to execute function.
818 /// After call, TFormula should be ready to evaluate formula.
819 /// Returns false on failure.
820 
822 {
823  if (!fMethod) {
824  Bool_t hasParameters = (fNpar > 0);
825  Bool_t hasVariables = (fNdim > 0);
826  fMethod = prepareMethod(hasParameters, hasVariables, fClingName,
827  fVectorized).release();
828  if (!fMethod) return false;
830  }
831  return fFuncPtr;
832 }
833 
834 ////////////////////////////////////////////////////////////////////////////////
835 /// Inputs formula, transfered to C++ code into Cling
836 
838 {
839 
841  // make sure the interpreter is initialized
842  ROOT::GetROOT();
843  R__ASSERT(gCling);
844 
845  // Trigger autoloading / autoparsing (ROOT-9840):
846  TString triggerAutoparsing = "namespace ROOT_TFormula_triggerAutoParse {\n"; triggerAutoparsing += fClingInput + "\n}";
847  gCling->ProcessLine(triggerAutoparsing);
848 
849  // add pragma for optimization of the formula
850  fClingInput = TString("#pragma cling optimize(2)\n") + fClingInput;
851 
852  // Now that all libraries and headers are loaded, Declare() a performant version
853  // of the same code:
856  if (!fClingInitialized) Error("InputFormulaIntoCling","Error compiling formula expression in Cling");
857  }
858 }
859 
860 ////////////////////////////////////////////////////////////////////////////////
861 /// Fill structures with default variables, constants and function shortcuts
862 
864 {
865  const TString defvars[] = { "x","y","z","t"};
866  const pair<TString, Double_t> defconsts[] = {{"pi", TMath::Pi()},
867  {"sqrt2", TMath::Sqrt2()},
868  {"infinity", TMath::Infinity()},
869  {"e", TMath::E()},
870  {"ln10", TMath::Ln10()},
871  {"loge", TMath::LogE()},
872  {"c", TMath::C()},
873  {"g", TMath::G()},
874  {"h", TMath::H()},
875  {"k", TMath::K()},
876  {"sigma", TMath::Sigma()},
877  {"r", TMath::R()},
878  {"eg", TMath::EulerGamma()},
879  {"true", 1},
880  {"false", 0}};
881  // const pair<TString,Double_t> defconsts[] = { {"pi",TMath::Pi()}, {"sqrt2",TMath::Sqrt2()},
882  // {"infinity",TMath::Infinity()}, {"ln10",TMath::Ln10()},
883  // {"loge",TMath::LogE()}, {"true",1},{"false",0} };
884  const pair<TString,TString> funShortcuts[] =
885  { {"sin","TMath::Sin" },
886  {"cos","TMath::Cos" }, {"exp","TMath::Exp"}, {"log","TMath::Log"}, {"log10","TMath::Log10"},
887  {"tan","TMath::Tan"}, {"sinh","TMath::SinH"}, {"cosh","TMath::CosH"},
888  {"tanh","TMath::TanH"}, {"asin","TMath::ASin"}, {"acos","TMath::ACos"},
889  {"atan","TMath::ATan"}, {"atan2","TMath::ATan2"}, {"sqrt","TMath::Sqrt"},
890  {"ceil","TMath::Ceil"}, {"floor","TMath::Floor"}, {"pow","TMath::Power"},
891  {"binomial","TMath::Binomial"},{"abs","TMath::Abs"},
892  {"min","TMath::Min"},{"max","TMath::Max"},{"sign","TMath::Sign" },
893  {"sq","TMath::Sq"}
894  };
895 
896  std::vector<TString> defvars2(10);
897  for (int i = 0; i < 9; ++i)
898  defvars2[i] = TString::Format("x[%d]",i);
899 
900  for (const auto &var : defvars) {
901  int pos = fVars.size();
902  fVars[var] = TFormulaVariable(var, 0, pos);
903  fClingVariables.push_back(0);
904  }
905  // add also the variables defined like x[0],x[1],x[2],...
906  // support up to x[9] - if needed extend that to higher value
907  // const int maxdim = 10;
908  // for (int i = 0; i < maxdim; ++i) {
909  // TString xvar = TString::Format("x[%d]",i);
910  // fVars[xvar] = TFormulaVariable(xvar,0,i);
911  // fClingVariables.push_back(0);
912  // }
913 
914  for (auto con : defconsts) {
915  fConsts[con.first] = con.second;
916  }
917  if (fVectorized) {
919  } else {
920  for (auto fun : funShortcuts) {
921  fFunctionsShortcuts[fun.first] = fun.second;
922  }
923  }
924 }
925 
926 ////////////////////////////////////////////////////////////////////////////////
927 /// Fill the shortcuts for vectorized functions
928 /// We will replace for example sin with vecCore::Mat::Sin
929 ///
930 
932 #ifdef R__HAS_VECCORE
933  const pair<TString,TString> vecFunShortcuts[] =
934  { {"sin","vecCore::math::Sin" },
935  {"cos","vecCore::math::Cos" }, {"exp","vecCore::math::Exp"}, {"log","vecCore::math::Log"}, {"log10","vecCore::math::Log10"},
936  {"tan","vecCore::math::Tan"},
937  //{"sinh","vecCore::math::Sinh"}, {"cosh","vecCore::math::Cosh"},{"tanh","vecCore::math::Tanh"},
938  {"asin","vecCore::math::ASin"},
939  {"acos","TMath::Pi()/2-vecCore::math::ASin"},
940  {"atan","vecCore::math::ATan"},
941  {"atan2","vecCore::math::ATan2"}, {"sqrt","vecCore::math::Sqrt"},
942  {"ceil","vecCore::math::Ceil"}, {"floor","vecCore::math::Floor"}, {"pow","vecCore::math::Pow"},
943  {"cbrt","vecCore::math::Cbrt"},{"abs","vecCore::math::Abs"},
944  {"min","vecCore::math::Min"},{"max","vecCore::math::Max"},{"sign","vecCore::math::Sign" }
945  //{"sq","TMath::Sq"}, {"binomial","TMath::Binomial"} // this last two functions will not work in vectorized mode
946  };
947  // replace in the data member maps fFunctionsShortcuts
948  for (auto fun : vecFunShortcuts) {
949  fFunctionsShortcuts[fun.first] = fun.second;
950  }
951 #endif
952  // do nothing in case Veccore is not enabled
953 }
954 
955 
956 ////////////////////////////////////////////////////////////////////////////////
957 /// Handling polN
958 /// If before 'pol' exist any name, this name will be treated as variable used in polynomial
959 /// eg.
960 /// varpol2(5) will be replaced with: [5] + [6]*var + [7]*var^2
961 /// Empty name is treated like variable x.
962 
964 {
965  Int_t polPos = formula.Index("pol");
966  while (polPos != kNPOS && !IsAParameterName(formula, polPos)) {
967 
968  Bool_t defaultVariable = false;
969  TString variable;
970  Int_t openingBracketPos = formula.Index('(', polPos);
971  Bool_t defaultCounter = openingBracketPos == kNPOS;
972  Bool_t defaultDegree = true;
973  Int_t degree, counter;
974  TString sdegree;
975  if (!defaultCounter) {
976  // verify first of opening parenthesis belongs to pol expression
977  // character between 'pol' and '(' must all be digits
978  sdegree = formula(polPos + 3, openingBracketPos - polPos - 3);
979  if (!sdegree.IsDigit())
980  defaultCounter = true;
981  }
982  if (!defaultCounter) {
983  degree = sdegree.Atoi();
984  counter = TString(formula(openingBracketPos + 1, formula.Index(')', polPos) - openingBracketPos)).Atoi();
985  } else {
986  Int_t temp = polPos + 3;
987  while (temp < formula.Length() && isdigit(formula[temp])) {
988  defaultDegree = false;
989  temp++;
990  }
991  degree = TString(formula(polPos + 3, temp - polPos - 3)).Atoi();
992  counter = 0;
993  }
994 
995  TString replacement = TString::Format("[%d]", counter);
996  if (polPos - 1 < 0 || !IsFunctionNameChar(formula[polPos - 1]) || formula[polPos - 1] == ':') {
997  variable = "x";
998  defaultVariable = true;
999  } else {
1000  Int_t tmp = polPos - 1;
1001  while (tmp >= 0 && IsFunctionNameChar(formula[tmp]) && formula[tmp] != ':') {
1002  tmp--;
1003  }
1004  variable = formula(tmp + 1, polPos - (tmp + 1));
1005  }
1006  Int_t param = counter + 1;
1007  Int_t tmp = 1;
1008  while (tmp <= degree) {
1009  if (tmp > 1)
1010  replacement.Append(TString::Format("+[%d]*%s^%d", param, variable.Data(), tmp));
1011  else
1012  replacement.Append(TString::Format("+[%d]*%s", param, variable.Data()));
1013  param++;
1014  tmp++;
1015  }
1016  // add parenthesis before and after
1017  if (degree > 0) {
1018  replacement.Insert(0, '(');
1019  replacement.Append(')');
1020  }
1021  TString pattern;
1022  if (defaultCounter && !defaultDegree) {
1023  pattern = TString::Format("%spol%d", (defaultVariable ? "" : variable.Data()), degree);
1024  } else if (defaultCounter && defaultDegree) {
1025  pattern = TString::Format("%spol", (defaultVariable ? "" : variable.Data()));
1026  } else {
1027  pattern = TString::Format("%spol%d(%d)", (defaultVariable ? "" : variable.Data()), degree, counter);
1028  }
1029 
1030  if (!formula.Contains(pattern)) {
1031  Error("HandlePolN", "Error handling polynomial function - expression is %s - trying to replace %s with %s ",
1032  formula.Data(), pattern.Data(), replacement.Data());
1033  break;
1034  }
1035  if (formula == pattern) {
1036  // case of single polynomial
1037  SetBit(kLinear, 1);
1038  fNumber = 300 + degree;
1039  }
1040  formula.ReplaceAll(pattern, replacement);
1041  polPos = formula.Index("pol");
1042  }
1043 }
1044 
1045 ////////////////////////////////////////////////////////////////////////////////
1046 /// Handling parametrized functions
1047 /// Function can be normalized, and have different variable then x.
1048 /// Variables should be placed in brackets after function name.
1049 /// No brackets are treated like [x].
1050 /// Normalized function has char 'n' after name, eg.
1051 /// gausn[var](0) will be replaced with [0]*exp(-0.5*((var-[1])/[2])^2)/(sqrt(2*pi)*[2])
1052 ///
1053 /// Adding function is easy, just follow these rules, and add to
1054 /// `TFormula::FillParametrizedFunctions` defined further below:
1055 ///
1056 /// - Key for function map is pair of name and dimension of function
1057 /// - value of key is a pair function body and normalized function body
1058 /// - {Vn} is a place where variable appear, n represents n-th variable from variable list.
1059 /// Count starts from 0.
1060 /// - [num] stands for parameter number.
1061 /// If user pass to function argument 5, num will stand for (5 + num) parameter.
1062 ///
1063 
1065 {
1066  // define all parametrized functions
1067  map< pair<TString,Int_t> ,pair<TString,TString> > functions;
1068  FillParametrizedFunctions(functions);
1069 
1070  map<TString,Int_t> functionsNumbers;
1071  functionsNumbers["gaus"] = 100;
1072  functionsNumbers["bigaus"] = 102;
1073  functionsNumbers["landau"] = 400;
1074  functionsNumbers["expo"] = 200;
1075  functionsNumbers["crystalball"] = 500;
1076 
1077  // replace old names xygaus -> gaus[x,y]
1078  formula.ReplaceAll("xyzgaus","gaus[x,y,z]");
1079  formula.ReplaceAll("xygaus","gaus[x,y]");
1080  formula.ReplaceAll("xgaus","gaus[x]");
1081  formula.ReplaceAll("ygaus","gaus[y]");
1082  formula.ReplaceAll("zgaus","gaus[z]");
1083  formula.ReplaceAll("xexpo","expo[x]");
1084  formula.ReplaceAll("yexpo","expo[y]");
1085  formula.ReplaceAll("zexpo","expo[z]");
1086  formula.ReplaceAll("xylandau","landau[x,y]");
1087  formula.ReplaceAll("xyexpo","expo[x,y]");
1088  // at the moment pre-defined functions have no more than 3 dimensions
1089  const char * defaultVariableNames[] = { "x","y","z"};
1090 
1091  for (map<pair<TString, Int_t>, pair<TString, TString>>::iterator it = functions.begin(); it != functions.end();
1092  ++it) {
1093 
1094  TString funName = it->first.first;
1095  Int_t funDim = it->first.second;
1096  Int_t funPos = formula.Index(funName);
1097 
1098  // std::cout << formula << " ---- " << funName << " " << funPos << std::endl;
1099  while (funPos != kNPOS && !IsAParameterName(formula, funPos)) {
1100 
1101  // should also check that function is not something else (e.g. exponential - parse the expo)
1102  Int_t lastFunPos = funPos + funName.Length();
1103 
1104  // check that first and last character is not a special character
1105  Int_t iposBefore = funPos - 1;
1106  // std::cout << "looping on funpos is " << funPos << " formula is " << formula << " function " << funName <<
1107  // std::endl;
1108  if (iposBefore >= 0) {
1109  assert(iposBefore < formula.Length());
1110  //if (isalpha(formula[iposBefore])) {
1111  if (IsFunctionNameChar(formula[iposBefore])) {
1112  // std::cout << "previous character for function " << funName << " is " << formula[iposBefore] << "- skip
1113  // " << std::endl;
1114  funPos = formula.Index(funName, lastFunPos);
1115  continue;
1116  }
1117  }
1118 
1119  Bool_t isNormalized = false;
1120  if (lastFunPos < formula.Length()) {
1121  // check if function is normalized by looking at "n" character after function name (e.g. gausn)
1122  isNormalized = (formula[lastFunPos] == 'n');
1123  if (isNormalized)
1124  lastFunPos += 1;
1125  if (lastFunPos < formula.Length()) {
1126  char c = formula[lastFunPos];
1127  // check if also last character is not alphanumeric or is not an operator and not a parenthesis ( or [.
1128  // Parenthesis [] are used to express the variables
1129  if (isalnum(c) || (!IsOperator(c) && c != '(' && c != ')' && c != '[' && c != ']')) {
1130  // std::cout << "last character for function " << funName << " is " << c << " skip .." << std::endl;
1131  funPos = formula.Index(funName, lastFunPos);
1132  continue;
1133  }
1134  }
1135  }
1136 
1137  if (isNormalized) {
1138  SetBit(kNormalized, 1);
1139  }
1140  std::vector<TString> variables;
1141  Int_t dim = 0;
1142  TString varList = "";
1143  Bool_t defaultVariables = false;
1144 
1145  // check if function has specified the [...] e.g. gaus[x,y]
1146  Int_t openingBracketPos = funPos + funName.Length() + (isNormalized ? 1 : 0);
1147  Int_t closingBracketPos = kNPOS;
1148  if (openingBracketPos > formula.Length() || formula[openingBracketPos] != '[') {
1149  dim = funDim;
1150  variables.resize(dim);
1151  for (Int_t idim = 0; idim < dim; ++idim)
1152  variables[idim] = defaultVariableNames[idim];
1153  defaultVariables = true;
1154  } else {
1155  // in case of [..] found, assume they specify all the variables. Use it to get function dimension
1156  closingBracketPos = formula.Index(']', openingBracketPos);
1157  varList = formula(openingBracketPos + 1, closingBracketPos - openingBracketPos - 1);
1158  dim = varList.CountChar(',') + 1;
1159  variables.resize(dim);
1160  Int_t Nvar = 0;
1161  TString varName = "";
1162  for (Int_t i = 0; i < varList.Length(); ++i) {
1163  if (IsFunctionNameChar(varList[i])) {
1164  varName.Append(varList[i]);
1165  }
1166  if (varList[i] == ',') {
1167  variables[Nvar] = varName;
1168  varName = "";
1169  Nvar++;
1170  }
1171  }
1172  if (varName != "") // we will miss last variable
1173  {
1174  variables[Nvar] = varName;
1175  }
1176  }
1177  // check if dimension obtained from [...] is compatible with what is defined in existing pre-defined functions
1178  // std::cout << " Found dim = " << dim << " and function dimension is " << funDim << std::endl;
1179  if (dim != funDim) {
1180  pair<TString, Int_t> key = make_pair(funName, dim);
1181  if (functions.find(key) == functions.end()) {
1182  Error("PreProcessFormula", "Dimension of function %s is detected to be of dimension %d and is not "
1183  "compatible with existing pre-defined function which has dim %d",
1184  funName.Data(), dim, funDim);
1185  return;
1186  }
1187  // skip the particular function found - we might find later on the corresponding pre-defined function
1188  funPos = formula.Index(funName, lastFunPos);
1189  continue;
1190  }
1191  // look now for the (..) brackets to get the parameter counter (e.g. gaus(0) + gaus(3) )
1192  // need to start for a position
1193  Int_t openingParenthesisPos = (closingBracketPos == kNPOS) ? openingBracketPos : closingBracketPos + 1;
1194  bool defaultCounter = (openingParenthesisPos > formula.Length() || formula[openingParenthesisPos] != '(');
1195 
1196  // Int_t openingParenthesisPos = formula.Index('(',funPos);
1197  // Bool_t defaultCounter = (openingParenthesisPos == kNPOS);
1198  Int_t counter;
1199  if (defaultCounter) {
1200  counter = 0;
1201  } else {
1202  // Check whether this is just a number in parentheses. If not, leave
1203  // it to `HandleFunctionArguments` to be parsed
1204 
1205  TRegexp counterPattern("([0-9]+)");
1206  Ssiz_t len;
1207  if (counterPattern.Index(formula, &len, openingParenthesisPos) == -1) {
1208  funPos = formula.Index(funName, funPos + 1);
1209  continue;
1210  } else {
1211  counter =
1212  TString(formula(openingParenthesisPos + 1, formula.Index(')', funPos) - openingParenthesisPos - 1))
1213  .Atoi();
1214  }
1215  }
1216  // std::cout << "openingParenthesisPos " << openingParenthesisPos << " counter is " << counter << std::endl;
1217 
1218  TString body = (isNormalized ? it->second.second : it->second.first);
1219  if (isNormalized && body == "") {
1220  Error("PreprocessFormula", "%d dimension function %s has no normalized form.", it->first.second,
1221  funName.Data());
1222  break;
1223  }
1224  for (int i = 0; i < body.Length(); ++i) {
1225  if (body[i] == '{') {
1226  // replace {Vn} with variable names
1227  i += 2; // skip '{' and 'V'
1228  Int_t num = TString(body(i, body.Index('}', i) - i)).Atoi();
1229  TString variable = variables[num];
1230  TString pattern = TString::Format("{V%d}", num);
1231  i -= 2; // restore original position
1232  body.Replace(i, pattern.Length(), variable, variable.Length());
1233  i += variable.Length() - 1; // update i to reflect change in body string
1234  } else if (body[i] == '[') {
1235  // update parameter counters in case of many functions (e.g. gaus(0)+gaus(3) )
1236  Int_t tmp = i;
1237  while (tmp < body.Length() && body[tmp] != ']') {
1238  tmp++;
1239  }
1240  Int_t num = TString(body(i + 1, tmp - 1 - i)).Atoi();
1241  num += counter;
1242  TString replacement = TString::Format("%d", num);
1243 
1244  body.Replace(i + 1, tmp - 1 - i, replacement, replacement.Length());
1245  i += replacement.Length() + 1;
1246  }
1247  }
1248  TString pattern;
1249  if (defaultCounter && defaultVariables) {
1250  pattern = TString::Format("%s%s", funName.Data(), (isNormalized ? "n" : ""));
1251  }
1252  if (!defaultCounter && defaultVariables) {
1253  pattern = TString::Format("%s%s(%d)", funName.Data(), (isNormalized ? "n" : ""), counter);
1254  }
1255  if (defaultCounter && !defaultVariables) {
1256  pattern = TString::Format("%s%s[%s]", funName.Data(), (isNormalized ? "n" : ""), varList.Data());
1257  }
1258  if (!defaultCounter && !defaultVariables) {
1259  pattern =
1260  TString::Format("%s%s[%s](%d)", funName.Data(), (isNormalized ? "n" : ""), varList.Data(), counter);
1261  }
1262  TString replacement = body;
1263 
1264  // set the number (only in case a function exists without anything else
1265  if (fNumber == 0 && formula.Length() <= (pattern.Length() - funPos) + 1) { // leave 1 extra
1266  fNumber = functionsNumbers[funName] + 10 * (dim - 1);
1267  }
1268 
1269  // std::cout << " replace " << pattern << " with " << replacement << std::endl;
1270 
1271  formula.Replace(funPos, pattern.Length(), replacement, replacement.Length());
1272 
1273  funPos = formula.Index(funName);
1274  }
1275  // std::cout << " End loop of " << funName << " formula is now " << formula << std::endl;
1276  }
1277 }
1278 
1279 ////////////////////////////////////////////////////////////////////////////////
1280 /// Handling parameter ranges, in the form of [1..5]
1282 {
1283  TRegexp rangePattern("\\[[0-9]+\\.\\.[0-9]+\\]");
1284  Ssiz_t len;
1285  int matchIdx = 0;
1286  while ((matchIdx = rangePattern.Index(formula, &len, matchIdx)) != -1) {
1287  int startIdx = matchIdx + 1;
1288  int endIdx = formula.Index("..", startIdx) + 2; // +2 for ".."
1289  int startCnt = TString(formula(startIdx, formula.Length())).Atoi();
1290  int endCnt = TString(formula(endIdx, formula.Length())).Atoi();
1291 
1292  if (endCnt <= startCnt)
1293  Error("HandleParamRanges", "End parameter (%d) <= start parameter (%d) in parameter range", endCnt, startCnt);
1294 
1295  TString newString = "[";
1296  for (int cnt = startCnt; cnt < endCnt; cnt++)
1297  newString += TString::Format("%d],[", cnt);
1298  newString += TString::Format("%d]", endCnt);
1299 
1300  // std::cout << "newString generated by HandleParamRanges is " << newString << std::endl;
1301  formula.Replace(matchIdx, formula.Index("]", matchIdx) + 1 - matchIdx, newString);
1302 
1303  matchIdx += newString.Length();
1304  }
1305 
1306  // std::cout << "final formula is now " << formula << std::endl;
1307 }
1308 
1309 ////////////////////////////////////////////////////////////////////////////////
1310 /// Handling user functions (and parametrized functions)
1311 /// to take variables and optionally parameters as arguments
1313 {
1314  // std::cout << "calling `HandleFunctionArguments` on " << formula << std::endl;
1315 
1316  // Define parametrized functions, in case we need to use them
1317  std::map<std::pair<TString, Int_t>, std::pair<TString, TString>> parFunctions;
1318  FillParametrizedFunctions(parFunctions);
1319 
1320  // loop through characters
1321  for (Int_t i = 0; i < formula.Length(); ++i) {
1322  // List of things to ignore (copied from `TFormula::ExtractFunctors`)
1323 
1324  // ignore things that start with square brackets
1325  if (formula[i] == '[') {
1326  while (formula[i] != ']')
1327  i++;
1328  continue;
1329  }
1330  // ignore strings
1331  if (formula[i] == '\"') {
1332  do
1333  i++;
1334  while (formula[i] != '\"');
1335  continue;
1336  }
1337  // ignore numbers (scientific notation)
1338  if (IsScientificNotation(formula, i))
1339  continue;
1340  // ignore x in hexadecimal number
1341  if (IsHexadecimal(formula, i)) {
1342  while (!IsOperator(formula[i]) && i < formula.Length())
1343  i++;
1344  continue;
1345  }
1346 
1347  // investigate possible start of function name
1348  if (isalpha(formula[i]) && !IsOperator(formula[i])) {
1349  // std::cout << "character : " << i << " " << formula[i] << " is not an operator and is alpha" << std::endl;
1350 
1351  int j; // index to end of name
1352  for (j = i; j < formula.Length() && IsFunctionNameChar(formula[j]); j++)
1353  ;
1354  TString name = (TString)formula(i, j - i);
1355  // std::cout << "parsed name " << name << std::endl;
1356 
1357  // Count arguments (careful about parentheses depth)
1358  // Make list of indices where each argument is separated
1359  int nArguments = 1;
1360  int depth = 1;
1361  std::vector<int> argSeparators;
1362  argSeparators.push_back(j); // opening parenthesis
1363  int k; // index for end of closing parenthesis
1364  for (k = j + 1; depth >= 1 && k < formula.Length(); k++) {
1365  if (formula[k] == ',' && depth == 1) {
1366  nArguments++;
1367  argSeparators.push_back(k);
1368  } else if (formula[k] == '(')
1369  depth++;
1370  else if (formula[k] == ')')
1371  depth--;
1372  }
1373  argSeparators.push_back(k - 1); // closing parenthesis
1374 
1375  // retrieve `f` (code copied from ExtractFunctors)
1376  TObject *obj = 0;
1377  {
1379  obj = gROOT->GetListOfFunctions()->FindObject(name);
1380  }
1381  TFormula *f = dynamic_cast<TFormula *>(obj);
1382  if (!f) {
1383  // maybe object is a TF1
1384  TF1 *f1 = dynamic_cast<TF1 *>(obj);
1385  if (f1)
1386  f = f1->GetFormula();
1387  }
1388  // `f` should be found by now, if it was a user-defined function.
1389  // The other possibility we need to consider is that this is a
1390  // parametrized function (else case below)
1391 
1392  bool nameRecognized = (f != nullptr);
1393 
1394  // Get ndim, npar, and replacementFormula of function
1395  int ndim = 0;
1396  int npar = 0;
1397  TString replacementFormula;
1398  if (f) {
1399  ndim = f->GetNdim();
1400  npar = f->GetNpar();
1401  replacementFormula = f->GetExpFormula();
1402  } else {
1403  // otherwise, try to match default parametrized functions
1404 
1405  for (auto keyval : parFunctions) {
1406  // (name, ndim)
1407  pair<TString, Int_t> name_ndim = keyval.first;
1408  // (formula without normalization, formula with normalization)
1409  pair<TString, TString> formulaPair = keyval.second;
1410 
1411  // match names like gaus, gausn, breitwigner
1412  if (name == name_ndim.first)
1413  replacementFormula = formulaPair.first;
1414  else if (name == name_ndim.first + "n" && formulaPair.second != "")
1415  replacementFormula = formulaPair.second;
1416  else
1417  continue;
1418 
1419  // set ndim
1420  ndim = name_ndim.second;
1421 
1422  // go through replacementFormula to find the number of parameters
1423  npar = 0;
1424  int idx = 0;
1425  while ((idx = replacementFormula.Index('[', idx)) != kNPOS) {
1426  npar = max(npar, 1 + TString(replacementFormula(idx + 1, replacementFormula.Length())).Atoi());
1427  idx = replacementFormula.Index(']', idx);
1428  if (idx == kNPOS)
1429  Error("HandleFunctionArguments", "Square brackets not matching in formula %s",
1430  (const char *)replacementFormula);
1431  }
1432  // npar should be set correctly now
1433 
1434  // break if number of arguments is good (note: `gaus`, has two
1435  // definitions with different numbers of arguments, but it works
1436  // out so that it should be unambiguous)
1437  if (nArguments == ndim + npar || nArguments == npar || nArguments == ndim) {
1438  nameRecognized = true;
1439  break;
1440  }
1441  }
1442  }
1443  if (nameRecognized && ndim > 4)
1444  Error("HandleFunctionArguments", "Number of dimensions %d greater than 4. Cannot parse formula.", ndim);
1445 
1446  // if we have "recognizedName(...", then apply substitutions
1447  if (nameRecognized && j < formula.Length() && formula[j] == '(') {
1448  // std::cout << "naive replacement formula: " << replacementFormula << std::endl;
1449  // std::cout << "formula: " << formula << std::endl;
1450 
1451  // map to rename each argument in `replacementFormula`
1452  map<TString, TString> argSubstitutions;
1453 
1454  const char *defaultVariableNames[] = {"x", "y", "z", "t"};
1455 
1456  // check nArguments and add to argSubstitutions map as appropriate
1457  bool canReplace = false;
1458  if (nArguments == ndim + npar) {
1459  // loop through all variables and parameters, filling in argSubstitutions
1460  for (int argNr = 0; argNr < nArguments; argNr++) {
1461 
1462  // Get new name (for either variable or parameter)
1463  TString newName =
1464  TString(formula(argSeparators[argNr] + 1, argSeparators[argNr + 1] - argSeparators[argNr] - 1));
1465  PreProcessFormula(newName); // so that nesting works
1466 
1467  // Get old name(s)
1468  // and add to argSubstitutions map as appropriate
1469  if (argNr < ndim) { // variable
1470  TString oldName = (f) ? TString::Format("x[%d]", argNr) : TString::Format("{V%d}", argNr);
1471  argSubstitutions[oldName] = newName;
1472 
1473  if (f)
1474  argSubstitutions[defaultVariableNames[argNr]] = newName;
1475 
1476  } else { // parameter
1477  int parNr = argNr - ndim;
1478  TString oldName =
1479  (f) ? TString::Format("[%s]", f->GetParName(parNr)) : TString::Format("[%d]", parNr);
1480  argSubstitutions[oldName] = newName;
1481 
1482  // If the name stays the same, keep the old value of the parameter
1483  if (f && oldName == newName)
1484  DoAddParameter(f->GetParName(parNr), f->GetParameter(parNr), false);
1485  }
1486  }
1487 
1488  canReplace = true;
1489  } else if (nArguments == npar) {
1490  // Try to assume variables are implicit (need all arguments to be
1491  // parameters)
1492 
1493  // loop to check if all arguments are parameters
1494  bool varsImplicit = true;
1495  for (int argNr = 0; argNr < nArguments && varsImplicit; argNr++) {
1496  int openIdx = argSeparators[argNr] + 1;
1497  int closeIdx = argSeparators[argNr + 1] - 1;
1498 
1499  // check brackets on either end
1500  if (formula[openIdx] != '[' || formula[closeIdx] != ']' || closeIdx <= openIdx + 1)
1501  varsImplicit = false;
1502 
1503  // check that the middle is a single function-name
1504  for (int idx = openIdx + 1; idx < closeIdx && varsImplicit; idx++)
1505  if (!IsFunctionNameChar(formula[idx]))
1506  varsImplicit = false;
1507 
1508  if (!varsImplicit)
1509  Warning("HandleFunctionArguments",
1510  "Argument %d is not a parameter. Cannot assume variables are implicit.", argNr);
1511  }
1512 
1513  // loop to replace parameter names
1514  if (varsImplicit) {
1515  // if parametrized function, still need to replace parameter names
1516  if (!f) {
1517  for (int dim = 0; dim < ndim; dim++) {
1518  argSubstitutions[TString::Format("{V%d}", dim)] = defaultVariableNames[dim];
1519  }
1520  }
1521 
1522  for (int argNr = 0; argNr < nArguments; argNr++) {
1523  TString oldName =
1524  (f) ? TString::Format("[%s]", f->GetParName(argNr)) : TString::Format("[%d]", argNr);
1525  TString newName =
1526  TString(formula(argSeparators[argNr] + 1, argSeparators[argNr + 1] - argSeparators[argNr] - 1));
1527 
1528  // preprocess the formula so that nesting works
1529  PreProcessFormula(newName);
1530  argSubstitutions[oldName] = newName;
1531 
1532  // If the name stays the same, keep the old value of the parameter
1533  if (f && oldName == newName)
1534  DoAddParameter(f->GetParName(argNr), f->GetParameter(argNr), false);
1535  }
1536 
1537  canReplace = true;
1538  }
1539  }
1540  if (!canReplace && nArguments == ndim) {
1541  // Treat parameters as implicit
1542 
1543  // loop to replace variable names
1544  for (int argNr = 0; argNr < nArguments; argNr++) {
1545  TString oldName = (f) ? TString::Format("x[%d]", argNr) : TString::Format("{V%d}", argNr);
1546  TString newName =
1547  TString(formula(argSeparators[argNr] + 1, argSeparators[argNr + 1] - argSeparators[argNr] - 1));
1548 
1549  // preprocess so nesting works
1550  PreProcessFormula(newName);
1551  argSubstitutions[oldName] = newName;
1552 
1553  if (f) // x, y, z are not used in parametrized function definitions
1554  argSubstitutions[defaultVariableNames[argNr]] = newName;
1555  }
1556 
1557  if (f) {
1558  // keep old values of the parameters
1559  for (int parNr = 0; parNr < npar; parNr++)
1560  DoAddParameter(f->GetParName(parNr), f->GetParameter(parNr), false);
1561  }
1562 
1563  canReplace = true;
1564  }
1565 
1566  if (canReplace)
1567  ReplaceAllNames(replacementFormula, argSubstitutions);
1568  // std::cout << "after replacement, replacementFormula is " << replacementFormula << std::endl;
1569 
1570  if (canReplace) {
1571  // std::cout << "about to replace position " << i << " length " << k-i << " in formula : " << formula <<
1572  // std::endl;
1573  formula.Replace(i, k - i, replacementFormula);
1574  i += replacementFormula.Length() - 1; // skip to end of replacement
1575  // std::cout << "new formula is : " << formula << std::endl;
1576  } else {
1577  Warning("HandleFunctionArguments", "Unable to make replacement. Number of parameters doesn't work : "
1578  "%d arguments, %d dimensions, %d parameters",
1579  nArguments, ndim, npar);
1580  i = j;
1581  }
1582 
1583  } else {
1584  i = j; // skip to end of candidate "name"
1585  }
1586  }
1587  }
1588 
1589 }
1590 
1591 ////////////////////////////////////////////////////////////////////////////////
1592 /// Handling exponentiation
1593 /// Can handle multiple carets, eg.
1594 /// 2^3^4 will be treated like 2^(3^4)
1595 
1597 {
1598  Int_t caretPos = formula.Last('^');
1599  while (caretPos != kNPOS && !IsAParameterName(formula, caretPos)) {
1600 
1601  TString right, left;
1602  Int_t temp = caretPos;
1603  temp--;
1604  // get the expression in ( ) which has the operator^ applied
1605  if (formula[temp] == ')') {
1606  Int_t depth = 1;
1607  temp--;
1608  while (depth != 0 && temp > 0) {
1609  if (formula[temp] == ')')
1610  depth++;
1611  if (formula[temp] == '(')
1612  depth--;
1613  temp--;
1614  }
1615  if (depth == 0)
1616  temp++;
1617  }
1618  // this in case of someting like sin(x+2)^2
1619  do {
1620  temp--; // go down one
1621  // handle scientific notation cases (1.e-2 ^ 3 )
1622  if (temp >= 2 && IsScientificNotation(formula, temp - 1))
1623  temp -= 3;
1624  } while (temp >= 0 && !IsOperator(formula[temp]) && !IsBracket(formula[temp]));
1625 
1626  assert(temp + 1 >= 0);
1627  Int_t leftPos = temp + 1;
1628  left = formula(leftPos, caretPos - leftPos);
1629  // std::cout << "left to replace is " << left << std::endl;
1630 
1631  // look now at the expression after the ^ operator
1632  temp = caretPos;
1633  temp++;
1634  if (temp >= formula.Length()) {
1635  Error("HandleExponentiation", "Invalid position of operator ^");
1636  return;
1637  }
1638  if (formula[temp] == '(') {
1639  Int_t depth = 1;
1640  temp++;
1641  while (depth != 0 && temp < formula.Length()) {
1642  if (formula[temp] == ')')
1643  depth--;
1644  if (formula[temp] == '(')
1645  depth++;
1646  temp++;
1647  }
1648  temp--;
1649  } else {
1650  // handle case first character is operator - or + continue
1651  if (formula[temp] == '-' || formula[temp] == '+')
1652  temp++;
1653  // handle cases x^-2 or x^+2
1654  // need to handle also cases x^sin(x+y)
1655  Int_t depth = 0;
1656  // stop right expression if is an operator or if is a ")" from a zero depth
1657  while (temp < formula.Length() && ((depth > 0) || !IsOperator(formula[temp]))) {
1658  temp++;
1659  // handle scientific notation cases (1.e-2 ^ 3 )
1660  if (temp >= 2 && IsScientificNotation(formula, temp))
1661  temp += 2;
1662  // for internal parenthesis
1663  if (temp < formula.Length() && formula[temp] == '(')
1664  depth++;
1665  if (temp < formula.Length() && formula[temp] == ')') {
1666  if (depth > 0)
1667  depth--;
1668  else
1669  break; // case of end of a previously started expression e.g. sin(x^2)
1670  }
1671  }
1672  }
1673  right = formula(caretPos + 1, (temp - 1) - caretPos);
1674  // std::cout << "right to replace is " << right << std::endl;
1675 
1676  TString pattern = TString::Format("%s^%s", left.Data(), right.Data());
1677  TString replacement = TString::Format("pow(%s,%s)", left.Data(), right.Data());
1678 
1679  // std::cout << "pattern : " << pattern << std::endl;
1680  // std::cout << "replacement : " << replacement << std::endl;
1681  formula.Replace(leftPos, pattern.Length(), replacement, replacement.Length());
1682 
1683  caretPos = formula.Last('^');
1684  }
1685 }
1686 
1687 ////////////////////////////////////////////////////////////////////////////////
1688 /// Handle linear functions defined with the operator ++.
1689 
1690 void TFormula::HandleLinear(TString &formula)
1691 {
1692  // Handle Linear functions identified with "@" operator
1693  Int_t linPos = formula.Index("@");
1694  if (linPos == kNPOS ) return; // function is not linear
1695  Int_t nofLinParts = formula.CountChar((int)'@');
1696  assert(nofLinParts > 0);
1697  fLinearParts.reserve(nofLinParts + 1);
1698  Int_t Nlinear = 0;
1699  bool first = true;
1700  while (linPos != kNPOS && !IsAParameterName(formula, linPos)) {
1701  SetBit(kLinear, 1);
1702  // analyze left part only the first time
1703  Int_t temp = 0;
1704  TString left;
1705  if (first) {
1706  temp = linPos - 1;
1707  while (temp >= 0 && formula[temp] != '@') {
1708  temp--;
1709  }
1710  left = formula(temp + 1, linPos - (temp + 1));
1711  }
1712  temp = linPos + 1;
1713  while (temp < formula.Length() && formula[temp] != '@') {
1714  temp++;
1715  }
1716  TString right = formula(linPos + 1, temp - (linPos + 1));
1717 
1718  TString pattern =
1719  (first) ? TString::Format("%s@%s", left.Data(), right.Data()) : TString::Format("@%s", right.Data());
1720  TString replacement =
1721  (first) ? TString::Format("([%d]*(%s))+([%d]*(%s))", Nlinear, left.Data(), Nlinear + 1, right.Data())
1722  : TString::Format("+([%d]*(%s))", Nlinear, right.Data());
1723  Nlinear += (first) ? 2 : 1;
1724 
1725  formula.ReplaceAll(pattern, replacement);
1726  if (first) {
1727  TFormula *lin1 = new TFormula("__linear1", left, false);
1728  fLinearParts.push_back(lin1);
1729  }
1730  TFormula *lin2 = new TFormula("__linear2", right, false);
1731  fLinearParts.push_back(lin2);
1732 
1733  linPos = formula.Index("@");
1734  first = false;
1735  }
1736 }
1737 
1738 ////////////////////////////////////////////////////////////////////////////////
1739 /// Preprocessing of formula
1740 /// Replace all ** by ^, and removes spaces.
1741 /// Handle also parametrized functions like polN,gaus,expo,landau
1742 /// and exponentiation.
1743 /// Similar functionality should be added here.
1744 
1745 void TFormula::PreProcessFormula(TString &formula)
1746 {
1747  formula.ReplaceAll("**","^");
1748  formula.ReplaceAll("++","@"); // for linear functions
1749  formula.ReplaceAll(" ","");
1750  HandlePolN(formula);
1751  HandleParametrizedFunctions(formula);
1752  HandleParamRanges(formula);
1753  HandleFunctionArguments(formula);
1754  HandleExponentiation(formula);
1755  // "++" wil be dealt with Handle Linear
1756  HandleLinear(formula);
1757  // special case for "--" and "++"
1758  // ("++" needs to be written with whitespace that is removed before but then we re-add it again
1759  formula.ReplaceAll("--","- -");
1760  formula.ReplaceAll("++","+ +");
1761 }
1762 
1763 ////////////////////////////////////////////////////////////////////////////////
1764 /// prepare the formula to be executed
1765 /// normally is called with fFormula
1766 
1768 {
1769  fFuncs.clear();
1770  fReadyToExecute = false;
1771  ExtractFunctors(formula);
1772 
1773  // update the expression with the new formula
1774  fFormula = formula;
1775  // save formula to parse variable and parameters for Cling
1776  fClingInput = formula;
1777  // replace all { and }
1778  fFormula.ReplaceAll("{","");
1779  fFormula.ReplaceAll("}","");
1780 
1781  // std::cout << "functors are extracted formula is " << std::endl;
1782  // std::cout << fFormula << std::endl << std::endl;
1783 
1784  fFuncs.sort();
1785  fFuncs.unique();
1786 
1787  // use inputFormula for Cling
1789 
1790  // for pre-defined functions (need after processing)
1791  if (fNumber != 0) SetPredefinedParamNames();
1792 
1794 }
1795 
1796 ////////////////////////////////////////////////////////////////////////////////
1797 /// Extracts functors from formula, and put them in fFuncs.
1798 /// Simple grammar:
1799 /// - <function> := name(arg1,arg2...)
1800 /// - <variable> := name
1801 /// - <parameter> := [number]
1802 /// - <name> := String containing lower and upper letters, numbers, underscores
1803 /// - <number> := Integer number
1804 /// Operators are omitted.
1805 
1806 void TFormula::ExtractFunctors(TString &formula)
1807 {
1808  // std::cout << "Commencing ExtractFunctors on " << formula << std::endl;
1809 
1810  TString name = "";
1811  TString body = "";
1812  // printf("formula is : %s \n",formula.Data() );
1813  for (Int_t i = 0; i < formula.Length(); ++i) {
1814 
1815  // std::cout << "loop on character : " << i << " " << formula[i] << std::endl;
1816  // case of parameters
1817  if (formula[i] == '[') {
1818  Int_t tmp = i;
1819  i++;
1820  TString param = "";
1821  while (i < formula.Length() && formula[i] != ']') {
1822  param.Append(formula[i++]);
1823  }
1824  i++;
1825  // rename parameter name XX to pXX
1826  // std::cout << "examine parameters " << param << std::endl;
1827  int paramIndex = -1;
1828  if (param.IsDigit()) {
1829  paramIndex = param.Atoi();
1830  param.Insert(0, 'p'); // needed for the replacement
1831  if (paramIndex >= fNpar || fParams.find(param) == fParams.end()) {
1832  // add all parameters up to given index found
1833  for (int idx = 0; idx <= paramIndex; ++idx) {
1834  TString pname = TString::Format("p%d", idx);
1835  if (fParams.find(pname) == fParams.end())
1836  DoAddParameter(pname, 0, false);
1837  }
1838  }
1839  } else {
1840  // handle whitespace characters in parname
1841  param.ReplaceAll("\\s", " ");
1842 
1843  // only add if parameter does not already exist, because maybe
1844  // `HandleFunctionArguments` already assigned a default value to the
1845  // parameter
1846  if (fParams.find(param) == fParams.end() || GetParNumber(param) < 0 ||
1847  (unsigned)GetParNumber(param) >= fClingParameters.size()) {
1848  // std::cout << "Setting parameter " << param << " to 0" << std::endl;
1849  DoAddParameter(param, 0, false);
1850  }
1851  }
1852  TString replacement = TString::Format("{[%s]}", param.Data());
1853  formula.Replace(tmp, i - tmp, replacement, replacement.Length());
1854  fFuncs.push_back(TFormulaFunction(param));
1855  // we need to change index i after replacing since string length changes
1856  // and we need to re-calculate i position
1857  int deltai = replacement.Length() - (i-tmp);
1858  i += deltai;
1859  // printf("found parameter %s \n",param.Data() );
1860  continue;
1861  }
1862  // case of strings
1863  if (formula[i] == '\"') {
1864  // look for next instance of "\"
1865  do {
1866  i++;
1867  } while (formula[i] != '\"');
1868  }
1869  // case of e or E for numbers in exponential notaton (e.g. 2.2e-3)
1870  if (IsScientificNotation(formula, i))
1871  continue;
1872  // case of x for hexadecimal numbers
1873  if (IsHexadecimal(formula, i)) {
1874  // find position of operator
1875  // do not check cases if character is not only a to f, but accept anything
1876  while (!IsOperator(formula[i]) && i < formula.Length()) {
1877  i++;
1878  }
1879  continue;
1880  }
1881 
1882  // std::cout << "investigating character : " << i << " " << formula[i] << " of formula " << formula <<
1883  // std::endl;
1884  // look for variable and function names. They start in C++ with alphanumeric characters
1885  if (isalpha(formula[i]) &&
1886  !IsOperator(formula[i])) // not really needed to check if operator (if isalpha is not an operator)
1887  {
1888  // std::cout << "character : " << i << " " << formula[i] << " is not an operator and is alpha " <<
1889  // std::endl;
1890 
1891  while (i < formula.Length() && IsFunctionNameChar(formula[i])) {
1892  // need special case for separating operator ":" from scope operator "::"
1893  if (formula[i] == ':' && ((i + 1) < formula.Length())) {
1894  if (formula[i + 1] == ':') {
1895  // case of :: (scopeOperator)
1896  name.Append("::");
1897  i += 2;
1898  continue;
1899  } else
1900  break;
1901  }
1902 
1903  name.Append(formula[i++]);
1904  }
1905  // printf(" build a name %s \n",name.Data() );
1906  if (formula[i] == '(') {
1907  i++;
1908  if (formula[i] == ')') {
1909  fFuncs.push_back(TFormulaFunction(name, body, 0));
1910  name = body = "";
1911  continue;
1912  }
1913  Int_t depth = 1;
1914  Int_t args = 1; // we will miss first argument
1915  while (depth != 0 && i < formula.Length()) {
1916  switch (formula[i]) {
1917  case '(': depth++; break;
1918  case ')': depth--; break;
1919  case ',':
1920  if (depth == 1)
1921  args++;
1922  break;
1923  }
1924  if (depth != 0) // we don't want last ')' inside body
1925  {
1926  body.Append(formula[i++]);
1927  }
1928  }
1929  Int_t originalBodyLen = body.Length();
1930  ExtractFunctors(body);
1931  formula.Replace(i - originalBodyLen, originalBodyLen, body, body.Length());
1932  i += body.Length() - originalBodyLen;
1933  fFuncs.push_back(TFormulaFunction(name, body, args));
1934  } else {
1935 
1936  // std::cout << "check if character : " << i << " " << formula[i] << " from name " << name << " is a
1937  // function " << std::endl;
1938 
1939  // check if function is provided by gROOT
1940  TObject *obj = 0;
1941  // exclude case function name is x,y,z,t
1942  if (!IsReservedName(name))
1943  {
1945  obj = gROOT->GetListOfFunctions()->FindObject(name);
1946  }
1947  TFormula *f = dynamic_cast<TFormula *>(obj);
1948  if (!f) {
1949  // maybe object is a TF1
1950  TF1 *f1 = dynamic_cast<TF1 *>(obj);
1951  if (f1)
1952  f = f1->GetFormula();
1953  }
1954  if (f) {
1955  // Replacing user formula the old way (as opposed to 'HandleFunctionArguments')
1956  // Note this is only for replacing functions that do
1957  // not specify variables and/or parameters in brackets
1958  // (the other case is done by `HandleFunctionArguments`)
1959 
1960  TString replacementFormula = f->GetExpFormula();
1961 
1962  // analyze expression string
1963  // std::cout << "formula to replace for " << f->GetName() << " is " << replacementFormula <<
1964  // std::endl;
1965  PreProcessFormula(replacementFormula);
1966  // we need to define different parameters if we use the unnamed default parameters ([0])
1967  // I need to replace all the terms in the functor for backward compatibility of the case
1968  // f1("[0]*x") f2("[0]*x") f1+f2 - it is weird but it is better to support
1969  // std::cout << "current number of parameter is " << fNpar << std::endl;
1970  int nparOffset = 0;
1971  // if (fParams.find("0") != fParams.end() ) {
1972  // do in any case if parameters are existing
1973  std::vector<TString> newNames;
1974  if (fNpar > 0) {
1975  nparOffset = fNpar;
1976  newNames.resize(f->GetNpar());
1977  // start from higher number to avoid overlap
1978  for (int jpar = f->GetNpar() - 1; jpar >= 0; --jpar) {
1979  // parameters name have a "p" added in front
1980  TString pj = TString(f->GetParName(jpar));
1981  if (pj[0] == 'p' && TString(pj(1, pj.Length())).IsDigit()) {
1982  TString oldName = TString::Format("[%s]", f->GetParName(jpar));
1983  TString newName = TString::Format("[p%d]", nparOffset + jpar);
1984  // std::cout << "replace - parameter " << f->GetParName(jpar) << " with " << newName <<
1985  // std::endl;
1986  replacementFormula.ReplaceAll(oldName, newName);
1987  newNames[jpar] = newName;
1988  } else
1989  newNames[jpar] = f->GetParName(jpar);
1990  }
1991  // std::cout << "after replacing params " << replacementFormula << std::endl;
1992  }
1993  ExtractFunctors(replacementFormula);
1994  // std::cout << "after re-extracting functors " << replacementFormula << std::endl;
1995 
1996  // set parameter value from replacement formula
1997  for (int jpar = 0; jpar < f->GetNpar(); ++jpar) {
1998  if (nparOffset > 0) {
1999  // parameter have an offset- so take this into account
2000  assert((int)newNames.size() == f->GetNpar());
2001  SetParameter(newNames[jpar], f->GetParameter(jpar));
2002  } else
2003  // names are the same between current formula and replaced one
2004  SetParameter(f->GetParName(jpar), f->GetParameter(jpar));
2005  }
2006  // need to add parenthesis at begin and end of replacementFormula
2007  replacementFormula.Insert(0, '(');
2008  replacementFormula.Insert(replacementFormula.Length(), ')');
2009  formula.Replace(i - name.Length(), name.Length(), replacementFormula, replacementFormula.Length());
2010  // move forward the index i of the main loop
2011  i += replacementFormula.Length() - name.Length();
2012 
2013  // we have extracted all the functor for "fname"
2014  // std::cout << "We have extracted all the functors for fname" << std::endl;
2015  // std::cout << " i = " << i << " f[i] = " << formula[i] << " - " << formula << std::endl;
2016  name = "";
2017 
2018  continue;
2019  }
2020 
2021  // add now functor in
2022  TString replacement = TString::Format("{%s}", name.Data());
2023  formula.Replace(i - name.Length(), name.Length(), replacement, replacement.Length());
2024  i += 2;
2025  fFuncs.push_back(TFormulaFunction(name));
2026  }
2027  }
2028  name = body = "";
2029  }
2030 }
2031 
2032 ////////////////////////////////////////////////////////////////////////////////
2033 /// Iterates through functors in fFuncs and performs the appropriate action.
2034 /// If functor has 0 arguments (has only name) can be:
2035 /// - variable
2036 /// * will be replaced with x[num], where x is an array containing value of this variable under num.
2037 /// - pre-defined formula
2038 /// * will be replaced with formulas body
2039 /// - constant
2040 /// * will be replaced with constant value
2041 /// - parameter
2042 /// * will be replaced with p[num], where p is an array containing value of this parameter under num.
2043 /// If has arguments it can be :
2044 /// - function shortcut, eg. sin
2045 /// * will be replaced with fullname of function, eg. sin -> TMath::Sin
2046 /// - function from cling environment, eg. TMath::BreitWigner(x,y,z)
2047 /// * first check if function exists, and has same number of arguments, then accept it and set as found.
2048 /// If all functors after iteration are matched with corresponding action,
2049 /// it inputs C++ code of formula into cling, and sets flag that formula is ready to evaluate.
2050 
2051 void TFormula::ProcessFormula(TString &formula)
2052 {
2053  // std::cout << "Begin: formula is " << formula << " list of functors " << fFuncs.size() << std::endl;
2054 
2055  for (list<TFormulaFunction>::iterator funcsIt = fFuncs.begin(); funcsIt != fFuncs.end(); ++funcsIt) {
2056  TFormulaFunction &fun = *funcsIt;
2057 
2058  // std::cout << "fun is " << fun.GetName() << std::endl;
2059 
2060  if (fun.fFound)
2061  continue;
2062  if (fun.IsFuncCall()) {
2063  // replace with pre-defined functions
2064  map<TString, TString>::iterator it = fFunctionsShortcuts.find(fun.GetName());
2065  if (it != fFunctionsShortcuts.end()) {
2066  TString shortcut = it->first;
2067  TString full = it->second;
2068  // std::cout << " functor " << fun.GetName() << " found - replace " << shortcut << " with " << full << " in
2069  // " << formula << std::endl;
2070  // replace all functors
2071  Ssiz_t index = formula.Index(shortcut, 0);
2072  while (index != kNPOS) {
2073  // check that function is not in a namespace and is not in other characters
2074  // std::cout << "analyzing " << shortcut << " in " << formula << std::endl;
2075  Ssiz_t i2 = index + shortcut.Length();
2076  if ((index > 0) && (isalpha(formula[index - 1]) || formula[index - 1] == ':')) {
2077  index = formula.Index(shortcut, i2);
2078  continue;
2079  }
2080  if (i2 < formula.Length() && formula[i2] != '(') {
2081  index = formula.Index(shortcut, i2);
2082  continue;
2083  }
2084  // now replace the string
2085  formula.Replace(index, shortcut.Length(), full);
2086  Ssiz_t inext = index + full.Length();
2087  index = formula.Index(shortcut, inext);
2088  fun.fFound = true;
2089  }
2090  }
2091  // for functions we can live it to cling to decide if it is a valid function or NOT
2092  // We don't need to retrieve this information from the ROOT interpreter
2093  // we assume that the function is then found and all the following code does not need to be there
2094 #ifdef TFORMULA_CHECK_FUNCTIONS
2095 
2096  if (fun.fName.Contains("::")) // add support for nested namespaces
2097  {
2098  // look for last occurence of "::"
2099  std::string name(fun.fName.Data());
2100  size_t index = name.rfind("::");
2101  assert(index != std::string::npos);
2102  TString className = fun.fName(0, fun.fName(0, index).Length());
2103  TString functionName = fun.fName(index + 2, fun.fName.Length());
2104 
2105  Bool_t silent = true;
2106  TClass *tclass = TClass::GetClass(className, silent);
2107  // std::cout << "looking for class " << className << std::endl;
2108  const TList *methodList = tclass->GetListOfAllPublicMethods();
2109  TIter next(methodList);
2110  TMethod *p;
2111  while ((p = (TMethod *)next())) {
2112  if (strcmp(p->GetName(), functionName.Data()) == 0 &&
2113  (fun.GetNargs() <= p->GetNargs() && fun.GetNargs() >= p->GetNargs() - p->GetNargsOpt())) {
2114  fun.fFound = true;
2115  break;
2116  }
2117  }
2118  }
2119  if (!fun.fFound) {
2120  // try to look into all the global functions in gROOT
2121  TFunction *f;
2122  {
2124  f = (TFunction *)gROOT->GetListOfGlobalFunctions(true)->FindObject(fun.fName);
2125  }
2126  // if found a function with matching arguments
2127  if (f && fun.GetNargs() <= f->GetNargs() && fun.GetNargs() >= f->GetNargs() - f->GetNargsOpt()) {
2128  fun.fFound = true;
2129  }
2130  }
2131 
2132  if (!fun.fFound) {
2133  // ignore not found functions
2134  if (gDebug)
2135  Info("TFormula", "Could not find %s function with %d argument(s)", fun.GetName(), fun.GetNargs());
2136  fun.fFound = false;
2137  }
2138 #endif
2139  } else {
2140  TFormula *old = 0;
2141  {
2143  old = (TFormula *)gROOT->GetListOfFunctions()->FindObject(gNamePrefix + fun.fName);
2144  }
2145  if (old) {
2146  // we should not go here (this analysis is done before in ExtractFunctors)
2147  assert(false);
2148  fun.fFound = true;
2149  TString pattern = TString::Format("{%s}", fun.GetName());
2150  TString replacement = old->GetExpFormula();
2151  PreProcessFormula(replacement);
2152  ExtractFunctors(replacement);
2153  formula.ReplaceAll(pattern, replacement);
2154  continue;
2155  }
2156  // looking for default variables defined in fVars
2157 
2158  map<TString, TFormulaVariable>::iterator varsIt = fVars.find(fun.GetName());
2159  if (varsIt != fVars.end()) {
2160 
2161  TString name = (*varsIt).second.GetName();
2162  Double_t value = (*varsIt).second.fValue;
2163 
2164  AddVariable(name, value); // this set the cling variable
2165  if (!fVars[name].fFound) {
2166 
2167  fVars[name].fFound = true;
2168  int varDim = (*varsIt).second.fArrayPos; // variable dimensions (0 for x, 1 for y, 2, for z)
2169  if (varDim >= fNdim) {
2170  fNdim = varDim + 1;
2171 
2172  // we need to be sure that all other variables are added with position less
2173  for (auto &v : fVars) {
2174  if (v.second.fArrayPos < varDim && !v.second.fFound) {
2175  AddVariable(v.first, v.second.fValue);
2176  v.second.fFound = true;
2177  }
2178  }
2179  }
2180  }
2181  // remove the "{.. }" added around the variable
2182  TString pattern = TString::Format("{%s}", name.Data());
2183  TString replacement = TString::Format("x[%d]", (*varsIt).second.fArrayPos);
2184  formula.ReplaceAll(pattern, replacement);
2185 
2186  // std::cout << "Found an observable for " << fun.GetName() << std::endl;
2187 
2188  fun.fFound = true;
2189  continue;
2190  }
2191  // check for observables defined as x[0],x[1],....
2192  // maybe could use a regular expression here
2193  // only in case match with defined variables is not successful
2194  TString funname = fun.GetName();
2195  if (funname.Contains("x[") && funname.Contains("]")) {
2196  TString sdigit = funname(2, funname.Index("]"));
2197  int digit = sdigit.Atoi();
2198  if (digit >= fNdim) {
2199  fNdim = digit + 1;
2200  // we need to add the variables in fVars all of them before x[n]
2201  for (int j = 0; j < fNdim; ++j) {
2202  TString vname = TString::Format("x[%d]", j);
2203  if (fVars.find(vname) == fVars.end()) {
2204  fVars[vname] = TFormulaVariable(vname, 0, j);
2205  fVars[vname].fFound = true;
2206  AddVariable(vname, 0.);
2207  }
2208  }
2209  }
2210  // std::cout << "Found matching observable for " << funname << std::endl;
2211  fun.fFound = true;
2212  // remove the "{.. }" added around the variable
2213  TString pattern = TString::Format("{%s}", funname.Data());
2214  formula.ReplaceAll(pattern, funname);
2215  continue;
2216  }
2217  //}
2218 
2219  auto paramsIt = fParams.find(fun.GetName());
2220  if (paramsIt != fParams.end()) {
2221  // TString name = (*paramsIt).second.GetName();
2222  TString pattern = TString::Format("{[%s]}", fun.GetName());
2223  // std::cout << "pattern is " << pattern << std::endl;
2224  if (formula.Index(pattern) != kNPOS) {
2225  // TString replacement = TString::Format("p[%d]",(*paramsIt).second.fArrayPos);
2226  TString replacement = TString::Format("p[%d]", (*paramsIt).second);
2227  // std::cout << "replace pattern " << pattern << " with " << replacement << std::endl;
2228  formula.ReplaceAll(pattern, replacement);
2229  }
2230  fun.fFound = true;
2231  continue;
2232  } else {
2233  // std::cout << "functor " << fun.GetName() << " is not a parameter " << std::endl;
2234  }
2235 
2236  // looking for constants (needs to be done after looking at the parameters)
2237  map<TString, Double_t>::iterator constIt = fConsts.find(fun.GetName());
2238  if (constIt != fConsts.end()) {
2239  TString pattern = TString::Format("{%s}", fun.GetName());
2240  TString value = TString::Format("%lf", (*constIt).second);
2241  formula.ReplaceAll(pattern, value);
2242  fun.fFound = true;
2243  // std::cout << "constant with name " << fun.GetName() << " is found " << std::endl;
2244  continue;
2245  }
2246 
2247  fun.fFound = false;
2248  }
2249  }
2250  // std::cout << "End: formula is " << formula << std::endl;
2251 
2252  // ignore case of functors have been matched - try to pass it to Cling
2253  if (!fReadyToExecute) {
2254  fReadyToExecute = true;
2255  Bool_t hasVariables = (fNdim > 0);
2256  Bool_t hasParameters = (fNpar > 0);
2257  if (!hasParameters) {
2258  fAllParametersSetted = true;
2259  }
2260  // assume a function without variables is always 1-dimensional ???
2261  // if (hasParameters && !hasVariables) {
2262  // fNdim = 1;
2263  // AddVariable("x", 0);
2264  // hasVariables = true;
2265  // }
2266  // does not make sense to vectorize function which is of FNDim=0
2267  if (!hasVariables) fVectorized=false;
2268  // when there are no variables but only parameter we still need to ad
2269  //Bool_t hasBoth = hasVariables && hasParameters;
2270  Bool_t inputIntoCling = (formula.Length() > 0);
2271  if (inputIntoCling) {
2272  // save copy of inputFormula in a std::strig for the unordered map
2273  // and also formula is same as FClingInput typically and it will be modified
2274  std::string inputFormula(formula.Data());
2275 
2276  // The name we really use for the unordered map will have a flag that
2277  // says whether the formula is vectorized
2278  std::string inputFormulaVecFlag = inputFormula;
2279  if (fVectorized)
2280  inputFormulaVecFlag += " (vectorized)";
2281 
2282  TString argType = fVectorized ? "ROOT::Double_v" : "Double_t";
2283 
2284  // valid input formula - try to put into Cling (in case of no variables but only parameter we need to add the standard signature)
2285  TString argumentsPrototype = TString::Format("%s%s%s", ( (hasVariables || hasParameters) ? (argType + " *x").Data() : ""),
2286  (hasParameters ? "," : ""), (hasParameters ? "Double_t *p" : ""));
2287 
2288  // set the name for Cling using the hash_function
2290 
2291  // check if formula exist already in the map
2293 
2294  // std::cout << "gClingFunctions list" << std::endl;
2295  // for (auto thing : gClingFunctions)
2296  // std::cout << "gClingFunctions : " << thing.first << std::endl;
2297 
2298  auto funcit = gClingFunctions.find(inputFormulaVecFlag);
2299 
2300  if (funcit != gClingFunctions.end()) {
2301  fFuncPtr = (TFormula::CallFuncSignature)funcit->second;
2302  fClingInitialized = true;
2303  inputIntoCling = false;
2304  }
2305 
2306 
2307 
2308  // set the cling name using hash of the static formulae map
2309  auto hasher = gClingFunctions.hash_function();
2310  fClingName = TString::Format("%s__id%zu", gNamePrefix.Data(), hasher(inputFormulaVecFlag));
2311 
2312  fClingInput = TString::Format("%s %s(%s){ return %s ; }", argType.Data(), fClingName.Data(),
2313  argumentsPrototype.Data(), inputFormula.c_str());
2314 
2315 
2316  // std::cout << "Input Formula " << inputFormula << " \t vec formula : " << inputFormulaVecFlag << std::endl;
2317  // std::cout << "Cling functions existing " << std::endl;
2318  // for (auto & ff : gClingFunctions)
2319  // std::cout << ff.first << std::endl;
2320  // std::cout << "\n";
2321  // std::cout << fClingName << std::endl;
2322 
2323  // this is not needed (maybe can be re-added in case of recompilation of identical expressions
2324  // // check in case of a change if need to re-initialize
2325  // if (fClingInitialized) {
2326  // if (oldClingInput == fClingInput)
2327  // inputIntoCling = false;
2328  // else
2329  // fClingInitialized = false;
2330  // }
2331 
2332  if (inputIntoCling) {
2333  if (!fLazyInitialization) {
2335  if (fClingInitialized) {
2336  // if Cling has been successfully initialized
2337  // put function ptr in the static map
2339  gClingFunctions.insert(std::make_pair(inputFormulaVecFlag, (void *)fFuncPtr));
2340  }
2341  }
2342  if (!fClingInitialized) {
2343  // needed in case of lazy initialization of failure compiling the expression
2344  fSavedInputFormula = inputFormulaVecFlag;
2345  }
2346 
2347  } else {
2348  fAllParametersSetted = true;
2349  fClingInitialized = true;
2350  }
2351  }
2352  }
2353 
2354  // In case of a Cling Error check components which are not found in Cling
2355  // check that all formula components are matched otherwise emit an error
2357  //Bool_t allFunctorsMatched = false;
2358  for (list<TFormulaFunction>::iterator it = fFuncs.begin(); it != fFuncs.end(); ++it) {
2359  // functions are now by default always not checked
2360  if (!it->fFound && !it->IsFuncCall()) {
2361  //allFunctorsMatched = false;
2362  if (it->GetNargs() == 0)
2363  Error("ProcessFormula", "\"%s\" has not been matched in the formula expression", it->GetName());
2364  else
2365  Error("ProcessFormula", "Could not find %s function with %d argument(s)", it->GetName(), it->GetNargs());
2366  }
2367  }
2368  Error("ProcessFormula","Formula \"%s\" is invalid !", GetExpFormula().Data() );
2369  fReadyToExecute = false;
2370  }
2371 
2372  // clean up un-used default variables in case formula is valid
2373  //if (fClingInitialized && fReadyToExecute) {
2374  //don't check fClingInitialized in case of lazy execution
2375  if (fReadyToExecute) {
2376  auto itvar = fVars.begin();
2377  // need this loop because after erase change iterators
2378  do {
2379  if (!itvar->second.fFound) {
2380  // std::cout << "Erase variable " << itvar->first << std::endl;
2381  itvar = fVars.erase(itvar);
2382  } else
2383  itvar++;
2384  } while (itvar != fVars.end());
2385  }
2386 }
2387 
2388 ////////////////////////////////////////////////////////////////////////////////
2389 /// Fill map with parametrized functions
2390 
2391 void TFormula::FillParametrizedFunctions(map<pair<TString, Int_t>, pair<TString, TString>> &functions)
2392 {
2393  // map< pair<TString,Int_t> ,pair<TString,TString> > functions;
2394  functions.insert(
2395  make_pair(make_pair("gaus", 1), make_pair("[0]*exp(-0.5*(({V0}-[1])/[2])*(({V0}-[1])/[2]))",
2396  "[0]*exp(-0.5*(({V0}-[1])/[2])*(({V0}-[1])/[2]))/(sqrt(2*pi)*[2])")));
2397  functions.insert(make_pair(make_pair("landau", 1), make_pair("[0]*TMath::Landau({V0},[1],[2],false)",
2398  "[0]*TMath::Landau({V0},[1],[2],true)")));
2399  functions.insert(make_pair(make_pair("expo", 1), make_pair("exp([0]+[1]*{V0})", "")));
2400  functions.insert(
2401  make_pair(make_pair("crystalball", 1), make_pair("[0]*ROOT::Math::crystalball_function({V0},[3],[4],[2],[1])",
2402  "[0]*ROOT::Math::crystalball_pdf({V0},[3],[4],[2],[1])")));
2403  functions.insert(
2404  make_pair(make_pair("breitwigner", 1), make_pair("[0]*ROOT::Math::breitwigner_pdf({V0},[2],[1])",
2405  "[0]*ROOT::Math::breitwigner_pdf({V0},[2],[4],[1])")));
2406  // chebyshev polynomial
2407  functions.insert(make_pair(make_pair("cheb0", 1), make_pair("ROOT::Math::Chebyshev0({V0},[0])", "")));
2408  functions.insert(make_pair(make_pair("cheb1", 1), make_pair("ROOT::Math::Chebyshev1({V0},[0],[1])", "")));
2409  functions.insert(make_pair(make_pair("cheb2", 1), make_pair("ROOT::Math::Chebyshev2({V0},[0],[1],[2])", "")));
2410  functions.insert(make_pair(make_pair("cheb3", 1), make_pair("ROOT::Math::Chebyshev3({V0},[0],[1],[2],[3])", "")));
2411  functions.insert(
2412  make_pair(make_pair("cheb4", 1), make_pair("ROOT::Math::Chebyshev4({V0},[0],[1],[2],[3],[4])", "")));
2413  functions.insert(
2414  make_pair(make_pair("cheb5", 1), make_pair("ROOT::Math::Chebyshev5({V0},[0],[1],[2],[3],[4],[5])", "")));
2415  functions.insert(
2416  make_pair(make_pair("cheb6", 1), make_pair("ROOT::Math::Chebyshev6({V0},[0],[1],[2],[3],[4],[5],[6])", "")));
2417  functions.insert(
2418  make_pair(make_pair("cheb7", 1), make_pair("ROOT::Math::Chebyshev7({V0},[0],[1],[2],[3],[4],[5],[6],[7])", "")));
2419  functions.insert(make_pair(make_pair("cheb8", 1),
2420  make_pair("ROOT::Math::Chebyshev8({V0},[0],[1],[2],[3],[4],[5],[6],[7],[8])", "")));
2421  functions.insert(make_pair(make_pair("cheb9", 1),
2422  make_pair("ROOT::Math::Chebyshev9({V0},[0],[1],[2],[3],[4],[5],[6],[7],[8],[9])", "")));
2423  functions.insert(
2424  make_pair(make_pair("cheb10", 1),
2425  make_pair("ROOT::Math::Chebyshev10({V0},[0],[1],[2],[3],[4],[5],[6],[7],[8],[9],[10])", "")));
2426  // 2-dimensional functions
2427  functions.insert(
2428  make_pair(make_pair("gaus", 2), make_pair("[0]*exp(-0.5*(({V0}-[1])/[2])^2 - 0.5*(({V1}-[3])/[4])^2)", "")));
2429  functions.insert(
2430  make_pair(make_pair("landau", 2),
2431  make_pair("[0]*TMath::Landau({V0},[1],[2],false)*TMath::Landau({V1},[3],[4],false)", "")));
2432  functions.insert(make_pair(make_pair("expo", 2), make_pair("exp([0]+[1]*{V0})", "exp([0]+[1]*{V0}+[2]*{V1})")));
2433  // 3-dimensional function
2434  functions.insert(
2435  make_pair(make_pair("gaus", 3), make_pair("[0]*exp(-0.5*(({V0}-[1])/[2])^2 - 0.5*(({V1}-[3])/[4])^2 - 0.5*(({V2}-[5])/[6])^2)", "")));
2436  // gaussian with correlations
2437  functions.insert(
2438  make_pair(make_pair("bigaus", 2), make_pair("[0]*ROOT::Math::bigaussian_pdf({V0},{V1},[2],[4],[5],[1],[3])",
2439  "[0]*ROOT::Math::bigaussian_pdf({V0},{V1},[2],[4],[5],[1],[3])")));
2440 }
2441 
2442 ////////////////////////////////////////////////////////////////////////////////
2443 /// Set parameter names only in case of pre-defined functions.
2444 
2446 
2447  if (fNumber == 0) return;
2448 
2449  if (fNumber == 100) { // Gaussian
2450  SetParName(0,"Constant");
2451  SetParName(1,"Mean");
2452  SetParName(2,"Sigma");
2453  return;
2454  }
2455  if (fNumber == 110) {
2456  SetParName(0,"Constant");
2457  SetParName(1,"MeanX");
2458  SetParName(2,"SigmaX");
2459  SetParName(3,"MeanY");
2460  SetParName(4,"SigmaY");
2461  return;
2462  }
2463  if (fNumber == 120) {
2464  SetParName(0,"Constant");
2465  SetParName(1,"MeanX");
2466  SetParName(2,"SigmaX");
2467  SetParName(3,"MeanY");
2468  SetParName(4,"SigmaY");
2469  SetParName(5,"MeanZ");
2470  SetParName(6,"SigmaZ");
2471  return;
2472  }
2473  if (fNumber == 112) { // bigaus
2474  SetParName(0,"Constant");
2475  SetParName(1,"MeanX");
2476  SetParName(2,"SigmaX");
2477  SetParName(3,"MeanY");
2478  SetParName(4,"SigmaY");
2479  SetParName(5,"Rho");
2480  return;
2481  }
2482  if (fNumber == 200) { // exponential
2483  SetParName(0,"Constant");
2484  SetParName(1,"Slope");
2485  return;
2486  }
2487  if (fNumber == 400) { // landau
2488  SetParName(0,"Constant");
2489  SetParName(1,"MPV");
2490  SetParName(2,"Sigma");
2491  return;
2492  }
2493  if (fNumber == 500) { // crystal-ball
2494  SetParName(0,"Constant");
2495  SetParName(1,"Mean");
2496  SetParName(2,"Sigma");
2497  SetParName(3,"Alpha");
2498  SetParName(4,"N");
2499  return;
2500  }
2501  if (fNumber == 600) { // breit-wigner
2502  SetParName(0,"Constant");
2503  SetParName(1,"Mean");
2504  SetParName(2,"Gamma");
2505  return;
2506  }
2507  // if formula is a polynomial (or chebyshev), set parameter names
2508  // not needed anymore (p0 is assigned by default)
2509  // if (fNumber == (300+fNpar-1) ) {
2510  // for (int i = 0; i < fNpar; i++) SetParName(i,TString::Format("p%d",i));
2511  // return;
2512  // }
2513 
2514  // // general case if parameters are digits (XX) change to pXX
2515  // auto paramMap = fParams; // need to copy the map because SetParName is going to modify it
2516  // for ( auto & p : paramMap) {
2517  // if (p.first.IsDigit() )
2518  // SetParName(p.second,TString::Format("p%s",p.first.Data()));
2519  // }
2520 
2521  return;
2522 }
2523 
2524 ////////////////////////////////////////////////////////////////////////////////
2525 /// Return linear part.
2526 
2527 const TObject* TFormula::GetLinearPart(Int_t i) const
2528 {
2529  if (!fLinearParts.empty()) {
2530  int n = fLinearParts.size();
2531  if (i < 0 || i >= n ) {
2532  Error("GetLinearPart","Formula %s has only %d linear parts - requested %d",GetName(),n,i);
2533  return nullptr;
2534  }
2535  return fLinearParts[i];
2536  }
2537  return nullptr;
2538 }
2539 
2540 ////////////////////////////////////////////////////////////////////////////////
2541 /// Adds variable to known variables, and reprocess formula.
2542 
2543 void TFormula::AddVariable(const TString &name, double value)
2544 {
2545  if (fVars.find(name) != fVars.end()) {
2546  TFormulaVariable &var = fVars[name];
2547  var.fValue = value;
2548 
2549  // If the position is not defined in the Cling vectors, make space for it
2550  // but normally is variable is defined in fVars a slot should be also present in fClingVariables
2551  if (var.fArrayPos < 0) {
2552  var.fArrayPos = fVars.size();
2553  }
2554  if (var.fArrayPos >= (int)fClingVariables.size()) {
2555  fClingVariables.resize(var.fArrayPos + 1);
2556  }
2557  fClingVariables[var.fArrayPos] = value;
2558  } else {
2559  TFormulaVariable var(name, value, fVars.size());
2560  fVars[name] = var;
2561  fClingVariables.push_back(value);
2562  if (!fFormula.IsNull()) {
2563  // printf("process formula again - %s \n",fClingInput.Data() );
2565  }
2566  }
2567 }
2568 
2569 ////////////////////////////////////////////////////////////////////////////////
2570 /// Adds multiple variables.
2571 /// First argument is an array of pairs<TString,Double>, where
2572 /// first argument is name of variable,
2573 /// second argument represents value.
2574 /// size - number of variables passed in first argument
2575 
2576 void TFormula::AddVariables(const TString *vars, const Int_t size)
2577 {
2578  Bool_t anyNewVar = false;
2579  for (Int_t i = 0; i < size; ++i) {
2580 
2581  const TString &vname = vars[i];
2582 
2583  TFormulaVariable &var = fVars[vname];
2584  if (var.fArrayPos < 0) {
2585 
2586  var.fName = vname;
2587  var.fArrayPos = fVars.size();
2588  anyNewVar = true;
2589  var.fValue = 0;
2590  if (var.fArrayPos >= (int)fClingVariables.capacity()) {
2591  Int_t multiplier = 2;
2592  if (fFuncs.size() > 100) {
2593  multiplier = TMath::Floor(TMath::Log10(fFuncs.size()) * 10);
2594  }
2595  fClingVariables.reserve(multiplier * fClingVariables.capacity());
2596  }
2597  fClingVariables.push_back(0.0);
2598  }
2599  // else
2600  // {
2601  // var.fValue = v.second;
2602  // fClingVariables[var.fArrayPos] = v.second;
2603  // }
2604  }
2605  if (anyNewVar && !fFormula.IsNull()) {
2607  }
2608 }
2609 
2610 ////////////////////////////////////////////////////////////////////////////////
2611 /// Set the name of the formula. We need to allow the list of function to
2612 /// properly handle the hashes.
2613 
2614 void TFormula::SetName(const char* name)
2615 {
2616  if (IsReservedName(name)) {
2617  Error("SetName", "The name \'%s\' is reserved as a TFormula variable name.\n"
2618  "\tThis function will not be renamed.",
2619  name);
2620  } else {
2621  // Here we need to remove and re-add to keep the hashes consistent with
2622  // the underlying names.
2623  auto listOfFunctions = gROOT->GetListOfFunctions();
2624  TObject* thisAsFunctionInList = nullptr;
2626  if (listOfFunctions){
2627  thisAsFunctionInList = listOfFunctions->FindObject(this);
2628  if (thisAsFunctionInList) listOfFunctions->Remove(thisAsFunctionInList);
2629  }
2631  if (thisAsFunctionInList) listOfFunctions->Add(thisAsFunctionInList);
2632  }
2633 }
2634 
2635 ////////////////////////////////////////////////////////////////////////////////
2636 ///
2637 /// Sets multiple variables.
2638 /// First argument is an array of pairs<TString,Double>, where
2639 /// first argument is name of variable,
2640 /// second argument represents value.
2641 /// size - number of variables passed in first argument
2642 
2643 void TFormula::SetVariables(const pair<TString,Double_t> *vars, const Int_t size)
2644 {
2645  for(Int_t i = 0; i < size; ++i)
2646  {
2647  auto &v = vars[i];
2648  if (fVars.find(v.first) != fVars.end()) {
2649  fVars[v.first].fValue = v.second;
2650  fClingVariables[fVars[v.first].fArrayPos] = v.second;
2651  } else {
2652  Error("SetVariables", "Variable %s is not defined.", v.first.Data());
2653  }
2654  }
2655 }
2656 
2657 ////////////////////////////////////////////////////////////////////////////////
2658 /// Returns variable value.
2659 
2660 Double_t TFormula::GetVariable(const char *name) const
2661 {
2662  const auto nameIt = fVars.find(name);
2663  if (fVars.end() == nameIt) {
2664  Error("GetVariable", "Variable %s is not defined.", name);
2665  return -1;
2666  }
2667  return nameIt->second.fValue;
2668 }
2669 
2670 ////////////////////////////////////////////////////////////////////////////////
2671 /// Returns variable number (positon in array) given its name.
2672 
2673 Int_t TFormula::GetVarNumber(const char *name) const
2674 {
2675  const auto nameIt = fVars.find(name);
2676  if (fVars.end() == nameIt) {
2677  Error("GetVarNumber", "Variable %s is not defined.", name);
2678  return -1;
2679  }
2680  return nameIt->second.fArrayPos;
2681 }
2682 
2683 ////////////////////////////////////////////////////////////////////////////////
2684 /// Returns variable name given its position in the array.
2685 
2687 {
2688  if (ivar < 0 || ivar >= fNdim) return "";
2689 
2690  // need to loop on the map to find corresponding variable
2691  for ( auto & v : fVars) {
2692  if (v.second.fArrayPos == ivar) return v.first;
2693  }
2694  Error("GetVarName","Variable with index %d not found !!",ivar);
2695  //return TString::Format("x%d",ivar);
2696  return "";
2697 }
2698 
2699 ////////////////////////////////////////////////////////////////////////////////
2700 /// Sets variable value.
2701 
2702 void TFormula::SetVariable(const TString &name, Double_t value)
2703 {
2704  if (fVars.find(name) == fVars.end()) {
2705  Error("SetVariable", "Variable %s is not defined.", name.Data());
2706  return;
2707  }
2708  fVars[name].fValue = value;
2709  fClingVariables[fVars[name].fArrayPos] = value;
2710 }
2711 
2712 ////////////////////////////////////////////////////////////////////////////////
2713 /// Adds parameter to known parameters.
2714 /// User should use SetParameter, because parameters are added during initialization part,
2715 /// and after that adding new will be pointless.
2716 
2717 void TFormula::DoAddParameter(const TString &name, Double_t value, Bool_t processFormula)
2718 {
2719  //std::cout << "adding parameter " << name << std::endl;
2720 
2721  // if parameter is already defined in fParams - just set the new value
2722  if(fParams.find(name) != fParams.end() )
2723  {
2724  int ipos = fParams[name];
2725  // TFormulaVariable & par = fParams[name];
2726  // par.fValue = value;
2727  if (ipos < 0) {
2728  ipos = fParams.size();
2729  fParams[name] = ipos;
2730  }
2731  //
2732  if (ipos >= (int)fClingParameters.size()) {
2733  if (ipos >= (int)fClingParameters.capacity())
2734  fClingParameters.reserve(TMath::Max(int(fParams.size()), ipos + 1));
2735  fClingParameters.insert(fClingParameters.end(), ipos + 1 - fClingParameters.size(), 0.0);
2736  }
2737  fClingParameters[ipos] = value;
2738  } else {
2739  // new parameter defined
2740  fNpar++;
2741  // TFormulaVariable(name,value,fParams.size());
2742  int pos = fParams.size();
2743  // fParams.insert(std::make_pair<TString,TFormulaVariable>(name,TFormulaVariable(name,value,pos)));
2744  auto ret = fParams.insert(std::make_pair(name, pos));
2745  // map returns a std::pair<iterator, bool>
2746  // use map the order for default position of parameters in the vector
2747  // (i.e use the alphabetic order)
2748  if (ret.second) {
2749  // a new element is inserted
2750  if (ret.first == fParams.begin())
2751  pos = 0;
2752  else {
2753  auto previous = (ret.first);
2754  --previous;
2755  pos = previous->second + 1;
2756  }
2757 
2758  if (pos < (int)fClingParameters.size())
2759  fClingParameters.insert(fClingParameters.begin() + pos, value);
2760  else {
2761  // this should not happen
2762  if (pos > (int)fClingParameters.size())
2763  Warning("inserting parameter %s at pos %d when vector size is %d \n", name.Data(), pos,
2764  (int)fClingParameters.size());
2765 
2766  if (pos >= (int)fClingParameters.capacity())
2767  fClingParameters.reserve(TMath::Max(int(fParams.size()), pos + 1));
2768  fClingParameters.insert(fClingParameters.end(), pos + 1 - fClingParameters.size(), 0.0);
2769  fClingParameters[pos] = value;
2770  }
2771 
2772  // need to adjust all other positions
2773  for (auto it = ret.first; it != fParams.end(); ++it) {
2774  it->second = pos;
2775  pos++;
2776  }
2777 
2778  // for (auto & p : fParams)
2779  // std::cout << "Parameter " << p.first << " position " << p.second << " value " <<
2780  // fClingParameters[p.second] << std::endl;
2781  // printf("inserted parameters size params %d size cling %d \n",fParams.size(), fClingParameters.size() );
2782  }
2783  if (processFormula) {
2784  // replace first in input parameter name with [name]
2785  fClingInput.ReplaceAll(name, TString::Format("[%s]", name.Data()));
2787  }
2788  }
2789 }
2790 
2791 ////////////////////////////////////////////////////////////////////////////////
2792 /// Return parameter index given a name (return -1 for not existing parameters)
2793 /// non need to print an error
2794 
2795 Int_t TFormula::GetParNumber(const char * name) const {
2796  auto it = fParams.find(name);
2797  if (it == fParams.end()) {
2798  return -1;
2799  }
2800  return it->second;
2801 
2802 }
2803 
2804 ////////////////////////////////////////////////////////////////////////////////
2805 /// Returns parameter value given by string.
2806 
2807 Double_t TFormula::GetParameter(const char * name) const
2808 {
2809  const int i = GetParNumber(name);
2810  if (i == -1) {
2811  Error("GetParameter","Parameter %s is not defined.",name);
2812  return TMath::QuietNaN();
2813  }
2814 
2815  return GetParameter( i );
2816 }
2817 
2818 ////////////////////////////////////////////////////////////////////////////////
2819 /// Return parameter value given by integer.
2820 
2822 {
2823  //TString name = TString::Format("%d",param);
2824  if(param >=0 && param < (int) fClingParameters.size())
2825  return fClingParameters[param];
2826  Error("GetParameter","wrong index used - use GetParameter(name)");
2827  return TMath::QuietNaN();
2828 }
2829 
2830 ////////////////////////////////////////////////////////////////////////////////
2831 /// Return parameter name given by integer.
2832 
2833 const char * TFormula::GetParName(Int_t ipar) const
2834 {
2835  if (ipar < 0 || ipar >= fNpar) return "";
2836 
2837  // need to loop on the map to find corresponding parameter
2838  for ( auto & p : fParams) {
2839  if (p.second == ipar) return p.first.Data();
2840  }
2841  Error("GetParName","Parameter with index %d not found !!",ipar);
2842  //return TString::Format("p%d",ipar);
2843  return "";
2844 }
2845 
2846 ////////////////////////////////////////////////////////////////////////////////
2848 {
2849  if(!fClingParameters.empty())
2850  return const_cast<Double_t*>(&fClingParameters[0]);
2851  return 0;
2852 }
2853 
2854 void TFormula::GetParameters(Double_t *params) const
2855 {
2856  for (Int_t i = 0; i < fNpar; ++i) {
2857  if (Int_t(fClingParameters.size()) > i)
2858  params[i] = fClingParameters[i];
2859  else
2860  params[i] = -1;
2861  }
2862 }
2863 
2864 ////////////////////////////////////////////////////////////////////////////////
2865 /// Sets parameter value.
2866 
2867 void TFormula::SetParameter(const char *name, Double_t value)
2868 {
2869  SetParameter( GetParNumber(name), value);
2870 
2871  // do we need this ???
2872 #ifdef OLDPARAMS
2873  if (fParams.find(name) == fParams.end()) {
2874  Error("SetParameter", "Parameter %s is not defined.", name.Data());
2875  return;
2876  }
2877  fParams[name].fValue = value;
2878  fParams[name].fFound = true;
2879  fClingParameters[fParams[name].fArrayPos] = value;
2880  fAllParametersSetted = true;
2881  for (map<TString, TFormulaVariable>::iterator it = fParams.begin(); it != fParams.end(); ++it) {
2882  if (!it->second.fFound) {
2883  fAllParametersSetted = false;
2884  break;
2885  }
2886  }
2887 #endif
2888 }
2889 
2890 #ifdef OLDPARAMS
2891 
2892 ////////////////////////////////////////////////////////////////////////////////
2893 /// Set multiple parameters.
2894 /// First argument is an array of pairs<TString,Double>, where
2895 /// first argument is name of parameter,
2896 /// second argument represents value.
2897 /// size - number of params passed in first argument
2898 
2899 void TFormula::SetParameters(const pair<TString,Double_t> *params,const Int_t size)
2900 {
2901  for(Int_t i = 0 ; i < size ; ++i)
2902  {
2903  pair<TString, Double_t> p = params[i];
2904  if (fParams.find(p.first) == fParams.end()) {
2905  Error("SetParameters", "Parameter %s is not defined", p.first.Data());
2906  continue;
2907  }
2908  fParams[p.first].fValue = p.second;
2909  fParams[p.first].fFound = true;
2910  fClingParameters[fParams[p.first].fArrayPos] = p.second;
2911  }
2912  fAllParametersSetted = true;
2913  for (map<TString, TFormulaVariable>::iterator it = fParams.begin(); it != fParams.end(); ++it) {
2914  if (!it->second.fFound) {
2915  fAllParametersSetted = false;
2916  break;
2917  }
2918  }
2919 }
2920 #endif
2921 
2922 ////////////////////////////////////////////////////////////////////////////////
2923 void TFormula::DoSetParameters(const Double_t *params, Int_t size)
2924 {
2925  if(!params || size < 0 || size > fNpar) return;
2926  // reset vector of cling parameters
2927  if (size != (int) fClingParameters.size() ) {
2928  Warning("SetParameters","size is not same of cling parameter size %d - %d",size,int(fClingParameters.size()) );
2929  for (Int_t i = 0; i < size; ++i) {
2930  TString name = TString::Format("%d", i);
2931  SetParameter(name, params[i]);
2932  }
2933  return;
2934  }
2935  fAllParametersSetted = true;
2936  std::copy(params, params+size, fClingParameters.begin() );
2937 }
2938 
2939 ////////////////////////////////////////////////////////////////////////////////
2940 /// Set a vector of parameters value.
2941 /// Order in the vector is by default the alphabetic order given to the parameters
2942 /// apart if the users has defined explicitly the parameter names
2943 
2944 void TFormula::SetParameters(const Double_t *params)
2945 {
2946  DoSetParameters(params,fNpar);
2947 }
2948 
2949 ////////////////////////////////////////////////////////////////////////////////
2950 /// Set a list of parameters.
2951 /// The order is by default the alphabetic order given to the parameters
2952 /// apart if the users has defined explicitly the parameter names
2953 
2955  Double_t p7, Double_t p8, Double_t p9, Double_t p10)
2956 {
2957  if(fNpar >= 1) SetParameter(0,p0);
2958  if(fNpar >= 2) SetParameter(1,p1);
2959  if(fNpar >= 3) SetParameter(2,p2);
2960  if(fNpar >= 4) SetParameter(3,p3);
2961  if(fNpar >= 5) SetParameter(4,p4);
2962  if(fNpar >= 6) SetParameter(5,p5);
2963  if(fNpar >= 7) SetParameter(6,p6);
2964  if(fNpar >= 8) SetParameter(7,p7);
2965  if(fNpar >= 9) SetParameter(8,p8);
2966  if(fNpar >= 10) SetParameter(9,p9);
2967  if(fNpar >= 11) SetParameter(10,p10);
2968 }
2969 
2970 ////////////////////////////////////////////////////////////////////////////////
2971 /// Set a parameter given a parameter index
2972 /// The parameter index is by default the alphabetic order given to the parameters
2973 /// apart if the users has defined explicitly the parameter names
2974 
2975 void TFormula::SetParameter(Int_t param, Double_t value)
2976 {
2977  if (param < 0 || param >= fNpar) return;
2978  assert(int(fClingParameters.size()) == fNpar);
2979  fClingParameters[param] = value;
2980  // TString name = TString::Format("%d",param);
2981  // SetParameter(name,value);
2982 }
2983 
2984 ////////////////////////////////////////////////////////////////////////////////
2985 void TFormula::SetParNames(const char *name0, const char *name1, const char *name2, const char *name3,
2986  const char *name4, const char *name5, const char *name6, const char *name7,
2987  const char *name8, const char *name9, const char *name10)
2988 {
2989  if (fNpar >= 1)
2990  SetParName(0, name0);
2991  if (fNpar >= 2)
2992  SetParName(1, name1);
2993  if (fNpar >= 3)
2994  SetParName(2, name2);
2995  if (fNpar >= 4)
2996  SetParName(3, name3);
2997  if (fNpar >= 5)
2998  SetParName(4, name4);
2999  if (fNpar >= 6)
3000  SetParName(5, name5);
3001  if (fNpar >= 7)
3002  SetParName(6, name6);
3003  if (fNpar >= 8)
3004  SetParName(7, name7);
3005  if (fNpar >= 9)
3006  SetParName(8, name8);
3007  if (fNpar >= 10)
3008  SetParName(9, name9);
3009  if (fNpar >= 11)
3010  SetParName(10, name10);
3011 }
3012 
3013 ////////////////////////////////////////////////////////////////////////////////
3014 void TFormula::SetParName(Int_t ipar, const char * name)
3015 {
3016 
3017  if (ipar < 0 || ipar > fNpar) {
3018  Error("SetParName","Wrong Parameter index %d ",ipar);
3019  return;
3020  }
3021  TString oldName;
3022  // find parameter with given index
3023  for ( auto &it : fParams) {
3024  if (it.second == ipar) {
3025  oldName = it.first;
3026  fParams.erase(oldName);
3027  fParams.insert(std::make_pair(name, ipar) );
3028  break;
3029  }
3030  }
3031  if (oldName.IsNull() ) {
3032  Error("SetParName","Parameter %d is not existing.",ipar);
3033  return;
3034  }
3035 
3036  //replace also parameter name in formula expression in case is not a lambda
3038 
3039 }
3040 
3041 ////////////////////////////////////////////////////////////////////////////////
3042 /// Replace in Formula expression the parameter name.
3043 
3044 void TFormula::ReplaceParamName(TString & formula, const TString & oldName, const TString & name){
3045  if (!formula.IsNull() ) {
3046  bool found = false;
3047  for(list<TFormulaFunction>::iterator it = fFuncs.begin(); it != fFuncs.end(); ++it)
3048  {
3049  if (oldName == it->GetName()) {
3050  found = true;
3051  it->fName = name;
3052  break;
3053  }
3054  }
3055  if (!found) {
3056  Error("SetParName", "Parameter %s is not defined.", oldName.Data());
3057  return;
3058  }
3059  // change whitespace to \s to avoid problems in parsing
3060  TString newName = name;
3061  newName.ReplaceAll(" ", "\\s");
3062  TString pattern = TString::Format("[%s]", oldName.Data());
3063  TString replacement = TString::Format("[%s]", newName.Data());
3064  formula.ReplaceAll(pattern, replacement);
3065  }
3066 
3067 }
3068 
3069 ////////////////////////////////////////////////////////////////////////////////
3070 void TFormula::SetVectorized(Bool_t vectorized)
3071 {
3072 #ifdef R__HAS_VECCORE
3073  if (fNdim == 0) {
3074  Info("SetVectorized","Cannot vectorized a function of zero dimension");
3075  return;
3076  }
3077  if (vectorized != fVectorized) {
3078  if (!fFormula)
3079  Error("SetVectorized", "Cannot set vectorized to %d -- Formula is missing", vectorized);
3080 
3081  fVectorized = vectorized;
3082  // no need to JIT a new signature in case of zero dimension
3083  //if (fNdim== 0) return;
3084  fClingInitialized = false;
3085  fReadyToExecute = false;
3086  fClingName = "";
3088 
3089  if (fMethod)
3090  fMethod->Delete();
3091  fMethod = nullptr;
3092 
3093  FillVecFunctionsShurtCuts(); // to replace with the right vectorized signature (e.g. sin -> vecCore::math::Sin)
3096  }
3097 #else
3098  if (vectorized)
3099  Warning("SetVectorized", "Cannot set vectorized -- try building with option -Dbuiltin_veccore=On");
3100 #endif
3101 }
3102 
3103 ////////////////////////////////////////////////////////////////////////////////
3104 Double_t TFormula::EvalPar(const Double_t *x,const Double_t *params) const
3105 {
3106  if (!fVectorized)
3107  return DoEval(x, params);
3108 
3109 #ifdef R__HAS_VECCORE
3110 
3111  if (fNdim == 0 || !x) {
3112  ROOT::Double_v ret = DoEvalVec(nullptr, params);
3113  return vecCore::Get( ret, 0 );
3114  }
3115 
3116  // otherwise, regular Double_t inputs on a vectorized function
3117 
3118  // convert our input into vectors then convert back
3119  if (gDebug)
3120  Info("EvalPar", "Function is vectorized - converting Double_t into ROOT::Double_v and back");
3121 
3122  if (fNdim < 5) {
3123  const int maxDim = 4;
3124  std::array<ROOT::Double_v, maxDim> xvec;
3125  for (int i = 0; i < fNdim; i++)
3126  xvec[i] = x[i];
3127 
3128  ROOT::Double_v ans = DoEvalVec(xvec.data(), params);
3129  return vecCore::Get(ans, 0);
3130  }
3131  // allocating a vector is much slower (we do only for dim > 4)
3132  std::vector<ROOT::Double_v> xvec(fNdim);
3133  for (int i = 0; i < fNdim; i++)
3134  xvec[i] = x[i];
3135 
3136  ROOT::Double_v ans = DoEvalVec(xvec.data(), params);
3137  return vecCore::Get(ans, 0);
3138 
3139 #else
3140  // this should never happen, because fVectorized can only be set true with
3141  // R__HAS_VECCORE, but just in case:
3142  Error("EvalPar", "Formula is vectorized (even though VECCORE is disabled!)");
3143  return TMath::QuietNaN();
3144 #endif
3145 }
3146 
3148 
3149 static bool functionExists(const string &Name) {
3150  return gInterpreter->GetFunction(/*cl*/0, Name.c_str());
3151 }
3152 
3153 /// returns true on success.
3155 {
3156  // We already have generated the gradient.
3157  if (fGradMethod)
3158  return true;
3159 
3160  if (!HasGradientGenerationFailed()) {
3161  // FIXME: Move this elsewhere
3164  gInterpreter->Declare("#include <Math/CladDerivator.h>\n#pragma clad OFF");
3165  }
3166 
3167  // Check if the gradient request was made as part of another TFormula.
3168  // This can happen when we create multiple TFormula objects with the same
3169  // formula. In that case, the hasher will give identical id and we can
3170  // reuse the already generated gradient function.
3171  if (!functionExists(GetGradientFuncName())) {
3172  std::string GradReqFuncName = GetGradientFuncName() + "_req";
3173  // We want to call clad::differentiate(TFormula_id);
3174  fGradGenerationInput = std::string("#pragma cling optimize(2)\n") +
3175  "#pragma clad ON\n" +
3176  "void " + GradReqFuncName + "() {\n" +
3177  "clad::gradient(" + std::string(fClingName.Data()) + ");\n }\n" +
3178  "#pragma clad OFF";
3179 
3180  if (!gInterpreter->Declare(fGradGenerationInput.c_str()))
3181  return false;
3182  }
3183 
3184  Bool_t hasParameters = (fNpar > 0);
3185  Bool_t hasVariables = (fNdim > 0);
3186  std::string GradFuncName = GetGradientFuncName();
3187  fGradMethod = prepareMethod(hasParameters, hasVariables,
3188  GradFuncName.c_str(),
3189  fVectorized, /*IsGradient*/ true);
3191  return true;
3192  }
3193  return false;
3194 }
3195 
3197 {
3198  if (DoEval(x) == TMath::QuietNaN())
3199  return;
3200 
3201  if (!fClingInitialized) {
3202  Error("GradientPar", "Could not initialize the formula!");
3203  return;
3204  }
3205 
3206  if (!GenerateGradientPar()) {
3207  Error("GradientPar", "Could not generate a gradient for the formula %s!",
3208  fClingName.Data());
3209  return;
3210  }
3211 
3212  if ((int)result.size() < fNpar) {
3213  Warning("GradientPar",
3214  "The size of gradient result is %zu but %d is required. Resizing.",
3215  result.size(), fNpar);
3216  result.resize(fNpar);
3217  }
3218  GradientPar(x, result.data());
3219 }
3220 
3221 void TFormula::GradientPar(const Double_t *x, Double_t *result)
3222 {
3223  void* args[3];
3224  const double * vars = (x) ? x : fClingVariables.data();
3225  args[0] = &vars;
3226  if (fNpar <= 0) {
3227  // __attribute__((used)) extern "C" void __cf_0(void* obj, int nargs, void** args, void* ret)
3228  // {
3229  // if (ret) {
3230  // new (ret) (double) (((double (&)(double*))TFormula____id)(*(double**)args[0]));
3231  // return;
3232  // } else {
3233  // ((double (&)(double*))TFormula____id)(*(double**)args[0]);
3234  // return;
3235  // }
3236  // }
3237  args[1] = &result;
3238  (*fGradFuncPtr)(0, 2, args, /*ret*/nullptr); // We do not use ret in a return-void func.
3239  } else {
3240  // __attribute__((used)) extern "C" void __cf_0(void* obj, int nargs, void** args, void* ret)
3241  // {
3242  // ((void (&)(double*, double*,
3243  // double*))TFormula____id_grad)(*(double**)args[0], *(double**)args[1],
3244  // *(double**)args[2]);
3245  // return;
3246  // }
3247  const double *pars = fClingParameters.data();
3248  args[1] = &pars;
3249  args[2] = &result;
3250  (*fGradFuncPtr)(0, 3, args, /*ret*/nullptr); // We do not use ret in a return-void func.
3251  }
3252 }
3253 
3254 ////////////////////////////////////////////////////////////////////////////////
3255 #ifdef R__HAS_VECCORE
3256 // ROOT::Double_v TFormula::Eval(ROOT::Double_v x, ROOT::Double_v y, ROOT::Double_v z, ROOT::Double_v t) const
3257 // {
3258 // ROOT::Double_v xxx[] = {x, y, z, t};
3259 // return EvalPar(xxx, nullptr);
3260 // }
3261 
3262 ROOT::Double_v TFormula::EvalParVec(const ROOT::Double_v *x, const Double_t *params) const
3263 {
3264  if (fVectorized)
3265  return DoEvalVec(x, params);
3266 
3267  if (fNdim == 0 || !x)
3268  return DoEval(nullptr, params); // automatic conversion to vectorized
3269 
3270  // otherwise, trying to input vectors into a scalar function
3271 
3272  if (gDebug)
3273  Info("EvalPar", "Function is not vectorized - converting ROOT::Double_v into Double_t and back");
3274 
3275  const int vecSize = vecCore::VectorSize<ROOT::Double_v>();
3276  std::vector<Double_t> xscalars(vecSize*fNdim);
3277 
3278  for (int i = 0; i < vecSize; i++)
3279  for (int j = 0; j < fNdim; j++)
3280  xscalars[i*fNdim+j] = vecCore::Get(x[j],i);
3281 
3282  ROOT::Double_v answers(0.);
3283  for (int i = 0; i < vecSize; i++)
3284  vecCore::Set(answers, i, DoEval(&xscalars[i*fNdim], params));
3285 
3286  return answers;
3287 }
3288 #endif
3289 
3290 ////////////////////////////////////////////////////////////////////////////////
3291 /// Sets first 4 variables (e.g. x, y, z, t) and evaluate formula.
3292 
3294 {
3295  double xxx[4] = {x,y,z,t};
3296  return EvalPar(xxx, nullptr); // takes care of case where formula is vectorized
3297 }
3298 
3299 ////////////////////////////////////////////////////////////////////////////////
3300 /// Sets first 3 variables (e.g. x, y, z) and evaluate formula.
3301 
3303 {
3304  double xxx[3] = {x,y,z};
3305  return EvalPar(xxx, nullptr);
3306 }
3307 
3308 ////////////////////////////////////////////////////////////////////////////////
3309 /// Sets first 2 variables (e.g. x and y) and evaluate formula.
3310 
3312 {
3313  double xxx[2] = {x,y};
3314  return EvalPar(xxx, nullptr);
3315 }
3316 
3317 ////////////////////////////////////////////////////////////////////////////////
3318 /// Sets first variable (e.g. x) and evaluate formula.
3319 
3321 {
3322  double * xxx = &x;
3323  return EvalPar(xxx, nullptr);
3324 }
3325 
3326 ////////////////////////////////////////////////////////////////////////////////
3327 /// Evaluate formula.
3328 /// If formula is not ready to execute(missing parameters/variables),
3329 /// print these which are not known.
3330 /// If parameter has default value, and has not been set, appropriate warning is shown.
3331 
3332 Double_t TFormula::DoEval(const double * x, const double * params) const
3333 {
3334  if(!fReadyToExecute)
3335  {
3336  Error("Eval", "Formula is invalid and not ready to execute ");
3337  for (auto it = fFuncs.begin(); it != fFuncs.end(); ++it) {
3338  TFormulaFunction fun = *it;
3339  if (!fun.fFound) {
3340  printf("%s is unknown.\n", fun.GetName());
3341  }
3342  }
3343  return TMath::QuietNaN();
3344  }
3345 
3346  // Lazy initialization is set and needed when reading from a file
3348  // try recompiling the formula. We need to lock because this is not anymore thread safe
3350  auto thisFormula = const_cast<TFormula*>(this);
3351  thisFormula->ReInitializeEvalMethod();
3352  }
3353  if (!fClingInitialized) {
3354  Error("DoEval", "Formula has error and it is not properly initialized ");
3355  return TMath::QuietNaN();
3356  }
3357 
3358  if (fLambdaPtr && TestBit(TFormula::kLambda)) {// case of lambda functions
3359  std::function<double(double *, double *)> & fptr = * ( (std::function<double(double *, double *)> *) fLambdaPtr);
3360  assert(x);
3361  //double * v = (x) ? const_cast<double*>(x) : const_cast<double*>(fClingVariables.data());
3362  double * v = const_cast<double*>(x);
3363  double * p = (params) ? const_cast<double*>(params) : const_cast<double*>(fClingParameters.data());
3364  return fptr(v, p);
3365  }
3366 
3367 
3368  Double_t result = 0;
3369  void* args[2];
3370  double * vars = (x) ? const_cast<double*>(x) : const_cast<double*>(fClingVariables.data());
3371  args[0] = &vars;
3372  if (fNpar <= 0) {
3373  (*fFuncPtr)(0, 1, args, &result);
3374  } else {
3375  double *pars = (params) ? const_cast<double *>(params) : const_cast<double *>(fClingParameters.data());
3376  args[1] = &pars;
3377  (*fFuncPtr)(0, 2, args, &result);
3378  }
3379  return result;
3380 }
3381 
3382 ////////////////////////////////////////////////////////////////////////////////
3383 // Copied from DoEval, but this is the vectorized version
3384 #ifdef R__HAS_VECCORE
3385 ROOT::Double_v TFormula::DoEvalVec(const ROOT::Double_v *x, const double *params) const
3386 {
3387  if (!fReadyToExecute) {
3388  Error("Eval", "Formula is invalid and not ready to execute ");
3389  for (auto it = fFuncs.begin(); it != fFuncs.end(); ++it) {
3390  TFormulaFunction fun = *it;
3391  if (!fun.fFound) {
3392  printf("%s is unknown.\n", fun.GetName());
3393  }
3394  }
3395  return TMath::QuietNaN();
3396  }
3397  // todo maybe save lambda ptr stuff for later
3398 
3400  // try recompiling the formula. We need to lock because this is not anymore thread safe
3402  auto thisFormula = const_cast<TFormula*>(this);
3403  thisFormula->ReInitializeEvalMethod();
3404  }
3405 
3406  ROOT::Double_v result = 0;
3407  void *args[2];
3408 
3409  ROOT::Double_v *vars = const_cast<ROOT::Double_v *>(x);
3410  args[0] = &vars;
3411  if (fNpar <= 0) {
3412  (*fFuncPtr)(0, 1, args, &result);
3413  }else {
3414  double *pars = (params) ? const_cast<double *>(params) : const_cast<double *>(fClingParameters.data());
3415  args[1] = &pars;
3416  (*fFuncPtr)(0, 2, args, &result);
3417  }
3418  return result;
3419 }
3420 #endif // R__HAS_VECCORE
3421 
3422 
3423 //////////////////////////////////////////////////////////////////////////////
3424 /// Re-initialize eval method
3425 ///
3426 /// This function is called by DoEval and DoEvalVector in case of a previous failure
3427 /// or in case of reading from a file
3428 ////////////////////////////////////////////////////////////////////////////////
3430 
3431 
3432  if (TestBit(TFormula::kLambda) ) {
3433  Info("ReInitializeEvalMethod","compile now lambda expression function using Cling");
3435  fLazyInitialization = false;
3436  return;
3437  }
3438  if (fMethod) {
3439  fMethod->Delete();
3440  fMethod = nullptr;
3441  }
3442  if (!fLazyInitialization) Warning("ReInitializeEvalMethod", "Formula is NOT properly initialized - try calling again TFormula::PrepareEvalMethod");
3443  //else Info("ReInitializeEvalMethod", "Compile now the formula expression using Cling");
3444 
3445  // check first if formula exists in the global map
3446  {
3447 
3449 
3450  // std::cout << "gClingFunctions list" << std::endl;
3451  // for (auto thing : gClingFunctions)
3452  // std::cout << "gClingFunctions : " << thing.first << std::endl;
3453 
3454  auto funcit = gClingFunctions.find(fSavedInputFormula);
3455 
3456  if (funcit != gClingFunctions.end()) {
3457  fFuncPtr = (TFormula::CallFuncSignature)funcit->second;
3458  fClingInitialized = true;
3459  fLazyInitialization = false;
3460  return;
3461  }
3462  }
3463  // compile now formula using cling
3465  if (fClingInitialized && !fLazyInitialization) Info("ReInitializeEvalMethod", "Formula is now properly initialized !!");
3466  fLazyInitialization = false;
3467 
3468  // add function pointer in global map
3469  if (fClingInitialized) {
3470  R__ASSERT(!fSavedInputFormula.empty());
3471  // if Cling has been successfully initialized
3472  // put function ptr in the static map
3474  gClingFunctions.insert(std::make_pair(fSavedInputFormula, (void *)fFuncPtr));
3475  }
3476 
3477 
3478  return;
3479 }
3480 
3481 ////////////////////////////////////////////////////////////////////////////////
3482 /// Return the expression formula.
3483 ///
3484 /// - If option = "P" replace the parameter names with their values
3485 /// - If option = "CLING" return the actual expression used to build the function passed to cling
3486 /// - If option = "CLINGP" replace in the CLING expression the parameter with their values
3487 
3489 {
3490  TString opt(option);
3491  if (opt.IsNull() || TestBit(TFormula::kLambda) ) return fFormula;
3492  opt.ToUpper();
3493 
3494  // if (opt.Contains("N") ) {
3495  // TString formula = fFormula;
3496  // ReplaceParName(formula, ....)
3497  // }
3498 
3499  if (opt.Contains("CLING") ) {
3500  std::string clingFunc = fClingInput.Data();
3501  std::size_t found = clingFunc.find("return");
3502  std::size_t found2 = clingFunc.rfind(";");
3503  if (found == std::string::npos || found2 == std::string::npos) {
3504  Error("GetExpFormula","Invalid Cling expression - return default formula expression");
3505  return fFormula;
3506  }
3507  TString clingFormula = fClingInput(found+7,found2-found-7);
3508  // to be implemented
3509  if (!opt.Contains("P")) return clingFormula;
3510  // replace all "p[" with "[parname"
3511  int i = 0;
3512  while (i < clingFormula.Length()-2 ) {
3513  // look for p[number
3514  if (clingFormula[i] == 'p' && clingFormula[i+1] == '[' && isdigit(clingFormula[i+2]) ) {
3515  int j = i+3;
3516  while ( isdigit(clingFormula[j]) ) { j++;}
3517  if (clingFormula[j] != ']') {
3518  Error("GetExpFormula","Parameters not found - invalid expression - return default cling formula");
3519  return clingFormula;
3520  }
3521  TString parNumbName = clingFormula(i+2,j-i-2);
3522  int parNumber = parNumbName.Atoi();
3523  assert(parNumber < fNpar);
3524  TString replacement = TString::Format("%f",GetParameter(parNumber));
3525  clingFormula.Replace(i,j-i+1, replacement );
3526  i += replacement.Length();
3527  }
3528  i++;
3529  }
3530  return clingFormula;
3531  }
3532  if (opt.Contains("P") ) {
3533  // replace parameter names with their values
3534  TString expFormula = fFormula;
3535  int i = 0;
3536  while (i < expFormula.Length()-2 ) {
3537  // look for [parName]
3538  if (expFormula[i] == '[') {
3539  int j = i+1;
3540  while ( expFormula[j] != ']' ) { j++;}
3541  if (expFormula[j] != ']') {
3542  Error("GetExpFormula","Parameter names not found - invalid expression - return default formula");
3543  return expFormula;
3544  }
3545  TString parName = expFormula(i+1,j-i-1);
3546  TString replacement = TString::Format("%g",GetParameter(parName));
3547  expFormula.Replace(i,j-i+1, replacement );
3548  i += replacement.Length();
3549  }
3550  i++;
3551  }
3552  return expFormula;
3553  }
3554  Warning("GetExpFormula","Invalid option - return default formula expression");
3555  return fFormula;
3556 }
3557 
3559  std::unique_ptr<TInterpreterValue> v = gInterpreter->MakeInterpreterValue();
3560  gInterpreter->Evaluate(GetGradientFuncName().c_str(), *v);
3561  return v->ToString();
3562 }
3563 
3564 ////////////////////////////////////////////////////////////////////////////////
3565 /// Print the formula and its attributes.
3566 
3567 void TFormula::Print(Option_t *option) const
3568 {
3569  printf(" %20s : %s Ndim= %d, Npar= %d, Number= %d \n",GetName(),GetTitle(), fNdim,fNpar,fNumber);
3570  printf(" Formula expression: \n");
3571  printf("\t%s \n",fFormula.Data() );
3572  TString opt(option);
3573  opt.ToUpper();
3574  // do an evaluation as a cross-check
3575  //if (fReadyToExecute) Eval();
3576 
3577  if (opt.Contains("V") ) {
3578  if (fNdim > 0 && !TestBit(TFormula::kLambda)) {
3579  printf("List of Variables: \n");
3580  assert(int(fClingVariables.size()) >= fNdim);
3581  for ( int ivar = 0; ivar < fNdim ; ++ivar) {
3582  printf("Var%4d %20s = %10f \n",ivar,GetVarName(ivar).Data(), fClingVariables[ivar]);
3583  }
3584  }
3585  if (fNpar > 0) {
3586  printf("List of Parameters: \n");
3587  if ( int(fClingParameters.size()) < fNpar)
3588  Error("Print","Number of stored parameters in vector %zu in map %zu is different than fNpar %d",fClingParameters.size(), fParams.size(), fNpar);
3589  assert(int(fClingParameters.size()) >= fNpar);
3590  // print with order passed to Cling function
3591  for ( int ipar = 0; ipar < fNpar ; ++ipar) {
3592  printf("Par%4d %20s = %10f \n",ipar,GetParName(ipar), fClingParameters[ipar] );
3593  }
3594  }
3595  printf("Expression passed to Cling:\n");
3596  printf("\t%s\n",fClingInput.Data() );
3597  if (fGradFuncPtr) {
3598  printf("Generated Gradient:\n");
3599  printf("%s\n", fGradGenerationInput.c_str());
3600  printf("%s\n", GetGradientFormula().Data());
3601  }
3602  }
3603  if(!fReadyToExecute)
3604  {
3605  Warning("Print", "Formula is not ready to execute. Missing parameters/variables");
3606  for (list<TFormulaFunction>::const_iterator it = fFuncs.begin(); it != fFuncs.end(); ++it) {
3607  TFormulaFunction fun = *it;
3608  if (!fun.fFound) {
3609  printf("%s is unknown.\n", fun.GetName());
3610  }
3611  }
3612  }
3613  if (!fAllParametersSetted) {
3614  // we can skip this
3615  // Info("Print","Not all parameters are set.");
3616  // for(map<TString,TFormulaVariable>::const_iterator it = fParams.begin(); it != fParams.end(); ++it)
3617  // {
3618  // pair<TString,TFormulaVariable> param = *it;
3619  // if(!param.second.fFound)
3620  // {
3621  // printf("%s has default value %lf\n",param.first.Data(),param.second.GetInitialValue());
3622  // }
3623  // }
3624  }
3625 }
3626 
3627 ////////////////////////////////////////////////////////////////////////////////
3628 /// Stream a class object.
3629 
3630 void TFormula::Streamer(TBuffer &b)
3631 {
3632  if (b.IsReading() ) {
3633  UInt_t R__s, R__c;
3634  Version_t v = b.ReadVersion(&R__s, &R__c);
3635  //std::cout << "version " << v << std::endl;
3636  if (v <= 8 && v > 3 && v != 6) {
3637  // old TFormula class
3638  ROOT::v5::TFormula * fold = new ROOT::v5::TFormula();
3639  // read old TFormula class
3640  fold->Streamer(b, v, R__s, R__c, TFormula::Class());
3641  //std::cout << "read old tformula class " << std::endl;
3642  TFormula fnew(fold->GetName(), fold->GetExpFormula() );
3643 
3644  *this = fnew;
3645 
3646  // printf("copying content in a new TFormula \n");
3647  SetParameters(fold->GetParameters() );
3648  if (!fReadyToExecute ) {
3649  Error("Streamer","Old formula read from file is NOT valid");
3650  Print("v");
3651  }
3652  delete fold;
3653  return;
3654  }
3655  else if (v > 8) {
3656  // new TFormula class
3657  b.ReadClassBuffer(TFormula::Class(), this, v, R__s, R__c);
3658 
3659  //std::cout << "reading npar = " << GetNpar() << std::endl;
3660 
3661  // initialize the formula
3662  // need to set size of fClingVariables which is transient
3663  //fClingVariables.resize(fNdim);
3664 
3665  // case of formula contains only parameters
3666  if (fFormula.IsNull() ) return;
3667 
3668 
3669  // store parameter values, names and order
3670  std::vector<double> parValues = fClingParameters;
3671  auto paramMap = fParams;
3672  fNpar = fParams.size();
3673 
3674  fLazyInitialization = true; // when reading we initialize the formula later to avoid problem of recursive Jitting
3675 
3676  if (!TestBit(TFormula::kLambda) ) {
3677 
3678  // save dimension read from the file (stored for V >=12)
3679  // and we check after initializing if it is the same
3680  int ndim = fNdim;
3681  fNdim = 0;
3682 
3683  //std::cout << "Streamer::Reading preprocess the formula " << fFormula << " ndim = " << fNdim << " npar = " << fNpar << std::endl;
3684  // for ( auto &p : fParams)
3685  // std::cout << "parameter " << p.first << " index " << p.second << std::endl;
3686 
3687  fClingParameters.clear(); // need to be reset before re-initializing it
3688 
3689  FillDefaults();
3690 
3691 
3693 
3694  //std::cout << "Streamer::after pre-process the formula " << fFormula << " ndim = " << fNdim << " npar = " << fNpar << std::endl;
3695 
3697 
3698  //std::cout << "Streamer::after prepared " << fClingInput << " ndim = " << fNdim << " npar = " << fNpar << std::endl;
3699 
3700 
3701  // restore parameter values
3702  if (fNpar != (int) parValues.size() ) {
3703  Error("Streamer","number of parameters computed (%d) is not same as the stored parameters (%d)",fNpar,int(parValues.size()) );
3704  Print("v");
3705  }
3706  if (v > 11 && fNdim != ndim) {
3707  Error("Streamer","number of dimension computed (%d) is not same as the stored value (%d)",fNdim, ndim );
3708  Print("v");
3709  }
3710  }
3711  else {
3712  // we also delay the initializtion of lamda expressions
3713  if (!fLazyInitialization) {
3714  bool ret = InitLambdaExpression(fFormula);
3715  if (ret) {
3716  fClingInitialized = true;
3717  }
3718  }else {
3719  fReadyToExecute = true;
3720  }
3721  }
3722  assert(fNpar == (int) parValues.size() );
3723  std::copy( parValues.begin(), parValues.end(), fClingParameters.begin() );
3724  // restore parameter names and order
3725  if (fParams.size() != paramMap.size() ) {
3726  Warning("Streamer","number of parameters list found (%zu) is not same as the stored one (%zu) - use re-created list",fParams.size(),paramMap.size()) ;
3727  //Print("v");
3728  }
3729  else
3730  //assert(fParams.size() == paramMap.size() );
3731  fParams = paramMap;
3732 
3733  // input formula into Cling
3734  // need to replace in cling the name of the pointer of this object
3735  // TString oldClingName = fClingName;
3736  // fClingName.Replace(fClingName.Index("_0x")+1,fClingName.Length(), TString::Format("%p",this) );
3737  // fClingInput.ReplaceAll(oldClingName, fClingName);
3738  // InputFormulaIntoCling();
3739 
3740  if (!TestBit(kNotGlobal)) {
3742  gROOT->GetListOfFunctions()->Add(this);
3743  }
3744  if (!fReadyToExecute ) {
3745  Error("Streamer","Formula read from file is NOT ready to execute");
3746  Print("v");
3747  }
3748  //std::cout << "reading 2 npar = " << GetNpar() << std::endl;
3749 
3750  return;
3751  }
3752  else {
3753  Error("Streamer","Reading version %d is not supported",v);
3754  return;
3755  }
3756  }
3757  else {
3758  // case of writing
3759  b.WriteClassBuffer(TFormula::Class(), this);
3760  // std::cout << "writing npar = " << GetNpar() << std::endl;
3761  }
3762 }
TFormula::~TFormula
virtual ~TFormula()
Definition: TFormula.cxx:414
c
#define c(i)
Definition: RSha256.hxx:119
TFormula::FillDefaults
void FillDefaults()
Fill structures with default variables, constants and function shortcuts.
Definition: TFormula.cxx:863
TRegexp::Index
Ssiz_t Index(const TString &str, Ssiz_t *len, Ssiz_t start=0) const
Find the first occurrence of the regexp in string and return the position, or -1 if there is no match...
Definition: TRegexp.cxx:209
m
auto * m
Definition: textangle.C:8
n
const Int_t n
Definition: legend1.C:16
TFormulaFunction
Definition: TFormula.h:28
R__v5TFormulaUpdater
static void R__v5TFormulaUpdater(Int_t nobjects, TObject **from, TObject **to)
Definition: TFormula.cxx:215
first
Definition: first.py:1
TFormula::InitLambdaExpression
Bool_t InitLambdaExpression(const char *formula)
Definition: TFormula.cxx:560
TFormula::HandleFunctionArguments
void HandleFunctionArguments(TString &formula)
TObject::TestBit
R__ALWAYS_INLINE Bool_t TestBit(UInt_t f) const
Definition: TObject.h:172
TClass::GetListOfAllPublicMethods
const TList * GetListOfAllPublicMethods(Bool_t load=kTRUE)
Returns a list of all public methods of this class and its base classes.
Definition: TClass.cxx:3747
prepareFuncPtr
static TInterpreter::CallFuncIFacePtr_t::Generic_t prepareFuncPtr(TMethodCall *Method)
Definition: TFormula.cxx:797
Version_t
short Version_t
Definition: RtypesCore.h:65
TFormulaFunction::GetNargs
Int_t GetNargs() const
Definition: TFormula.h:38
TFormula::kNormalized
@ kNormalized
Definition: TFormula.h:172
TFormula::GetVarNumber
Int_t GetVarNumber(const char *name) const
f
#define f(i)
Definition: RSha256.hxx:122
TFormula::IsValid
Bool_t IsValid() const
Definition: TFormula.h:235
TFormula::TFormula
TFormula()
Definition: TFormula.cxx:387
TFormula::SetVectorized
void SetVectorized(Bool_t vectorized)
TNamed::SetName
virtual void SetName(const char *name)
Set the name of the TNamed.
Definition: TNamed.cxx:140
TMath::Max
Short_t Max(Short_t a, Short_t b)
Definition: TMathBase.h:212
TFormula::ProcessFormula
void ProcessFormula(TString &formula)
TString::Atoi
Int_t Atoi() const
Return integer value of string.
Definition: TString.cxx:1921
TFormula
Definition: TFormula.h:85
TMath::QuietNaN
Double_t QuietNaN()
Returns a quiet NaN as defined by IEEE 754
Definition: TMath.h:901
kNPOS
const Ssiz_t kNPOS
Definition: RtypesCore.h:115
TFormulaFunction::GetName
const char * GetName() const
Definition: TFormula.h:36
TFormula::PrepareFormula
Bool_t PrepareFormula(TString &formula)
TString::Data
const char * Data() const
Definition: TString.h:369
TFormula::fGradGenerationInput
std::string fGradGenerationInput
Definition: TFormula.h:104
TFormula::operator=
TFormula & operator=(const TFormula &rhs)
= operator.
Definition: TFormula.cxx:550
TFormula::HasGradientGenerationFailed
bool HasGradientGenerationFailed() const
Definition: TFormula.h:129
ClassImp
#define ClassImp(name)
Definition: Rtypes.h:364
TInterpreter::CallFuncIFacePtr_t::fGeneric
Generic_t fGeneric
Definition: TInterpreter.h:108
TFormula::GetParameters
Double_t * GetParameters() const
TNamed::GetTitle
virtual const char * GetTitle() const
Returns title of object.
Definition: TNamed.h:54
TMath::LogE
constexpr Double_t LogE()
Base-10 log of e (to convert ln to log)
Definition: TMath.h:116
TFormulaVariable::fName
TString fName
Definition: TFormula.h:64
TGeant4Unit::degree
static constexpr double degree
Definition: TGeant4SystemOfUnits.h:141
R__RegisterTFormulaUpdaterTrigger
int R__RegisterTFormulaUpdaterTrigger
Definition: TFormula.cxx:232
TFormula::FillVecFunctionsShurtCuts
void FillVecFunctionsShurtCuts()
Fill the shortcuts for vectorized functions We will replace for example sin with vecCore::Mat::Sin.
Definition: TFormula.cxx:931
TFormula::Clear
virtual void Clear(Option_t *option="")
Clear the formula setting expression to empty and reset the variables and parameters containers.
Definition: TFormula.cxx:727
TFormulaUpdater_t
void(*)(Int_t nobjects, TObject **from, TObject **to) TFormulaUpdater_t
Definition: TFormula.cxx:229
TString::Replace
TString & Replace(Ssiz_t pos, Ssiz_t n, const char *s)
Definition: TString.h:682
TFormula::kLambda
@ kLambda
Definition: TFormula.h:174
TFormula::ExtractFunctors
void ExtractFunctors(TString &formula)
gNamePrefix
static const TString gNamePrefix
Definition: TFormula.cxx:209
TMethod.h
TFormula::fFunctionsShortcuts
std::map< TString, TString > fFunctionsShortcuts
Definition: TFormula.h:139
TObject::Info
virtual void Info(const char *method, const char *msgfmt,...) const
Issue info message.
Definition: TObject.cxx:864
TObject::Error
virtual void Error(const char *method, const char *msgfmt,...) const
Issue error message.
Definition: TObject.cxx:890
TInterpreterValue.h
TFormula::fNdim
Int_t fNdim
Definition: TFormula.h:141
gInterpreter
#define gInterpreter
Definition: TInterpreter.h:558
TFormula::SetVariables
void SetVariables(const std::pair< TString, Double_t > *vars, const Int_t size)
TFormula::ReInitializeEvalMethod
void ReInitializeEvalMethod()
Int_t
int Int_t
Definition: RtypesCore.h:45
TFormula::IsDefaultVariableName
static Bool_t IsDefaultVariableName(const TString &name)
Definition: TFormula.cxx:261
TString::Contains
Bool_t Contains(const char *pat, ECaseCompare cmp=kExact) const
Definition: TString.h:624
TFormula::AddVariables
void AddVariables(const TString *vars, const Int_t size)
x
Double_t x[n]
Definition: legend1.C:17
TString::Length
Ssiz_t Length() const
Definition: TString.h:410
TFormula::fLazyInitialization
Bool_t fLazyInitialization
Definition: TFormula.h:97
ROOT::GetROOT
TROOT * GetROOT()
Definition: TROOT.cxx:465
TBuffer
Definition: TBuffer.h:43
TMethodCall::GetCallFunc
CallFunc_t * GetCallFunc() const
Definition: TMethodCall.h:93
TString::Format
static TString Format(const char *fmt,...)
Static method which formats a string using a printf style format descriptor and return a TString.
Definition: TString.cxx:2311
TFormula::fClingParameters
std::vector< Double_t > fClingParameters
cached variables
Definition: TFormula.h:93
TMath::C
constexpr Double_t C()
Velocity of light in .
Definition: TMath.h:123
TFormula::HandleParametrizedFunctions
void HandleParametrizedFunctions(TString &formula)
Handling parametrized functions Function can be normalized, and have different variable then x.
Definition: TFormula.cxx:1064
TString
Definition: TString.h:136
TFormula::InputFormulaIntoCling
void InputFormulaIntoCling()
Inputs formula, transfered to C++ code into Cling.
Definition: TFormula.cxx:837
Bool_t
bool Bool_t
Definition: RtypesCore.h:63
TFormula::fClingName
TString fClingName
pointer to a methodcall
Definition: TFormula.h:100
v
@ v
Definition: rootcling_impl.cxx:3635
TFormulaParamOrder::operator()
bool operator()(const TString &a, const TString &b) const
Definition: TFormula.cxx:324
b
#define b(i)
Definition: RSha256.hxx:118
TFormula::SetParNames
void SetParNames(const char *name0="p0", const char *name1="p1", const char *name2="p2", const char *name3="p3", const char *name4="p4", const char *name5="p5", const char *name6="p6", const char *name7="p7", const char *name8="p8", const char *name9="p9", const char *name10="p10")
bool
TFormula::fIsCladRuntimeIncluded
static bool fIsCladRuntimeIncluded
pointer to the lambda function
Definition: TFormula.h:108
TString::ReplaceAll
TString & ReplaceAll(const TString &s1, const TString &s2)
Definition: TString.h:692
TFormula::fClingVariables
std::vector< Double_t > fClingVariables
input function passed to Cling
Definition: TFormula.h:92
TFormula::SetParName
void SetParName(Int_t ipar, const char *name)
TString::CountChar
Int_t CountChar(Int_t c) const
Return number of times character c occurs in the string.
Definition: TString.cxx:476
TFormula::GradientStorage
std::vector< Double_t > GradientStorage
Definition: TFormula.h:176
TFormula::CallFuncSignature
TInterpreter::CallFuncIFacePtr_t::Generic_t CallFuncSignature
unique name used to defined the function and used in the global map (need to be saved in case of lazy...
Definition: TFormula.h:103
TString::Last
Ssiz_t Last(char c) const
Find last occurrence of a character c.
Definition: TString.cxx:892
TFormula::Print
void Print(Option_t *option="") const
Print TNamed name and title.
TROOT.h
TString::ToUpper
void ToUpper()
Change string to upper case.
Definition: TString.cxx:1138
TInterpreter::CallFuncIFacePtr_t::Generic_t
void(* Generic_t)(void *, int, void **, void *)
Definition: TInterpreter.h:93
TFormula::DoEval
Double_t DoEval(const Double_t *x, const Double_t *p=nullptr) const
TFormula::fGradMethod
std::unique_ptr< TMethodCall > fGradMethod
pointer to methodcall
Definition: TFormula.h:99
TRegexp.h
TNamed::Copy
virtual void Copy(TObject &named) const
Copy this to obj.
Definition: TNamed.cxx:94
TMath::Sigma
constexpr Double_t Sigma()
Stefan-Boltzmann constant in .
Definition: TMath.h:283
TFormula::EvalPar
Double_t EvalPar(const Double_t *x, const Double_t *params=0) const
TFormula::fFuncPtr
CallFuncSignature fFuncPtr
input query to clad to generate a gradient
Definition: TFormula.h:105
TFormula::GetVarName
TString GetVarName(Int_t ivar) const
TMath::Pi
constexpr Double_t Pi()
Definition: TMath.h:43
TFunction::GetNargsOpt
Int_t GetNargsOpt() const
Number of function optional (default) arguments.
Definition: TFunction.cxx:175
TFormula::Copy
virtual void Copy(TObject &f1) const
Copy this to obj.
Definition: TFormula.cxx:639
TFormula::fNumber
Int_t fNumber
Number of parameter (transient since we save the vector)
Definition: TFormula.h:143
Option_t
const typedef char Option_t
Definition: RtypesCore.h:66
TFormula::GetExpFormula
TString GetExpFormula(Option_t *option="") const
TFormula::fClingInitialized
Bool_t fClingInitialized
trasient to force initialization
Definition: TFormula.h:95
TFormula::fParams
std::map< TString, Int_t, TFormulaParamOrder > fParams
list of variable names
Definition: TFormula.h:137
R__SetClonesArrayTFormulaUpdater
bool R__SetClonesArrayTFormulaUpdater(TFormulaUpdater_t func)
Definition: TClonesArray.cxx:151
TFormula::fConsts
std::map< TString, Double_t > fConsts
Definition: TFormula.h:138
TFormula::GetParName
const char * GetParName(Int_t ipar) const
TFormula::ReplaceParamName
void ReplaceParamName(TString &formula, const TString &oldname, const TString &name)
TBuffer.h
TFormula::PrepareEvalMethod
Bool_t PrepareEvalMethod()
Sets TMethodCall to function inside Cling environment.
Definition: TFormula.cxx:821
IsReservedName
static bool IsReservedName(const char *name)
Definition: TFormula.cxx:405
TFormula::fAllParametersSetted
Bool_t fAllParametersSetted
transient to force re-initialization
Definition: TFormula.h:96
TFormula::fFormula
TString fFormula
Definition: TFormula.h:140
TMath::Log10
Double_t Log10(Double_t x)
Definition: TMath.h:764
ROOT::v5::TFormula::GetParameters
virtual Double_t * GetParameters() const
Definition: TFormula.h:243
TFormulaFunction::fName
TString fName
Definition: TFormula.h:31
gROOTMutex
R__EXTERN TVirtualMutex * gROOTMutex
Definition: TROOT.h:61
TFormula::fVectorized
Bool_t fVectorized
Definition: TFormula.h:145
TObject::SetBit
void SetBit(UInt_t f, Bool_t set)
Set or unset the user status bits as specified in f.
Definition: TObject.cxx:696
TFormula::IsBracket
static Bool_t IsBracket(const char c)
Definition: TFormula.cxx:243
TFormula::fReadyToExecute
Bool_t fReadyToExecute
Definition: TFormula.h:94
TInterpreter::CallFunc_IFacePtr
virtual CallFuncIFacePtr_t CallFunc_IFacePtr(CallFunc_t *) const
Definition: TInterpreter.h:326
TMVA::variables
void variables(TString dataset, TString fin="TMVA.root", TString dirName="InputVariables_Id", TString title="TMVA Input Variables", Bool_t isRegression=kFALSE, Bool_t useTMVAStyle=kTRUE)
TMath::Floor
Double_t Floor(Double_t x)
Definition: TMath.h:703
TFormula::IsHexadecimal
static Bool_t IsHexadecimal(const TString &formula, int ipos)
Definition: TFormula.cxx:279
a
auto * a
Definition: textangle.C:12
TNamed
Definition: TNamed.h:29
TString::Append
TString & Append(const char *cs)
Definition: TString.h:564
TFormula::DoAddParameter
void DoAddParameter(const TString &name, Double_t value, bool processFormula)
TFormula::HandleLinear
void HandleLinear(TString &formula)
ROOT::R::function
void function(const Char_t *name_, T fun, const Char_t *docstring=0)
Definition: RExports.h:151
gDebug
R__EXTERN Int_t gDebug
Definition: RtypesCore.h:119
TMethod
Definition: TMethod.h:38
TFormula::Eval
Double_t Eval(Double_t x) const
ROOT::v5::TFormula::GetExpFormula
virtual TString GetExpFormula(Option_t *option="") const
Reconstruct the formula expression from the internal TFormula member variables.
Definition: TFormula_v5.cxx:3022
TFormula::fNpar
Int_t fNpar
Definition: TFormula.h:142
TObject::FindObject
virtual TObject * FindObject(const char *name) const
Must be redefined in derived classes.
Definition: TObject.cxx:323
TFormula::SetVariable
void SetVariable(const TString &name, Double_t value)
TBenchmark.h
R__LOCKGUARD
#define R__LOCKGUARD(mutex)
Definition: TVirtualMutex.h:104
TString::Insert
TString & Insert(Ssiz_t pos, const char *s)
Definition: TString.h:649
double
double
Definition: Converters.cxx:921
TFormula::IsAParameterName
static Bool_t IsAParameterName(const TString &formula, int ipos)
Definition: TFormula.cxx:302
TClass::GetClass
static TClass * GetClass(const char *name, Bool_t load=kTRUE, Bool_t silent=kFALSE)
Static method returning pointer to TClass of the specified class name.
Definition: TClass.cxx:2925
y
Double_t y[n]
Definition: legend1.C:17
TFormula::GetVariable
Double_t GetVariable(const char *name) const
ROOT::Double_v
Double_t Double_v
Definition: Types.h:51
TFormula::PreProcessFormula
void PreProcessFormula(TString &formula)
TMath::Infinity
Double_t Infinity()
Returns an infinity as defined by the IEEE standard.
Definition: TMath.h:914
TMath::G
constexpr Double_t G()
Gravitational constant in: .
Definition: TMath.h:144
ROOT::v5::TFormula::Streamer
void Streamer(TBuffer &b, const TClass *onfile_class)
Stream a class object.
Definition: TFormula_v5.cxx:3460
TFunction::GetNargs
Int_t GetNargs() const
Number of function arguments.
Definition: TFunction.cxx:165
TFormula::fVars
std::map< TString, TFormulaVariable > fVars
Definition: TFormula.h:136
TFormula::fLambdaPtr
void * fLambdaPtr
function pointer, owned by the JIT.
Definition: TFormula.h:107
TFormula::FillParametrizedFunctions
void FillParametrizedFunctions(std::map< std::pair< TString, Int_t >, std::pair< TString, TString >> &functions)
TObject::Warning
virtual void Warning(const char *method, const char *msgfmt,...) const
Issue warning message.
Definition: TObject.cxx:876
TFormula::IsScientificNotation
static Bool_t IsScientificNotation(const TString &formula, int ipos)
Definition: TFormula.cxx:267
TFormula::GetGradientFormula
TString GetGradientFormula() const
TString::IsDigit
Bool_t IsDigit() const
Returns true if all characters in string are digits (0-9) or white spaces, i.e.
Definition: TString.cxx:1763
gClingFunctions
static std::unordered_map< std::string, void * > gClingFunctions
Definition: TFormula.cxx:213
void
typedef void((*Func_t)())
TFormula::GenerateGradientPar
bool GenerateGradientPar()
Generate gradient computation routine with respect to the parameters.
TFormula::HandleParamRanges
void HandleParamRanges(TString &formula)
Handling parameter ranges, in the form of [1..5].
Definition: TFormula.cxx:1281
unsigned int
TMath::R
constexpr Double_t R()
Universal gas constant ( ) in
Definition: TMath.h:315
TRegexp
Definition: TRegexp.h:31
TFormula::GetLinearPart
const TObject * GetLinearPart(Int_t i) const
TFormulaFunction::fFound
Bool_t fFound
Definition: TFormula.h:34
TString::Index
Ssiz_t Index(const char *pat, Ssiz_t i=0, ECaseCompare cmp=kExact) const
Definition: TString.h:639
xmlio::Name
const char * Name
Definition: TXMLSetup.cxx:73
prepareMethod
static std::unique_ptr< TMethodCall > prepareMethod(bool HasParameters, bool HasVariables, const char *FuncName, bool IsVectorized, bool IsGradient=false)
Definition: TFormula.cxx:761
TFormula::SetName
void SetName(const char *name)
Set the name of the TNamed.
TFormula::IsFunctionNameChar
static Bool_t IsFunctionNameChar(const char c)
Definition: TFormula.cxx:255
f1
TF1 * f1
Definition: legend1.C:11
TString::IsNull
Bool_t IsNull() const
Definition: TString.h:407
Double_t
double Double_t
Definition: RtypesCore.h:59
ROOT::v5::TFormula
Definition: TFormula.h:65
TFormula::kLinear
@ kLinear
Definition: TFormula.h:173
TFormula::GetParNumber
Int_t GetParNumber(const char *name) const
R__ASSERT
#define R__ASSERT(e)
Definition: TError.h:120
TMath::H
constexpr Double_t H()
Planck's constant in .
Definition: TMath.h:198
TF1.h
TFormula::HandlePolN
void HandlePolN(TString &formula)
Handling polN If before 'pol' exist any name, this name will be treated as variable used in polynomia...
Definition: TFormula.cxx:963
TFormula::SetPredefinedParamNames
void SetPredefinedParamNames()
TInterpreter::CallFunc_IsValid
virtual Bool_t CallFunc_IsValid(CallFunc_t *) const
Definition: TInterpreter.h:325
TFormulaVariable
Definition: TFormula.h:61
TMath::Ln10
constexpr Double_t Ln10()
Natural log of 10 (to convert log to ln)
Definition: TMath.h:109
TInterpreter.h
TClass
Definition: TClass.h:80
TFormula::SetParameters
void SetParameters(const Double_t *params)
TFormula::AddVariable
void AddVariable(const TString &name, Double_t value=0)
TObject
Definition: TObject.h:37
gCling
R__EXTERN TInterpreter * gCling
Definition: TInterpreter.h:559
TFormula::kNotGlobal
@ kNotGlobal
Definition: TFormula.h:171
TFormulaVariable::fArrayPos
Int_t fArrayPos
Definition: TFormula.h:66
TFormula::SetParameter
void SetParameter(const char *name, Double_t value)
TMath::EulerGamma
constexpr Double_t EulerGamma()
Euler-Mascheroni Constant.
Definition: TMath.h:345
name
char name[80]
Definition: TGX11.cxx:110
TObject::Delete
virtual void Delete(Option_t *option="")
Delete this object.
Definition: TObject.cxx:171
TMath::Sqrt2
constexpr Double_t Sqrt2()
Definition: TMath.h:94
TFormula::Compile
Int_t Compile(const char *expression="")
Compile the given expression with Cling backward compatibility method to be used in combination with ...
Definition: TFormula.cxx:605
TIter
Definition: TCollection.h:233
TFormula::GradientPar
void GradientPar(const Double_t *x, TFormula::GradientStorage &result)
Compute the gradient employing automatic differentiation.
GetParameters
void GetParameters(TFitEditor::FuncParams_t &pars, TF1 *func)
Stores the parameters of the given function into pars.
Definition: TFitEditor.cxx:260
TMethodCall
Definition: TMethodCall.h:37
TFormula::fClingInput
TString fClingInput
Definition: TFormula.h:91
TMath::K
constexpr Double_t K()
Boltzmann's constant in .
Definition: TMath.h:259
TFormulaVariable::fValue
Double_t fValue
Definition: TFormula.h:65
xmlio::cnt
const char * cnt
Definition: TXMLSetup.cxx:81
TNamed::GetName
virtual const char * GetName() const
Returns name of object.
Definition: TNamed.h:53
TF1
1-Dim function class
Definition: TF1.h:212
Class
void Class()
Definition: Class.C:29
TInterpreter::Declare
virtual Bool_t Declare(const char *code)=0
TFormula::IsOperator
static Bool_t IsOperator(const char c)
Definition: TFormula.cxx:235
TFormula::fLinearParts
std::vector< TObject * > fLinearParts
Definition: TFormula.h:144
TFormula::fMethod
TMethodCall * fMethod
transient flag to control lazy initialization (needed for reading from files)
Definition: TFormula.h:98
TFormula.h
TFormula::HandleExponentiation
void HandleExponentiation(TString &formula)
TFormula::fGradFuncPtr
CallFuncSignature fGradFuncPtr
function pointer, owned by the JIT.
Definition: TFormula.h:106
TMath::E
constexpr Double_t E()
Base of natural log:
Definition: TMath.h:102
TFormula::fFuncs
std::list< TFormulaFunction > fFuncs
Definition: TFormula.h:135
TFunction
Definition: TFunction.h:30
TFormula::fSavedInputFormula
std::string fSavedInputFormula
unique name passed to Cling to define the function ( double clingName(double*x, double*p) )
Definition: TFormula.h:101
TInterpreter::ProcessLine
virtual Long_t ProcessLine(const char *line, EErrorCode *error=0)=0
TFormula::DoSetParameters
void DoSetParameters(const Double_t *p, Int_t size)
TList
Definition: TList.h:44
TFormula::GetGradientFuncName
std::string GetGradientFuncName() const
Definition: TFormula.h:125
TMath.h
ROOT::TMetaUtils::propNames::pattern
static const std::string pattern("pattern")
gROOT
#define gROOT
Definition: TROOT.h:406
TFormula::ReplaceAllNames
void ReplaceAllNames(TString &formula, std::map< TString, TString > &substitutions)
Definition: TFormula.cxx:354
int
TF1::GetFormula
virtual TFormula * GetFormula()
Definition: TF1.h:449
Error
void Error(const char *location, const char *msgfmt,...)
Use this function in case an error occurred.
Definition: TError.cxx:187
TError.h
TMethodCall.h
TFormulaFunction::IsFuncCall
Bool_t IsFuncCall() const
Definition: TFormula.h:39
TFormula::GetParameter
Double_t GetParameter(const char *name) const