// @(#)root/graf:$Name:  $:$Id: TSpline.cxx,v 1.9 2004/06/01 09:56:25 brun Exp $
// Author: Federico Carminati   28/02/2000

/*************************************************************************
 * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TSpline                                                              //
//                                                                      //
// Base class for spline implementation containing the Draw/Paint       //
// methods                                                              //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

#include "TSpline.h"
#include "TVirtualPad.h"
#include "TF1.h"
#include "TSystem.h"
#include "Riostream.h"

ClassImp(TSplinePoly)
ClassImp(TSplinePoly3)
ClassImp(TSplinePoly5)
ClassImp(TSpline3)
ClassImp(TSpline5)
ClassImp(TSpline)

//____________________________________________________________________________
void TSpline::Draw(Option_t *option)
{
//*-*-*-*-*-*-*-*-*-*Draw this function with its current attributes*-*-*-*-*
//*-*                  ==============================================
//*-*
//*-* Possible option values are:
//*-*   "SAME"  superimpose on top of existing picture
//*-*   "L"     connect all computed points with a straight line
//*-*   "C"     connect all computed points with a smooth curve.
//*-*   "P"     add a polymarker at each knot
//*-*
//*-* Note that the default value is "L". Therefore to draw on top
//*-* of an existing picture, specify option "LSAME"
//*-*
//*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*

   TString opt = option;
   opt.ToLower();
   if (gPad && !opt.Contains("same")) gPad->Clear();

   AppendPad(option);
}

//____________________________________________________________________________
void TSpline::Paint(Option_t *option)
{
//*-*-*-*-*-*-*-*-*-*Paint this function with its current attributes*-*-*-*-*
//*-*                ===============================================

   Int_t i;
   Double_t xv;

   TString opt = option;
   opt.ToLower();
   Double_t xmin, xmax, pmin, pmax;
   pmin = gPad->PadtoX(gPad->GetUxmin());
   pmax = gPad->PadtoX(gPad->GetUxmax());
   xmin = fXmin;
   xmax = fXmax;
   if (opt.Contains("same")) {
      if (xmax < pmin) return;  // Otto: completely outside
      if (xmin > pmax) return;
      if (xmin < pmin) xmin = pmin;
      if (xmax > pmax) xmax = pmax;
   } else {
      gPad->Clear();
   }

//*-*-  Create a temporary histogram and fill each channel with the function value
   if (fHistogram)
      if ((!gPad->GetLogx() && fHistogram->TestBit(TH1::kLogX)) ||
	  (gPad->GetLogx() && !fHistogram->TestBit(TH1::kLogX)))
	{ delete fHistogram; fHistogram = 0;}

   if (fHistogram) {
     //if (xmin != fXmin || xmax != fXmax)
     fHistogram->GetXaxis()->SetLimits(xmin,xmax);
   } else {
//      if logx, we must bin in logx and not in x !!!
//      otherwise if several decades, one gets crazy results
     if (xmin > 0 && gPad->GetLogx()) {
       Double_t *xbins  = new Double_t[fNpx+1];
       Double_t xlogmin = TMath::Log10(xmin);
       Double_t xlogmax = TMath::Log10(xmax);
       Double_t dlogx   = (xlogmax-xlogmin)/((Double_t)fNpx);
       for (i=0;i<=fNpx;i++) {
	 xbins[i] = gPad->PadtoX(xlogmin+ i*dlogx);
       }
       fHistogram = new TH1F("Spline",GetTitle(),fNpx,xbins);
       fHistogram->SetBit(TH1::kLogX);
       delete [] xbins;
     } else {
       fHistogram = new TH1F("Spline",GetTitle(),fNpx,xmin,xmax);
     }
     if (!fHistogram) return;
     fHistogram->SetDirectory(0);
   }
   for (i=1;i<=fNpx;i++) {
     xv = fHistogram->GetBinCenter(i);
     fHistogram->SetBinContent(i,this->Eval(xv));
   }

//*-*- Copy Function attributes to histogram attributes
   fHistogram->SetBit(TH1::kNoStats);
   //   fHistogram->SetMinimum(fMinimum);
   //    fHistogram->SetMaximum(fMaximum);
   fHistogram->SetLineColor(GetLineColor());
   fHistogram->SetLineStyle(GetLineStyle());
   fHistogram->SetLineWidth(GetLineWidth());
   fHistogram->SetFillColor(GetFillColor());
   fHistogram->SetFillStyle(GetFillStyle());
   fHistogram->SetMarkerColor(GetMarkerColor());
   fHistogram->SetMarkerStyle(GetMarkerStyle());
   fHistogram->SetMarkerSize(GetMarkerSize());

//*-*-  Draw the histogram
//*-*-  but first strip off the 'p' option if any
   char *o = (char *) opt.Data();
   Int_t j=0;
   i=0;
   Bool_t graph=kFALSE;
   do
     if(o[i]=='p') graph=kTRUE ; else o[j++]=o[i];
   while(o[i++]);
   if (opt.Length() == 0 ) fHistogram->Paint("lf");
   else if (opt == "same") fHistogram->Paint("lfsame");
   else                    fHistogram->Paint(opt.Data());

//*-*- Think about the graph, if demanded

   if(graph) {
     if(!fGraph) {
       Double_t *xx = new Double_t[fNp];
       Double_t *yy = new Double_t[fNp];
       for(i=0; i<fNp; ++i)
	 GetKnot(i,xx[i],yy[i]);
       fGraph=new TGraph(fNp,xx,yy);
       delete [] xx;
       delete [] yy;
     }
     fGraph->SetMarkerColor(GetMarkerColor());
     fGraph->SetMarkerStyle(GetMarkerStyle());
     fGraph->SetMarkerSize(GetMarkerSize());
     fGraph->Paint("p");
   }
}

//______________________________________________________________________________
void TSpline::Streamer(TBuffer &R__b)
{
   // Stream an object of class TSpline.

   if (R__b.IsReading()) {
      UInt_t R__s, R__c;
      Version_t R__v = R__b.ReadVersion(&R__s, &R__c);
      if (R__v > 1) {
         TSpline::Class()->ReadBuffer(R__b, this, R__v, R__s, R__c);
         return;
      } 
      //====process old versions before automatic schema evolution
      TNamed::Streamer(R__b);
      TAttLine::Streamer(R__b);
      TAttFill::Streamer(R__b);
      TAttMarker::Streamer(R__b);
      
      fNp = 0;
      /*
      R__b >> fDelta;
      R__b >> fXmin;
      R__b >> fXmax;
      R__b >> fNp;
      R__b >> fKstep;
      R__b >> fHistogram;
      R__b >> fGraph;
      R__b >> fNpx;
      */
      R__b.CheckByteCount(R__s, R__c, TSpline::IsA());
      //====end of old versions
      
   } else {
      TSpline::Class()->WriteBuffer(R__b,this);
   }
}

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TSpline3                                                             //
//                                                                      //
// Class to create third splines to interpolate knots                   //
// Arbitrary conditions can be introduced for first and second          //
// derivatives at beginning and ending points                           //
//                                                                      //
//////////////////////////////////////////////////////////////////////////


//____________________________________________________________________________
TSpline3::TSpline3(const char *title,
		   Double_t x[], Double_t y[], Int_t n, const char *opt,
		   Double_t valbeg, Double_t valend) :
  TSpline(title,-1,x[0],x[n-1],n,kFALSE),
  fValBeg(valbeg), fValEnd(valend), fBegCond(0), fEndCond(0)
{
  //
  // Third spline creator given an array of
  // arbitrary knots in increasing abscissa order and
  // possibly end point conditions
  //

  fName="Spline3";
  //
  // Set endpoint conditions
  if(opt) SetCond(opt);
  //
  // Create the plynomial terms and fill
  // them with node information
  fPoly = new TSplinePoly3[n];
  for (Int_t i=0; i<n; ++i) {
    fPoly[i].X() = x[i];
    fPoly[i].Y() = y[i];
  }
  //
  // Build the spline coefficients
  BuildCoeff();
}

//____________________________________________________________________________
TSpline3::TSpline3(const char *title,
		   Double_t xmin, Double_t xmax,
		   Double_t y[], Int_t n, const char *opt,
		   Double_t valbeg, Double_t valend) :
  TSpline(title,(xmax-xmin)/(n-1), xmin, xmax, n, kTRUE),
  fValBeg(valbeg), fValEnd(valend),
  fBegCond(0), fEndCond(0)
{
  //
  // Third spline creator given an array of
  // arbitrary function values on equidistant n abscissa
  // values from xmin to xmax and possibly end point conditions
  //

  fName="Spline3";
  //
  // Set endpoint conditions
  if(opt) SetCond(opt);
  //
  // Create the plynomial terms and fill
  // them with node information
  fPoly = new TSplinePoly3[n];
  for (Int_t i=0; i<n; ++i) {
    fPoly[i].X() = fXmin+i*fDelta;
    fPoly[i].Y() = y[i];
  }
  //
  // Build the spline coefficients
  BuildCoeff();
}

//____________________________________________________________________________
TSpline3::TSpline3(const char *title,
		   Double_t x[], const TF1 *func, Int_t n, const char *opt,
		   Double_t valbeg, Double_t valend) :
  TSpline(title,-1, x[0], x[n-1], n, kFALSE),
  fValBeg(valbeg), fValEnd(valend),
  fBegCond(0), fEndCond(0)
{
  //
  // Third spline creator given an array of
  // arbitrary abscissas in increasing order and a function
  // to interpolate and possibly end point conditions
  //

  fName="Spline3";
  //
  // Set endpoint conditions
  if(opt) SetCond(opt);
  //
  // Create the plynomial terms and fill
  // them with node information
  fPoly = new TSplinePoly3[n];
  for (Int_t i=0; i<n; ++i) {
    fPoly[i].X() = x[i];
    fPoly[i].Y() = ((TF1*)func)->Eval(x[i]);
  }
  //
  // Build the spline coefficients
  BuildCoeff();
}

//____________________________________________________________________________
TSpline3::TSpline3(const char *title,
		   Double_t xmin, Double_t xmax,
		   const TF1 *func, Int_t n, const char *opt,
		   Double_t valbeg, Double_t valend) :
  TSpline(title,(xmax-xmin)/(n-1), xmin, xmax, n, kTRUE),
  fValBeg(valbeg), fValEnd(valend),
  fBegCond(0), fEndCond(0)
{
  //
  // Third spline creator given a function to be
  // evaluated on n equidistand abscissa points between xmin
  // and xmax and possibly end point conditions
  //

  fName="Spline3";
  //
  // Set endpoint conditions
  if(opt) SetCond(opt);
  //
  // Create the plynomial terms and fill
  // them with node information
  fPoly = new TSplinePoly3[n];
  for (Int_t i=0; i<n; ++i) {
    Double_t x=fXmin+i*fDelta;
    fPoly[i].X() = x;
    fPoly[i].Y() = ((TF1*)func)->Eval(x);
  }
  //
  // Build the spline coefficients
  BuildCoeff();
}

