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