diff --git a/hist/hist/inc/TF1.h b/hist/hist/inc/TF1.h
index 7bfa97f27bc8aaae9d095fdcf45236f180f3f4bc..0d2e6abbac24041925a7cbb4ccb9ad6b7d937895 100644
--- a/hist/hist/inc/TF1.h
+++ b/hist/hist/inc/TF1.h
@@ -24,6 +24,7 @@
 #include "RConfigure.h"
 #include <functional>
 #include <cassert>
+#include <memory> // for shared_ptr
 #include "TFormula.h"
 #include "TAttLine.h"
 #include "TAttFill.h"
@@ -37,6 +38,7 @@ class TF1;
 class TH1;
 class TAxis;
 class TMethodCall;
+class TF1NormSum;
 
 namespace ROOT {
    namespace Fit {
@@ -257,14 +259,22 @@ protected:
    TF1Parameters *fParams = nullptr;   //Pointer to Function parameters object (exists only for not-formula functions)
 
    /// General constructor for TF1. Most of the other constructors delegate on it
-   TF1(EFType functionType, const char *name, Double_t xmin, Double_t xmax, Int_t npar, Int_t ndim, EAddToList addToGlobList, TF1Parameters *params = nullptr, TF1FunctorPointer * functor = nullptr):
-      TNamed(name, name), TAttLine(), TAttFill(), TAttMarker(), fXmin(xmin), fXmax(xmax), fNpar(npar), fNdim(ndim),
+ TF1(EFType functionType, const char *name, Double_t xmin, Double_t xmax, Int_t npar, Int_t ndim, EAddToList addToGlobList, TF1Parameters *params = nullptr, TF1FunctorPointer * functor = nullptr):
+   TNamed(name, name), TAttLine(), TAttFill(), TAttMarker(), fXmin(xmin), fXmax(xmax), fNpar(npar), fNdim(ndim),
       fType(functionType), fParErrors(npar), fParMin(npar), fParMax(npar), fFunctor(functor), fParams(params)
    {
       DoInitialize(addToGlobList);
    };
 
 
+private:
+   // NSUM parsing helper functions
+   void DefineNSUMTerm(TObjArray *newFuncs, TObjArray *coeffNames,
+		       TString &fullFormula,
+		       TString &formula, int termStart, int termEnd,
+		       Double_t xmin, Double_t xmax);
+   int TermCoeffLength(TString &term);
+
 public:
 
    template <class T>
@@ -305,7 +315,6 @@ public:
    TF1(const char *name, const char *formula, Double_t xmin = 0, Double_t xmax = 1, EAddToList addToGlobList = EAddToList::kDefault);
    TF1(const char *name, Double_t xmin, Double_t xmax, Int_t npar, Int_t ndim = 1, EAddToList addToGlobList = EAddToList::kDefault);
    TF1(const char *name, Double_t (*fcn)(Double_t *, Double_t *), Double_t xmin = 0, Double_t xmax = 1, Int_t npar = 0, Int_t ndim = 1, EAddToList addToGlobList = EAddToList::kDefault);
-   TF1(const char *name, Double_t (*fcn)(const Double_t *, const Double_t *), Double_t xmin = 0, Double_t xmax = 1, Int_t npar = 0, Int_t ndim = 1, EAddToList addToGlobList = EAddToList::kDefault);
 
    template <class T>
    TF1(const char *name, std::function<T(const T *data, const Double_t *param)> &fcn, Double_t xmin = 0, Double_t xmax = 1, Int_t npar = 0, Int_t ndim = 1, EAddToList addToGlobList = EAddToList::kDefault):
@@ -316,17 +325,18 @@ public:
    }
 
    ////////////////////////////////////////////////////////////////////////////////
-   /// Constructor using a pointer to function.
+   /// Constructor using a pointer to real function.
    ///
    /// \param npar is the number of free parameters used by the function
    ///
    /// This constructor creates a function of type C when invoked
    /// with the normal C++ compiler.
    ///
+   /// see test program test/stress.cxx (function stress1) for an example.
+   /// note the interface with an intermediate pointer.
    ///
    /// WARNING! A function created with this constructor cannot be Cloned
 
-
    template <class T>
    TF1(const char *name, T(*fcn)(const T *, const Double_t *), Double_t xmin = 0, Double_t xmax = 1, Int_t npar = 0, Int_t ndim = 1, EAddToList addToGlobList = EAddToList::kDefault):
       TF1(EFType::kTemplVec, name, xmin, xmax, npar, ndim, addToGlobList, new TF1Parameters(npar), new TF1FunctorPointerImpl<T>(fcn))
diff --git a/hist/hist/src/TF1.cxx b/hist/hist/src/TF1.cxx
index a1c00176df75e2d70f4591955662565a6f75b595..ffeb9e20a508f97631a7f4613b0c3b15faa199d0 100644
--- a/hist/hist/src/TF1.cxx
+++ b/hist/hist/src/TF1.cxx
@@ -25,6 +25,7 @@
 #include "TClass.h"
 #include "TMethodCall.h"
 #include "TF1Helper.h"
+#include "TF1NormSum.h"
 #include "TVirtualMutex.h"
 #include "Math/WrappedFunction.h"
 #include "Math/WrappedTF1.h"
@@ -431,9 +432,65 @@ TF1::TF1(const char *name, const char *formula, Double_t xmin, Double_t xmax, EA
       fXmax = xmin;
    }
    // create rep formula (no need to add to gROOT list since we will add the TF1 object)
-   fFormula = new TFormula(name, formula, false);
-   fNpar = fFormula->GetNpar();
-   fNdim = fFormula->GetNdim();
+   // First check if we need NSUM syntax:
+   if (TString(formula, 5) == "NSUM(" && formula[strlen(formula)-1] == ')') {
+            // using comma as delimiter
+      char delimiter = ',';
+      // first, remove "NSUM(" and ")" and spaces
+      TString formDense = TString(formula)(5,strlen(formula)-5-1);
+      formDense.ReplaceAll(' ', "");
+      
+      // make sure standard functions are defined (e.g. gaus, expo)
+      InitStandardFunctions();
+  
+      // Go char-by-char to split terms and define the relevant functions
+      int parenCount = 0;
+      int termStart = 0;
+      TObjArray *newFuncs = new TObjArray();
+      newFuncs->SetOwner(kTRUE);
+      TObjArray *coeffNames = new TObjArray();
+      coeffNames->SetOwner(kTRUE);
+      TString fullFormula("");
+      for (int i = 0; i < formDense.Length(); ++i) {
+      	 if (formDense[i] == '(')
+      	    parenCount++;
+      	 else if (formDense[i] == ')')
+      	    parenCount--;
+      	 else if (formDense[i] == delimiter && parenCount == 0) {
+      	    // term goes from termStart to i
+      	    DefineNSUMTerm(newFuncs, coeffNames, fullFormula, formDense, termStart, i, xmin, xmax);
+      	    termStart = i + 1;
+      	 }
+      }
+      DefineNSUMTerm(newFuncs, coeffNames, fullFormula, formDense, termStart, formDense.Length(), xmin, xmax);
+
+      TF1NormSum *normSum = new TF1NormSum(fullFormula, xmin, xmax);
+      
+      fNpar = normSum->GetNpar();
+      fNdim = 1; // (note: may want to extend functionality in the future)
+
+      fType = EFType::kPtrScalarFreeFcn; // (note: may want to add new fType for this case)
+      // fFunctor = ROOT::Math::ParamFunctor(normSum);
+      using Fnc_t = typename ROOT::Internal::GetFunctorType<decltype(ROOT::Internal::GetTheRightOp(&TF1NormSum::operator()))>::type;
+      fFunctor = new TF1::TF1FunctorPointerImpl<Fnc_t>(ROOT::Math::ParamFunctorTempl<Fnc_t>(normSum));
+      fParams = new TF1Parameters(fNpar);
+      fParams->SetParameters(&(normSum->GetParameters())[0]); // inherit default parameters from normSum
+
+      // Parameter names
+      for (int i = 0; i < fNpar; i++) {
+      	 if (coeffNames->At(i) != nullptr) {
+	    TString coeffName = ((TObjString *)coeffNames->At(i))->GetString();
+	    this->SetParName(i, (const char *)coeffName);
+      	 } else {
+	    this->SetParName(i, normSum->GetParName(i));
+	 }
+      }
+      
+   } else { // regular TFormula
+      fFormula = new TFormula(name, formula, false);
+      fNpar = fFormula->GetNpar();
+      fNdim = fFormula->GetNdim();
+   }
    if (fNpar) {
       fParErrors.resize(fNpar);
       fParMin.resize(fNpar);
@@ -499,22 +556,6 @@ TF1::TF1(const char *name, Double_t (*fcn)(Double_t *, Double_t *), Double_t xmi
    TF1(EFType::kPtrScalarFreeFcn, name, xmin, xmax, npar, ndim, addToGlobList, new TF1Parameters(npar), new TF1FunctorPointerImpl<double>(ROOT::Math::ParamFunctor(fcn)))
 {}
 
-////////////////////////////////////////////////////////////////////////////////
-/// Constructor using a pointer to real function.
-///
-/// \param npar is the number of free parameters used by the function
-///
-/// This constructor creates a function of type C when invoked
-/// with the normal C++ compiler.
-///
-/// see test program test/stress.cxx (function stress1) for an example.
-/// note the interface with an intermediate pointer.
-///
-/// WARNING! A function created with this constructor cannot be Cloned.
-
-TF1::TF1(const char *name, Double_t (*fcn)(const Double_t *, const Double_t *), Double_t xmin, Double_t xmax, Int_t npar, Int_t ndim, EAddToList addToGlobList) :
-   TF1(EFType::kPtrScalarFreeFcn, name, xmin, xmax, npar, ndim, addToGlobList, new TF1Parameters(npar), new TF1FunctorPointerImpl<double>(ROOT::Math::ParamFunctor(fcn)))
-{}
 
 ////////////////////////////////////////////////////////////////////////////////
 /// Constructor using the Functor class.
@@ -615,6 +656,67 @@ Bool_t TF1::AddToGlobalList(Bool_t on)
    return prevStatus;
 }
 
+////////////////////////////////////////////////////////////////////////////////
+/// Helper functions for NSUM parsing
+
+// Defines the formula that a given term uses, if not already defined,
+// and appends "sanitized" formula to `fullFormula` string
+void TF1::DefineNSUMTerm(TObjArray *newFuncs, TObjArray *coeffNames,
+			 TString &fullFormula,
+			 TString &formula, int termStart, int termEnd,
+			 Double_t xmin, Double_t xmax) {
+   TString originalTerm = formula(termStart, termEnd-termStart);
+   int coeffLength = TermCoeffLength(originalTerm);
+   if (coeffLength != -1)
+      termStart += coeffLength + 1;
+  
+   // `originalFunc` is the real formula and `cleanedFunc` is the
+   // sanitized version that will not confuse the TF1NormSum
+   // constructor
+   TString originalFunc = formula(termStart, termEnd-termStart);
+   TString cleanedFunc = TString(formula(termStart, termEnd-termStart))
+      .ReplaceAll('+', "<plus>")
+      .ReplaceAll('*',"<times>");
+
+   // define function (if necessary)
+   if (!gROOT->GetListOfFunctions()->FindObject(cleanedFunc))
+      newFuncs->Add(new TF1(cleanedFunc, originalFunc, xmin, xmax));
+
+   // append sanitized term to `fullFormula`
+   if (fullFormula.Length() != 0)
+      fullFormula.Append('+');
+
+   // include numerical coefficient
+   if (coeffLength != -1 && originalTerm[0] != '[')
+      fullFormula.Append(originalTerm(0, coeffLength+1));
+
+   // add coefficient name
+   if (coeffLength != -1 && originalTerm[0] == '[')
+      coeffNames->Add(new TObjString(TString(originalTerm(1,coeffLength-2))));
+   else
+      coeffNames->Add(nullptr);
+
+   fullFormula.Append(cleanedFunc);
+}
+
+
+// Returns length of coeff at beginning of a given term, not counting the '*'
+// Returns -1 if no coeff found
+// Coeff can be either a number or parameter name
+int TF1::TermCoeffLength(TString &term) {
+  int firstAsterisk = term.First('*');
+  if (firstAsterisk == -1) // no asterisk found
+    return -1;
+
+  if (TString(term(0,firstAsterisk)).IsFloat())
+     return firstAsterisk;
+     
+  if (term[0] == '[' && term[firstAsterisk-1] == ']'
+      && TString(term(1,firstAsterisk-2)).IsAlnum())
+     return firstAsterisk;
+
+  return -1;
+}
 
 ////////////////////////////////////////////////////////////////////////////////
 /// Operator =
diff --git a/hist/hist/test/CMakeLists.txt b/hist/hist/test/CMakeLists.txt
index a851ac9ea005de314b6cef0715c44eb028ebd83c..dec3662c0bcebf3a6b6c454dbd8a9ba747192268 100644
--- a/hist/hist/test/CMakeLists.txt
+++ b/hist/hist/test/CMakeLists.txt
@@ -1,2 +1,5 @@
 ROOT_ADD_GTEST(testTProfile2Poly test_tprofile2poly.cxx LIBRARIES Hist Matrix MathCore RIO)
 ROOT_ADD_GTEST(testTHn THn.cxx LIBRARIES Hist Matrix MathCore RIO)
+
+# ROOT_ADD_GTEST(testTF1 test_tf1.cxx LIBRARIES Core Hist Matrix MathCore RIO)
+ROOT_ADD_GTEST(testTF1 test_tf1.cxx LIBRARIES Hist)
diff --git a/hist/hist/test/test_tf1.cxx b/hist/hist/test/test_tf1.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..312bd3ef5045db4885180c2f02d7c72ea289add9
--- /dev/null
+++ b/hist/hist/test/test_tf1.cxx
@@ -0,0 +1,71 @@
+#include "TF1.h"
+#include "TObjString.h"
+// #include "TObject.h"
+
+#include "gtest/gtest.h"
+
+#include <iostream>
+
+using namespace std;
+
+Float_t delta = 0.00000000001;
+
+void coeffNamesGeneric(TString *formula, TObjArray *coeffNames) {
+   TF1 *cn0 = new TF1("cn0", *formula, 0, 1);
+   ASSERT_EQ(cn0->GetNpar(), coeffNames->GetEntries());
+   for (int i = 0; i < coeffNames->GetEntries(); i++) {
+      TObjString *coeffObj = (TObjString *)coeffNames->At(i);
+      TString coeffName = coeffObj->GetString();
+      EXPECT_EQ(coeffName, TString(cn0->GetParName(i)));
+   }
+}
+
+void test_coeffNames() {
+   // cout << "About to start" << endl;
+   
+   TObjArray *coeffNames = new TObjArray();
+   coeffNames->SetOwner(kTRUE);
+   coeffNames->Add(new TObjString("sg"));
+   coeffNames->Add(new TObjString("bg"));
+   coeffNames->Add(new TObjString("Mean"));
+   coeffNames->Add(new TObjString("Sigma"));
+   coeffNames->Add(new TObjString("Slope"));
+   TString *formula = new TString("NSUM([sg] * gaus, [bg] * expo)");
+
+   // cout << "Almost done" << endl;
+   
+   coeffNamesGeneric(formula, coeffNames);
+}
+
+void test_normalization() {
+   double xmin = -5;
+   double xmax = 5;
+   TF1 *n0 = new TF1("n0", "NSUM(.5 * gaus, .5 * (x+[0])**2)", xmin, xmax);
+   EXPECT_NEAR(n0->Integral(xmin, xmax), 1, delta);
+   n0->SetParameter(4,1); // should not affect integral
+   EXPECT_NEAR(n0->Integral(xmin, xmax), 1, delta);
+   n0->SetParameter(0,0);
+   EXPECT_NEAR(n0->Integral(xmin, xmax), .5, delta);
+   
+   TF1 *n1 = new TF1("n1", "NSUM([sg] * gaus, [bg] * (x+[0])**2)", xmin, xmax);
+   n1->SetParameter(0,.5);
+   n1->SetParameter(1,.5);
+   EXPECT_NEAR(n1->Integral(xmin, xmax), 1, delta);
+   n0->SetParameter(0,0);
+   EXPECT_NEAR(n0->Integral(xmin, xmax), .5, delta);
+
+   TF1 *n2 = new TF1("n2", "NSUM([sg] * gaus, -0.5 * (x+[0])**2)", xmin, xmax);
+   n2->SetParameter(0,.5);
+   EXPECT_NEAR(n2->GetParameter(1), -.5, delta);
+   EXPECT_NEAR(n2->Integral(xmin, xmax), 0, delta);
+   n2->SetParameter(0,0);
+   EXPECT_NEAR(n2->Integral(xmin, xmax), -.5, delta);
+}
+
+TEST(TF1, CoeffNames) {
+   test_coeffNames();
+}
+
+TEST(TF1, Normalization) {
+   test_normalization();
+}