//____________________________________________________________________________
TSpline3::TSpline3(const char *title,
		   const TGraph *g, const char *opt,
		   Double_t valbeg, Double_t valend) :
  TSpline(title,-1,0,0,g->GetN(),kFALSE),
  fValBeg(valbeg), fValEnd(valend),
  fBegCond(0), fEndCond(0)
{
  //
  // Third spline creator given a TGraph with
  // abscissa in increasing order and possibly end
  // point conditions
  //

  fName="Spline3";
  //
  // Set endpoint conditions
  if(opt) SetCond(opt);
  //
  // Create the plynomial terms and fill
  // them with node information
  fPoly = new TSplinePoly3[fNp];
  for (Int_t i=0; i<fNp; ++i) {
    Double_t xx, yy;
    g->GetPoint(i,xx,yy);
    fPoly[i].X()=xx;
    fPoly[i].Y()=yy;
  }
  fXmin = fPoly[0].X();
  fXmax = fPoly[fNp-1].X();
  //
  // Build the spline coefficients
  BuildCoeff();
}

//____________________________________________________________________________
void TSpline3::SetCond(const char *opt)
{
  //
  // Check the boundary conditions
  //
  const char *b1 = strstr(opt,"b1");
  const char *e1 = strstr(opt,"e1");
  const char *b2 = strstr(opt,"b2");
  const char *e2 = strstr(opt,"e2");
  if (b1 && b2)
    Error("SetCond","Cannot specify first and second derivative at first point");
  if (e1 && e2)
    Error("SetCond","Cannot specify first and second derivative at last point");
  if (b1) fBegCond=1;
  else if (b2) fBegCond=2;
  if (e1) fEndCond=1;
  else if (e2) fEndCond=2;
}

//___________________________________________________________________________
void TSpline3::Test()
{
  //
  // Test method for TSpline5
  //
  //   n          number of data points.
  //   m          2*m-1 is order of spline.
  //                 m = 2 always for third spline.
  //   nn,nm1,mm,
  //   mm1,i,k,
  //   j,jj       temporary integer variables.
  //   z,p        temporary double precision variables.
  //   x[n]       the sequence of knots.
  //   y[n]       the prescribed function values at the knots.
  //   a[200][4]  two dimensional array whose columns are
  //                 the computed spline coefficients
  //   diff[3]    maximum values of differences of values and
  //                 derivatives to right and left of knots.
  //   com[3]     maximum values of coefficients.
  //
  //
  //   test of TSpline3 with nonequidistant knots and
  //      equidistant knots follows.
  //
  //
  Double_t hx;
  Double_t diff[3];
  Double_t a[800], c[4];
  Int_t i, j, k, m, n;
  Double_t x[200], y[200], z;
  Int_t jj, mm;
  Int_t mm1, nm1;
  Double_t com[3];
  printf("1         TEST OF TSpline3 WITH NONEQUIDISTANT KNOTS\n");
  n = 5;
  x[0] = -3;
  x[1] = -1;
  x[2] = 0;
  x[3] = 3;
  x[4] = 4;
  y[0] = 7;
  y[1] = 11;
  y[2] = 26;
  y[3] = 56;
  y[4] = 29;
  m = 2;
  mm = m << 1;
  mm1 = mm-1;
  printf("\n-N = %3d    M =%2d\n",n,m);
  TSpline3 *spline = new TSpline3("Test",x,y,n);
  for (i = 0; i < n; ++i)
    spline->GetCoeff(i,hx, a[i],a[i+200],a[i+400],a[i+600]);
  delete spline;
  for (i = 0; i < mm1; ++i) diff[i] = com[i] = 0;
  for (k = 0; k < n; ++k) {
    for (i = 0; i < mm; ++i) c[i] = a[k+i*200];
    printf(" ---------------------------------------%3d --------------------------------------------\n",k+1);
    printf("%12.8f\n",x[k]);
    if (k == n-1) {
      printf("%16.8f\n",c[0]);
    } else {
      for (i = 0; i < mm; ++i) printf("%16.8f",c[i]);
      printf("\n");
      for (i = 0; i < mm1; ++i)
	if ((z=TMath::Abs(a[k+i*200])) > com[i]) com[i] = z;
      z = x[k+1]-x[k];
      for (i = 1; i < mm; ++i)
	for (jj = i; jj < mm; ++jj) {
	  j = mm+i-jj;
	  c[j-2] = c[j-1]*z+c[j-2];
	}
      for (i = 0; i < mm; ++i) printf("%16.8f",c[i]);
      printf("\n");
      for (i = 0; i < mm1; ++i)
	if (!(k >= n-2 && i != 0))
	  if((z = TMath::Abs(c[i]-a[k+1+i*200]))
	     > diff[i]) diff[i] = z;
    }
  }
  printf("  MAXIMUM ABSOLUTE VALUES OF DIFFERENCES \n");
  for (i = 0; i < mm1; ++i) printf("%18.9E",diff[i]);
  printf("\n");
  printf("  MAXIMUM ABSOLUTE VALUES OF COEFFICIENTS \n");
  if (TMath::Abs(c[0]) > com[0])
    com[0] = TMath::Abs(c[0]);
  for (i = 0; i < mm1; ++i) printf("%16.8f",com[i]);
  printf("\n");
  m = 2;
  for (n = 10; n <= 100; n += 10) {
    mm = m << 1;
    mm1 = mm-1;
    nm1 = n-1;
    for (i = 0; i < nm1; i += 2) {
      x[i] = i+1;
      x[i+1] = i+2;
      y[i] = 1;
      y[i+1] = 0;
    }
    if (n % 2 != 0) {
      x[n-1] = n;
      y[n-1] = 1;
    }
    printf("\n-N = %3d    M =%2d\n",n,m);
    spline = new TSpline3("Test",x,y,n);
    for (i = 0; i < n; ++i)
      spline->GetCoeff(i,hx,a[i],a[i+200],a[i+400],a[i+600]);
    delete spline;
    for (i = 0; i < mm1; ++i)
      diff[i] = com[i] = 0;
    for (k = 0; k < n; ++k) {
      for (i = 0; i < mm; ++i)
	c[i] = a[k+i*200];
      if (n < 11) {
	printf(" ---------------------------------------%3d --------------------------------------------\n",k+1);
	printf("%12.8f\n",x[k]);
	if (k == n-1) printf("%16.8f\n",c[0]);
      }
      if (k == n-1) break;
      if (n <= 10) {
	for (i = 0; i < mm; ++i) printf("%16.8f",c[i]);
	printf("\n");
      }
      for (i = 0; i < mm1; ++i)
	if ((z=TMath::Abs(a[k+i*200])) > com[i])
	  com[i] = z;
      z = x[k+1]-x[k];
      for (i = 1; i < mm; ++i)
	for (jj = i; jj < mm; ++jj) {
	  j = mm+i-jj;
	  c[j-2] = c[j-1]*z+c[j-2];
	}
      if (n <= 10) {
	for (i = 0; i < mm; ++i) printf("%16.8f",c[i]);
	printf("\n");
      }
      for (i = 0; i < mm1; ++i)
	if (!(k >= n-2 && i != 0))
	  if ((z = TMath::Abs(c[i]-a[k+1+i*200]))
	      > diff[i]) diff[i] = z;
    }
    printf("  MAXIMUM ABSOLUTE VALUES OF DIFFERENCES \n");
    for (i = 0; i < mm1; ++i) printf("%18.9E",diff[i]);
    printf("\n");
    printf("  MAXIMUM ABSOLUTE VALUES OF COEFFICIENTS \n");
    if (TMath::Abs(c[0]) > com[0])
      com[0] = TMath::Abs(c[0]);
    for (i = 0; i < mm1; ++i) printf("%16.8E",com[i]);
    printf("\n");
  }
}

//____________________________________________________________________________
Double_t TSpline3::Eval(Double_t x) const
{
  //
  // Evaluate spline polynomial
  //
  Int_t klow=0;
  //
  // If out of boundaries, extrapolate
  // It may be badly wrong
  if(x<=fXmin) klow=0;
  else if(x>=fXmax) klow=fNp-1;
  else {
    if(fKstep) {
      //
      // Equidistant knots, use histogramming
      klow = TMath::Min(Int_t((x-fXmin)/fDelta),fNp-1);
    } else {
      Int_t khig=fNp-1, khalf;
      //
      // Non equidistant knots, binary search
      while(khig-klow>1)
	if(x>fPoly[khalf=(klow+khig)/2].X())
	  klow=khalf;
	else
	  khig=khalf;
    }
    //
    // This could be removed, sanity check
    if(!(fPoly[klow].X()<=x && x<=fPoly[klow+1].X()))
      Error("Eval",
	    "Binary search failed x(%d) = %f < %f < x(%d) = %f\n",
	    klow,fPoly[klow].X(),x,fPoly[klow+1].X());
  }
  //
  // Evaluate now
  return fPoly[klow].Eval(x);
}

