From 084292ab638923f9260d3a9f813227ada0728565 Mon Sep 17 00:00:00 2001
From: moneta <lorenzo.moneta@cern.ch>
Date: Mon, 15 Jun 2020 10:46:09 +0200
Subject: [PATCH] Fix in TFormula the parsing of user defined functions which
 contain pre-defined functions (e.g. gaus) in their name. Fixes ROOT-10815.
 For example when a user defines a function with the name "f1gaus" and then
 reuses that name could cause in same case an error parsing the expression.
 Example : ``` TF1 f1("f1gaus","gaus"); TF1 f2("f2gaus","f1gaus+gaus(3)"); ```
 If the function name is for example  "fgaus" it was working before, but not
 if a character number is used before the pre-defined function name such as 
 "f1gaus".

Add also a test for parsing these cases in TFormulaParsingTest.

This commit fixes ROOT-10815
---
 hist/hist/src/TFormula.cxx  |  5 +++--
 test/TFormulaParsingTests.h | 32 ++++++++++++++++++++++++++++++++
 2 files changed, 35 insertions(+), 2 deletions(-)

diff --git a/hist/hist/src/TFormula.cxx b/hist/hist/src/TFormula.cxx
index 05e1dfaf471..64ae71e82b2 100644
--- a/hist/hist/src/TFormula.cxx
+++ b/hist/hist/src/TFormula.cxx
@@ -1101,13 +1101,14 @@ void TFormula::HandleParametrizedFunctions(TString &formula)
          // should also check that function is not something else (e.g. exponential - parse the expo)
          Int_t lastFunPos = funPos + funName.Length();
 
-         // check that first and last character is not alphanumeric
+         // check that first and last character is not a special character
          Int_t iposBefore = funPos - 1;
          // std::cout << "looping on  funpos is " << funPos << " formula is " << formula << " function " << funName <<
          // std::endl;
          if (iposBefore >= 0) {
             assert(iposBefore < formula.Length());
-            if (isalpha(formula[iposBefore])) {
+            //if (isalpha(formula[iposBefore])) {
+            if (IsFunctionNameChar(formula[iposBefore])) {
                // std::cout << "previous character for function " << funName << " is " << formula[iposBefore] << "- skip
                // " << std::endl;
                funPos = formula.Index(funName, lastFunPos);
diff --git a/test/TFormulaParsingTests.h b/test/TFormulaParsingTests.h
index 93915b15800..b462256df27 100644
--- a/test/TFormulaParsingTests.h
+++ b/test/TFormulaParsingTests.h
@@ -1017,6 +1017,37 @@ bool test51() {
    return ok;
 }
 
+bool test52() {
+   // test for bug 10815
+   // mixing user previous defined functions (available in gROOT)
+   // and pre-defined functions
+   bool ok  = true;
+   TF1 f1("f1gaus","[0]*gaus(1)",-10,10);
+   TF1 f2("f2","[0]*f1gaus",-10,10);
+   f1.SetParameters(2,3,1,2);
+   f2.SetParameters(3,2,3,1,2);
+   ok &=  TMath::AreEqualAbs( f1.Eval(1), 2.*3.*TMath::Gaus(1,1,2), 1.E-10);
+   if (!ok) Error("test52","Error testing f1");
+   bool ret =  TMath::AreEqualAbs( f2.Eval(1), 3.*2.*3.*TMath::Gaus(1,1,2), 1.E-10);
+   if (!ret) Error("test52","Error testing f2");
+   ok &= ret; 
+   TF1 f3("f3","f1gaus*gaus(4)",-10,10);
+   f3.SetParameters(2,3,1,2,3,2,3);
+   ret =  TMath::AreEqualAbs( f3.Eval(1), 2.*3.*TMath::Gaus(1,1,2) * 3. * TMath::Gaus(1,2,3), 1.E-10);    
+   if (!ret) Error("test52","Error testing f3");
+   ok &= ret; 
+   // check also after
+   TF1 f4("gaus2a","[0]*gaus(1)",-10,10);
+   TF1 f5("f2","[0]*gaus2a",-10,10);
+   f4.SetParameters(2,3,1,2);
+   f5.SetParameters(3,2,3,1,2);
+   ret =  TMath::AreEqualAbs( f5.Eval(1), 3.*f4.Eval(1),1.E-10);
+   if (!ret) Error("test52","Error testing f4 & f5");
+   return ok;
+
+}
+
+
 ///////////////////////////////////////////////////////////////////////////////////////
 
 void PrintError(int itest)  {
@@ -1087,6 +1118,7 @@ int runTests(bool debug = false) {
    IncrTest(itest); if (!test49() ) { PrintError(itest); }
    IncrTest(itest); if (!test50() ) { PrintError(itest); }
    IncrTest(itest); if (!test51() ) { PrintError(itest); }
+   IncrTest(itest); if (!test52() ) { PrintError(itest); }
 
    std::cout << ".\n";
 
-- 
GitLab