//____________________________________________________________________________
void TSpline3::SaveAs(const char *filename) const
{
  // write this spline as a C++ function that can be executed without ROOT
  // the name of the function is the name of the file up to the "." if any
    
   //open the file
   ofstream *f = new ofstream(filename,ios::out);
   if (f == 0 || gSystem->AccessPathName(filename,kWritePermission)) {
      Error("SaveAs","Cannot open file:%s\n",filename);
      return;
   }
   
   //write the function name and the spline constants
   char buffer[512];
   Int_t nch = strlen(filename);
   sprintf(buffer,"double %s",filename);
   char *dot = strstr(buffer,".");
   if (dot) *dot = 0;
   strcat(buffer,"(double x) {\n");
   nch = strlen(buffer); f->write(buffer,nch);
   sprintf(buffer,"   const int fNp = %d, fKstep = %d;\n",fNp,fKstep);
   nch = strlen(buffer); f->write(buffer,nch);
   sprintf(buffer,"   const double fDelta = %g, fXmin = %g, fXmax = %g;\n",fDelta,fXmin,fXmax);
   nch = strlen(buffer); f->write(buffer,nch);

   //write the spline coefficients
   //array fX
   sprintf(buffer,"   const double fX[%d] = {",fNp);   
   nch = strlen(buffer); f->write(buffer,nch);
   buffer[0] = 0;
   Int_t i;
   char numb[20];
   for (i=0;i<fNp;i++) {
      sprintf(numb," %g,",fPoly[i].X());
      nch = strlen(numb);
      if (i == fNp-1) numb[nch-1]=0;
      strcat(buffer,numb);
      if (i%5 == 4 || i == fNp-1) {
         nch = strlen(buffer); f->write(buffer,nch);
         if (i != fNp-1) sprintf(buffer,"\n                       ");
      }
   }
   sprintf(buffer," };\n");
   nch = strlen(buffer); f->write(buffer,nch);
   //array fY
   sprintf(buffer,"   const double fY[%d] = {",fNp);   
   nch = strlen(buffer); f->write(buffer,nch);
   buffer[0] = 0;
   for (i=0;i<fNp;i++) {
      sprintf(numb," %g,",fPoly[i].Y());
      nch = strlen(numb);
      if (i == fNp-1) numb[nch-1]=0;
      strcat(buffer,numb);
      if (i%5 == 4 || i == fNp-1) {
         nch = strlen(buffer); f->write(buffer,nch);
         if (i != fNp-1) sprintf(buffer,"\n                       ");
      }
   }
   sprintf(buffer," };\n");
   nch = strlen(buffer); f->write(buffer,nch);
   //array fB
   sprintf(buffer,"   const double fB[%d] = {",fNp);   
   nch = strlen(buffer); f->write(buffer,nch);
   buffer[0] = 0;
   for (i=0;i<fNp;i++) {
      sprintf(numb," %g,",fPoly[i].B());
      nch = strlen(numb);
      if (i == fNp-1) numb[nch-1]=0;
      strcat(buffer,numb);
      if (i%5 == 4 || i == fNp-1) {
         nch = strlen(buffer); f->write(buffer,nch);
         if (i != fNp-1) sprintf(buffer,"\n                       ");
      }
   }
   sprintf(buffer," };\n");
   nch = strlen(buffer); f->write(buffer,nch);
   //array fC
   sprintf(buffer,"   const double fC[%d] = {",fNp);   
   nch = strlen(buffer); f->write(buffer,nch);
   buffer[0] = 0;
   for (i=0;i<fNp;i++) {
      sprintf(numb," %g,",fPoly[i].C());
      nch = strlen(numb);
      if (i == fNp-1) numb[nch-1]=0;
      strcat(buffer,numb);
      if (i%5 == 4 || i == fNp-1) {
         nch = strlen(buffer); f->write(buffer,nch);
         if (i != fNp-1) sprintf(buffer,"\n                       ");
      }
   }
   sprintf(buffer," };\n");
   nch = strlen(buffer); f->write(buffer,nch);
    //array fD
   sprintf(buffer,"   const double fD[%d] = {",fNp);   
   nch = strlen(buffer); f->write(buffer,nch);
   buffer[0] = 0;
   for (i=0;i<fNp;i++) {
      sprintf(numb," %g,",fPoly[i].D());
      nch = strlen(numb);
      if (i == fNp-1) numb[nch-1]=0;
      strcat(buffer,numb);
      if (i%5 == 4 || i == fNp-1) {
         nch = strlen(buffer); f->write(buffer,nch);
         if (i != fNp-1) sprintf(buffer,"\n                       ");
      }
   }
   sprintf(buffer," };\n");
   nch = strlen(buffer); f->write(buffer,nch);
    
   //generate code for the spline evaluation
   sprintf(buffer,"   int klow=0;\n");
   nch = strlen(buffer); f->write(buffer,nch);

   sprintf(buffer,"   // If out of boundaries, extrapolate. It may be badly wrong\n");
   sprintf(buffer,"   if(x<=fXmin) klow=0;\n");
   nch = strlen(buffer); f->write(buffer,nch);
   sprintf(buffer,"   else if(x>=fXmax) klow=fNp-1;\n");
   nch = strlen(buffer); f->write(buffer,nch);
   sprintf(buffer,"   else {\n");
   nch = strlen(buffer); f->write(buffer,nch);
   sprintf(buffer,"     if(fKstep) {\n");
   nch = strlen(buffer); f->write(buffer,nch);

   sprintf(buffer,"       // Equidistant knots, use histogramming\n");
   nch = strlen(buffer); f->write(buffer,nch);
   sprintf(buffer,"       klow = int((x-fXmin)/fDelta);\n");
   nch = strlen(buffer); f->write(buffer,nch);
   sprintf(buffer,"       if (klow < fNp-1) klow = fNp-1;\n");
   nch = strlen(buffer); f->write(buffer,nch);
   sprintf(buffer,"     } else {\n");
   nch = strlen(buffer); f->write(buffer,nch);
   sprintf(buffer,"       int khig=fNp-1, khalf;\n");
   nch = strlen(buffer); f->write(buffer,nch);

   sprintf(buffer,"       // Non equidistant knots, binary search\n");
   nch = strlen(buffer); f->write(buffer,nch);
   sprintf(buffer,"       while(khig-klow>1)\n");
   nch = strlen(buffer); f->write(buffer,nch);
   sprintf(buffer,"	 if(x>fX[khalf=(klow+khig)/2]) klow=khalf;\n");
   nch = strlen(buffer); f->write(buffer,nch);
   sprintf(buffer,"	 else khig=khalf;\n");
   nch = strlen(buffer); f->write(buffer,nch);
   sprintf(buffer,"     }\n");
   nch = strlen(buffer); f->write(buffer,nch);
   sprintf(buffer,"   }\n");
   nch = strlen(buffer); f->write(buffer,nch);
   sprintf(buffer,"   // Evaluate now\n");
   nch = strlen(buffer); f->write(buffer,nch);
   sprintf(buffer,"   double dx=x-fX[klow];\n");
   nch = strlen(buffer); f->write(buffer,nch);
   sprintf(buffer,"   return (fY[klow]+dx*(fB[klow]+dx*(fC[klow]+dx*fD[klow])));\n");
   nch = strlen(buffer); f->write(buffer,nch);
    
   //close file
   f->write("}\n",2);
      
   if (f) { f->close(); delete f;}
}

//____________________________________________________________________________
void TSpline3::BuildCoeff()
{
//      subroutine cubspl ( tau, c, n, ibcbeg, ibcend )
//  from  * a practical guide to splines *  by c. de boor
//     ************************  input  ***************************
//     n = number of data points. assumed to be .ge. 2.
//     (tau(i), c(1,i), i=1,...,n) = abscissae and ordinates of the
//        data points. tau is assumed to be strictly increasing.
//     ibcbeg, ibcend = boundary condition indicators, and
//     c(2,1), c(2,n) = boundary condition information. specifically,
//        ibcbeg = 0  means no boundary condition at tau(1) is given.
//           in this case, the not-a-knot condition is used, i.e. the
//           jump in the third derivative across tau(2) is forced to
//           zero, thus the first and the second cubic polynomial pieces
//           are made to coincide.)
//        ibcbeg = 1  means that the slope at tau(1) is made to equal
//           c(2,1), supplied by input.
//        ibcbeg = 2  means that the second derivative at tau(1) is
//           made to equal c(2,1), supplied by input.
//        ibcend = 0, 1, or 2 has analogous meaning concerning the
//           boundary condition at tau(n), with the additional infor-
//           mation taken from c(2,n).
//     ***********************  output  **************************
//     c(j,i), j=1,...,4; i=1,...,l (= n-1) = the polynomial coefficients
//        of the cubic interpolating spline with interior knots (or
//        joints) tau(2), ..., tau(n-1). precisely, in the interval
//        (tau(i), tau(i+1)), the spline f is given by
//           f(x) = c(1,i)+h*(c(2,i)+h*(c(3,i)+h*c(4,i)/3.)/2.)
//        where h = x - tau(i). the function program *ppvalu* may be
//        used to evaluate f or its derivatives from tau,c, l = n-1,
//        and k=4.

  Int_t i, j, l, m;
  Double_t   divdf1,divdf3,dtau,g=0;
//***** a tridiagonal linear system for the unknown slopes s(i) of
//  f  at tau(i), i=1,...,n, is generated and then solved by gauss elim-
//  ination, with s(i) ending up in c(2,i), all i.
//     c(3,.) and c(4,.) are used initially for temporary storage.
  l = fNp-1;
// compute first differences of x sequence and store in C also,
// compute first divided difference of data and store in D.
  for (m=1; m<fNp ; ++m) {
    fPoly[m].C() = fPoly[m].X() - fPoly[m-1].X();
    fPoly[m].D() = (fPoly[m].Y() - fPoly[m-1].Y())/fPoly[m].C();
  }
// construct first equation from the boundary condition, of the form
//             D[0]*s[0] + C[0]*s[1] = B[0]
  if(fBegCond==0) {
    if(fNp == 2) {
//     no condition at left end and n = 2.
      fPoly[0].D() = 1.;
      fPoly[0].C() = 1.;
      fPoly[0].B() = 2.*fPoly[1].D();
    } else {
//     not-a-knot condition at left end and n .gt. 2.
      fPoly[0].D() = fPoly[2].C();
      fPoly[0].C() = fPoly[1].C() + fPoly[2].C();
      fPoly[0].B() =((fPoly[1].C()+2.*fPoly[0].C())*fPoly[1].D()*fPoly[2].C()+fPoly[1].C()*fPoly[1].C()*fPoly[2].D())/fPoly[0].C();
    }
  } else if (fBegCond==1) {
//     slope prescribed at left end.
    fPoly[0].B() = fValBeg;
    fPoly[0].D() = 1.;
    fPoly[0].C() = 0.;
  } else if (fBegCond==2) {
//     second derivative prescribed at left end.
    fPoly[0].D() = 2.;
    fPoly[0].C() = 1.;
    fPoly[0].B() = 3.*fPoly[1].D() - fPoly[1].C()/2.*fValBeg;
  }
  if(fNp > 2) {
//  if there are interior knots, generate the corresp. equations and car-
//  ry out the forward pass of gauss elimination, after which the m-th
//  equation reads    D[m]*s[m] + C[m]*s[m+1] = B[m].
    for (m=1; m<l; ++m) {
      g = -fPoly[m+1].C()/fPoly[m-1].D();
      fPoly[m].B() = g*fPoly[m-1].B() + 3.*(fPoly[m].C()*fPoly[m+1].D()+fPoly[m+1].C()*fPoly[m].D());
      fPoly[m].D() = g*fPoly[m-1].C() + 2.*(fPoly[m].C() + fPoly[m+1].C());
    }
// construct last equation from the second boundary condition, of the form
//           (-g*D[n-2])*s[n-2] + D[n-1]*s[n-1] = B[n-1]
//     if slope is prescribed at right end, one can go directly to back-
//     substitution, since c array happens to be set up just right for it
//     at this point.
    if(fEndCond == 0) {
      if (fNp > 3 || fBegCond != 0) {
//     not-a-knot and n .ge. 3, and either n.gt.3 or  also not-a-knot at
//     left end point.
	g = fPoly[fNp-2].C() + fPoly[fNp-1].C();
	fPoly[fNp-1].B() = ((fPoly[fNp-1].C()+2.*g)*fPoly[fNp-1].D()*fPoly[fNp-2].C()
		     + fPoly[fNp-1].C()*fPoly[fNp-1].C()*(fPoly[fNp-2].Y()-fPoly[fNp-3].Y())/fPoly[fNp-2].C())/g;
	g = -g/fPoly[fNp-2].D();
	fPoly[fNp-1].D() = fPoly[fNp-2].C();
      } else {
//     either (n=3 and not-a-knot also at left) or (n=2 and not not-a-
//     knot at left end point).
	fPoly[fNp-1].B() = 2.*fPoly[fNp-1].D();
	fPoly[fNp-1].D() = 1.;
	g = -1./fPoly[fNp-2].D();
      }
    } else if (fEndCond == 1) {
      fPoly[fNp-1].B() = fValEnd;
      goto L30;
    } else if (fEndCond == 2) {
//     second derivative prescribed at right endpoint.
      fPoly[fNp-1].B() = 3.*fPoly[fNp-1].D() + fPoly[fNp-1].C()/2.*fValEnd;
      fPoly[fNp-1].D() = 2.;
      g = -1./fPoly[fNp-2].D();
    }
   } else {
     if(fEndCond == 0) {
       if (fBegCond > 0) {
//     either (n=3 and not-a-knot also at left) or (n=2 and not not-a-
//     knot at left end point).
	 fPoly[fNp-1].B() = 2.*fPoly[fNp-1].D();
	 fPoly[fNp-1].D() = 1.;
	 g = -1./fPoly[fNp-2].D();
       } else {
//     not-a-knot at right endpoint and at left endpoint and n = 2.
	 fPoly[fNp-1].B() = fPoly[fNp-1].D();
	 goto L30;
       }
     } else if(fEndCond == 1) {
       fPoly[fNp-1].B() = fValEnd;
       goto L30;
     } else if(fEndCond == 2) {
//     second derivative prescribed at right endpoint.
       fPoly[fNp-1].B() = 3.*fPoly[fNp-1].D() + fPoly[fNp-1].C()/2.*fValEnd;
       fPoly[fNp-1].D() = 2.;
       g = -1./fPoly[fNp-2].D();
     }
   }
// complete forward pass of gauss elimination.
  fPoly[fNp-1].D() = g*fPoly[fNp-2].C() + fPoly[fNp-1].D();
  fPoly[fNp-1].B() = (g*fPoly[fNp-2].B() + fPoly[fNp-1].B())/fPoly[fNp-1].D();
// carry out back substitution
 L30: j = l-1;
  do {
    fPoly[j].B() = (fPoly[j].B() - fPoly[j].C()*fPoly[j+1].B())/fPoly[j].D();
    --j;
  }  while (j>=0);
//****** generate cubic coefficients in each interval, i.e., the deriv.s
//  at its left endpoint, from value and slope at its endpoints.
  for (i=1; i<fNp; ++i) {
    dtau = fPoly[i].C();
    divdf1 = (fPoly[i].Y() - fPoly[i-1].Y())/dtau;
    divdf3 = fPoly[i-1].B() + fPoly[i].B() - 2.*divdf1;
    fPoly[i-1].C() = (divdf1 - fPoly[i-1].B() - divdf3)/dtau;
    fPoly[i-1].D() = (divdf3/dtau)/dtau;
  }
}

//______________________________________________________________________________
void TSpline3::Streamer(TBuffer &R__b)
{
   // Stream an object of class TSpline3.

   if (R__b.IsReading()) {
      UInt_t R__s, R__c;
      Version_t R__v = R__b.ReadVersion(&R__s, &R__c);
      if (R__v > 1) {
         TSpline3::Class()->ReadBuffer(R__b, this, R__v, R__s, R__c);
         return;
      } 
      //====process old versions before automatic schema evolution
      TSpline::Streamer(R__b);
      if (fNp > 0) {
         fPoly = new TSplinePoly3[fNp];
         for(Int_t i=0; i<fNp; ++i) {
	   fPoly[i].Streamer(R__b);
         }
      }
      //      R__b >> fPoly;
      R__b >> fValBeg;
      R__b >> fValEnd;
      R__b >> fBegCond;
      R__b >> fEndCond;
   } else {
      TSpline3::Class()->WriteBuffer(R__b,this);
   }
}


//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TSpline5                                                             //
//                                                                      //
// Class to create quintic natural splines to interpolate knots         //
// Arbitrary conditions can be introduced for first and second          //
// derivatives using double knots (see BuildCoeff) for more on this.    //
// Double knots are automatically introduced at ending points           //
//                                                                      //
//////////////////////////////////////////////////////////////////////////


//____________________________________________________________________________
TSpline5::TSpline5(const char *title,
		   Double_t x[], Double_t y[], Int_t n,
		   const char *opt, Double_t b1, Double_t e1,
		   Double_t b2, Double_t e2) :
  TSpline(title,-1, x[0], x[n-1], n, kFALSE)
{
  //
  // Quintic natural spline creator given an array of
  // arbitrary knots in increasing abscissa order and
  // possibly end point conditions
  //
  Int_t beg, end;
  const char *cb1, *ce1, *cb2, *ce2;
  fName="Spline5";
  //
  // Check endpoint conditions
  BoundaryConditions(opt,beg,end,cb1,ce1,cb2,ce2);
  //
  // Create the plynomial terms and fill
  // them with node information
  fPoly = new TSplinePoly5[fNp];
  for (Int_t i=0; i<n; ++i) {
    fPoly[i+beg].X() = x[i];
    fPoly[i+beg].Y() = y[i];
  }
  //
  // Set the double knots at boundaries
  SetBoundaries(b1,e1,b2,e2,cb1,ce1,cb2,ce2);
  //
  // Build the spline coefficients
  BuildCoeff();
}

//____________________________________________________________________________
TSpline5::TSpline5(const char *title,
		   Double_t xmin, Double_t xmax,
		   Double_t y[], Int_t n,
		   const char *opt, Double_t b1, Double_t e1,
		   Double_t b2, Double_t e2) :
  TSpline(title,(xmax-xmin)/(n-1), xmin, xmax, n, kTRUE)
{
  //
  // Quintic natural spline creator given an array of
  // arbitrary function values on equidistant n abscissa
  // values from xmin to xmax and possibly end point conditions
  //
  Int_t beg, end;
  const char *cb1, *ce1, *cb2, *ce2;
  fName="Spline5";
  //
  // Check endpoint conditions
  BoundaryConditions(opt,beg,end,cb1,ce1,cb2,ce2);
  //
  // Create the plynomial terms and fill
  // them with node information
  fPoly = new TSplinePoly5[fNp];
  for (Int_t i=0; i<n; ++i) {
    fPoly[i+beg].X() = fXmin+i*fDelta;
    fPoly[i+beg].Y() = y[i];
  }
  //
  // Set the double knots at boundaries
  SetBoundaries(b1,e1,b2,e2,cb1,ce1,cb2,ce2);
  //
  // Build the spline coefficients
  BuildCoeff();
}

//____________________________________________________________________________
TSpline5::TSpline5(const char *title,
		   Double_t x[], const TF1 *func, Int_t n,
		   const char *opt, Double_t b1, Double_t e1,
		   Double_t b2, Double_t e2) :
  TSpline(title,-1, x[0], x[n-1], n, kFALSE)
{
  //
  // Quintic natural spline creator given an array of
  // arbitrary abscissas in increasing order and a function
  // to interpolate and possibly end point conditions
  //
  Int_t beg, end;
  const char *cb1, *ce1, *cb2, *ce2;
  fName="Spline5";
  //
  // Check endpoint conditions
  BoundaryConditions(opt,beg,end,cb1,ce1,cb2,ce2);
  //
  // Create the plynomial terms and fill
  // them with node information
  fPoly = new TSplinePoly5[fNp];
  for (Int_t i=0; i<n; i++) {
    fPoly[i+beg].X() = x[i];
    fPoly[i+beg].Y() = ((TF1*)func)->Eval(x[i]);
  }
  //
  // Set the double knots at boundaries
  SetBoundaries(b1,e1,b2,e2,cb1,ce1,cb2,ce2);
  //
  // Build the spline coefficients
  BuildCoeff();
}

//____________________________________________________________________________
TSpline5::TSpline5(const char *title,
		   Double_t xmin, Double_t xmax,
		   const TF1 *func, Int_t n,
		   const char *opt, Double_t b1, Double_t e1,
		   Double_t b2, Double_t e2) :
  TSpline(title,(xmax-xmin)/(n-1), xmin, xmax, n, kTRUE)
{
  //
  // Quintic natural spline creator given a function to be
  // evaluated on n equidistand abscissa points between xmin
  // and xmax and possibly end point conditions
  //
  Int_t beg, end;
  const char *cb1, *ce1, *cb2, *ce2;
  fName="Spline5";
  //
  // Check endpoint conditions
  BoundaryConditions(opt,beg,end,cb1,ce1,cb2,ce2);
  //
  // Create the plynomial terms and fill
  // them with node information
  fPoly = new TSplinePoly5[fNp];
  for (Int_t i=0; i<n; ++i) {
    Double_t x=fXmin+i*fDelta;
    fPoly[i+beg].X() = x;
    fPoly[i+beg].Y() = ((TF1*)func)->Eval(x);
  }
  //
  // Set the double knots at boundaries
  SetBoundaries(b1,e1,b2,e2,cb1,ce1,cb2,ce2);
  //
  // Build the spline coefficients
  BuildCoeff();
}

//____________________________________________________________________________
TSpline5::TSpline5(const char *title,
		   const TGraph *g,
		   const char *opt, Double_t b1, Double_t e1,
		   Double_t b2, Double_t e2) :
  TSpline(title,-1,0,0,g->GetN(),kFALSE)
{
  //
  // Quintic natural spline creator given a TGraph with
  // abscissa in increasing order and possibly end
  // point conditions
  //
  Int_t beg, end;
  const char *cb1, *ce1, *cb2, *ce2;
  fName="Spline5";
  //
  // Check endpoint conditions
  BoundaryConditions(opt,beg,end,cb1,ce1,cb2,ce2);
  //
  // Create the plynomial terms and fill
  // them with node information
  fPoly = new TSplinePoly5[fNp];
  for (Int_t i=0; i<fNp-beg; ++i) {
    Double_t xx, yy;
    g->GetPoint(i,xx,yy);
    fPoly[i+beg].X()=xx;
    fPoly[i+beg].Y()=yy;
  }
  //
  // Set the double knots at boundaries
  SetBoundaries(b1,e1,b2,e2,cb1,ce1,cb2,ce2);
  fXmin = fPoly[0].X();
  fXmax = fPoly[fNp-1].X();
  //
  // Build the spline coefficients
  BuildCoeff();
}

//____________________________________________________________________________
void TSpline5::BoundaryConditions(const char *opt,Int_t &beg,Int_t &end,
				  const char *&cb1,const char *&ce1,
                                  const char *&cb2,const char *&ce2)
{
  //
  // Check the boundary conditions and the
  // amount of extra double knots needed
  //
  cb1=ce1=cb2=ce2=0;
  beg=end=0;
  if(opt) {
    cb1 = strstr(opt,"b1");
    ce1 = strstr(opt,"e1");
    cb2 = strstr(opt,"b2");
    ce2 = strstr(opt,"e2");
    if(cb2) {
      fNp=fNp+2;
      beg=2;
    } else if(cb1) {
      fNp=fNp+1;
      beg=1;
    }
    if(ce2) {
      fNp=fNp+2;
      end=2;
    } else if(ce1) {
      fNp=fNp+1;
      end=1;
    }
  }
}

//____________________________________________________________________________
void TSpline5::SetBoundaries(Double_t b1, Double_t e1, Double_t b2, Double_t e2,
			     const char *cb1, const char *ce1, const char *cb2,
                             const char *ce2)
{
  //
  // Set the boundary conditions at double/triple knots
  //
  if(cb2) {
    //
    // Second derivative at the beginning
    fPoly[0].X() = fPoly[1].X() = fPoly[2].X();
    fPoly[0].Y() = fPoly[2].Y();
    fPoly[2].Y()=b2;
    //
    // If first derivative not given, we take the finite
    // difference from first and second point... not recommended
    if(cb1)
      fPoly[1].Y()=b1;
    else
      fPoly[1].Y()=(fPoly[3].Y()-fPoly[0].Y())/(fPoly[3].X()-fPoly[2].X());
  } else if(cb1) {
    //
    // First derivative at the end
    fPoly[0].X() = fPoly[1].X();
    fPoly[0].Y() = fPoly[1].Y();
    fPoly[1].Y()=b1;
  }
  if(ce2) {
    //
    // Second derivative at the end
    fPoly[fNp-1].X() = fPoly[fNp-2].X() = fPoly[fNp-3].X();
    fPoly[fNp-1].Y()=e2;
    //
    // If first derivative not given, we take the finite
    // difference from first and second point... not recommended
    if(ce1)
      fPoly[fNp-2].Y()=e1;
    else
      fPoly[fNp-2].Y()=
	(fPoly[fNp-3].Y()-fPoly[fNp-4].Y())
	/(fPoly[fNp-3].X()-fPoly[fNp-4].X());
  } else if(ce1) {
    //
    // First derivative at the end
    fPoly[fNp-1].X() = fPoly[fNp-2].X();
    fPoly[fNp-1].Y()=e1;
  }
}

//____________________________________________________________________________
Double_t TSpline5::Eval(Double_t x) const
{
  //
  // Evaluate spline polynomial
  //
  Int_t klow=0;
  //
  // If out of boundaries, extrapolate
  // It may be badly wrong
  if(x<=fXmin) klow=0;
  else if(x>=fXmax) klow=fNp-1;
  else {
    if(fKstep) {
      //
      // Equidistant knots, use histogramming
      klow = TMath::Min(Int_t((x-fXmin)/fDelta),fNp-1);
    } else {
      Int_t khig=fNp-1, khalf;
      //
      // Non equidistant knots, binary search
      while(khig-klow>1)
	if(x>fPoly[khalf=(klow+khig)/2].X())
	  klow=khalf;
	else
	  khig=khalf;
    }
    //
    // This could be removed, sanity check
    if(!(fPoly[klow].X()<=x && x<=fPoly[klow+1].X()))
      Error("Eval",
	    "Binary search failed x(%d) = %f < %f < x(%d) = %f\n",
	      klow,fPoly[klow].X(),x,fPoly[klow+1].X());
  }
  //
  // Evaluate now
  return fPoly[klow].Eval(x);
}

//____________________________________________________________________________
void TSpline5::SaveAs(const char *filename) const
{
  // write this spline as a C++ function that can be executed without ROOT
  // the name of the function is the name of the file up to the "." if any
    
   //open the file
   ofstream *f = new ofstream(filename,ios::out);
   if (f == 0 || gSystem->AccessPathName(filename,kWritePermission)) {
      Error("SaveAs","Cannot open file:%s\n",filename);
      return;
   }
   
   //write the function name and the spline constants
   char buffer[512];
   Int_t nch = strlen(filename);
   sprintf(buffer,"double %s",filename);
   char *dot = strstr(buffer,".");
   if (dot) *dot = 0;
   strcat(buffer,"(double x) {\n");
   nch = strlen(buffer); f->write(buffer,nch);
   sprintf(buffer,"   const int fNp = %d, fKstep = %d;\n",fNp,fKstep);
   nch = strlen(buffer); f->write(buffer,nch);
   sprintf(buffer,"   const double fDelta = %g, fXmin = %g, fXmax = %g;\n",fDelta,fXmin,fXmax);
   nch = strlen(buffer); f->write(buffer,nch);

   //write the spline coefficients
   //array fX
   sprintf(buffer,"   const double fX[%d] = {",fNp);   
   nch = strlen(buffer); f->write(buffer,nch);
   buffer[0] = 0;
   Int_t i;
   char numb[20];
   for (i=0;i<fNp;i++) {
      sprintf(numb," %g,",fPoly[i].X());
      nch = strlen(numb);
      if (i == fNp-1) numb[nch-1]=0;
      strcat(buffer,numb);
      if (i%5 == 4 || i == fNp-1) {
         nch = strlen(buffer); f->write(buffer,nch);
         if (i != fNp-1) sprintf(buffer,"\n                       ");
      }
   }
   sprintf(buffer," };\n");
   nch = strlen(buffer); f->write(buffer,nch);
   //array fY
   sprintf(buffer,"   const double fY[%d] = {",fNp);   
   nch = strlen(buffer); f->write(buffer,nch);
   buffer[0] = 0;
   for (i=0;i<fNp;i++) {
      sprintf(numb," %g,",fPoly[i].Y());
      nch = strlen(numb);
      if (i == fNp-1) numb[nch-1]=0;
      strcat(buffer,numb);
      if (i%5 == 4 || i == fNp-1) {
         nch = strlen(buffer); f->write(buffer,nch);
         if (i != fNp-1) sprintf(buffer,"\n                       ");
      }
   }
   sprintf(buffer," };\n");
   nch = strlen(buffer); f->write(buffer,nch);
   //array fB
   sprintf(buffer,"   const double fB[%d] = {",fNp);   
   nch = strlen(buffer); f->write(buffer,nch);
   buffer[0] = 0;
   for (i=0;i<fNp;i++) {
      sprintf(numb," %g,",fPoly[i].B());
      nch = strlen(numb);
      if (i == fNp-1) numb[nch-1]=0;
      strcat(buffer,numb);
      if (i%5 == 4 || i == fNp-1) {
         nch = strlen(buffer); f->write(buffer,nch);
         if (i != fNp-1) sprintf(buffer,"\n                       ");
      }
   }
   sprintf(buffer," };\n");
   nch = strlen(buffer); f->write(buffer,nch);
   //array fC
   sprintf(buffer,"   const double fC[%d] = {",fNp);   
   nch = strlen(buffer); f->write(buffer,nch);
   buffer[0] = 0;
   for (i=0;i<fNp;i++) {
      sprintf(numb," %g,",fPoly[i].C());
      nch = strlen(numb);
      if (i == fNp-1) numb[nch-1]=0;
      strcat(buffer,numb);
      if (i%5 == 4 || i == fNp-1) {
         nch = strlen(buffer); f->write(buffer,nch);
         if (i != fNp-1) sprintf(buffer,"\n                       ");
      }
   }
   sprintf(buffer," };\n");
   nch = strlen(buffer); f->write(buffer,nch);
    //array fD
   sprintf(buffer,"   const double fD[%d] = {",fNp);   
   nch = strlen(buffer); f->write(buffer,nch);
   buffer[0] = 0;
   for (i=0;i<fNp;i++) {
      sprintf(numb," %g,",fPoly[i].D());
      nch = strlen(numb);
      if (i == fNp-1) numb[nch-1]=0;
      strcat(buffer,numb);
      if (i%5 == 4 || i == fNp-1) {
         nch = strlen(buffer); f->write(buffer,nch);
         if (i != fNp-1) sprintf(buffer,"\n                       ");
      }
   }
   sprintf(buffer," };\n");
   nch = strlen(buffer); f->write(buffer,nch);
    //array fE
   sprintf(buffer,"   const double fE[%d] = {",fNp);   
   nch = strlen(buffer); f->write(buffer,nch);
   buffer[0] = 0;
   for (i=0;i<fNp;i++) {
      sprintf(numb," %g,",fPoly[i].E());
      nch = strlen(numb);
      if (i == fNp-1) numb[nch-1]=0;
      strcat(buffer,numb);
      if (i%5 == 4 || i == fNp-1) {
         nch = strlen(buffer); f->write(buffer,nch);
         if (i != fNp-1) sprintf(buffer,"\n                       ");
      }
   }
   sprintf(buffer," };\n");
   nch = strlen(buffer); f->write(buffer,nch);
    //array fF
   sprintf(buffer,"   const double fF[%d] = {",fNp);   
   nch = strlen(buffer); f->write(buffer,nch);
   buffer[0] = 0;
   for (i=0;i<fNp;i++) {
      sprintf(numb," %g,",fPoly[i].F());
      nch = strlen(numb);
      if (i == fNp-1) numb[nch-1]=0;
      strcat(buffer,numb);
      if (i%5 == 4 || i == fNp-1) {
         nch = strlen(buffer); f->write(buffer,nch);
         if (i != fNp-1) sprintf(buffer,"\n                       ");
      }
   }
   sprintf(buffer," };\n");
   nch = strlen(buffer); f->write(buffer,nch);
    
   //generate code for the spline evaluation
   sprintf(buffer,"   int klow=0;\n");
   nch = strlen(buffer); f->write(buffer,nch);

   sprintf(buffer,"   // If out of boundaries, extrapolate. It may be badly wrong\n");
   sprintf(buffer,"   if(x<=fXmin) klow=0;\n");
   nch = strlen(buffer); f->write(buffer,nch);
   sprintf(buffer,"   else if(x>=fXmax) klow=fNp-1;\n");
   nch = strlen(buffer); f->write(buffer,nch);
   sprintf(buffer,"   else {\n");
   nch = strlen(buffer); f->write(buffer,nch);
   sprintf(buffer,"     if(fKstep) {\n");
   nch = strlen(buffer); f->write(buffer,nch);

   sprintf(buffer,"       // Equidistant knots, use histogramming\n");
   nch = strlen(buffer); f->write(buffer,nch);
   sprintf(buffer,"       klow = int((x-fXmin)/fDelta);\n");
   nch = strlen(buffer); f->write(buffer,nch);
   sprintf(buffer,"       if (klow < fNp-1) klow = fNp-1;\n");
   nch = strlen(buffer); f->write(buffer,nch);
   sprintf(buffer,"     } else {\n");
   nch = strlen(buffer); f->write(buffer,nch);
   sprintf(buffer,"       int khig=fNp-1, khalf;\n");
   nch = strlen(buffer); f->write(buffer,nch);

   sprintf(buffer,"       // Non equidistant knots, binary search\n");
   nch = strlen(buffer); f->write(buffer,nch);
   sprintf(buffer,"       while(khig-klow>1)\n");
   nch = strlen(buffer); f->write(buffer,nch);
   sprintf(buffer,"	 if(x>fX[khalf=(klow+khig)/2]) klow=khalf;\n");
   nch = strlen(buffer); f->write(buffer,nch);
   sprintf(buffer,"	 else khig=khalf;\n");
   nch = strlen(buffer); f->write(buffer,nch);
   sprintf(buffer,"     }\n");
   nch = strlen(buffer); f->write(buffer,nch);
   sprintf(buffer,"   }\n");
   nch = strlen(buffer); f->write(buffer,nch);
   sprintf(buffer,"   // Evaluate now\n");
   nch = strlen(buffer); f->write(buffer,nch);
   sprintf(buffer,"   double dx=x-fX[klow];\n");
   nch = strlen(buffer); f->write(buffer,nch);
   sprintf(buffer,"   return (fY[klow]+dx*(fB[klow]+dx*(fC[klow]+dx*(fD[klow]+dx*(fE[klow]+dx*fF[klow])))));\n");
   nch = strlen(buffer); f->write(buffer,nch);
    
   //close file
   f->write("}\n",2);
      
   if (f) { f->close(); delete f;}
}

//____________________________________________________________________________
void TSpline5::BuildCoeff()
{
  //
  //     algorithm 600, collected algorithms from acm.
  //     algorithm appeared in acm-trans. math. software, vol.9, no. 2,
  //     jun., 1983, p. 258-259.
  //
  //     TSpline5 computes the coefficients of a quintic natural quintic spli
  //     s(x) with knots x(i) interpolating there to given function values:
  //               s(x(i)) = y(i)  for i = 1,2, ..., n.
  //     in each interval (x(i),x(i+1)) the spline function s(xx) is a
  //     polynomial of fifth degree:
  //     s(xx) = ((((f(i)*p+e(i))*p+d(i))*p+c(i))*p+b(i))*p+y(i)    (*)
  //           = ((((-f(i)*q+e(i+1))*q-d(i+1))*q+c(i+1))*q-b(i+1))*q+y(i+1)
  //     where  p = xx - x(i)  and  q = x(i+1) - xx.
  //     (note the first subscript in the second expression.)
  //     the different polynomials are pieced together so that s(x) and
  //     its derivatives up to s"" are continuous.
  //
  //        input:
  //
  //     n          number of data points, (at least three, i.e. n > 2)
  //     x(1:n)     the strictly increasing or decreasing sequence of
  //                knots.  the spacing must be such that the fifth power
  //                of x(i+1) - x(i) can be formed without overflow or
  //                underflow of exponents.
  //     y(1:n)     the prescribed function values at the knots.
  //
  //        output:
  //
  //     b,c,d,e,f  the computed spline coefficients as in (*).
  //         (1:n)  specifically
  //                b(i) = s'(x(i)), c(i) = s"(x(i))/2, d(i) = s"'(x(i))/6,
  //                e(i) = s""(x(i))/24,  f(i) = s""'(x(i))/120.
  //                f(n) is neither used nor altered.  the five arrays
  //                b,c,d,e,f must always be distinct.
  //
  //        option:
  //
  //     it is possible to specify values for the first and second
  //     derivatives of the spline function at arbitrarily many knots.
  //     this is done by relaxing the requirement that the sequence of
  //     knots be strictly increasing or decreasing.  specifically:
  //
  //     if x(j) = x(j+1) then s(x(j)) = y(j) and s'(x(j)) = y(j+1),
  //     if x(j) = x(j+1) = x(j+2) then in addition s"(x(j)) = y(j+2).
  //
  //     note that s""(x) is discontinuous at a double knot and, in
  //     addition, s"'(x) is discontinuous at a triple knot.  the
  //     subroutine assigns y(i) to y(i+1) in these cases and also to
  //     y(i+2) at a triple knot.  the representation (*) remains
  //     valid in each open interval (x(i),x(i+1)).  at a double knot,
  //     x(j) = x(j+1), the output coefficients have the following values:
  //       y(j) = s(x(j))          = y(j+1)
  //       b(j) = s'(x(j))         = b(j+1)
  //       c(j) = s"(x(j))/2       = c(j+1)
  //       d(j) = s"'(x(j))/6      = d(j+1)
  //       e(j) = s""(x(j)-0)/24     e(j+1) = s""(x(j)+0)/24
  //       f(j) = s""'(x(j)-0)/120   f(j+1) = s""'(x(j)+0)/120
  //     at a triple knot, x(j) = x(j+1) = x(j+2), the output
  //     coefficients have the following values:
  //       y(j) = s(x(j))         = y(j+1)    = y(j+2)
  //       b(j) = s'(x(j))        = b(j+1)    = b(j+2)
  //       c(j) = s"(x(j))/2      = c(j+1)    = c(j+2)
  //       d(j) = s"'((x(j)-0)/6    d(j+1) = 0  d(j+2) = s"'(x(j)+0)/6
  //       e(j) = s""(x(j)-0)/24    e(j+1) = 0  e(j+2) = s""(x(j)+0)/24
  //       f(j) = s""'(x(j)-0)/120  f(j+1) = 0  f(j+2) = s""'(x(j)+0)/120
  //
  Int_t i, m;
  Double_t pqqr, p, q, r, s, t, u, v,
    b1, p2, p3, q2, q3, r2, pq, pr, qr;

  if (fNp <= 2) {
    return;
  }
  //
  //     coefficients of a positive definite, pentadiagonal matrix,
  //     stored in D, E, F from 1 to n-3.
  //
  m = fNp-2;
  q = fPoly[1].X()-fPoly[0].X();
  r = fPoly[2].X()-fPoly[1].X();
  q2 = q*q;
  r2 = r*r;
  qr = q+r;
  fPoly[0].D() = fPoly[0].E() = 0;
  if (q) fPoly[1].D() = q*6.*q2/(qr*qr);
  else fPoly[1].D() = 0;

  if (m > 1) {
    for (i = 1; i < m; ++i) {
      p = q;
      q = r;
      r = fPoly[i+2].X()-fPoly[i+1].X();
      p2 = q2;
      q2 = r2;
      r2 = r*r;
      pq = qr;
      qr = q+r;
      if (q) {
	q3 = q2*q;
	pr = p*r;
	pqqr = pq*qr;
	fPoly[i+1].D() = q3*6./(qr*qr);
	fPoly[i].D() += (q+q)*(pr*15.*pr+(p+r)*q
			       *(pr* 20.+q2*7.)+q2*
			       ((p2+r2)*8.+pr*21.+q2+q2))/(pqqr*pqqr);
	fPoly[i-1].D() += q3*6./(pq*pq);
	fPoly[i].E() = q2*(p*qr+pq*3.*(qr+r+r))/(pqqr*qr);
	fPoly[i-1].E() += q2*(r*pq+qr*3.*(pq+p+p))/(pqqr*pq);
	fPoly[i-1].F() = q3/pqqr;
      } else
	fPoly[i+1].D() = fPoly[i].E() = fPoly[i-1].F() = 0;
    }
  }
  if (r) fPoly[m-1].D() += r*6.*r2/(qr*qr);
  //
  //     First and second order divided differences of the given function
  //     values, stored in b from 2 to n and in c from 3 to n
  //     respectively. care is taken of double and triple knots.
  //
  for (i = 1; i < fNp; ++i) {
    if (fPoly[i].X() != fPoly[i-1].X()) {
      fPoly[i].B() =
	(fPoly[i].Y()-fPoly[i-1].Y())/(fPoly[i].X()-fPoly[i-1].X());
    } else {
      fPoly[i].B() = fPoly[i].Y();
      fPoly[i].Y() = fPoly[i-1].Y();
    }
  }
  for (i = 2; i < fNp; ++i) {
    if (fPoly[i].X() != fPoly[i-2].X()) {
      fPoly[i].C() =
	(fPoly[i].B()-fPoly[i-1].B())/(fPoly[i].X()-fPoly[i-2].X());
    } else {
      fPoly[i].C() = fPoly[i].B()*.5;
      fPoly[i].B() = fPoly[i-1].B();
    }
  }
  //
  //     Solve the linear system with c(i+2) - c(i+1) as right-hand side. */
  //
  if (m > 1) {
    p=fPoly[0].C()=fPoly[m-1].E()=fPoly[0].F()
      =fPoly[m-2].F()=fPoly[m-1].F()=0;
    fPoly[1].C() = fPoly[3].C()-fPoly[2].C();
    fPoly[1].D() = 1./fPoly[1].D();

    if (m > 2) {
      for (i = 2; i < m; ++i) {
	q = fPoly[i-1].D()*fPoly[i-1].E();
	fPoly[i].D() = 1./(fPoly[i].D()-p*fPoly[i-2].F()-q*fPoly[i-1].E());
	fPoly[i].E() -= q*fPoly[i-1].F();
	fPoly[i].C() = fPoly[i+2].C()-fPoly[i+1].C()-p*fPoly[i-2].C()
	  -q*fPoly[i-1].C();
	p = fPoly[i-1].D()*fPoly[i-1].F();
      }
    }
  }

  fPoly[fNp-2].C() = fPoly[fNp-1].C() = 0;
  if (fNp > 3)
    for (i=fNp-3; i > 0; --i)
      fPoly[i].C() = (fPoly[i].C()-fPoly[i].E()*fPoly[i+1].C()
		      -fPoly[i].F()*fPoly[i+2].C())*fPoly[i].D();
  //
  //     Integrate the third derivative of s(x)
  //
  m = fNp-1;
  q = fPoly[1].X()-fPoly[0].X();
  r = fPoly[2].X()-fPoly[1].X();
  b1 = fPoly[1].B();
  q3 = q*q*q;
  qr = q+r;
  if (qr) {
    v = fPoly[1].C()/qr;
    t = v;
  } else
    v = t = 0;
  if (q) fPoly[0].F() = v/q;
  else fPoly[0].F() = 0;
  for (i = 1; i < m; ++i) {
    p = q;
    q = r;
    if (i != m-1) r = fPoly[i+2].X()-fPoly[i+1].X();
    else r = 0;
    p3 = q3;
    q3 = q*q*q;
    pq = qr;
    qr = q+r;
    s = t;
    if (qr) t = (fPoly[i+1].C()-fPoly[i].C())/qr;
    else t = 0;
    u = v;
    v = t-s;
    if (pq) {
      fPoly[i].F() = fPoly[i-1].F();
      if (q) fPoly[i].F() = v/q;
      fPoly[i].E() = s*5.;
      fPoly[i].D() = (fPoly[i].C()-q*s)*10;
      fPoly[i].C() =
	fPoly[i].D()*(p-q)+(fPoly[i+1].B()-fPoly[i].B()+(u-fPoly[i].E())*
			    p3-(v+fPoly[i].E())*q3)/pq;
      fPoly[i].B() = (p*(fPoly[i+1].B()-v*q3)+q*(fPoly[i].B()-u*p3))/pq-p
	*q*(fPoly[i].D()+fPoly[i].E()*(q-p));
    } else {
      fPoly[i].C() = fPoly[i-1].C();
      fPoly[i].D() = fPoly[i].E() = fPoly[i].F() = 0;
    }
  }
  //
  //     End points x(1) and x(n)
  //
  p = fPoly[1].X()-fPoly[0].X();
  s = fPoly[0].F()*p*p*p;
  fPoly[0].E() = fPoly[0].D() = 0;
  fPoly[0].C() = fPoly[1].C()-s*10;
  fPoly[0].B() = b1-(fPoly[0].C()+s)*p;

  q = fPoly[fNp-1].X()-fPoly[fNp-2].X();
  t = fPoly[fNp-2].F()*q*q*q;
  fPoly[fNp-1].E() = fPoly[fNp-1].D() = 0;
  fPoly[fNp-1].C() = fPoly[fNp-2].C()+t*10;
  fPoly[fNp-1].B() += (fPoly[fNp-1].C()-t)*q;
}

//___________________________________________________________________________
void TSpline5::Test()
{
  //
  // Test method for TSpline5
  //
  //
  //   n          number of data points.
  //   m          2*m-1 is order of spline.
  //                 m = 3 always for quintic spline.
  //   nn,nm1,mm,
  //   mm1,i,k,
  //   j,jj       temporary integer variables.
  //   z,p        temporary double precision variables.
  //   x[n]       the sequence of knots.
  //   y[n]       the prescribed function values at the knots.
  //   a[200][6]  two dimensional array whose columns are
  //                 the computed spline coefficients
  //   diff[5]    maximum values of differences of values and
  //                 derivatives to right and left of knots.
  //   com[5]     maximum values of coefficients.
  //
  //
  //   test of TSpline5 with nonequidistant knots and
  //      equidistant knots follows.
  //
  //
  Double_t hx;
  Double_t diff[5];
  Double_t a[1200], c[6];
  Int_t i, j, k, m, n;
  Double_t p, x[200], y[200], z;
  Int_t jj, mm, nn;
  Int_t mm1, nm1;
  Double_t com[5];

  printf("1         TEST OF TSpline5 WITH NONEQUIDISTANT KNOTS\n");
  n = 5;
  x[0] = -3;
  x[1] = -1;
  x[2] = 0;
  x[3] = 3;
  x[4] = 4;
  y[0] = 7;
  y[1] = 11;
  y[2] = 26;
  y[3] = 56;
  y[4] = 29;
  m = 3;
  mm = m << 1;
  mm1 = mm-1;
  printf("\n-N = %3d    M =%2d\n",n,m);
  TSpline5 *spline = new TSpline5("Test",x,y,n);
  for (i = 0; i < n; ++i)
    spline->GetCoeff(i,hx, a[i],a[i+200],a[i+400],
		     a[i+600],a[i+800],a[i+1000]);
  delete spline;
  for (i = 0; i < mm1; ++i) diff[i] = com[i] = 0;
  for (k = 0; k < n; ++k) {
    for (i = 0; i < mm; ++i) c[i] = a[k+i*200];
    printf(" ---------------------------------------%3d --------------------------------------------\n",k+1);
    printf("%12.8f\n",x[k]);
    if (k == n-1) {
      printf("%16.8f\n",c[0]);
    } else {
      for (i = 0; i < mm; ++i) printf("%16.8f",c[i]);
      printf("\n");
      for (i = 0; i < mm1; ++i)
	if ((z=TMath::Abs(a[k+i*200])) > com[i]) com[i] = z;
      z = x[k+1]-x[k];
      for (i = 1; i < mm; ++i)
	for (jj = i; jj < mm; ++jj) {
	  j = mm+i-jj;
	  c[j-2] = c[j-1]*z+c[j-2];
	}
      for (i = 0; i < mm; ++i) printf("%16.8f",c[i]);
      printf("\n");
      for (i = 0; i < mm1; ++i)
	if (!(k >= n-2 && i != 0))
	  if((z = TMath::Abs(c[i]-a[k+1+i*200]))
	     > diff[i]) diff[i] = z;
    }
  }
  printf("  MAXIMUM ABSOLUTE VALUES OF DIFFERENCES \n");
  for (i = 0; i < mm1; ++i) printf("%18.9E",diff[i]);
  printf("\n");
  printf("  MAXIMUM ABSOLUTE VALUES OF COEFFICIENTS \n");
  if (TMath::Abs(c[0]) > com[0])
    com[0] = TMath::Abs(c[0]);
  for (i = 0; i < mm1; ++i) printf("%16.8f",com[i]);
  printf("\n");
  m = 3;
  for (n = 10; n <= 100; n += 10) {
    mm = m << 1;
    mm1 = mm-1;
    nm1 = n-1;
    for (i = 0; i < nm1; i += 2) {
      x[i] = i+1;
      x[i+1] = i+2;
      y[i] = 1;
      y[i+1] = 0;
    }
    if (n % 2 != 0) {
      x[n-1] = n;
      y[n-1] = 1;
    }
    printf("\n-N = %3d    M =%2d\n",n,m);
    spline = new TSpline5("Test",x,y,n);
    for (i = 0; i < n; ++i)
      spline->GetCoeff(i,hx,a[i],a[i+200],a[i+400],
		       a[i+600],a[i+800],a[i+1000]);
    delete spline;
    for (i = 0; i < mm1; ++i)
      diff[i] = com[i] = 0;
    for (k = 0; k < n; ++k) {
      for (i = 0; i < mm; ++i)
	c[i] = a[k+i*200];
      if (n < 11) {
	printf(" ---------------------------------------%3d --------------------------------------------\n",k+1);
	printf("%12.8f\n",x[k]);
	if (k == n-1) printf("%16.8f\n",c[0]);
      }
      if (k == n-1) break;
      if (n <= 10) {
	for (i = 0; i < mm; ++i) printf("%16.8f",c[i]);
	printf("\n");
      }
      for (i = 0; i < mm1; ++i)
	if ((z=TMath::Abs(a[k+i*200])) > com[i])
	  com[i] = z;
      z = x[k+1]-x[k];
      for (i = 1; i < mm; ++i)
	for (jj = i; jj < mm; ++jj) {
	  j = mm+i-jj;
	  c[j-2] = c[j-1]*z+c[j-2];
	}
      if (n <= 10) {
	for (i = 0; i < mm; ++i) printf("%16.8f",c[i]);
	printf("\n");
      }
      for (i = 0; i < mm1; ++i)
	if (!(k >= n-2 && i != 0))
	  if ((z = TMath::Abs(c[i]-a[k+1+i*200]))
	      > diff[i]) diff[i] = z;
    }
    printf("  MAXIMUM ABSOLUTE VALUES OF DIFFERENCES \n");
    for (i = 0; i < mm1; ++i) printf("%18.9E",diff[i]);
    printf("\n");
    printf("  MAXIMUM ABSOLUTE VALUES OF COEFFICIENTS \n");
    if (TMath::Abs(c[0]) > com[0])
      com[0] = TMath::Abs(c[0]);
    for (i = 0; i < mm1; ++i) printf("%16.8E",com[i]);
    printf("\n");
  }
  //
  //     Test of TSpline5 with nonequidistant double knots follows
  //
  printf("1  TEST OF TSpline5 WITH NONEQUIDISTANT DOUBLE KNOTS\n");
  n = 5;
  x[0] = -3;
  x[1] = -3;
  x[2] = -1;
  x[3] = -1;
  x[4] = 0;
  x[5] = 0;
  x[6] = 3;
  x[7] = 3;
  x[8] = 4;
  x[9] = 4;
  y[0] = 7;
  y[1] = 2;
  y[2] = 11;
  y[3] = 15;
  y[4] = 26;
  y[5] = 10;
  y[6] = 56;
  y[7] = -27;
  y[8] = 29;
  y[9] = -30;
  m = 3;
  nn = n << 1;
  mm = m << 1;
  mm1 = mm-1;
  printf("-N = %3d    M =%2d\n",n,m);
  spline = new TSpline5("Test",x,y,nn);
  for (i = 0; i < nn; ++i)
    spline->GetCoeff(i,hx,a[i],a[i+200],a[i+400],
		     a[i+600],a[i+800],a[i+1000]);
  delete spline;
  for (i = 0; i < mm1; ++i)
    diff[i] = com[i] = 0;
  for (k = 0; k < nn; ++k) {
    for (i = 0; i < mm; ++i)
      c[i] = a[k+i*200];
    printf(" ---------------------------------------%3d --------------------------------------------\n",k+1);
    printf("%12.8f\n",x[k]);
    if (k == nn-1) {
      printf("%16.8f\n",c[0]);
      break;
    }
    for (i = 0; i < mm; ++i) printf("%16.8f",c[i]);
    printf("\n");
    for (i = 0; i < mm1; ++i)
      if ((z=TMath::Abs(a[k+i*200])) > com[i]) com[i] = z;
    z = x[k+1]-x[k];
    for (i = 1; i < mm; ++i)
      for (jj = i; jj < mm; ++jj) {
	j = mm+i-jj;
	c[j-2] = c[j-1]*z+c[j-2];
      }
    for (i = 0; i < mm; ++i) printf("%16.8f",c[i]);
    printf("\n");
    for (i = 0; i < mm1; ++i)
      if (!(k >= nn-2 && i != 0))
	if ((z = TMath::Abs(c[i]-a[k+1+i*200]))
	    > diff[i]) diff[i] = z;
  }
  printf("  MAXIMUM ABSOLUTE VALUES OF DIFFERENCES \n");
  for (i = 1; i <= mm1; ++i) {
    printf("%18.9E",diff[i-1]);
  }
  printf("\n");
  if (TMath::Abs(c[0]) > com[0])
    com[0] = TMath::Abs(c[0]);
  printf("  MAXIMUM ABSOLUTE VALUES OF COEFFICIENTS \n");
  for (i = 0; i < mm1; ++i) printf("%16.8f",com[i]);
  printf("\n");
  m = 3;
  for (n = 10; n <= 100; n += 10) {
    nn = n << 1;
    mm = m << 1;
    mm1 = mm-1;
    p = 0;
    for (i = 0; i < n; ++i) {
      p += TMath::Abs(TMath::Sin(i+1));
      x[(i << 1)] = p;
      x[(i << 1)+1] = p;
      y[(i << 1)] = TMath::Cos(i+1)-.5;
      y[(i << 1)+1] = TMath::Cos((i << 1)+2)-.5;
    }
    printf("-N = %3d    M =%2d\n",n,m);
    spline = new TSpline5("Test",x,y,nn);
    for (i = 0; i < nn; ++i)
      spline->GetCoeff(i,hx,a[i],a[i+200],a[i+400],
		       a[i+600],a[i+800],a[i+1000]);
    delete spline;
    for (i = 0; i < mm1; ++i)
      diff[i] = com[i] = 0;
    for (k = 0; k < nn; ++k) {
      for (i = 0; i < mm; ++i)
	c[i] = a[k+i*200];
      if (n < 11) {
	printf(" ---------------------------------------%3d --------------------------------------------\n",k+1);
	printf("%12.8f\n",x[k]);
	if (k == nn-1) printf("%16.8f\n",c[0]);
      }
      if (k == nn-1) break;
      if (n <= 10) {
	for (i = 0; i < mm; ++i) printf("%16.8f",c[i]);
	printf("\n");
      }
      for (i = 0; i < mm1; ++i)
	if ((z=TMath::Abs(a[k+i*200])) > com[i]) com[i] = z;
      z = x[k+1]-x[k];
      for (i = 1; i < mm; ++i) {
	for (jj = i; jj < mm; ++jj) {
	  j = mm+i-jj;
	  c[j-2] = c[j-1]*z+c[j-2];
	}
      }
      if (n <= 10) {
	for (i = 0; i < mm; ++i) printf("%16.8f",c[i]);
	printf("\n");
      }
      for (i = 0; i < mm1; ++i)
	if (!(k >= nn-2 && i != 0))
	  if ((z = TMath::Abs(c[i]-a[k+1+i*200]))
	      > diff[i]) diff[i] = z;
    }
    printf("  MAXIMUM ABSOLUTE VALUES OF DIFFERENCES \n");
    for (i = 0; i < mm1; ++i) printf("%18.9E",diff[i]);
    printf("\n");
    printf("  MAXIMUM ABSOLUTE VALUES OF COEFFICIENTS \n");
    if (TMath::Abs(c[0]) > com[0])
      com[0] = TMath::Abs(c[0]);
    for (i = 0; i < mm1; ++i) printf("%18.9E",com[i]);
    printf("\n");
  }
  //
  //     test of TSpline5 with nonequidistant knots, one double knot,
  //        one triple knot, follows.
  //
  printf("1         TEST OF TSpline5 WITH NONEQUIDISTANT KNOTS,\n");
  printf("             ONE DOUBLE, ONE TRIPLE KNOT\n");
  n = 8;
  x[0] = -3;
  x[1] = -1;
  x[2] = -1;
  x[3] = 0;
  x[4] = 3;
  x[5] = 3;
  x[6] = 3;
  x[7] = 4;
  y[0] = 7;
  y[1] = 11;
  y[2] = 15;
  y[3] = 26;
  y[4] = 56;
  y[5] = -30;
  y[6] = -7;
  y[7] = 29;
  m = 3;
  mm = m << 1;
  mm1 = mm-1;
  printf("-N = %3d    M =%2d\n",n,m);
  spline=new TSpline5("Test",x,y,n);
  for (i = 0; i < n; ++i)
    spline->GetCoeff(i,hx,a[i],a[i+200],a[i+400],
		     a[i+600],a[i+800],a[i+1000]);
  delete spline;
  for (i = 0; i < mm1; ++i)
    diff[i] = com[i] = 0;
  for (k = 0; k < n; ++k) {
    for (i = 0; i < mm; ++i)
      c[i] = a[k+i*200];
    printf(" ---------------------------------------%3d --------------------------------------------\n",k+1);
    printf("%12.8f\n",x[k]);
    if (k == n-1) {
      printf("%16.8f\n",c[0]);
      break;
    }
    for (i = 0; i < mm; ++i) printf("%16.8f",c[i]);
    printf("\n");
    for (i = 0; i < mm1; ++i)
      if ((z=TMath::Abs(a[k+i*200])) > com[i]) com[i] = z;
    z = x[k+1]-x[k];
    for (i = 1; i < mm; ++i)
      for (jj = i; jj < mm; ++jj) {
	j = mm+i-jj;
	c[j-2] = c[j-1]*z+c[j-2];
      }
    for (i = 0; i < mm; ++i) printf("%16.8f",c[i]);
    printf("\n");
    for (i = 0; i < mm1; ++i)
      if (!(k >= n-2 && i != 0))
	if ((z = TMath::Abs(c[i]-a[k+1+i*200]))
	    > diff[i]) diff[i] = z;
  }
  printf("  MAXIMUM ABSOLUTE VALUES OF DIFFERENCES \n");
  for (i = 0; i < mm1; ++i) printf("%18.9E",diff[i]);
  printf("\n");
  printf("  MAXIMUM ABSOLUTE VALUES OF COEFFICIENTS \n");
  if (TMath::Abs(c[0]) > com[0])
    com[0] = TMath::Abs(c[0]);
  for (i = 0; i < mm1; ++i) printf("%16.8f",com[i]);
  printf("\n");
  //
  //     Test of TSpline5 with nonequidistant knots, two double knots,
  //        one triple knot,follows.
  //
  printf("1         TEST OF TSpline5 WITH NONEQUIDISTANT KNOTS,\n");
  printf("             TWO DOUBLE, ONE TRIPLE KNOT\n");
  n = 10;
  x[0] = 0;
  x[1] = 2;
  x[2] = 2;
  x[3] = 3;
  x[4] = 3;
  x[5] = 3;
  x[6] = 5;
  x[7] = 8;
  x[8] = 9;
  x[9] = 9;
  y[0] = 163;
  y[1] = 237;
  y[2] = -127;
  y[3] = 119;
  y[4] = -65;
  y[5] = 192;
  y[6] = 293;
  y[7] = 326;
  y[8] = 0;
  y[9] = -414;
  m = 3;
  mm = m << 1;
  mm1 = mm-1;
  printf("-N = %3d    M =%2d\n",n,m);
  spline = new TSpline5("Test",x,y,n);
  for (i = 0; i < n; ++i)
    spline->GetCoeff(i,hx,a[i],a[i+200],a[i+400],
		     a[i+600],a[i+800],a[i+1000]);
  delete spline;
  for (i = 0; i < mm1; ++i)
    diff[i] = com[i] = 0;
  for (k = 0; k < n; ++k) {
    for (i = 0; i < mm; ++i)
      c[i] = a[k+i*200];
    printf(" ---------------------------------------%3d --------------------------------------------\n",k+1);
    printf("%12.8f\n",x[k]);
    if (k == n-1) {
      printf("%16.8f\n",c[0]);
      break;
    }
    for (i = 0; i < mm; ++i) printf("%16.8f",c[i]);
    printf("\n");
    for (i = 0; i < mm1; ++i)
      if ((z=TMath::Abs(a[k+i*200])) > com[i]) com[i] = z;
    z = x[k+1]-x[k];
    for (i = 1; i < mm; ++i)
      for (jj = i; jj < mm; ++jj) {
	j = mm+i-jj;
	c[j-2] = c[j-1]*z+c[j-2];
      }
    for (i = 0; i < mm; ++i) printf("%16.8f",c[i]);
    printf("\n");
    for (i = 0; i < mm1; ++i)
      if (!(k >= n-2 && i != 0))
	if((z = TMath::Abs(c[i]-a[k+1+i*200]))
	   > diff[i]) diff[i] = z;
  }
  printf("  MAXIMUM ABSOLUTE VALUES OF DIFFERENCES \n");
  for (i = 0; i < mm1; ++i) printf("%18.9E",diff[i]);
  printf("\n");
  printf("  MAXIMUM ABSOLUTE VALUES OF COEFFICIENTS \n");
  if (TMath::Abs(c[0]) > com[0])
    com[0] = TMath::Abs(c[0]);
  for (i = 0; i < mm1; ++i) printf("%16.8f",com[i]);
  printf("\n");
}

//______________________________________________________________________________
void TSpline5::Streamer(TBuffer &R__b)
{
   // Stream an object of class TSpline5.

   if (R__b.IsReading()) {
      UInt_t R__s, R__c;
      Version_t R__v = R__b.ReadVersion(&R__s, &R__c);
      if (R__v > 1) {
         TSpline5::Class()->ReadBuffer(R__b, this, R__v, R__s, R__c);
         return;
      } 
      //====process old versions before automatic schema evolution
      TSpline::Streamer(R__b);
      if (fNp > 0) {
         fPoly = new TSplinePoly5[fNp];
         for(Int_t i=0; i<fNp; ++i) {
	   fPoly[i].Streamer(R__b);
         }
      }
      //      R__b >> fPoly;
   } else {
      TSpline5::Class()->WriteBuffer(R__b,this);
   }
}


