//+------------------------------------------------------------------+
//|                                                interpolation.mqh |
//|            Copyright 2003-2012 Sergey Bochkanov (ALGLIB project) |
//|                   Copyright 2012-2017, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
//| Implementation of ALGLIB library in MetaQuotes Language 5        |
//|                                                                  |
//| The features of the library include:                             |
//| - Linear algebra (direct algorithms, EVD, SVD)                   |
//| - Solving systems of linear and non-linear equations             |
//| - Interpolation                                                  |
//| - Optimization                                                   |
//| - FFT (Fast Fourier Transform)                                   |
//| - Numerical integration                                          |
//| - Linear and nonlinear least-squares fitting                     |
//| - Ordinary differential equations                                |
//| - Computation of special functions                               |
//| - Descriptive statistics and hypothesis testing                  |
//| - Data analysis - classification, regression                     |
//| - Implementing linear algebra algorithms, interpolation, etc.    |
//|   in high-precision arithmetic (using MPFR)                      |
//|                                                                  |
//| This file is free software; you can redistribute it and/or       |
//| modify it under the terms of the GNU General Public License as   |
//| published by the Free Software Foundation (www.fsf.org); either  |
//| version 2 of the License, or (at your option) any later version. |
//|                                                                  |
//| This program is distributed in the hope that it will be useful,  |
//| but WITHOUT ANY WARRANTY; without even the implied warranty of   |
//| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the     |
//| GNU General Public License for more details.                     |
//+------------------------------------------------------------------+
#include "alglibmisc.mqh"
#include "optimization.mqh"
#include "solvers.mqh"
#include "integration.mqh"
//+------------------------------------------------------------------+
//| IDW interpolant.                                                 |
//+------------------------------------------------------------------+
class CIDWInterpolant
  {
public:
   int               m_n;
   int               m_nx;
   int               m_d;
   double            m_r;
   int               m_nw;
   CKDTree           m_tree;
   int               m_modeltype;
   int               m_debugsolverfailures;
   double            m_debugworstrcond;
   double            m_debugbestrcond;
   //--- arrays
   double            m_xbuf[];
   int               m_tbuf[];
   double            m_rbuf[];
   //--- matrices
   CMatrixDouble     m_q;
   CMatrixDouble     m_xybuf;
   
public:   
                     CIDWInterpolant(void);
                    ~CIDWInterpolant(void);

   void              Copy(CIDWInterpolant &obj);
  };
//+------------------------------------------------------------------+
//| Constructor without parameters                                   |
//+------------------------------------------------------------------+
CIDWInterpolant::CIDWInterpolant(void)
  {

  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CIDWInterpolant::~CIDWInterpolant(void)
  {

  }
//+------------------------------------------------------------------+
//| Copy                                                             |
//+------------------------------------------------------------------+
void CIDWInterpolant::Copy(CIDWInterpolant &obj)
  {
//--- copy variables
   m_n=obj.m_n;
   m_nx=obj.m_nx;
   m_d=obj.m_d;
   m_r=obj.m_r;
   m_nw=obj.m_nw;
   m_modeltype=obj.m_modeltype;
   m_debugsolverfailures=obj.m_debugsolverfailures;
   m_debugworstrcond=obj.m_debugworstrcond;
   m_debugbestrcond=obj.m_debugbestrcond;
   m_tree.Copy(obj.m_tree);
//--- copy arrays
   ArrayCopy(m_xbuf,obj.m_xbuf);
   ArrayCopy(m_tbuf,obj.m_tbuf);
   ArrayCopy(m_rbuf,obj.m_rbuf);
//--- copy matrices
   m_q=obj.m_q;
   m_xybuf=obj.m_xybuf;
  }
//+------------------------------------------------------------------+
//| IDW interpolant.                                                 |
//+------------------------------------------------------------------+
class CIDWInterpolantShell
  {
private:
   CIDWInterpolant   m_innerobj;
public:
   //--- constructors, destructor
                     CIDWInterpolantShell(void);
                     CIDWInterpolantShell(CIDWInterpolant &obj);
                    ~CIDWInterpolantShell(void);
   //--- method
   CIDWInterpolant *GetInnerObj(void);
  };
//+------------------------------------------------------------------+
//| Constructor without parameters                                   |
//+------------------------------------------------------------------+
CIDWInterpolantShell::CIDWInterpolantShell(void)
  {

  }
//+------------------------------------------------------------------+
//| Copy constructor                                                 |
//+------------------------------------------------------------------+
CIDWInterpolantShell::CIDWInterpolantShell(CIDWInterpolant &obj)
  {
//--- copy
   m_innerobj.Copy(obj);
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CIDWInterpolantShell::~CIDWInterpolantShell(void)
  {

  }
//+------------------------------------------------------------------+
//| Return object of class                                           |
//+------------------------------------------------------------------+
CIDWInterpolant *CIDWInterpolantShell::GetInnerObj(void)
  {
//--- return result
   return(GetPointer(m_innerobj));
  }
//+------------------------------------------------------------------+
//| Inverse distance weighting interpolation                         |
//+------------------------------------------------------------------+
class CIDWInt
  {
private:
   //--- private methods
   static double     IDWCalcQ(CIDWInterpolant &z,double &x[],const int k);
   static void       IDWInit1(const int n,const int nx,const int d,int nq,int nw,CIDWInterpolant &z);
   static void       IDWInternalSolver(double &y[],double &w[],CMatrixDouble &fmatrix,double &temp[],const int n,const int m,int &info,double &x[],double &taskrcond);
public:
   //--- class constants
   static const double m_idwqfactor;
   static const int  m_idwkmin;
   //--- constructor, destructor
                     CIDWInt(void);
                    ~CIDWInt(void);
   //--- public methods
   static double     IDWCalc(CIDWInterpolant &z,double &x[]);
   static void       IDWBuildModifiedShepard(CMatrixDouble &xy,const int n,const int nx,const int d,int nq,int nw,CIDWInterpolant &z);
   static void       IDWBuildModifiedShepardR(CMatrixDouble &xy,const int n,const int nx,const double r,CIDWInterpolant &z);
   static void       IDWBuildNoisy(CMatrixDouble &xy,const int n,const int nx,const int d,int nq,int nw,CIDWInterpolant &z);
  };
//+------------------------------------------------------------------+
//| Initialize constants                                             |
//+------------------------------------------------------------------+
const double CIDWInt::m_idwqfactor=1.5;
const int    CIDWInt::m_idwkmin=5;
//+------------------------------------------------------------------+
//| Constructor without parameters                                   |
//+------------------------------------------------------------------+
CIDWInt::CIDWInt(void)
  {

  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CIDWInt::~CIDWInt(void)
  {

  }
//+------------------------------------------------------------------+
//| IDW interpolation                                                |
//| INPUT PARAMETERS:                                                |
//|     Z   -   IDW interpolant built with one of model building     |
//|             subroutines.                                         |
//|     X   -   array[0..NX-1], interpolation point                  |
//| Result:                                                          |
//|     IDW interpolant Z(X)                                         |
//+------------------------------------------------------------------+
static double CIDWInt::IDWCalc(CIDWInterpolant &z,double &x[])
  {
//--- create variables
   double result=0;
   int    nx=0;
   int    i=0;
   int    k=0;
   double r=0;
   double s=0;
   double w=0;
   double v1=0;
   double v2=0;
   double d0=0;
   double di=0;
//--- these initializers are not really necessary,
//--- but without them compiler complains about uninitialized locals
   k=0;
//--- Query
   if(z.m_modeltype==0)
     {
      //--- NQ/NW-based model
      nx=z.m_nx;
      k=CNearestNeighbor::KDTreeQueryKNN(z.m_tree,x,z.m_nw,true);
      //--- function call
      CNearestNeighbor::KDTreeQueryResultsDistances(z.m_tree,z.m_rbuf);
      //--- function call
      CNearestNeighbor::KDTreeQueryResultsTags(z.m_tree,z.m_tbuf);
     }
   if(z.m_modeltype==1)
     {
      //--- R-based model
      nx=z.m_nx;
      k=CNearestNeighbor::KDTreeQueryRNN(z.m_tree,x,z.m_r,true);
      //--- function call
      CNearestNeighbor::KDTreeQueryResultsDistances(z.m_tree,z.m_rbuf);
      //--- function call
      CNearestNeighbor::KDTreeQueryResultsTags(z.m_tree,z.m_tbuf);
      if(k<m_idwkmin)
        {
         //--- we need at least IDWKMin points
         k=CNearestNeighbor::KDTreeQueryKNN(z.m_tree,x,m_idwkmin,true);
         //--- function call
         CNearestNeighbor::KDTreeQueryResultsDistances(z.m_tree,z.m_rbuf);
         //--- function call
         CNearestNeighbor::KDTreeQueryResultsTags(z.m_tree,z.m_tbuf);
        }
     }
//--- initialize weights for linear/quadratic members calculation.
//--- NOTE 1: weights are calculated using NORMALIZED modified
//--- Shepard's formula. Original formula gives w(i)=sqr((R-di)/(R*di)),
//--- where di is i-th distance,R is max(di). Modified formula have
//--- following form:
//---     w_mod(i)=1,if di=d0
//---     w_mod(i)=w(i)/w(0),if di<>d0
//--- NOTE 2: self-match is USED for this query
//--- NOTE 3: last point almost always gain zero weight,but it MUST
//--- be used for fitting because sometimes it will gain NON-ZERO
//--- weight - for example,when all distances are equal.
   r=z.m_rbuf[k-1];
   d0=z.m_rbuf[0];
   result=0;
   s=0;
   for(i=0;i<=k-1;i++)
     {
      di=z.m_rbuf[i];
      //--- check
      if(di==d0)
        {
         //--- distance is equal to shortest,set it 1.0
         //--- without explicitly calculating (which would give
         //--- us same result,but 'll expose us to the risk of
         //--- division by zero).
         w=1;
        }
      else
        {
         //--- use normalized formula
         v1=(r-di)/(r-d0);
         v2=d0/di;
         w=CMath::Sqr(v1*v2);
        }
      //--- change result
      result=result+w*IDWCalcQ(z,x,z.m_tbuf[i]);
      s=s+w;
     }
//--- return result
   return(result/s);
  }
//+------------------------------------------------------------------+
//| IDW interpolant using modified Shepard method for uniform point  |
//| distributions.                                                   |
//| INPUT PARAMETERS:                                                |
//|     XY  -   X and Y values, array[0..N-1,0..NX].                 |
//|             First NX columns contain X-values, last column       |
//|             contain Y-values.                                    |
//|     N   -   number of nodes, N>0.                                |
//|     NX  -   space dimension, NX>=1.                              |
//|     D   -   nodal function type, either:                         |
//|             * 0     constant  model. Just for demonstration only,|
//|                     worst model ever.                            |
//|             * 1     linear model, least squares fitting. Simpe   |
//|                     model for datasets too small for quadratic   |
//|                     models                                       |
//|             * 2     quadratic model, least squares fitting.      |
//|                     Best model available (if your dataset is     |
//|                     large enough).                               |
//|             * -1    "fast" linear model, use with caution!!! It  |
//|                     is significantly faster than linear/quadratic|
//|                     and better than constant model. But it is    |
//|                     less robust (especially in the presence of   |
//|                     noise).                                      |
//|     NQ  -   number of points used to calculate  nodal  functions |
//|             (ignored for constant models). NQ should be LARGER   |
//|             than:                                                |
//|             * max(1.5*(1+NX),2^NX+1) for linear model,           |
//|             * max(3/4*(NX+2)*(NX+1),2^NX+1) for quadratic model. |
//|             Values less than this threshold will be silently     |
//|             increased.                                           |
//|     NW  -   number of points used to calculate weights and to    |
//|             interpolate. Required: >=2^NX+1, values less than    |
//|             this  threshold will be silently increased.          |
//|             Recommended value: about 2*NQ                        |
//| OUTPUT PARAMETERS:                                               |
//|     Z   -   IDW interpolant.                                     |
//| NOTES:                                                           |
//|   * best results are obtained with quadratic models, worst - with|
//|     constant models                                              |
//|   * when N is large, NQ and NW must be significantly smaller than|
//|     N  both to obtain optimal performance and to obtain optimal  |
//|     accuracy. In 2 or 3-dimensional tasks NQ=15 and NW=25 are    |
//|     good values to start with.                                   |
//|   * NQ and NW may be greater than N. In such cases they will be  |
//|     automatically decreased.                                     |
//|   * this subroutine is always succeeds (as long as correct       |
//|     parameters are passed).                                      |
//|   * see 'Multivariate Interpolation of Large Sets of Scattered   |
//|     Data' by Robert J. Renka for more information on this        |
//|     algorithm.                                                   |
//|   * this subroutine assumes that point distribution is uniform at|
//|     the small scales. If it isn't  -  for example, points are    |
//|     concentrated along "lines", but "lines" distribution is      |
//|     uniform at the larger scale - then you should use            |
//|     IDWBuildModifiedShepardR()                                   |
//+------------------------------------------------------------------+
static void CIDWInt::IDWBuildModifiedShepard(CMatrixDouble &xy,const int n,
                                             const int nx,const int d,
                                             int nq,int nw,CIDWInterpolant &z)
  {
//--- create variables
   int    i=0;
   int    j=0;
   int    k=0;
   int    j2=0;
   int    j3=0;
   double v=0;
   double r=0;
   double s=0;
   double d0=0;
   double di=0;
   double v1=0;
   double v2=0;
   int    nc=0;
   int    offs=0;
   int    info=0;
   double taskrcond=0;
   int    i_=0;
//--- create arrays
   double x[];
   double qrbuf[];
   double y[];
   double w[];
   double qsol[];
   double temp[];
   int    tags[];
//--- create matrix
   CMatrixDouble qxybuf;
   CMatrixDouble fmatrix;
//--- these initializers are not really necessary,
//--- but without them compiler complains about uninitialized locals
   nc=0;
//--- assertions
   if(!CAp::Assert(n>0,__FUNCTION__+": N<=0!"))
      return;
//--- check
   if(!CAp::Assert(nx>=1,__FUNCTION__+": NX<1!"))
      return;
//--- check
   if(!CAp::Assert(d>=-1 && d<=2,__FUNCTION__+": D<>-1 and D<>0 and D<>1 and D<>2!"))
      return;
//--- Correct parameters if needed
   if(d==1)
     {
      nq=MathMax(nq,(int)MathCeil(m_idwqfactor*(1+nx))+1);
      nq=MathMax(nq,(int)MathRound(MathPow(2,nx))+1);
     }
//--- check
   if(d==2)
     {
      nq=MathMax(nq,(int)MathCeil(m_idwqfactor*(nx+2)*(nx+1)/2)+1);
      nq=MathMax(nq,(int)MathRound(MathPow(2,nx))+1);
     }
//--- change values
   nw=MathMax(nw,(int)MathRound(MathPow(2,nx))+1);
   nq=MathMin(nq,n);
   nw=MathMin(nw,n);
//--- primary initialization of Z
   IDWInit1(n,nx,d,nq,nw,z);
   z.m_modeltype=0;
//--- Create KD-tree
   ArrayResizeAL(tags,n);
   for(i=0;i<=n-1;i++)
      tags[i]=i;
//--- function call
   CNearestNeighbor::KDTreeBuildTagged(xy,tags,n,nx,1,2,z.m_tree);
//--- build nodal functions
   ArrayResizeAL(temp,nq+1);
   ArrayResizeAL(x,nx);
   ArrayResizeAL(qrbuf,nq);
   qxybuf.Resize(nq,nx+1);
//--- check
   if(d==-1)
      ArrayResizeAL(w,nq);
//--- check
   if(d==1)
     {
      //--- allocation
      ArrayResizeAL(y,nq);
      ArrayResizeAL(w,nq);
      ArrayResizeAL(qsol,nx);
      //--- NX for linear members,
      //--- 1 for temporary storage
      fmatrix.Resize(nq,nx+1);
     }
//--- check
   if(d==2)
     {
      //--- allocation
      ArrayResizeAL(y,nq);
      ArrayResizeAL(w,nq);
      ArrayResizeAL(qsol,nx+(int)MathRound(nx*(nx+1)*0.5));
      //--- NX for linear members,
      //--- Round(NX*(NX+1)*0.5) for quadratic model,
      //--- 1 for temporary storage
      fmatrix.Resize(nq,nx+(int)MathRound(nx*(nx+1)*0.5)+1);
     }
   for(i=0;i<=n-1;i++)
     {
      //--- Initialize center and function value.
      //--- If D=0 it is all what we need
      for(i_=0;i_<=nx;i_++)
         z.m_q[i].Set(i_,xy[i][i_]);
      //--- check
      if(d==0)
         continue;
      //--- calculate weights for linear/quadratic members calculation.
      //--- NOTE 1: weights are calculated using NORMALIZED modified
      //--- Shepard's formula. Original formula is w(i)=sqr((R-di)/(R*di)),
      //--- where di is i-th distance,R is max(di). Modified formula have
      //--- following form:
      //---     w_mod(i)=1,if di=d0
      //---     w_mod(i)=w(i)/w(0),if di<>d0
      //--- NOTE 2: self-match is NOT used for this query
      //--- NOTE 3: last point almost always gain zero weight,but it MUST
      //--- be used for fitting because sometimes it will gain NON-ZERO
      //--- weight - for example,when all distances are equal.
      for(i_=0;i_<=nx-1;i_++)
         x[i_]=xy[i][i_];
      k=CNearestNeighbor::KDTreeQueryKNN(z.m_tree,x,nq,false);
      //--- function call
      CNearestNeighbor::KDTreeQueryResultsXY(z.m_tree,qxybuf);
      //--- function call
      CNearestNeighbor::KDTreeQueryResultsDistances(z.m_tree,qrbuf);
      r=qrbuf[k-1];
      d0=qrbuf[0];
      //--- calculation
      for(j=0;j<=k-1;j++)
        {
         di=qrbuf[j];
         //--- check
         if(di==d0)
           {
            //--- distance is equal to shortest,set it 1.0
            //--- without explicitly calculating (which would give
            //--- us same result,but 'll expose us to the risk of
            //--- division by zero).
            w[j]=1;
           }
         else
           {
            //--- use normalized formula
            v1=(r-di)/(r-d0);
            v2=d0/di;
            w[j]=CMath::Sqr(v1*v2);
           }
        }
      //--- calculate linear/quadratic members
      if(d==-1)
        {
         //--- "Fast" linear nodal function calculated using
         //--- inverse distance weighting
         for(j=0;j<=nx-1;j++)
            x[j]=0;
         s=0;
         //--- calculation
         for(j=0;j<=k-1;j++)
           {
            //--- calculate J-th inverse distance weighted gradient:
            //---     grad_k=(y_j-y_k)*(x_j-x_k)/sqr(norm(x_j-x_k))
            //---     grad=sum(wk*grad_k)/sum(w_k)
            v=0;
            for(j2=0;j2<=nx-1;j2++)
               v=v+CMath::Sqr(qxybuf[j][j2]-xy[i][j2]);
            //--- Although x_j<>x_k,sqr(norm(x_j-x_k)) may be zero due to
            //--- underflow. If it is,we assume than J-th gradient is zero
            //--- (i.m_e. don't add anything)
            if(v!=0.0)
              {
               for(j2=0;j2<=nx-1;j2++)
                  x[j2]=x[j2]+w[j]*(qxybuf[j][nx]-xy[i][nx])*(qxybuf[j][j2]-xy[i][j2])/v;
              }
            s=s+w[j];
           }
         for(j=0;j<=nx-1;j++)
            z.m_q[i].Set(nx+1+j,x[j]/s);
        }
      else
        {
         //--- Least squares models: build
         if(d==1)
           {
            //--- Linear nodal function calculated using
            //--- least squares fitting to its neighbors
            for(j=0;j<=k-1;j++)
              {
               for(j2=0;j2<=nx-1;j2++)
                  fmatrix[j].Set(j2,qxybuf[j][j2]-xy[i][j2]);
               y[j]=qxybuf[j][nx]-xy[i][nx];
              }
            nc=nx;
           }
         //--- check
         if(d==2)
           {
            //--- Quadratic nodal function calculated using
            //--- least squares fitting to its neighbors
            for(j=0;j<=k-1;j++)
              {
               offs=0;
               for(j2=0;j2<=nx-1;j2++)
                 {
                  fmatrix[j].Set(offs,qxybuf[j][j2]-xy[i][j2]);
                  offs=offs+1;
                 }
               //--- calculation
               for(j2=0;j2<=nx-1;j2++)
                 {
                  for(j3=j2;j3<=nx-1;j3++)
                    {
                     fmatrix[j].Set(offs,(qxybuf[j][j2]-xy[i][j2])*(qxybuf[j][j3]-xy[i][j3]));
                     offs=offs+1;
                    }
                 }
               y[j]=qxybuf[j][nx]-xy[i][nx];
              }
            nc=nx+(int)MathRound(nx*(nx+1)*0.5);
           }
         //--- function call
         IDWInternalSolver(y,w,fmatrix,temp,k,nc,info,qsol,taskrcond);
         //--- Least squares models: copy results
         if(info>0)
           {
            //--- LLS task is solved,copy results
            z.m_debugworstrcond=MathMin(z.m_debugworstrcond,taskrcond);
            z.m_debugbestrcond=MathMax(z.m_debugbestrcond,taskrcond);
            for(j=0;j<=nc-1;j++)
               z.m_q[i].Set(nx+1+j,qsol[j]);
           }
         else
           {
            //--- Solver failure,very strange,but we will use
            //--- zero values to handle it.
            z.m_debugsolverfailures=z.m_debugsolverfailures+1;
            for(j=0;j<=nc-1;j++)
               z.m_q[i].Set(nx+1+j,0);
           }
        }
     }
  }
//+------------------------------------------------------------------+
//| IDW interpolant using modified Shepard method for non-uniform    |
//| datasets.                                                        |
//| This type of model uses constant nodal functions and interpolates|
//| using all nodes which are closer than user-specified radius R. It|
//| may be used when points distribution is non-uniform at the small |
//| scale, but it is at the distances as large as R.                 |
//| INPUT PARAMETERS:                                                |
//|     XY  -   X and Y values, array[0..N-1,0..NX].                 |
//|             First NX columns contain X-values, last column       |
//|             contain Y-values.                                    |
//|     N   -   number of nodes, N>0.                                |
//|     NX  -   space dimension, NX>=1.                              |
//|     R   -   radius, R>0                                          |
//| OUTPUT PARAMETERS:                                               |
//|     Z   -   IDW interpolant.                                     |
//| NOTES:                                                           |
//| * if there is less than IDWKMin points within R-ball, algorithm  |
//|   selects IDWKMin closest ones, so that continuity properties of |
//|   interpolant are preserved even far from points.                |
//+------------------------------------------------------------------+
static void CIDWInt::IDWBuildModifiedShepardR(CMatrixDouble &xy,const int n,
                                              const int nx,const double r,
                                              CIDWInterpolant &z)
  {
//--- create variables
   int i=0;
   int i_=0;
//--- create array
   int tags[];
//--- assertions
   if(!CAp::Assert(n>0,__FUNCTION__+": N<=0!"))
      return;
//--- check
   if(!CAp::Assert(nx>=1,__FUNCTION__+": NX<1!"))
      return;
//--- check
   if(!CAp::Assert(r>0.0,__FUNCTION__+": R<=0!"))
      return;
//--- primary initialization of Z
   IDWInit1(n,nx,0,0,n,z);
   z.m_modeltype=1;
   z.m_r=r;
//--- Create KD-tree
   ArrayResizeAL(tags,n);
   for(i=0;i<=n-1;i++)
      tags[i]=i;
//--- function call
   CNearestNeighbor::KDTreeBuildTagged(xy,tags,n,nx,1,2,z.m_tree);
//--- build nodal functions
   for(i=0;i<=n-1;i++)
     {
      for(i_=0;i_<=nx;i_++)
         z.m_q[i].Set(i_,xy[i][i_]);
     }
  }
//+------------------------------------------------------------------+
//| IDW model for noisy data.                                        |
//| This subroutine may be used to handle noisy data, i.e. data with |
//| noise in OUTPUT values. It differs from IDWBuildModifiedShepard()|
//| in the following aspects:                                        |
//| * nodal functions are not constrained to pass through nodes:     |
//|   Qi(xi)<>yi, i.e. we have fitting instead of interpolation.     |
//| * weights which are used during least squares fitting stage are  |
//|   all equal to 1.0 (independently of distance)                   |
//| * "fast"-linear or constant nodal functions are not supported    |
//|   (either not robust enough or too rigid)                        |
//| This problem require far more complex tuning than interpolation  |
//| problems.                                                        |
//| Below you can find some recommendations regarding this problem:  |
//| * focus on tuning NQ; it controls noise reduction. As for NW, you|
//|   can just make it equal to 2*NQ.                                |
//| * you can use cross-validation to determine optimal NQ.          |
//| * optimal NQ is a result of complex tradeoff between noise level |
//|   (more noise = larger NQ required) and underlying function      |
//|   complexity (given fixed N, larger NQ means smoothing of compex |
//|   features in the data). For example, NQ=N will reduce noise to  |
//|   the minimum level possible, but you will end up with just      |
//|   constant/linear/quadratic (depending on D) least squares       |
//|   model for the whole dataset.                                   |
//| INPUT PARAMETERS:                                                |
//|     XY  -   X and Y values, array[0..N-1,0..NX].                 |
//|             First NX columns contain X-values, last column       |
//|             contain Y-values.                                    |
//|     N   -   number of nodes, N>0.                                |
//|     NX  -   space dimension, NX>=1.                              |
//|     D   -   nodal function degree, either:                       |
//|             * 1     linear model, least squares fitting. Simpe   |
//|                     model for datasets too small for quadratic   |
//|                     models (or for very noisy problems).         |
//|             * 2     quadratic model, least squares fitting. Best |
//|                     model available (if your dataset is large    |
//|                     enough).                                     |
//|     NQ  -   number of points used to calculate nodal functions.  |
//|             NQ should be significantly larger than 1.5 times the |
//|             number of coefficients in a nodal function to        |
//|             overcome effects of noise:                           |
//|             * larger than 1.5*(1+NX) for linear model,           |
//|             * larger than 3/4*(NX+2)*(NX+1) for quadratic model. |
//|             Values less than this threshold will be silently     |
//|             increased.                                           |
//|     NW  -   number of points used to calculate weights and to    |
//|             interpolate. Required: >=2^NX+1, values less than    |
//|             this threshold will be silently increased.           |
//|             Recommended value: about 2*NQ or larger              |
//| OUTPUT PARAMETERS:                                               |
//|     Z   -   IDW inte rpolant.                                    |
//| NOTES:                                                           |
//|   * best results are obtained with quadratic models, linear      |
//|     models are not recommended to use unless you are pretty sure |
//|     that it is what you want                                     |
//|   * this subroutine is always succeeds (as long as correct       |
//|     parameters are passed).                                      |
//|   * see 'Multivariate Interpolation of Large Sets of Scattered   |
//|     Data' by Robert J. Renka for more information on this        |
//|     algorithm.                                                   |
//+------------------------------------------------------------------+
static void CIDWInt::IDWBuildNoisy(CMatrixDouble &xy,const int n,const int nx,
                                   const int d,int nq,int nw,CIDWInterpolant &z)
  {
//--- create variables
   int    i=0;
   int    j=0;
   int    k=0;
   int    j2=0;
   int    j3=0;
   double v=0;
   int    nc=0;
   int    offs=0;
   double taskrcond=0;
   int    info=0;
   int    i_=0;
//--- create arrays
   double x[];
   double qrbuf[];
   double y[];
   double w[];
   double qsol[];
   int    tags[];
   double temp[];
//--- create matrix
   CMatrixDouble qxybuf;
   CMatrixDouble fmatrix;
//--- these initializers are not really necessary,
//--- but without them compiler complains about uninitialized locals
   nc=0;
//--- assertions
   if(!CAp::Assert(n>0,__FUNCTION__+": N<=0!"))
      return;
//--- check
   if(!CAp::Assert(nx>=1,__FUNCTION__+": NX<1!"))
      return;
//--- check
   if(!CAp::Assert(d>=1 && d<=2,__FUNCTION__+": D<>1 and D<>2!"))
      return;
//--- Correct parameters if needed
   if(d==1)
      nq=MathMax(nq,(int)MathCeil(m_idwqfactor*(1+nx))+1);
//--- check
   if(d==2)
      nq=MathMax(nq,(int)MathCeil(m_idwqfactor*(nx+2)*(nx+1)/2)+1);
//--- change values
   nw=MathMax(nw,(int)MathRound(MathPow(2,nx))+1);
   nq=MathMin(nq,n);
   nw=MathMin(nw,n);
//--- primary initialization of Z
   IDWInit1(n,nx,d,nq,nw,z);
   z.m_modeltype=0;
//--- Create KD-tree
   ArrayResizeAL(tags,n);
   for(i=0;i<=n-1;i++)
      tags[i]=i;
//--- function call
   CNearestNeighbor::KDTreeBuildTagged(xy,tags,n,nx,1,2,z.m_tree);
//--- build nodal functions
//--- (special algorithm for noisy data is used)
   ArrayResizeAL(temp,nq+1);
   ArrayResizeAL(x,nx);
   ArrayResizeAL(qrbuf,nq);
   qxybuf.Resize(nq,nx+1);
//--- check
   if(d==1)
     {
      //--- allocation
      ArrayResizeAL(y,nq);
      ArrayResizeAL(w,nq);
      ArrayResizeAL(qsol,1+nx);
      //--- 1 for constant member,
      //--- NX for linear members,
      //--- 1 for temporary storage
      fmatrix.Resize(nq,1+nx+1);
     }
//--- check
   if(d==2)
     {
      //--- allocation
      ArrayResizeAL(y,nq);
      ArrayResizeAL(w,nq);
      ArrayResizeAL(qsol,1+nx+(int)MathRound(nx*(nx+1)*0.5));
      //--- 1 for constant member,
      //--- NX for linear members,
      //--- Round(NX*(NX+1)*0.5) for quadratic model,
      //--- 1 for temporary storage
      fmatrix.Resize(nq,1+nx+(int)MathRound(nx*(nx+1)*0.5)+1);
     }
//--- calculation
   for(i=0;i<=n-1;i++)
     {
      //--- Initialize center.
      for(i_=0;i_<=nx-1;i_++)
         z.m_q[i].Set(i_,xy[i][i_]);
      //--- Calculate linear/quadratic members
      //--- using least squares fit
      //--- NOTE 1: all weight are equal to 1.0
      //--- NOTE 2: self-match is USED for this query
      for(i_=0;i_<=nx-1;i_++)
         x[i_]=xy[i][i_];
      k=CNearestNeighbor::KDTreeQueryKNN(z.m_tree,x,nq,true);
      //--- function call
      CNearestNeighbor::KDTreeQueryResultsXY(z.m_tree,qxybuf);
      //--- function call
      CNearestNeighbor::KDTreeQueryResultsDistances(z.m_tree,qrbuf);
      //--- check
      if(d==1)
        {
         //--- Linear nodal function calculated using
         //--- least squares fitting to its neighbors
         for(j=0;j<=k-1;j++)
           {
            fmatrix[j].Set(0,1.0);
            for(j2=0;j2<=nx-1;j2++)
               fmatrix[j].Set(1+j2,qxybuf[j][j2]-xy[i][j2]);
            //--- change values
            y[j]=qxybuf[j][nx];
            w[j]=1;
           }
         nc=1+nx;
        }
      //--- check
      if(d==2)
        {
         //--- Quadratic nodal function calculated using
         //--- least squares fitting to its neighbors
         for(j=0;j<=k-1;j++)
           {
            fmatrix[j].Set(0,1);
            offs=1;
            for(j2=0;j2<=nx-1;j2++)
              {
               fmatrix[j].Set(offs,qxybuf[j][j2]-xy[i][j2]);
               offs=offs+1;
              }
            //--- calculation
            for(j2=0;j2<=nx-1;j2++)
              {
               for(j3=j2;j3<=nx-1;j3++)
                 {
                  fmatrix[j].Set(offs,(qxybuf[j][j2]-xy[i][j2])*(qxybuf[j][j3]-xy[i][j3]));
                  offs=offs+1;
                 }
              }
            //--- change values
            y[j]=qxybuf[j][nx];
            w[j]=1;
           }
         nc=1+nx+(int)MathRound(nx*(nx+1)*0.5);
        }
      //--- function call
      IDWInternalSolver(y,w,fmatrix,temp,k,nc,info,qsol,taskrcond);
      //--- Least squares models: copy results
      if(info>0)
        {
         //--- LLS task is solved,copy results
         z.m_debugworstrcond=MathMin(z.m_debugworstrcond,taskrcond);
         z.m_debugbestrcond=MathMax(z.m_debugbestrcond,taskrcond);
         for(j=0;j<=nc-1;j++)
            z.m_q[i].Set(nx+j,qsol[j]);
        }
      else
        {
         //--- Solver failure,very strange,but we will use
         //--- zero values to handle it.
         z.m_debugsolverfailures=z.m_debugsolverfailures+1;
         v=0;
         for(j=0;j<=k-1;j++)
            v=v+qxybuf[j][nx];
         z.m_q[i].Set(nx,v/k);
         for(j=0;j<=nc-2;j++)
            z.m_q[i].Set(nx+1+j,0);
        }
     }
  }
//+------------------------------------------------------------------+
//| Internal subroutine: K-th nodal function calculation             |
//+------------------------------------------------------------------+
static double CIDWInt::IDWCalcQ(CIDWInterpolant &z,double &x[],const int k)
  {
//--- create variables
   double result=0;
   int    nx=0;
   int    i=0;
   int    j=0;
   int    offs=0;
//--- initialization
   nx=z.m_nx;
//--- constant member
   result=z.m_q[k][nx];
//--- linear members
   if(z.m_d>=1)
     {
      for(i=0;i<=nx-1;i++)
         result=result+z.m_q[k][nx+1+i]*(x[i]-z.m_q[k][i]);
     }
//--- quadratic members
   if(z.m_d>=2)
     {
      offs=nx+1+nx;
      for(i=0;i<=nx-1;i++)
        {
         for(j=i;j<=nx-1;j++)
           {
            result=result+z.m_q[k][offs]*(x[i]-z.m_q[k][i])*(x[j]-z.m_q[k][j]);
            offs=offs+1;
           }
        }
     }
//--- return result
   return(result);
  }
//+------------------------------------------------------------------+
//| Initialization of internal structures.                           |
//| It assumes correctness of all parameters.                        |
//+------------------------------------------------------------------+
static void CIDWInt::IDWInit1(const int n,const int nx,const int d,
                              int nq,int nw,CIDWInterpolant &z)
  {
//--- initialization
   z.m_debugsolverfailures=0;
   z.m_debugworstrcond=1.0;
   z.m_debugbestrcond=0;
   z.m_n=n;
   z.m_nx=nx;
   z.m_d=0;
//--- check
   if(d==1)
      z.m_d=1;
//--- check
   if(d==2)
      z.m_d=2;
//--- check
   if(d==-1)
      z.m_d=1;
   z.m_nw=nw;
//--- check
   if(d==-1)
      z.m_q.Resize(n,2*nx+1);
//--- check
   if(d==0)
      z.m_q.Resize(n,nx+1);
//--- check
   if(d==1)
      z.m_q.Resize(n,2*nx+1);
//--- check
   if(d==2)
      z.m_q.Resize(n,nx+1+nx+(int)MathRound(nx*(nx+1)*0.5));
//--- allocation
   ArrayResizeAL(z.m_tbuf,nw);
   ArrayResizeAL(z.m_rbuf,nw);
   z.m_xybuf.Resize(nw,nx+1);
   ArrayResizeAL(z.m_xbuf,nx);
  }
//+------------------------------------------------------------------+
//| Linear least squares solver for small tasks.                     |
//| Works faster than standard ALGLIB solver in non-degenerate       |
//| cases (due to absense of internal allocations and optimized      |
//| row/colums). In  degenerate cases it calls standard solver, which|
//| results in small performance penalty associated with preliminary |
//| steps.                                                           |
//| INPUT PARAMETERS:                                                |
//|     Y           array[0..N-1]                                    |
//|     W           array[0..N-1]                                    |
//|     FMatrix     array[0..N-1,0..M], have additional column for   |
//|                 temporary values                                 |
//|     Temp        array[0..N]                                      |
//+------------------------------------------------------------------+
static void CIDWInt::IDWInternalSolver(double &y[],double &w[],CMatrixDouble &fmatrix,
                                       double &temp[],const int n,const int m,
                                       int &info,double &x[],double &taskrcond)
  {
//--- create variables
   int    i=0;
   int    j=0;
   double v=0;
   double tau=0;
   int    i_=0;
   int    i1_=0;
//--- create array
   double b[];
//--- object of class
   CDenseSolverLSReport srep;
//--- set up info
   info=1;
//--- prepare matrix
   for(i=0;i<=n-1;i++)
     {
      fmatrix[i].Set(m,y[i]);
      v=w[i];
      for(i_=0;i_<=m;i_++)
         fmatrix[i].Set(i_,v*fmatrix[i][i_]);
     }
//--- use either fast algorithm or general algorithm
   if(m<=n)
     {
      //--- QR decomposition
      //--- We assume that M<=N (we would have called LSFit() otherwise)
      for(i=0;i<=m-1;i++)
        {
         //--- check
         if(i<n-1)
           {
            i1_=i-1;
            for(i_=1;i_<=n-i;i_++)
               temp[i_]=fmatrix[i_+i1_][i];
            //--- function call
            CReflections::GenerateReflection(temp,n-i,tau);
            //--- change values
            fmatrix[i].Set(i,temp[1]);
            temp[1]=1;
            //--- calculation
            for(j=i+1;j<=m;j++)
              {
               i1_=1-i;
               v=0.0;
               for(i_=i;i_<=n-1;i_++)
                  v+=fmatrix[i_][j]*temp[i_+i1_];
               //--- change values
               v=tau*v;
               i1_=1-i;
               for(i_=i;i_<=n-1;i_++)
                  fmatrix[i_].Set(j,fmatrix[i_][j]-v*temp[i_+i1_]);
              }
           }
        }
      //--- Check condition number
      taskrcond=CRCond::RMatrixTrRCondInf(fmatrix,m,true,false);
      //--- use either fast algorithm for non-degenerate cases
      //--- or slow algorithm for degenerate cases
      if(taskrcond>10000*n*CMath::m_machineepsilon)
        {
         //--- solve triangular system R*x=FMatrix[0:M-1,M]
         //--- using fast algorithm,then exit
         x[m-1]=fmatrix[m-1][m]/fmatrix[m-1][m-1];
         for(i=m-2;i>=0;i--)
           {
            v=0.0;
            for(i_=i+1;i_<=m-1;i_++)
               v+=fmatrix[i][i_]*x[i_];
            x[i]=(fmatrix[i][m]-v)/fmatrix[i][i];
           }
        }
      else
        {
         //--- use more general algorithm
         ArrayResizeAL(b,m);
         for(i=0;i<=m-1;i++)
           {
            for(j=0;j<=i-1;j++)
               fmatrix[i].Set(j,0.0);
            b[i]=fmatrix[i][m];
           }
         //--- function call
         CDenseSolver::RMatrixSolveLS(fmatrix,m,m,b,10000*CMath::m_machineepsilon,info,srep,x);
        }
     }
   else
     {
      //--- use more general algorithm
      ArrayResizeAL(b,n);
      for(i=0;i<=n-1;i++)
         b[i]=fmatrix[i][m];
      //--- function call
      CDenseSolver::RMatrixSolveLS(fmatrix,n,m,b,10000*CMath::m_machineepsilon,info,srep,x);
      taskrcond=srep.m_r2;
     }
  }
//+------------------------------------------------------------------+
//| Barycentric interpolant.                                         |
//+------------------------------------------------------------------+
class CBarycentricInterpolant
  {
public:
   //--- variables
   int               m_n;
   double            m_sy;
   //--- arrays
   double            m_x[];
   double            m_y[];
   double            m_w[];
   //--- constructor, destructor
                     CBarycentricInterpolant(void);
                    ~CBarycentricInterpolant(void);
   //--- copy
   void              Copy(CBarycentricInterpolant &obj);
  };
//+------------------------------------------------------------------+
//| Constructor without parameters                                   |
//+------------------------------------------------------------------+
CBarycentricInterpolant::CBarycentricInterpolant(void)
  {

  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CBarycentricInterpolant::~CBarycentricInterpolant(void)
  {

  }
//+------------------------------------------------------------------+
//| Copy                                                             |
//+------------------------------------------------------------------+
void CBarycentricInterpolant::Copy(CBarycentricInterpolant &obj)
  {
//--- copy variables
   m_n=obj.m_n;
   m_sy=obj.m_sy;
//--- copy arrays
   ArrayCopy(m_x,obj.m_x);
   ArrayCopy(m_y,obj.m_y);
   ArrayCopy(m_w,obj.m_w);
  }
//+------------------------------------------------------------------+
//| Barycentric interpolant.                                         |
//+------------------------------------------------------------------+
class CBarycentricInterpolantShell
  {
private:
   CBarycentricInterpolant m_innerobj;
public:
   //--- constructor, destructor
                     CBarycentricInterpolantShell(void);
                     CBarycentricInterpolantShell(CBarycentricInterpolant &obj);
                    ~CBarycentricInterpolantShell(void);
   //--- method
   CBarycentricInterpolant *GetInnerObj(void);
  };
//+------------------------------------------------------------------+
//| Constructor without parameters                                   |
//+------------------------------------------------------------------+
CBarycentricInterpolantShell::CBarycentricInterpolantShell(void)
  {

  }
//+------------------------------------------------------------------+
//| Copy constructor                                                 |
//+------------------------------------------------------------------+
CBarycentricInterpolantShell::CBarycentricInterpolantShell(CBarycentricInterpolant &obj)
  {
//--- copy
   m_innerobj.Copy(obj);
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CBarycentricInterpolantShell::~CBarycentricInterpolantShell(void)
  {

  }
//+------------------------------------------------------------------+
//| Return object of class                                           |
//+------------------------------------------------------------------+
CBarycentricInterpolant *CBarycentricInterpolantShell::GetInnerObj(void)
  {
//--- return result
   return(GetPointer(m_innerobj));
  }
//+------------------------------------------------------------------+
//| Rational interpolation                                           |
//+------------------------------------------------------------------+
class CRatInt
  {
private:
   //--- private method
   static void       BarycentricNormalize(CBarycentricInterpolant &b);
public:
   //--- constructor, destructor
                     CRatInt(void);
                    ~CRatInt(void);
   //--- public methods
   static double     BarycentricCalc(CBarycentricInterpolant &b,const double t);
   static void       BarycentricDiff1(CBarycentricInterpolant &b,double t,double &f,double &df);
   static void       BarycentricDiff2(CBarycentricInterpolant &b,const double t,double &f,double &df,double &d2f);
   static void       BarycentricLinTransX(CBarycentricInterpolant &b,const double ca,const double cb);
   static void       BarycentricLinTransY(CBarycentricInterpolant &b,const double ca,const double cb);
   static void       BarycentricUnpack(CBarycentricInterpolant &b,int &n,double &x[],double &y[],double &w[]);
   static void       BarycentricBuildXYW(double &x[],double &y[],double &w[],const int n,CBarycentricInterpolant &b);
   static void       BarycentricBuildFloaterHormann(double &x[],double &y[],const int n,int d,CBarycentricInterpolant &b);
   static void       BarycentricCopy(CBarycentricInterpolant &b,CBarycentricInterpolant &b2);
  };
//+------------------------------------------------------------------+
//| Constructor without parameters                                   |
//+------------------------------------------------------------------+
CRatInt::CRatInt(void)
  {

  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CRatInt::~CRatInt(void)
  {

  }
//+------------------------------------------------------------------+
//| Rational interpolation using barycentric formula                 |
//| F(t)=SUM(i=0,n-1,w[i]*f[i]/(t-x[i])) / SUM(i=0,n-1,w[i]/(t-x[i]))|
//| Input parameters:                                                |
//|     B   -   barycentric interpolant built with one of model      |
//|             building subroutines.                                |
//|     T   -   interpolation point                                  |
//| Result:                                                          |
//|     barycentric interpolant F(t)                                 |
//+------------------------------------------------------------------+
static double CRatInt::BarycentricCalc(CBarycentricInterpolant &b,const double t)
  {
//--- create variables
   double s1=0;
   double s2=0;
   double s=0;
   double v=0;
   int    i=0;
//--- check
   if(!CAp::Assert(!CInfOrNaN::IsInfinity(t),__FUNCTION__+": infinite T!"))
      return(EMPTY_VALUE);
//--- special case: NaN
   if(CInfOrNaN::IsNaN(t))
      return(CInfOrNaN::NaN());
//--- special case: N=1
   if(b.m_n==1)
      return(b.m_sy*b.m_y[0]);
//--- Here we assume that task is normalized,i.m_e.:
//--- 1. abs(Y[i])<=1
//--- 2. abs(W[i])<=1
//--- 3. X[] is ordered
   s=MathAbs(t-b.m_x[0]);
   for(i=0;i<=b.m_n-1;i++)
     {
      v=b.m_x[i];
      //--- check
      if(v==(double)(t))
         return(b.m_sy*b.m_y[i]);
      v=MathAbs(t-v);
      //--- check
      if(v<s)
         s=v;
     }
//--- change values
   s1=0;
   s2=0;
//--- calculation
   for(i=0;i<=b.m_n-1;i++)
     {
      v=s/(t-b.m_x[i]);
      v=v*b.m_w[i];
      s1=s1+v*b.m_y[i];
      s2=s2+v;
     }
//--- return result
   return(b.m_sy*s1/s2);
  }
//+------------------------------------------------------------------+
//| Differentiation of barycentric interpolant: first derivative.    |
//| Algorithm used in this subroutine is very robust and should not  |
//| fail until provided with values too close to MaxRealNumber       |
//| (usually  MaxRealNumber/N or greater will overflow).             |
//| INPUT PARAMETERS:                                                |
//|     B   -   barycentric interpolant built with one of model      |
//|             building subroutines.                                |
//|     T   -   interpolation point                                  |
//| OUTPUT PARAMETERS:                                               |
//|     F   -   barycentric interpolant at T                         |
//|     DF  -   first derivative                                     |
//+------------------------------------------------------------------+
static void CRatInt::BarycentricDiff1(CBarycentricInterpolant &b,double t,
                                      double &f,double &df)
  {
//--- create variables
   double v=0;
   double vv=0;
   int    i=0;
   int    k=0;
   double n0=0;
   double n1=0;
   double d0=0;
   double d1=0;
   double s0=0;
   double s1=0;
   double xk=0;
   double xi=0;
   double xmin=0;
   double xmax=0;
   double xscale1=0;
   double xoffs1=0;
   double xscale2=0;
   double xoffs2=0;
   double xprev=0;
//--- initialization
   f=0;
   df=0;
//--- check
   if(!CAp::Assert(!CInfOrNaN::IsInfinity(t),__FUNCTION__+": infinite T!"))
      return;
//--- special case: NaN
   if(CInfOrNaN::IsNaN(t))
     {
      //--- change values
      f=CInfOrNaN::NaN();
      df=CInfOrNaN::NaN();
      //--- exit the function
      return;
     }
//--- special case: N=1
   if(b.m_n==1)
     {
      //--- change values
      f=b.m_sy*b.m_y[0];
      df=0;
      //--- exit the function
      return;
     }
//--- check
   if(b.m_sy==0.0)
     {
      //--- change values
      f=0;
      df=0;
      //--- exit the function
      return;
     }
//--- check
   if(!CAp::Assert(b.m_sy>0.0,__FUNCTION__+": internal error"))
      return;
//--- We assume than N>1 and B.SY>0. Find:
//--- 1. pivot point (X[i] closest to T)
//--- 2. width of interval containing X[i]
   v=MathAbs(b.m_x[0]-t);
   k=0;
   xmin=b.m_x[0];
   xmax=b.m_x[0];
//--- calculation
   for(i=1;i<=b.m_n-1;i++)
     {
      vv=b.m_x[i];
      //--- check
      if(MathAbs(vv-t)<v)
        {
         v=MathAbs(vv-t);
         k=i;
        }
      //--- change values
      xmin=MathMin(xmin,vv);
      xmax=MathMax(xmax,vv);
     }
//--- pivot point found,calculate dNumerator and dDenominator
   xscale1=1/(xmax-xmin);
   xoffs1=-(xmin/(xmax-xmin))+1;
   xscale2=2;
   xoffs2=-3;
   t=t*xscale1+xoffs1;
   t=t*xscale2+xoffs2;
   xk=b.m_x[k];
   xk=xk*xscale1+xoffs1;
   xk=xk*xscale2+xoffs2;
   v=t-xk;
   n0=0;
   n1=0;
   d0=0;
   d1=0;
   xprev=-2;
//--- calculation
   for(i=0;i<=b.m_n-1;i++)
     {
      //--- change values
      xi=b.m_x[i];
      xi=xi*xscale1+xoffs1;
      xi=xi*xscale2+xoffs2;
      //--- check
      if(!CAp::Assert(xi>xprev,__FUNCTION__+": points are too close!"))
         return;
      xprev=xi;
      //--- check
      if(i!=k)
        {
         vv=CMath::Sqr(t-xi);
         s0=(t-xk)/(t-xi);
         s1=(xk-xi)/vv;
        }
      else
        {
         s0=1;
         s1=0;
        }
      //--- change values
      vv=b.m_w[i]*b.m_y[i];
      n0=n0+s0*vv;
      n1=n1+s1*vv;
      vv=b.m_w[i];
      d0=d0+s0*vv;
      d1=d1+s1*vv;
     }
//--- change values
   f=b.m_sy*n0/d0;
   df=(n1*d0-n0*d1)/CMath::Sqr(d0);
//--- check
   if(df!=0.0)
      df=MathSign(df)*MathExp(MathLog(MathAbs(df))+MathLog(b.m_sy)+MathLog(xscale1)+MathLog(xscale2));
  }
//+------------------------------------------------------------------+
//| Differentiation of barycentric interpolant: first/second         |
//| derivatives.                                                     |
//| INPUT PARAMETERS:                                                |
//|     B   -   barycentric interpolant built with one of model      |
//|             building subroutines.                                |
//|     T   -   interpolation point                                  |
//| OUTPUT PARAMETERS:                                               |
//|     F   -   barycentric interpolant at T                         |
//|     DF  -   first derivative                                     |
//|     D2F -   second derivative                                    |
//| NOTE: this algorithm may fail due to overflow/underflor if used  |
//| on data whose values are close to MaxRealNumber or MinRealNumber.|
//| Use more robust BarycentricDiff1() subroutine in such cases.     |
//+------------------------------------------------------------------+
static void CRatInt::BarycentricDiff2(CBarycentricInterpolant &b,const double t,
                                      double &f,double &df,double &d2f)
  {
//--- create variables
   double v=0;
   double vv=0;
   int    i=0;
   int    k=0;
   double n0=0;
   double n1=0;
   double n2=0;
   double d0=0;
   double d1=0;
   double d2=0;
   double s0=0;
   double s1=0;
   double s2=0;
   double xk=0;
   double xi=0;
//--- initialization
   f=0;
   df=0;
   d2f=0;
//--- check
   if(!CAp::Assert(!CInfOrNaN::IsInfinity(t),__FUNCTION__+": infinite T!"))
      return;
//--- special case: NaN
   if(CInfOrNaN::IsNaN(t))
     {
      //--- change values
      f=CInfOrNaN::NaN();
      df=CInfOrNaN::NaN();
      d2f=CInfOrNaN::NaN();
      //--- exit the function
      return;
     }
//--- special case: N=1
   if(b.m_n==1)
     {
      //--- change values
      f=b.m_sy*b.m_y[0];
      df=0;
      d2f=0;
      //--- exit the function
      return;
     }
//--- check
   if(b.m_sy==0.0)
     {
      //--- change values
      f=0;
      df=0;
      d2f=0;
      //--- exit the function
      return;
     }
//--- We assume than N>1 and B.SY>0. Find:
//--- 1. pivot point (X[i] closest to T)
//--- 2. width of interval containing X[i]
   if(!CAp::Assert(b.m_sy>0.0,__FUNCTION__+": internal error"))
      return;
//--- change values
   f=0;
   df=0;
   d2f=0;
   v=MathAbs(b.m_x[0]-t);
   k=0;
   for(i=1;i<=b.m_n-1;i++)
     {
      vv=b.m_x[i];
      //--- check
      if(MathAbs(vv-t)<v)
        {
         v=MathAbs(vv-t);
         k=i;
        }
     }
//--- pivot point found, calculate dNumerator and dDenominator
   xk=b.m_x[k];
   v=t-xk;
   n0=0;
   n1=0;
   n2=0;
   d0=0;
   d1=0;
   d2=0;
//--- calculation
   for(i=0;i<=b.m_n-1;i++)
     {
      //--- check
      if(i!=k)
        {
         xi=b.m_x[i];
         vv=CMath::Sqr(t-xi);
         s0=(t-xk)/(t-xi);
         s1=(xk-xi)/vv;
         s2=-(2*(xk-xi)/(vv*(t-xi)));
        }
      else
        {
         s0=1;
         s1=0;
         s2=0;
        }
      //--- change values
      vv=b.m_w[i]*b.m_y[i];
      n0=n0+s0*vv;
      n1=n1+s1*vv;
      n2=n2+s2*vv;
      vv=b.m_w[i];
      d0=d0+s0*vv;
      d1=d1+s1*vv;
      d2=d2+s2*vv;
     }
//--- change values
   f=b.m_sy*n0/d0;
   df=b.m_sy*(n1*d0-n0*d1)/CMath::Sqr(d0);
   d2f=b.m_sy*((n2*d0-n0*d2)*CMath::Sqr(d0)-(n1*d0-n0*d1)*2*d0*d1)/CMath::Sqr(CMath::Sqr(d0));
  }
//+------------------------------------------------------------------+
//| This subroutine performs linear transformation of the argument.  |
//| INPUT PARAMETERS:                                                |
//|     B       -   rational interpolant in barycentric form         |
//|     CA, CB  -   transformation coefficients: x = CA*t + CB       |
//| OUTPUT PARAMETERS:                                               |
//|     B       -   transformed interpolant with X replaced by T     |
//+------------------------------------------------------------------+
static void CRatInt::BarycentricLinTransX(CBarycentricInterpolant &b,
                                          const double ca,const double cb)
  {
//--- create variables
   int    i=0;
   int    j=0;
   double v=0;
//--- special case,replace by constant F(CB)
   if(ca==0.0)
     {
      b.m_sy=BarycentricCalc(b,cb);
      v=1;
      for(i=0;i<=b.m_n-1;i++)
        {
         b.m_y[i]=1;
         b.m_w[i]=v;
         v=-v;
        }
      //--- exit the function
      return;
     }
//--- general case: CA<>0
   for(i=0;i<=b.m_n-1;i++)
      b.m_x[i]=(b.m_x[i]-cb)/ca;
//--- check
   if(ca<0.0)
     {
      for(i=0;i<=b.m_n-1;i++)
        {
         //--- check
         if(i<b.m_n-1-i)
           {
            //--- change values
            j=b.m_n-1-i;
            v=b.m_x[i];
            b.m_x[i]=b.m_x[j];
            b.m_x[j]=v;
            v=b.m_y[i];
            b.m_y[i]=b.m_y[j];
            b.m_y[j]=v;
            v=b.m_w[i];
            b.m_w[i]=b.m_w[j];
            b.m_w[j]=v;
           }
         else
            break;
        }
     }
  }
//+------------------------------------------------------------------+
//| This subroutine performs linear transformation of the barycentric|
//| interpolant.                                                     |
//| INPUT PARAMETERS:                                                |
//|     B       -   rational interpolant in barycentric form         |
//|     CA, CB  -   transformation coefficients: B2(x) = CA*B(x) + CB|
//| OUTPUT PARAMETERS:                                               |
//|     B       -   transformed interpolant                          |
//+------------------------------------------------------------------+
static void CRatInt::BarycentricLinTransY(CBarycentricInterpolant &b,
                                          const double ca,const double cb)
  {
//--- create variables
   int    i=0;
   double v=0;
   int    i_=0;
//--- calculation
   for(i=0;i<=b.m_n-1;i++)
      b.m_y[i]=ca*b.m_sy*b.m_y[i]+cb;
//--- change value
   b.m_sy=0;
   for(i=0;i<=b.m_n-1;i++)
      b.m_sy=MathMax(b.m_sy,MathAbs(b.m_y[i]));
//--- check
   if(b.m_sy>0.0)
     {
      v=1/b.m_sy;
      //--- calculation
      for(i_=0;i_<=b.m_n-1;i_++)
         b.m_y[i_]=v*b.m_y[i_];
     }
  }
//+------------------------------------------------------------------+
//| Extracts X/Y/W arrays from rational interpolant                  |
//| INPUT PARAMETERS:                                                |
//|     B   -   barycentric interpolant                              |
//| OUTPUT PARAMETERS:                                               |
//|     N   -   nodes count, N>0                                     |
//|     X   -   interpolation nodes, array[0..N-1]                   |
//|     F   -   function values, array[0..N-1]                       |
//|     W   -   barycentric weights, array[0..N-1]                   |
//+------------------------------------------------------------------+
static void CRatInt::BarycentricUnpack(CBarycentricInterpolant &b,int &n,
                                       double &x[],double &y[],double &w[])
  {
//--- create variables
   double v=0;
   int    i_=0;
//--- initialization
   n=b.m_n;
//--- allocation
   ArrayResizeAL(x,n);
   ArrayResizeAL(y,n);
   ArrayResizeAL(w,n);
//--- initialization
   v=b.m_sy;
//--- copy
   for(i_=0;i_<=n-1;i_++)
      x[i_]=b.m_x[i_];
   for(i_=0;i_<=n-1;i_++)
      y[i_]=v*b.m_y[i_];
   for(i_=0;i_<=n-1;i_++)
      w[i_]=b.m_w[i_];
  }
//+------------------------------------------------------------------+
//| Rational interpolant from X/Y/W arrays                           |
//| F(t)=SUM(i=0,n-1,w[i]*f[i]/(t-x[i])) / SUM(i=0,n-1,w[i]/(t-x[i]))|
//| INPUT PARAMETERS:                                                |
//|     X   -   interpolation nodes, array[0..N-1]                   |
//|     F   -   function values, array[0..N-1]                       |
//|     W   -   barycentric weights, array[0..N-1]                   |
//|     N   -   nodes count, N>0                                     |
//| OUTPUT PARAMETERS:                                               |
//|     B   -   barycentric interpolant built from (X, Y, W)         |
//+------------------------------------------------------------------+
static void CRatInt::BarycentricBuildXYW(double &x[],double &y[],double &w[],
                                         const int n,CBarycentricInterpolant &b)
  {
//--- create a variable
   int i_=0;
//--- check
   if(!CAp::Assert(n>0,__FUNCTION__+": incorrect N!"))
      return;
//--- fill X/Y/W
   ArrayResizeAL(b.m_x,n);
   ArrayResizeAL(b.m_y,n);
   ArrayResizeAL(b.m_w,n);
//--- copy
   for(i_=0;i_<=n-1;i_++)
      b.m_x[i_]=x[i_];
   for(i_=0;i_<=n-1;i_++)
      b.m_y[i_]=y[i_];
   for(i_=0;i_<=n-1;i_++)
      b.m_w[i_]=w[i_];
   b.m_n=n;
//--- Normalize
   BarycentricNormalize(b);
  }
//+------------------------------------------------------------------+
//| Rational interpolant without poles                               |
//| The subroutine constructs the rational interpolating function    |
//| without real poles (see 'Barycentric rational interpolation with |
//| no poles and high rates of approximation', Michael S. Floater.   |
//| and Kai Hormann, for more information on this subject).          |
//| Input parameters:                                                |
//|     X   -   interpolation nodes, array[0..N-1].                  |
//|     Y   -   function values, array[0..N-1].                      |
//|     N   -   number of nodes, N>0.                                |
//|     D   -   order of the interpolation scheme, 0 <= D <= N-1.    |
//|             D<0 will cause an error.                             |
//|             D>=N it will be replaced with D=N-1.                 |
//|             if you don't know what D to choose, use small value  |
//|             about 3-5.                                           |
//| Output parameters:                                               |
//|     B   -   barycentric interpolant.                             |
//| Note:                                                            |
//|     this algorithm always succeeds and calculates the weights    |
//|     with close to machine precision.                             |
//+------------------------------------------------------------------+
static void CRatInt::BarycentricBuildFloaterHormann(double &x[],double &y[],
                                                    const int n,int d,
                                                    CBarycentricInterpolant &b)
  {
//--- create variables
   double s0=0;
   double s=0;
   double v=0;
   int    i=0;
   int    j=0;
   int    k=0;
   int    i_=0;
//--- create arrays
   int    perm[];
   double wtemp[];
   double sortrbuf[];
   double sortrbuf2[];
//--- check
   if(!CAp::Assert(n>0,__FUNCTION__+": N<=0!"))
      return;
//--- check
   if(!CAp::Assert(d>=0,__FUNCTION__+": incorrect D!"))
      return;
//--- Prepare
   if(d>n-1)
      d=n-1;
   b.m_n=n;
//--- special case: N=1
   if(n==1)
     {
      //--- allocation
      ArrayResizeAL(b.m_x,n);
      ArrayResizeAL(b.m_y,n);
      ArrayResizeAL(b.m_w,n);
      //--- change values
      b.m_x[0]=x[0];
      b.m_y[0]=y[0];
      b.m_w[0]=1;
      //--- function call
      BarycentricNormalize(b);
      //--- exit the function
      return;
     }
//--- Fill X/Y
   ArrayResizeAL(b.m_x,n);
   ArrayResizeAL(b.m_y,n);
//--- copy
   for(i_=0;i_<=n-1;i_++)
      b.m_x[i_]=x[i_];
   for(i_=0;i_<=n-1;i_++)
      b.m_y[i_]=y[i_];
//--- function call
   CTSort::TagSortFastR(b.m_x,b.m_y,sortrbuf,sortrbuf2,n);
//--- Calculate Wk
   ArrayResizeAL(b.m_w,n);
   s0=1;
   for(k=1;k<=d;k++)
      s0=-s0;
//--- calculation
   for(k=0;k<=n-1;k++)
     {
      //--- Wk
      s=0;
      for(i=(int)(MathMax(k-d,0));i<=MathMin(k,n-1-d);i++)
        {
         v=1;
         for(j=i;j<=i+d;j++)
           {
            //--- check
            if(j!=k)
               v=v/MathAbs(b.m_x[k]-b.m_x[j]);
           }
         s=s+v;
        }
      b.m_w[k]=s0*s;
      //--- Next S0
      s0=-s0;
     }
//--- Normalize
   BarycentricNormalize(b);
  }
//+------------------------------------------------------------------+
//| Copying of the barycentric interpolant (for internal use only)   |
//| INPUT PARAMETERS:                                                |
//|     B   -   barycentric interpolant                              |
//| OUTPUT PARAMETERS:                                               |
//|     B2  -   copy(B1)                                             |
//+------------------------------------------------------------------+
static void CRatInt::BarycentricCopy(CBarycentricInterpolant &b,
                                     CBarycentricInterpolant &b2)
  {
//--- create a variable
   int i_=0;
//--- change values
   b2.m_n=b.m_n;
   b2.m_sy=b.m_sy;
//--- allocation
   ArrayResizeAL(b2.m_x,b2.m_n);
   ArrayResizeAL(b2.m_y,b2.m_n);
   ArrayResizeAL(b2.m_w,b2.m_n);
//--- copy
   for(i_=0;i_<=b2.m_n-1;i_++)
      b2.m_x[i_]=b.m_x[i_];
   for(i_=0;i_<=b2.m_n-1;i_++)
      b2.m_y[i_]=b.m_y[i_];
   for(i_=0;i_<=b2.m_n-1;i_++)
      b2.m_w[i_]=b.m_w[i_];
  }
//+------------------------------------------------------------------+
//| Normalization of barycentric interpolant:                        |
//| * B.N, B.X, B.Y and B.W are initialized                          |
//| * B.SY is NOT initialized                                        |
//| * Y[] is normalized, scaling coefficient is stored in B.SY       |
//| * W[] is normalized, no scaling coefficient is stored            |
//| * X[] is sorted                                                  |
//| Internal subroutine.                                             |
//+------------------------------------------------------------------+
static void CRatInt::BarycentricNormalize(CBarycentricInterpolant &b)
  {
//--- create variables
   int    i=0;
   int    j=0;
   int    j2=0;
   double v=0;
   int    i_=0;
//--- create arrays
   int p1[];
   int p2[];
//--- Normalize task: |Y|<=1,|W|<=1,sort X[]
   b.m_sy=0;
   for(i=0;i<=b.m_n-1;i++)
      b.m_sy=MathMax(b.m_sy,MathAbs(b.m_y[i]));
//--- check
   if(b.m_sy>0.0 && MathAbs(b.m_sy-1)>10*CMath::m_machineepsilon)
     {
      v=1/b.m_sy;
      for(i_=0;i_<=b.m_n-1;i_++)
         b.m_y[i_]=v*b.m_y[i_];
     }
//--- change value
   v=0;
   for(i=0;i<=b.m_n-1;i++)
      v=MathMax(v,MathAbs(b.m_w[i]));
//--- check
   if(v>0.0 && MathAbs(v-1)>10*CMath::m_machineepsilon)
     {
      v=1/v;
      for(i_=0;i_<=b.m_n-1;i_++)
         b.m_w[i_]=v*b.m_w[i_];
     }
   for(i=0;i<=b.m_n-2;i++)
     {
      //--- check
      if(b.m_x[i+1]<b.m_x[i])
        {
         //--- function call
         CTSort::TagSort(b.m_x,b.m_n,p1,p2);
         //--- calculation
         for(j=0;j<=b.m_n-1;j++)
           {
            j2=p2[j];
            v=b.m_y[j];
            b.m_y[j]=b.m_y[j2];
            b.m_y[j2]=v;
            v=b.m_w[j];
            b.m_w[j]=b.m_w[j2];
            b.m_w[j2]=v;
           }
         break;
        }
     }
  }
//+------------------------------------------------------------------+
//| Polynomial interpolant                                           |
//+------------------------------------------------------------------+
class CPolInt
  {
public:
   //--- constructor, destructor
                     CPolInt(void);
                    ~CPolInt(void);
   //--- methods
   static void       PolynomialBar2Cheb(CBarycentricInterpolant &p,const double a,const double b,double &t[]);
   static void       PolynomialCheb2Bar(double &t[],const int n,const double a,const double b,CBarycentricInterpolant &p);
   static void       PolynomialBar2Pow(CBarycentricInterpolant &p,const double c,const double s,double &a[]);
   static void       PolynomialPow2Bar(double &a[],const int n,const double c,const double s,CBarycentricInterpolant &p);
   static void       PolynomialBuild(double &cx[],double &cy[],const int n,CBarycentricInterpolant &p);
   static void       PolynomialBuildEqDist(const double a,const double b,double &y[],const int n,CBarycentricInterpolant &p);
   static void       PolynomialBuildCheb1(const double a,const double b,double &y[],const int n,CBarycentricInterpolant &p);
   static void       PolynomialBuildCheb2(const double a,const double b,double &y[],const int n,CBarycentricInterpolant &p);
   static double     PolynomialCalcEqDist(const double a,const double b,double &f[],const int n,const double t);
   static double     PolynomialCalcCheb1(const double a,const double b,double &f[],const int n,double t);
   static double     PolynomialCalcCheb2(const double a,const double b,double &f[],const int n,double t);
  };
//+------------------------------------------------------------------+
//| Constructor without parameters                                   |
//+------------------------------------------------------------------+
CPolInt::CPolInt(void)
  {

  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CPolInt::~CPolInt(void)
  {

  }
//+------------------------------------------------------------------+
//| Conversion from barycentric representation to Chebyshev basis.   |
//| This function has O(N^2) complexity.                             |
//| INPUT PARAMETERS:                                                |
//|     P   -   polynomial in barycentric form                       |
//|     A,B -   base interval for Chebyshev polynomials (see below)  |
//|             A<>B                                                 |
//| OUTPUT PARAMETERS                                                |
//|     T   -   coefficients of Chebyshev representation;            |
//|             P(x) = sum { T[i]*Ti(2*(x-A)/(B-A)-1), i=0..N-1 },   |
//|             where Ti - I-th Chebyshev polynomial.                |
//| NOTES:                                                           |
//|     barycentric interpolant passed as P may be either polynomial |
//|     obtained from polynomial interpolation/ fitting or rational  |
//|     function which is NOT polynomial. We can't distinguish       |
//|     between these two cases, and this algorithm just tries to    |
//|     work assuming that P IS a polynomial. If not, algorithm will |
//|     return results, but they won't have any meaning.             |
//+------------------------------------------------------------------+
static void CPolInt::PolynomialBar2Cheb(CBarycentricInterpolant &p,
                                        const double a,const double b,
                                        double &t[])
  {
//--- create variables
   int    i=0;
   int    k=0;
   double v=0;
   int    i_=0;
//--- create arrays
   double vp[];
   double vx[];
   double tk[];
   double tk1[];
//--- check
   if(!CAp::Assert(CMath::IsFinite(a),__FUNCTION__+": A is not finite!"))
      return;
//--- check
   if(!CAp::Assert(CMath::IsFinite(b),__FUNCTION__+": B is not finite!"))
      return;
//--- check
   if(!CAp::Assert(a!=b,__FUNCTION__+": A=B!"))
      return;
//--- check
   if(!CAp::Assert(p.m_n>0,__FUNCTION__+": P is not correctly initialized barycentric interpolant!"))
      return;
//--- Calculate function values on a Chebyshev grid
   ArrayResizeAL(vp,p.m_n);
   ArrayResizeAL(vx,p.m_n);
   for(i=0;i<=p.m_n-1;i++)
     {
      vx[i]=MathCos(M_PI*(i+0.5)/p.m_n);
      vp[i]=CRatInt::BarycentricCalc(p,0.5*(vx[i]+1)*(b-a)+a);
     }
//--- T[0]
   ArrayResizeAL(t,p.m_n);
   v=0;
   for(i=0;i<=p.m_n-1;i++)
      v=v+vp[i];
   t[0]=v/p.m_n;
//--- other T's.
//--- NOTES:
//--- 1. TK stores T{k} on VX,TK1 stores T{k-1} on VX
//--- 2. we can do same calculations with fast DCT,but it
//---    * adds dependencies
//---    * still leaves us with O(N^2) algorithm because
//---      preparation of function values is O(N^2) process
   if(p.m_n>1)
     {
      //--- allocation
      ArrayResizeAL(tk,p.m_n);
      ArrayResizeAL(tk1,p.m_n);
      for(i=0;i<=p.m_n-1;i++)
        {
         tk[i]=vx[i];
         tk1[i]=1;
        }
      //--- calculation
      for(k=1;k<=p.m_n-1;k++)
        {
         //--- calculate discrete product of function vector and TK
         v=0.0;
         for(i_=0;i_<=p.m_n-1;i_++)
            v+=tk[i_]*vp[i_];
         t[k]=v/(0.5*p.m_n);
         //--- Update TK and TK1
         for(i=0;i<=p.m_n-1;i++)
           {
            v=2*vx[i]*tk[i]-tk1[i];
            tk1[i]=tk[i];
            tk[i]=v;
           }
        }
     }
  }
//+------------------------------------------------------------------+
//| Conversion from Chebyshev basis to barycentric representation.   |
//| This function has O(N^2) complexity.                             |
//| INPUT PARAMETERS:                                                |
//|     T   -   coefficients of Chebyshev representation;            |
//|             P(x) = sum { T[i]*Ti(2*(x-A)/(B-A)-1), i=0..N },     |
//|             where Ti - I-th Chebyshev polynomial.                |
//|     N   -   number of coefficients:                              |
//|             * if given, only leading N elements of T are used    |
//|             * if not given, automatically determined from size   |
//|               of T                                               |
//|     A,B -   base interval for Chebyshev polynomials (see above)  |
//|             A<B                                                  |
//| OUTPUT PARAMETERS                                                |
//|     P   -   polynomial in barycentric form                       |
//+------------------------------------------------------------------+
static void CPolInt::PolynomialCheb2Bar(double &t[],const int n,const double a,
                                        const double b,CBarycentricInterpolant &p)
  {
//--- create variables
   int    i=0;
   int    k=0;
   double tk=0;
   double tk1=0;
   double vx=0;
   double vy=0;
   double v=0;
//--- create array
   double y[];
//--- check
   if(!CAp::Assert(CMath::IsFinite(a),__FUNCTION__+": A is not finite!"))
      return;
//--- check
   if(!CAp::Assert(CMath::IsFinite(b),__FUNCTION__+": B is not finite!"))
      return;
//--- check
   if(!CAp::Assert(a!=b,__FUNCTION__+": A=B!"))
      return;
//--- check
   if(!CAp::Assert(n>=1,__FUNCTION__+": N<1"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(t)>=n,__FUNCTION__+": Length(T)<N"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(t,n),__FUNCTION__+": T[] contains INF or NAN"))
      return;
//--- Calculate function values on a Chebyshev grid spanning [-1,+1]
   ArrayResizeAL(y,n);
   for(i=0;i<=n-1;i++)
     {
      //--- Calculate value on a grid spanning [-1,+1]
      vx=MathCos(M_PI*(i+0.5)/n);
      vy=t[0];
      tk1=1;
      tk=vx;
      //--- change values
      for(k=1;k<=n-1;k++)
        {
         vy=vy+t[k]*tk;
         v=2*vx*tk-tk1;
         tk1=tk;
         tk=v;
        }
      y[i]=vy;
     }
//--- Build barycentric interpolant,map grid from [-1,+1] to [A,B]
   PolynomialBuildCheb1(a,b,y,n,p);
  }
//+------------------------------------------------------------------+
//| Conversion from barycentric representation to power basis.       |
//| This function has O(N^2) complexity.                             |
//| INPUT PARAMETERS:                                                |
//|     P   -   polynomial in barycentric form                       |
//|     C   -   offset (see below); 0.0 is used as default value.    |
//|     S   -   scale (see below); 1.0 is used as default value.     |
//|             S<>0.                                                |
//| OUTPUT PARAMETERS                                                |
//|     A   -   coefficients,                                        |
//|             P(x) = sum { A[i]*((X-C)/S)^i, i=0..N-1 }            |
//|     N   -   number of coefficients (polynomial degree plus 1)    |
//| NOTES:                                                           |
//| 1.  this function accepts offset and scale, which can be set to  |
//|     improve numerical properties of polynomial. For example, if  |
//|     P was obtained as result of interpolation on [-1,+1], you can|
//|     set C=0 and S=1 and represent P as sum of 1, x, x^2, x^3 and |
//|     so on. In most cases you it is exactly what you need.        |
//|     However, if your interpolation model was built on [999,1001],|
//|     you will see significant growth of numerical errors when     |
//|     using {1, x, x^2, x^3} as basis. Representing P as sum of 1, |
//|     (x-1000), (x-1000)^2, (x-1000)^3 will be better option. Such |
//|     representation can be  obtained  by  using 1000.0 as offset  |
//|     C and 1.0 as scale S.                                        |
//| 2.  power basis is ill-conditioned and tricks described above    |
//|     can't solve this problem completely. This function  will     |
//|     return coefficients in any case, but for N>8 they will become|
//|     unreliable. However, N's less than 5 are pretty safe.        |
//| 3.  barycentric interpolant passed as P may be either polynomial |
//|     obtained from polynomial interpolation/ fitting or rational  |
//|     function which is NOT polynomial. We can't distinguish       |
//|     between these two cases, and this algorithm just tries to    |
//|     work assuming that P IS a polynomial. If not, algorithm will |
//|     return results, but they won't have any meaning.             |
//+------------------------------------------------------------------+
static void CPolInt::PolynomialBar2Pow(CBarycentricInterpolant &p,
                                       const double c,const double s,
                                       double &a[])
  {
//--- create variables
   int    i=0;
   int    k=0;
   double e=0;
   double d=0;
   double v=0;
   int    i_=0;
//--- create arrays
   double vp[];
   double vx[];
   double tk[];
   double tk1[];
   double t[];
//--- check
   if(!CAp::Assert(CMath::IsFinite(c),__FUNCTION__+": C is not finite!"))
      return;
//--- check
   if(!CAp::Assert(CMath::IsFinite(s),__FUNCTION__+": S is not finite!"))
      return;
//--- check
   if(!CAp::Assert(s!=0.0,__FUNCTION__+": S=0!"))
      return;
//--- check
   if(!CAp::Assert(p.m_n>0,__FUNCTION__+": P is not correctly initialized barycentric interpolant!"))
      return;
//--- Calculate function values on a Chebyshev grid
   ArrayResizeAL(vp,p.m_n);
   ArrayResizeAL(vx,p.m_n);
   for(i=0;i<=p.m_n-1;i++)
     {
      vx[i]=MathCos(M_PI*(i+0.5)/p.m_n);
      vp[i]=CRatInt::BarycentricCalc(p,s*vx[i]+c);
     }
//--- T[0]
   ArrayResizeAL(t,p.m_n);
   v=0;
   for(i=0;i<=p.m_n-1;i++)
      v=v+vp[i];
   t[0]=v/p.m_n;
//--- other T's.
//--- NOTES:
//--- 1. TK stores T{k} on VX,TK1 stores T{k-1} on VX
//--- 2. we can do same calculations with fast DCT,but it
//---    * adds dependencies
//---    * still leaves us with O(N^2) algorithm because
//---      preparation of function values is O(N^2) process
   if(p.m_n>1)
     {
      //--- allocation
      ArrayResizeAL(tk,p.m_n);
      ArrayResizeAL(tk1,p.m_n);
      for(i=0;i<=p.m_n-1;i++)
        {
         tk[i]=vx[i];
         tk1[i]=1;
        }
      //--- calculation
      for(k=1;k<=p.m_n-1;k++)
        {
         //--- calculate discrete product of function vector and TK
         v=0.0;
         for(i_=0;i_<=p.m_n-1;i_++)
            v+=tk[i_]*vp[i_];
         t[k]=v/(0.5*p.m_n);
         //--- Update TK and TK1
         for(i=0;i<=p.m_n-1;i++)
           {
            v=2*vx[i]*tk[i]-tk1[i];
            tk1[i]=tk[i];
            tk[i]=v;
           }
        }
     }
//--- Convert from Chebyshev basis to power basis
   ArrayResizeAL(a,p.m_n);
   for(i=0;i<=p.m_n-1;i++)
      a[i]=0;
   d=0;
//--- calculation
   for(i=0;i<=p.m_n-1;i++)
     {
      for(k=i;k<=p.m_n-1;k++)
        {
         e=a[k];
         a[k]=0;
         //--- check
         if(i<=1 && k==i)
            a[k]=1;
         else
           {
            //--- check
            if(i!=0)
               a[k]=2*d;
            //--- check
            if(k>i+1)
               a[k]=a[k]-a[k-2];
           }
         d=e;
        }
      //--- change values
      d=a[i];
      e=0;
      k=i;
      //--- cycle
      while(k<=p.m_n-1)
        {
         e=e+a[k]*t[k];
         k=k+2;
        }
      a[i]=e;
     }
  }
//+------------------------------------------------------------------+
//| Conversion from power basis to barycentric representation.       |
//| This function has O(N^2) complexity.                             |
//| INPUT PARAMETERS:                                                |
//|     A   -   coefficients, P(x)=sum { A[i]*((X-C)/S)^i, i=0..N-1 }|
//|     N   -   number of coefficients (polynomial degree plus 1)    |
//|             * if given, only leading N elements of A are used    |
//|             * if not given, automatically determined from size   |
//|               of A                                               |
//|     C   -   offset (see below); 0.0 is used as default value.    |
//|     S   -   scale (see below); 1.0 is used as default value.     |
//|             S<>0.                                                |
//| OUTPUT PARAMETERS                                                |
//|     P   -   polynomial in barycentric form                       |
//| NOTES:                                                           |
//| 1.  this function accepts offset and scale, which can be set to  |
//|     improve numerical properties of polynomial. For example, if  |
//|     you interpolate on [-1,+1], you can set C=0 and S=1 and      |
//|     convert from sum of 1, x, x^2, x^3 and so on. In most cases  |
//|     you it is exactly what you need.                             |
//|     However, if your interpolation model was built on [999,1001],|
//|     you will see significant growth of numerical errors when     |
//|     using {1, x, x^2, x^3} as input basis. Converting from sum   |
//|     of 1, (x-1000), (x-1000)^2, (x-1000)^3 will be better option |
//|     (you have to specify 1000.0 as offset C and 1.0 as scale S). |
//| 2.  power basis is ill-conditioned and tricks described above    |
//|     can't solve this problem completely. This function will      |
//|     return barycentric model in any case, but for N>8 accuracy   |
//|     well degrade. However, N's less than 5 are pretty safe.      |
//+------------------------------------------------------------------+
static void CPolInt::PolynomialPow2Bar(double &a[],const int n,const double c,
                                       const double s,CBarycentricInterpolant &p)
  {
//--- create variables
   int    i=0;
   int    k=0;
   double vx=0;
   double vy=0;
   double px=0;
//--- create array
   double y[];
//--- check
   if(!CAp::Assert(CMath::IsFinite(c),__FUNCTION__+": C is not finite!"))
      return;
//--- check
   if(!CAp::Assert(CMath::IsFinite(s),__FUNCTION__+": S is not finite!"))
      return;
//--- check
   if(!CAp::Assert(s!=0.0,__FUNCTION__+": S is zero!"))
      return;
//--- check
   if(!CAp::Assert(n>=1,__FUNCTION__+": N<1"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(a)>=n,__FUNCTION__+": Length(A)<N"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(a,n),__FUNCTION__+": A[] contains INF or NAN"))
      return;
//--- Calculate function values on a Chebyshev grid spanning [-1,+1]
   ArrayResizeAL(y,n);
   for(i=0;i<=n-1;i++)
     {
      //--- Calculate value on a grid spanning [-1,+1]
      vx=MathCos(M_PI*(i+0.5)/n);
      vy=a[0];
      px=vx;
      //--- calculation
      for(k=1;k<=n-1;k++)
        {
         vy=vy+px*a[k];
         px=px*vx;
        }
      y[i]=vy;
     }
//--- Build barycentric interpolant,map grid from [-1,+1] to [A,B]
   PolynomialBuildCheb1(c-s,c+s,y,n,p);
  }
//+------------------------------------------------------------------+
//| Lagrange intepolant: generation of the model on the general grid.|
//| This function has O(N^2) complexity.                             |
//| INPUT PARAMETERS:                                                |
//|     X   -   abscissas, array[0..N-1]                             |
//|     Y   -   function values, array[0..N-1]                       |
//|     N   -   number of points, N>=1                               |
//| OUTPUT PARAMETERS                                                |
//|     P   -   barycentric model which represents Lagrange          |
//|             interpolant (see ratint unit info and                |
//|             BarycentricCalc() description for more information). |
//+------------------------------------------------------------------+
static void CPolInt::PolynomialBuild(double &cx[],double &cy[],const int n,
                                     CBarycentricInterpolant &p)
  {
//--- create variables
   int    j=0;
   int    k=0;
   double b=0;
   double a=0;
   double v=0;
   double mx=0;
   int    i_=0;
//--- create arrays
   double w[];
   double sortrbuf[];
   double sortrbuf2[];
   double x[];
   double y[];
//--- copy arrays
   ArrayCopy(x,cx);
   ArrayCopy(y,cy);
//--- check
   if(!CAp::Assert(n>0,__FUNCTION__+": N<=0!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(x)>=n,__FUNCTION__+": Length(X)<N!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(y)>=n,__FUNCTION__+": Length(Y)<N!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(x,n),__FUNCTION__+": X contains infinite or NaN values!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(y,n),__FUNCTION__+": Y contains infinite or NaN values!"))
      return;
//--- function call
   CTSort::TagSortFastR(x,y,sortrbuf,sortrbuf2,n);
//--- check
   if(!CAp::Assert(CApServ::AreDistinct(x,n),__FUNCTION__+": at least two consequent points are too close!"))
      return;
//--- calculate W[j]
//--- multi-pass algorithm is used to avoid overflow
   ArrayResizeAL(w,n);
   a=x[0];
   b=x[0];
   for(j=0;j<=n-1;j++)
     {
      w[j]=1;
      a=MathMin(a,x[j]);
      b=MathMax(b,x[j]);
     }
//--- calculation
   for(k=0;k<=n-1;k++)
     {
      //--- W[K] is used instead of 0.0 because
      //--- cycle on J does not touch K-th element
      //--- and we MUST get maximum from ALL elements
      mx=MathAbs(w[k]);
      for(j=0;j<=n-1;j++)
        {
         //--- check
         if(j!=k)
           {
            v=(b-a)/(x[j]-x[k]);
            w[j]=w[j]*v;
            mx=MathMax(mx,MathAbs(w[j]));
           }
        }
      //--- check
      if(k%5==0)
        {
         //--- every 5-th run we renormalize W[]
         v=1/mx;
         for(i_=0;i_<=n-1;i_++)
            w[i_]=v*w[i_];
        }
     }
//--- function call
   CRatInt::BarycentricBuildXYW(x,y,w,n,p);
  }
//+------------------------------------------------------------------+
//| Lagrange intepolant: generation of the model on equidistant grid.|
//| This function has O(N) complexity.                               |
//| INPUT PARAMETERS:                                                |
//|     A   -   left boundary of [A,B]                               |
//|     B   -   right boundary of [A,B]                              |
//|     Y   -   function values at the nodes, array[0..N-1]          |
//|     N   -   number of points, N>=1                               |
//|             for N=1 a constant model is constructed.             |
//| OUTPUT PARAMETERS                                                |
//|     P   -   barycentric model which represents Lagrange          |
//|             interpolant (see ratint unit info and                |
//|             BarycentricCalc() description for more information). |
//+------------------------------------------------------------------+
static void CPolInt::PolynomialBuildEqDist(const double a,const double b,
                                           double &y[],const int n,
                                           CBarycentricInterpolant &p)
  {
//--- create variables
   int    i=0;
   double v=0;
//--- create arrays
   double w[];
   double x[];
//--- check
   if(!CAp::Assert(n>0,__FUNCTION__+": N<=0!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(y)>=n,__FUNCTION__+": Length(Y)<N!"))
      return;
//--- check
   if(!CAp::Assert(CMath::IsFinite(a),__FUNCTION__+": A is infinite or NaN!"))
      return;
//--- check
   if(!CAp::Assert(CMath::IsFinite(b),__FUNCTION__+": B is infinite or NaN!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(y,n),__FUNCTION__+": Y contains infinite or NaN values!"))
      return;
//--- check
   if(!CAp::Assert(b!=a,__FUNCTION__+": B=A!"))
      return;
//--- check
   if(!CAp::Assert((double)(a+(b-a)/n)!=a,__FUNCTION__+": B is too close to A!"))
      return;
//--- Special case: N=1
   if(n==1)
     {
      //--- allocation
      ArrayResizeAL(x,1);
      ArrayResizeAL(w,1);
      x[0]=0.5*(b+a);
      w[0]=1;
      //--- function call
      CRatInt::BarycentricBuildXYW(x,y,w,1,p);
      //--- exit the function
      return;
     }
//--- general case
   ArrayResizeAL(x,n);
   ArrayResizeAL(w,n);
   v=1;
//--- calculation
   for(i=0;i<=n-1;i++)
     {
      w[i]=v;
      x[i]=a+(b-a)*i/(n-1);
      v=-(v*(n-1-i));
      v=v/(i+1);
     }
//--- function call
   CRatInt::BarycentricBuildXYW(x,y,w,n,p);
  }
//+------------------------------------------------------------------+
//| Lagrange intepolant on Chebyshev grid (first kind).              |
//| This function has O(N) complexity.                               |
//| INPUT PARAMETERS:                                                |
//|     A   -   left boundary of [A,B]                               |
//|     B   -   right boundary of [A,B]                              |
//|     Y   -   function values at the nodes, array[0..N-1],         |
//|             Y[I] = Y(0.5*(B+A) + 0.5*(B-A)*Cos(PI*(2*i+1)/(2*n)))|
//|     N   -   number of points, N>=1                               |
//|             for N=1 a constant model is constructed.             |
//| OUTPUT PARAMETERS                                                |
//|     P   -   barycentric model which represents Lagrange          |
//|             interpolant (see ratint unit info and                |
//|             BarycentricCalc() description for more information). |
//+------------------------------------------------------------------+
static void CPolInt::PolynomialBuildCheb1(const double a,const double b,
                                          double &y[],const int n,
                                          CBarycentricInterpolant &p)
  {
//--- create variables
   int    i=0;
   double v=0;
   double t=0;
//--- create arrays
   double w[];
   double x[];
//--- check
   if(!CAp::Assert(n>0,__FUNCTION__+": N<=0!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(y)>=n,__FUNCTION__+": Length(Y)<N!"))
      return;
//--- check
   if(!CAp::Assert(CMath::IsFinite(a),__FUNCTION__+": A is infinite or NaN!"))
      return;
//--- check
   if(!CAp::Assert(CMath::IsFinite(b),__FUNCTION__+": B is infinite or NaN!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(y,n),__FUNCTION__+": Y contains infinite or NaN values!"))
      return;
//--- check
   if(!CAp::Assert(b!=a,__FUNCTION__+": B=A!"))
      return;
//--- Special case: N=1
   if(n==1)
     {
      //--- allocation
      ArrayResizeAL(x,1);
      ArrayResizeAL(w,1);
      x[0]=0.5*(b+a);
      w[0]=1;
      //--- function call
      CRatInt::BarycentricBuildXYW(x,y,w,1,p);
      //--- exit the function
      return;
     }
//--- general case
   ArrayResizeAL(x,n);
   ArrayResizeAL(w,n);
   v=1;
//--- calculation
   for(i=0;i<=n-1;i++)
     {
      t=MathTan(0.5*M_PI*(2*i+1)/(2*n));
      w[i]=2*v*t/(1+CMath::Sqr(t));
      x[i]=0.5*(b+a)+0.5*(b-a)*(1-CMath::Sqr(t))/(1+CMath::Sqr(t));
      v=-v;
     }
//--- function call
   CRatInt::BarycentricBuildXYW(x,y,w,n,p);
  }
//+------------------------------------------------------------------+
//| Lagrange intepolant on Chebyshev grid (second kind).             |
//| This function has O(N) complexity.                               |
//| INPUT PARAMETERS:                                                |
//|     A   -   left boundary of [A,B]                               |
//|     B   -   right boundary of [A,B]                              |
//|     Y   -   function values at the nodes, array[0..N-1],         |
//|             Y[I] = Y(0.5*(B+A) + 0.5*(B-A)*Cos(PI*i/(n-1)))      |
//|     N   -   number of points, N>=1                               |
//|             for N=1 a constant model is constructed.             |
//| OUTPUT PARAMETERS                                                |
//|     P   -   barycentric model which represents Lagrange          |
//|             interpolant (see ratint unit info and                |
//|             BarycentricCalc() description for more information). |
//+------------------------------------------------------------------+
static void CPolInt::PolynomialBuildCheb2(const double a,const double b,
                                          double &y[],const int n,
                                          CBarycentricInterpolant &p)
  {
//--- create variables
   int    i=0;
   double v=0;
//--- create arrays
   double w[];
   double x[];
//--- check
   if(!CAp::Assert(n>0,__FUNCTION__+": N<=0!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(y)>=n,__FUNCTION__+": Length(Y)<N!"))
      return;
//--- check
   if(!CAp::Assert(CMath::IsFinite(a),__FUNCTION__+": A is infinite or NaN!"))
      return;
//--- check
   if(!CAp::Assert(CMath::IsFinite(b),__FUNCTION__+": B is infinite or NaN!"))
      return;
//--- check
   if(!CAp::Assert(b!=a,__FUNCTION__+": B=A!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(y,n),__FUNCTION__+": Y contains infinite or NaN values!"))
      return;
//--- Special case: N=1
   if(n==1)
     {
      //--- allocation
      ArrayResizeAL(x,1);
      ArrayResizeAL(w,1);
      x[0]=0.5*(b+a);
      w[0]=1;
      //--- function call
      CRatInt::BarycentricBuildXYW(x,y,w,1,p);
      //--- exit the function
      return;
     }
//--- general case
   ArrayResizeAL(x,n);
   ArrayResizeAL(w,n);
   v=1;
//--- calculation
   for(i=0;i<=n-1;i++)
     {
      //--- check
      if(i==0 || i==n-1)
         w[i]=v*0.5;
      else
         w[i]=v;
      x[i]=0.5*(b+a)+0.5*(b-a)*MathCos(M_PI*i/(n-1));
      v=-v;
     }
//--- function call
   CRatInt::BarycentricBuildXYW(x,y,w,n,p);
  }
//+------------------------------------------------------------------+
//| Fast equidistant polynomial interpolation function with O(N)     |
//| complexity                                                       |
//| INPUT PARAMETERS:                                                |
//|     A   -   left boundary of [A,B]                               |
//|     B   -   right boundary of [A,B]                              |
//|     F   -   function values, array[0..N-1]                       |
//|     N   -   number of points on equidistant grid, N>=1           |
//|             for N=1 a constant model is constructed.             |
//|     T   -   position where P(x) is calculated                    |
//| RESULT                                                           |
//|     value of the Lagrange interpolant at T                       |
//| IMPORTANT                                                        |
//|     this function provides fast interface which is not           |
//|     overflow-safe nor it is very precise.                        |
//|     the best option is to use PolynomialBuildEqDist() or         |
//|     BarycentricCalc() subroutines unless you are pretty sure that|
//|     your data will not result in overflow.                       |
//+------------------------------------------------------------------+
static double CPolInt::PolynomialCalcEqDist(const double a,const double b,
                                            double &f[],const int n,const double t)
  {
//--- create variables
   double s1=0;
   double s2=0;
   double v=0;
   double threshold=0;
   double s=0;
   double h=0;
   int    i=0;
   int    j=0;
   double w=0;
   double x=0;
//--- check
   if(!CAp::Assert(n>0,__FUNCTION__+": N<=0!"))
      return(EMPTY_VALUE);
//--- check
   if(!CAp::Assert(CAp::Len(f)>=n,__FUNCTION__+": Length(F)<N!"))
      return(EMPTY_VALUE);
//--- check
   if(!CAp::Assert(CMath::IsFinite(a),__FUNCTION__+": A is infinite or NaN!"))
      return(EMPTY_VALUE);
//--- check
   if(!CAp::Assert(CMath::IsFinite(b),__FUNCTION__+": B is infinite or NaN!"))
      return(EMPTY_VALUE);
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(f,n),__FUNCTION__+": F contains infinite or NaN values!"))
      return(EMPTY_VALUE);
//--- check
   if(!CAp::Assert(b!=a,__FUNCTION__+": B=A!"))
      return(EMPTY_VALUE);
//--- check
   if(!CAp::Assert(!CInfOrNaN::IsInfinity(t),__FUNCTION__+": T is infinite!"))
      return(EMPTY_VALUE);
//--- Special case: T is NAN
   if(CInfOrNaN::IsNaN(t))
      return(CInfOrNaN::NaN());
//--- Special case: N=1
   if(n==1)
      return(f[0]);
//--- First,decide: should we use "safe" formula (guarded
//--- against overflow) or fast one?
   threshold=MathSqrt(CMath::m_minrealnumber);
   j=0;
   s=t-a;
//--- calculation
   for(i=1;i<=n-1;i++)
     {
      x=a+(double)i/(double)(n-1)*(b-a);
      //--- check
      if(MathAbs(t-x)<MathAbs(s))
        {
         s=t-x;
         j=i;
        }
     }
//--- check
   if(s==0.0)
      return(f[j]);
//--- check
   if(MathAbs(s)>threshold)
     {
      //--- use fast formula
      j=-1;
      s=1.0;
     }
//--- Calculate using safe or fast barycentric formula
   s1=0;
   s2=0;
   w=1.0;
   h=(b-a)/(n-1);
//--- calculation
   for(i=0;i<=n-1;i++)
     {
      //--- check
      if(i!=j)
        {
         v=s*w/(t-(a+i*h));
         s1=s1+v*f[i];
         s2=s2+v;
        }
      else
        {
         v=w;
         s1=s1+v*f[i];
         s2=s2+v;
        }
      //--- change values
      w=-(w*(n-1-i));
      w=w/(i+1);
     }
//--- return result
   return(s1/s2);
  }
//+------------------------------------------------------------------+
//| Fast polynomial interpolation function on Chebyshev points (first|
//| kind) with O(N) complexity.                                      |
//| INPUT PARAMETERS:                                                |
//|     A   -   left boundary of [A,B]                               |
//|     B   -   right boundary of [A,B]                              |
//|     F   -   function values, array[0..N-1]                       |
//|     N   -   number of points on Chebyshev grid (first kind),     |
//|             X[i] = 0.5*(B+A) + 0.5*(B-A)*Cos(PI*(2*i+1)/(2*n))   |
//|             for N=1 a constant model is constructed.             |
//|     T   -   position where P(x) is calculated                    |
//| RESULT                                                           |
//|     value of the Lagrange interpolant at T                       |
//| IMPORTANT                                                        |
//|     this function provides fast interface which is not           |
//|     overflow-safe nor it is very precise                         |
//|     the best option is to use  PolIntBuildCheb1() or             |
//|     BarycentricCalc() subroutines unless you are pretty sure that|
//|     your data will not result in overflow.                       |
//+------------------------------------------------------------------+
static double CPolInt::PolynomialCalcCheb1(const double a,const double b,
                                           double &f[],const int n,double t)
  {
//--- create variables
   double s1=0;
   double s2=0;
   double v=0;
   double threshold=0;
   double s=0;
   int    i=0;
   int    j=0;
   double a0=0;
   double delta=0;
   double alpha=0;
   double beta=0;
   double ca=0;
   double sa=0;
   double tempc=0;
   double temps=0;
   double x=0;
   double w=0;
   double p1=0;
//--- check
   if(!CAp::Assert(n>0,__FUNCTION__+": N<=0!"))
      return(EMPTY_VALUE);
//--- check
   if(!CAp::Assert(CAp::Len(f)>=n,__FUNCTION__+": Length(F)<N!"))
      return(EMPTY_VALUE);
//--- check
   if(!CAp::Assert(CMath::IsFinite(a),__FUNCTION__+": A is infinite or NaN!"))
      return(EMPTY_VALUE);
//--- check
   if(!CAp::Assert(CMath::IsFinite(b),__FUNCTION__+": B is infinite or NaN!"))
      return(EMPTY_VALUE);
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(f,n),__FUNCTION__+": F contains infinite or NaN values!"))
      return(EMPTY_VALUE);
//--- check
   if(!CAp::Assert(b!=a,__FUNCTION__+": B=A!"))
      return(EMPTY_VALUE);
//--- check
   if(!CAp::Assert(!CInfOrNaN::IsInfinity(t),__FUNCTION__+": T is infinite!"))
      return(EMPTY_VALUE);
//--- Special case: T is NAN
   if(CInfOrNaN::IsNaN(t))
      return(CInfOrNaN::NaN());
//--- Special case: N=1
   if(n==1)
      return(f[0]);
//--- Prepare information for the recurrence formula
//--- used to calculate sin(pi*(2j+1)/(2n+2)) and
//--- cos(pi*(2j+1)/(2n+2)):
//--- A0=pi/(2n+2)
//--- Delta=pi/(n+1)
//--- Alpha=2 sin^2 (Delta/2)
//--- Beta=sin(Delta)
//--- so that sin(..)=sin(A0+j*delta) and cos(..)=cos(A0+j*delta).
//--- Then we use
//--- sin(x+delta)=sin(x) - (alpha*sin(x) - beta*cos(x))
//--- cos(x+delta)=cos(x) - (alpha*cos(x) - beta*sin(x))
//--- to repeatedly calculate sin(..) and cos(..).
   threshold=MathSqrt(CMath::m_minrealnumber);
   t=(t-0.5*(a+b))/(0.5*(b-a));
   a0=M_PI/(2*(n-1)+2);
   delta=2*M_PI/(2*(n-1)+2);
   alpha=2*CMath::Sqr(MathSin(delta/2));
   beta=MathSin(delta);
//--- First, decide: should we use "safe" formula (guarded
//--- against overflow) or fast one?
   ca=MathCos(a0);
   sa=MathSin(a0);
   j=0;
   x=ca;
   s=t-x;
//--- calculation
   for(i=1;i<=n-1;i++)
     {
      //--- Next X[i]
      temps=sa-(alpha*sa-beta*ca);
      tempc=ca-(alpha*ca+beta*sa);
      sa=temps;
      ca=tempc;
      x=ca;
      //--- Use X[i]
      if(MathAbs(t-x)<MathAbs(s))
        {
         s=t-x;
         j=i;
        }
     }
//--- check
   if(s==0.0)
      return(f[j]);
//--- check
   if(MathAbs(s)>threshold)
     {
      //--- use fast formula
      j=-1;
      s=1.0;
     }
//--- Calculate using safe or fast barycentric formula
   s1=0;
   s2=0;
   ca=MathCos(a0);
   sa=MathSin(a0);
   p1=1.0;
//--- calculation
   for(i=0;i<=n-1;i++)
     {
      //--- Calculate X[i],W[i]
      x=ca;
      w=p1*sa;
      //--- Proceed
      if(i!=j)
        {
         v=s*w/(t-x);
         s1=s1+v*f[i];
         s2=s2+v;
        }
      else
        {
         v=w;
         s1=s1+v*f[i];
         s2=s2+v;
        }
      //--- Next CA,SA,P1
      temps=sa-(alpha*sa-beta*ca);
      tempc=ca-(alpha*ca+beta*sa);
      sa=temps;
      ca=tempc;
      p1=-p1;
     }
//--- return result
   return(s1/s2);
  }
//+------------------------------------------------------------------+
//| Fast polynomial interpolation function on Chebyshev points       |
//| (second kind) with O(N) complexity.                              |
//| INPUT PARAMETERS:                                                |
//|     A   -   left boundary of [A,B]                               |
//|     B   -   right boundary of [A,B]                              |
//|     F   -   function values, array[0..N-1]                       |
//|     N   -   number of points on Chebyshev grid (second kind),    |
//|             X[i] = 0.5*(B+A) + 0.5*(B-A)*Cos(PI*i/(n-1))         |
//|             for N=1 a constant model is constructed.             |
//|     T   -   position where P(x) is calculated                    |
//| RESULT                                                           |
//|     value of the Lagrange interpolant at T                       |
//| IMPORTANT                                                        |
//|     this function provides fast interface which is not           |
//|     overflow-safe nor it is very precise.                        |
//|     the best option is to use PolIntBuildCheb2() or              |
//|     BarycentricCalc() subroutines unless you are pretty sure that|
//|     your data will not result in overflow.                       |
//+------------------------------------------------------------------+
static double CPolInt::PolynomialCalcCheb2(const double a,const double b,
                                           double &f[],const int n,double t)
  {
//--- create variables
   double s1=0;
   double s2=0;
   double v=0;
   double threshold=0;
   double s=0;
   int    i=0;
   int    j=0;
   double a0=0;
   double delta=0;
   double alpha=0;
   double beta=0;
   double ca=0;
   double sa=0;
   double tempc=0;
   double temps=0;
   double x=0;
   double w=0;
   double p1=0;
//--- check
   if(!CAp::Assert(n>0,__FUNCTION__+": N<=0!"))
      return(EMPTY_VALUE);
//--- check
   if(!CAp::Assert(CAp::Len(f)>=n,__FUNCTION__+": Length(F)<N!"))
      return(EMPTY_VALUE);
//--- check
   if(!CAp::Assert(CMath::IsFinite(a),__FUNCTION__+": A is infinite or NaN!"))
      return(EMPTY_VALUE);
//--- check
   if(!CAp::Assert(CMath::IsFinite(b),__FUNCTION__+": B is infinite or NaN!"))
      return(EMPTY_VALUE);
//--- check
   if(!CAp::Assert(b!=a,__FUNCTION__+": B=A!"))
      return(EMPTY_VALUE);
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(f,n),__FUNCTION__+": F contains infinite or NaN values!"))
      return(EMPTY_VALUE);
//--- check
   if(!CAp::Assert(!CInfOrNaN::IsInfinity(t),__FUNCTION__+": T is infinite!"))
      return(EMPTY_VALUE);
//--- Special case: T is NAN
   if(CInfOrNaN::IsNaN(t))
      return(CInfOrNaN::NaN());
//--- Special case: N=1
   if(n==1)
      return(f[0]);
//--- Prepare information for the recurrence formula
//--- used to calculate sin(pi*i/n) and
//--- cos(pi*i/n):
//--- A0=0
//--- Delta=pi/n
//--- Alpha=2 sin^2 (Delta/2)
//--- Beta=sin(Delta)
//--- so that sin(..)=sin(A0+j*delta) and cos(..)=cos(A0+j*delta).
//--- Then we use
//--- sin(x+delta)=sin(x) - (alpha*sin(x) - beta*cos(x))
//--- cos(x+delta)=cos(x) - (alpha*cos(x) - beta*sin(x))
//--- to repeatedly calculate sin(..) and cos(..).
   threshold=MathSqrt(CMath::m_minrealnumber);
   t=(t-0.5*(a+b))/(0.5*(b-a));
   a0=0.0;
   delta=M_PI/(n-1);
   alpha=2*CMath::Sqr(MathSin(delta/2));
   beta=MathSin(delta);
//--- First,decide: should we use "safe" formula (guarded
//--- against overflow) or fast one?
   ca=MathCos(a0);
   sa=MathSin(a0);
   j=0;
   x=ca;
   s=t-x;
//--- calculation
   for(i=1;i<=n-1;i++)
     {
      //--- Next X[i]
      temps=sa-(alpha*sa-beta*ca);
      tempc=ca-(alpha*ca+beta*sa);
      sa=temps;
      ca=tempc;
      x=ca;
      //--- Use X[i]
      if(MathAbs(t-x)<MathAbs(s))
        {
         s=t-x;
         j=i;
        }
     }
//--- check
   if(s==0.0)
      return(f[j]);
//--- check
   if(MathAbs(s)>threshold)
     {
      //--- use fast formula
      j=-1;
      s=1.0;
     }
//--- Calculate using safe or fast barycentric formula
   s1=0;
   s2=0;
   ca=MathCos(a0);
   sa=MathSin(a0);
   p1=1.0;
//--- calculation
   for(i=0;i<=n-1;i++)
     {
      //--- Calculate X[i],W[i]
      x=ca;
      //--- check
      if(i==0 || i==n-1)
         w=0.5*p1;
      else
         w=1.0*p1;
      //--- Proceed
      if(i!=j)
        {
         v=s*w/(t-x);
         s1=s1+v*f[i];
         s2=s2+v;
        }
      else
        {
         v=w;
         s1=s1+v*f[i];
         s2=s2+v;
        }
      //--- Next CA,SA,P1
      temps=sa-(alpha*sa-beta*ca);
      tempc=ca-(alpha*ca+beta*sa);
      sa=temps;
      ca=tempc;
      p1=-p1;
     }
//--- return result
   return(s1/s2);
  }
//+------------------------------------------------------------------+
//| 1-dimensional spline inteprolant                                 |
//+------------------------------------------------------------------+
class CSpline1DInterpolant
  {
public:
   //--- variables
   bool              m_periodic;
   int               m_n;
   int               m_k;
   //--- arrays
   double            m_x[];
   double            m_c[];
   //--- constructor, destructor
                     CSpline1DInterpolant(void);
                    ~CSpline1DInterpolant(void);
   //--- copy
   void              Copy(CSpline1DInterpolant &obj);
  };
//+------------------------------------------------------------------+
//| Constructor without parameters                                   |
//+------------------------------------------------------------------+
CSpline1DInterpolant::CSpline1DInterpolant(void)
  {

  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CSpline1DInterpolant::~CSpline1DInterpolant(void)
  {

  }
//+------------------------------------------------------------------+
//| Copy                                                             |
//+------------------------------------------------------------------+
void CSpline1DInterpolant::Copy(CSpline1DInterpolant &obj)
  {
//--- copy variables
   m_periodic=obj.m_periodic;
   m_n=obj.m_n;
   m_k=obj.m_k;
//--- copy arrays
   ArrayCopy(m_x,obj.m_x);
   ArrayCopy(m_c,obj.m_c);
  }
//+------------------------------------------------------------------+
//| 1-dimensional spline inteprolant                                 |
//+------------------------------------------------------------------+
class CSpline1DInterpolantShell
  {
private:
   CSpline1DInterpolant m_innerobj;
public:
   //--- constructors, destructor
                     CSpline1DInterpolantShell(void);
                     CSpline1DInterpolantShell(CSpline1DInterpolant &obj);
                    ~CSpline1DInterpolantShell(void);
   //--- method
   CSpline1DInterpolant *GetInnerObj(void);
  };
//+------------------------------------------------------------------+
//| Constructor without parameters                                   |
//+------------------------------------------------------------------+
CSpline1DInterpolantShell::CSpline1DInterpolantShell(void)
  {

  }
//+------------------------------------------------------------------+
//| Copy constructor                                                 |
//+------------------------------------------------------------------+
CSpline1DInterpolantShell::CSpline1DInterpolantShell(CSpline1DInterpolant &obj)
  {
//--- copy
   m_innerobj.Copy(obj);
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CSpline1DInterpolantShell::~CSpline1DInterpolantShell(void)
  {

  }
//+------------------------------------------------------------------+
//| Return object of class                                           |
//+------------------------------------------------------------------+
CSpline1DInterpolant *CSpline1DInterpolantShell::GetInnerObj(void)
  {
//--- return result
   return(GetPointer(m_innerobj));
  }
//+------------------------------------------------------------------+
//| 1-dimensional spline interpolation                               |
//+------------------------------------------------------------------+
class CSpline1D
  {
private:
   //--- private methods
   static void       Spline1DGridDiffCubicInternal(double &x[],double &y[],const int n,const int boundltype,const double boundl,const int boundrtype,const double boundr,double &d[],double &a1[],double &a2[],double &a3[],double &b[],double &dt[]);
   static void       HeapSortPoints(double &x[],double &y[],const int n);
   static void       HeapSortPPoints(double &x[],double &y[],int &p[],const int n);
   static void       SolveTridiagonal(double &a[],double &cb[],double &c[],double &cd[],const int n,double &x[]);
   static void       SolveCyclicTridiagonal(double &a[],double &cb[],double &c[],double &d[],const int n,double &x[]);
   static double     DiffThreePoint(double t,const double x0,const double f0,double x1,const double f1,double x2,const double f2);
public:
   //--- constructor, destructor
                     CSpline1D(void);
                    ~CSpline1D(void);
   //--- public methods
   static void       Spline1DBuildLinear(double &cx[],double &cy[],const int n,CSpline1DInterpolant &c);
   static void       Spline1DBuildCubic(double &cx[],double &cy[],const int n,const int boundltype,const double boundl,const int boundrtype,const double boundr,CSpline1DInterpolant &c);
   static void       Spline1DGridDiffCubic(double &cx[],double &cy[],const int n,const int boundltype,const double boundl,const int boundrtype,const double boundr,double &d[]);
   static void       Spline1DGridDiff2Cubic(double &cx[],double &cy[],const int n,const int boundltype,const double boundl,const int boundrtype,const double boundr,double &d1[],double &d2[]);
   static void       Spline1DConvCubic(double &cx[],double &cy[],const int n,const int boundltype,const double boundl,const int boundrtype,const double boundr,double &cx2[],const int n2,double &y2[]);
   static void       Spline1DConvDiffCubic(double &cx[],double &cy[],const int n,const int boundltype,const double boundl,const int boundrtype,const double boundr,double &cx2[],const int n2,double &y2[],double &d2[]);
   static void       Spline1DConvDiff2Cubic(double &cx[],double &cy[],const int n,const int boundltype,const double boundl,const int boundrtype,const double boundr,double &cx2[],const int n2,double &y2[],double &d2[],double &dd2[]);
   static void       Spline1DBuildCatmullRom(double &cx[],double &cy[],const int n,const int boundtype,const double tension,CSpline1DInterpolant &c);
   static void       Spline1DBuildHermite(double &cx[],double &cy[],double &cd[],const int n,CSpline1DInterpolant &c);
   static void       Spline1DBuildAkima(double &cx[],double &cy[],const int n,CSpline1DInterpolant &c);
   static double     Spline1DCalc(CSpline1DInterpolant &c,double x);
   static void       Spline1DDiff(CSpline1DInterpolant &c,double x,double &s,double &ds,double &d2s);
   static void       Spline1DCopy(CSpline1DInterpolant &c,CSpline1DInterpolant &cc);
   static void       Spline1DUnpack(CSpline1DInterpolant &c,int &n,CMatrixDouble &tbl);
   static void       Spline1DLinTransX(CSpline1DInterpolant &c,const double a,const double b);
   static void       Spline1DLinTransY(CSpline1DInterpolant &c,const double a,const double b);
   static double     Spline1DIntegrate(CSpline1DInterpolant &c,double x);
   static void       Spline1DConvDiffInternal(double &xold[],double &yold[],double &dold[],const int n,double &x2[],const int n2,double &y[],const bool needy,double &d1[],const bool needd1,double &d2[],const bool needd2);
   static void       HeapSortDPoints(double &x[],double &y[],double &d[],const int n);
  };
//+------------------------------------------------------------------+
//| Constructor without parameters                                   |
//+------------------------------------------------------------------+
CSpline1D::CSpline1D(void)
  {

  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CSpline1D::~CSpline1D(void)
  {

  }
//+------------------------------------------------------------------+
//| This subroutine builds linear spline interpolant                 |
//| INPUT PARAMETERS:                                                |
//|     X   -   spline nodes, array[0..N-1]                          |
//|     Y   -   function values, array[0..N-1]                       |
//|     N   -   points count (optional):                             |
//|             * N>=2                                               |
//|             * if given, only first N points are used to build    |
//|               spline                                             |
//|             * if not given, automatically detected from X/Y      |
//|               sizes (len(X) must be equal to len(Y))             |
//| OUTPUT PARAMETERS:                                               |
//|     C   -   spline interpolant                                   |
//| ORDER OF POINTS                                                  |
//| Subroutine automatically sorts points, so caller may pass        |
//| unsorted array.                                                  |
//+------------------------------------------------------------------+
static void CSpline1D::Spline1DBuildLinear(double &cx[],double &cy[],
                                           const int n,CSpline1DInterpolant &c)
  {
//--- create a variable
   int i=0;
//--- create arrays
   double x[];
   double y[];
//--- copy arrays
   ArrayCopy(x,cx);
   ArrayCopy(y,cy);
//--- check
   if(!CAp::Assert(n>1,__FUNCTION__+": N<2!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(x)>=n,__FUNCTION__+": Length(X)<N!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(y)>=n,__FUNCTION__+": Length(Y)<N!"))
      return;
//--- check and sort points
   if(!CAp::Assert(CApServ::IsFiniteVector(x,n),__FUNCTION__+": X contains infinite or NAN values!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(y,n),__FUNCTION__+": Y contains infinite or NAN values!"))
      return;
//--- function call
   HeapSortPoints(x,y,n);
//--- check
   if(!CAp::Assert(CApServ::AreDistinct(x,n),__FUNCTION__+": at least two consequent points are too close!"))
      return;
//--- Build
   c.m_periodic=false;
   c.m_n=n;
   c.m_k=3;
//--- allocation
   ArrayResizeAL(c.m_x,n);
   ArrayResizeAL(c.m_c,4*(n-1));
//--- copy
   for(i=0;i<=n-1;i++)
      c.m_x[i]=x[i];
//--- calculation
   for(i=0;i<=n-2;i++)
     {
      c.m_c[4*i+0]=y[i];
      c.m_c[4*i+1]=(y[i+1]-y[i])/(x[i+1]-x[i]);
      c.m_c[4*i+2]=0;
      c.m_c[4*i+3]=0;
     }
  }
//+------------------------------------------------------------------+
//| This subroutine builds cubic spline interpolant.                 |
//| INPUT PARAMETERS:                                                |
//|     X           -   spline nodes, array[0..N-1].                 |
//|     Y           -   function values, array[0..N-1].              |
//| OPTIONAL PARAMETERS:                                             |
//|     N           -   points count:                                |
//|                     * N>=2                                       |
//|                     * if given, only first N points are used to  |
//|                       build spline                               |
//|                     * if not given, automatically detected from  |
//|                       X/Y sizes (len(X) must be equal to len(Y)) |
//|     BoundLType  -   boundary condition type for the left boundary|
//|     BoundL      -   left boundary condition (first or second     |
//|                     derivative, depending on the BoundLType)     |
//|     BoundRType  -   boundary condition type for the right        |
//|                     boundary                                     |
//|     BoundR      -   right boundary condition (first or second    |
//|                     derivative, depending on the BoundRType)     |
//| OUTPUT PARAMETERS:                                               |
//|     C           -   spline interpolant                           |
//| ORDER OF POINTS                                                  |
//| Subroutine automatically sorts points, so caller may pass        |
//| unsorted array.                                                  |
//| SETTING BOUNDARY VALUES:                                         |
//| The BoundLType/BoundRType parameters can have the following      |
//| values:                                                          |
//|     * -1, which corresonds to the periodic (cyclic) boundary     |
//|           conditions. In this case:                              |
//|           * both BoundLType and BoundRType must be equal to -1.  |
//|           * BoundL/BoundR are ignored                            |
//|           * Y[last] is ignored (it is assumed to be equal to     |
//|             Y[first]).                                           |
//|     *  0, which  corresponds to the parabolically terminated     |
//|           spline (BoundL and/or BoundR are ignored).             |
//|     *  1, which corresponds to the first derivative boundary     |
//|           condition                                              |
//|     *  2, which corresponds to the second derivative boundary    |
//|           condition                                              |
//|     *  by default, BoundType=0 is used                           |
//| PROBLEMS WITH PERIODIC BOUNDARY CONDITIONS:                      |
//| Problems with periodic boundary conditions have                  |
//| Y[first_point]=Y[last_point]. However, this subroutine doesn't   |
//| require you to specify equal values for the first and last       |
//| points - it automatically forces them to be equal by copying     |
//| Y[first_point] (corresponds to the leftmost, minimal X[]) to     |
//| Y[last_point]. However it is recommended to pass consistent      |
//| values of Y[], i.e. to make Y[first_point]=Y[last_point].        |
//+------------------------------------------------------------------+
static void CSpline1D::Spline1DBuildCubic(double &cx[],double &cy[],
                                          const int n,const int boundltype,
                                          const double boundl,const int boundrtype,
                                          const double boundr,CSpline1DInterpolant &c)
  {
//--- create a variable
   int ylen=0;
//--- create arrays
   double a1[];
   double a2[];
   double a3[];
   double b[];
   double dt[];
   double d[];
   int    p[];
   double x[];
   double y[];
//--- copy arrays
   ArrayCopy(x,cx);
   ArrayCopy(y,cy);
//--- check correctness of boundary conditions
   if(!CAp::Assert(((boundltype==-1 || boundltype==0) || boundltype==1) || boundltype==2,__FUNCTION__+": incorrect BoundLType!"))
      return;
//--- check
   if(!CAp::Assert(((boundrtype==-1 || boundrtype==0) || boundrtype==1) || boundrtype==2,__FUNCTION__+": incorrect BoundRType!"))
      return;
//--- check
   if(!CAp::Assert((boundrtype==-1 && boundltype==-1) || (boundrtype!=-1 && boundltype!=-1),__FUNCTION__+": incorrect BoundLType/BoundRType!"))
      return;
//--- check
   if(boundltype==1 || boundltype==2)
     {
      //--- check
      if(!CAp::Assert(CMath::IsFinite(boundl),__FUNCTION__+": BoundL is infinite or NAN!"))
         return;
     }
//--- check
   if(boundrtype==1 || boundrtype==2)
     {
      //--- check
      if(!CAp::Assert(CMath::IsFinite(boundr),__FUNCTION__+": BoundR is infinite or NAN!"))
         return;
     }
//--- check lengths of arguments
   if(!CAp::Assert(n>=2,__FUNCTION__+": N<2!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(x)>=n,__FUNCTION__+": Length(X)<N!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(y)>=n,__FUNCTION__+": Length(Y)<N!"))
      return;
//--- check and sort points
   ylen=n;
//--- check
   if(boundltype==-1)
      ylen=n-1;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(x,n),__FUNCTION__+": X contains infinite or NAN values!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(y,ylen),__FUNCTION__+": Y contains infinite or NAN values!"))
      return;
//--- function call
   HeapSortPPoints(x,y,p,n);
//--- check
   if(!CAp::Assert(CApServ::AreDistinct(x,n),__FUNCTION__+": at least two consequent points are too close!"))
      return;
//--- Now we've checked and preordered everything,
//--- so we can call internal function to calculate derivatives,
//--- and then build Hermite spline using these derivatives
   Spline1DGridDiffCubicInternal(x,y,n,boundltype,boundl,boundrtype,boundr,d,a1,a2,a3,b,dt);
   Spline1DBuildHermite(x,y,d,n,c);
//--- check
   if(boundltype==-1 || boundrtype==-1)
      c.m_periodic=1;
   else
      c.m_periodic=0;
  }
//+------------------------------------------------------------------+
//| This function solves following problem: given table y[] of       |
//| function values at nodes x[], it calculates and returns table of |
//| function derivatives  d[] (calculated at the same nodes x[]).    |
//| This function yields same result as Spline1DBuildCubic() call    |
//| followed  by sequence of Spline1DDiff() calls, but it can be     |
//| several times faster when called for ordered X[] and X2[].       |
//| INPUT PARAMETERS:                                                |
//|     X           -   spline nodes                                 |
//|     Y           -   function values                              |
//| OPTIONAL PARAMETERS:                                             |
//|     N           -   points count:                                |
//|                     * N>=2                                       |
//|                     * if given, only first N points are used     |
//|                     * if not given, automatically detected from  |
//|                       X/Y sizes (len(X) must be equal to len(Y)) |
//|     BoundLType  -   boundary condition type for the left boundary|
//|     BoundL      -   left boundary condition (first or second     |
//|                     derivative, depending on the BoundLType)     |
//|     BoundRType  -   boundary condition type for the right        |
//|                     boundary                                     |
//|     BoundR      -   right boundary condition (first or second    |
//|                     derivative, depending on the BoundRType)     |
//| OUTPUT PARAMETERS:                                               |
//|     D           -   derivative values at X[]                     |
//| ORDER OF POINTS                                                  |
//| Subroutine automatically sorts points, so caller may pass        |
//| unsorted array. Derivative values are correctly reordered on     |
//| return, so D[I] is always equal to S'(X[I]) independently of     |
//| points order.                                                    |
//| SETTING BOUNDARY VALUES:                                         |
//| The BoundLType/BoundRType parameters can have the following      |
//| values:                                                          |
//|     * -1, which corresonds to the periodic (cyclic) boundary     |
//|           conditions. In this case:                              |
//|           * both BoundLType and BoundRType must be equal to -1.  |
//|           * BoundL/BoundR are ignored                            |
//|           * Y[last] is ignored (it is assumed to be equal to     |
//|           Y[first]).                                             |
//|     *  0, which corresponds to the parabolically terminated      |
//|           spline (BoundL and/or BoundR are ignored).             |
//|     *  1, which corresponds to the first derivative boundary     |
//|           condition                                              |
//|     *  2, which corresponds to the second derivative boundary    |
//|           condition                                              |
//|     *  by default, BoundType=0 is used                           |
//| PROBLEMS WITH PERIODIC BOUNDARY CONDITIONS:                      |
//| Problems with periodic boundary conditions have                  |
//| Y[first_point]=Y[last_point]. However, this subroutine doesn't   |
//| require you to specify equal values for the first and last       |
//| points - it automatically forces them to be equal by copying     |
//| Y[first_point] (corresponds to the leftmost, minimal X[]) to     |
//| Y[last_point]. However it is recommended to pass consistent      |
//| values of Y[], i.e. to make Y[first_point]=Y[last_point].        |
//+------------------------------------------------------------------+
static void CSpline1D::Spline1DGridDiffCubic(double &cx[],double &cy[],
                                             const int n,const int boundltype,
                                             const double boundl,const int boundrtype,
                                             const double boundr,double &d[])
  {
//--- create variables
   int i=0;
   int ylen=0;
   int i_=0;
//--- create arrays
   double a1[];
   double a2[];
   double a3[];
   double b[];
   double dt[];
   int    p[];
   double x[];
   double y[];
//--- copy arrays
   ArrayCopy(x,cx);
   ArrayCopy(y,cy);
//--- check correctness of boundary conditions
   if(!CAp::Assert(((boundltype==-1 || boundltype==0) || boundltype==1) || boundltype==2,__FUNCTION__+": incorrect BoundLType!"))
      return;
//--- check
   if(!CAp::Assert(((boundrtype==-1 || boundrtype==0) || boundrtype==1) || boundrtype==2,__FUNCTION__+": incorrect BoundRType!"))
      return;
//--- check
   if(!CAp::Assert((boundrtype==-1 && boundltype==-1) || (boundrtype!=-1 && boundltype!=-1),__FUNCTION__+": incorrect BoundLType/BoundRType!"))
      return;
//--- check
   if(boundltype==1 || boundltype==2)
     {
      //--- check
      if(!CAp::Assert(CMath::IsFinite(boundl),__FUNCTION__+": BoundL is infinite or NAN!"))
         return;
     }
//--- check
   if(boundrtype==1 || boundrtype==2)
     {
      //--- check
      if(!CAp::Assert(CMath::IsFinite(boundr),__FUNCTION__+": BoundR is infinite or NAN!"))
         return;
     }
//--- check lengths of arguments
   if(!CAp::Assert(n>=2,__FUNCTION__+": N<2!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(x)>=n,__FUNCTION__+": Length(X)<N!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(y)>=n,__FUNCTION__+": Length(Y)<N!"))
      return;
//--- check and sort points
   ylen=n;
//--- check
   if(boundltype==-1)
      ylen=n-1;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(x,n),__FUNCTION__+": X contains infinite or NAN values!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(y,ylen),__FUNCTION__+": Y contains infinite or NAN values!"))
      return;
//--- function call
   HeapSortPPoints(x,y,p,n);
//--- check
   if(!CAp::Assert(CApServ::AreDistinct(x,n),__FUNCTION__+": at least two consequent points are too close!"))
      return;
//--- Now we've checked and preordered everything,
//--- so we can call internal function.
   Spline1DGridDiffCubicInternal(x,y,n,boundltype,boundl,boundrtype,boundr,d,a1,a2,a3,b,dt);
//--- Remember that HeapSortPPoints() call?
//--- Now we have to reorder them back.
   if(CAp::Len(dt)<n)
      ArrayResizeAL(dt,n);
//--- copy
   for(i=0;i<=n-1;i++)
      dt[p[i]]=d[i];
   for(i_=0;i_<=n-1;i_++)
      d[i_]=dt[i_];
  }
//+------------------------------------------------------------------+
//| This function solves following problem: given table y[] of       |
//| function values at nodes x[], it calculates and returns tables of|
//| first and second function derivatives d1[] and d2[] (calculated  |
//| at the same nodes x[]).                                          |
//| This function yields same result as Spline1DBuildCubic() call    |
//| followed  by sequence of Spline1DDiff() calls, but it can be     |
//| several times faster when called for ordered X[] and X2[].       |
//| INPUT PARAMETERS:                                                |
//|     X           -   spline nodes                                 |
//|     Y           -   function values                              |
//| OPTIONAL PARAMETERS:                                             |
//|     N           -   points count:                                |
//|                     * N>=2                                       |
//|                     * if given, only first N points are used     |
//|                     * if not given, automatically detected from  |
//|                       X/Y sizes (len(X) must be equal to len(Y)) |
//|     BoundLType  -   boundary condition type for the left boundary|
//|     BoundL      -   left boundary condition (first or second     |
//|                     derivative, depending on the BoundLType)     |
//|     BoundRType  -   boundary condition type for the right        |
//|                     boundary                                     |
//|     BoundR      -   right boundary condition (first or second    |
//|                     derivative, depending on the BoundRType)     |
//| OUTPUT PARAMETERS:                                               |
//|     D1          -   S' values at X[]                             |
//|     D2          -   S'' values at X[]                            |
//| ORDER OF POINTS                                                  |
//| Subroutine automatically sorts points, so caller may pass        |
//| unsorted array. Derivative values are correctly reordered on     |
//| return, so D[I] is always equal to S'(X[I]) independently of     |
//| points order.                                                    |
//| SETTING BOUNDARY VALUES:                                         |
//| The BoundLType/BoundRType parameters can have the following      |
//| values:                                                          |
//|     * -1, which corresonds to the periodic (cyclic) boundary     |
//|           conditions. In this case:                              |
//|           * both BoundLType and BoundRType must be equal to -1.  |
//|           * BoundL/BoundR are ignored                            |
//|           * Y[last] is ignored (it is assumed to be equal to     |
//|           Y[first]).                                             |
//|     *  0, which corresponds to the parabolically terminated      |
//|           spline (BoundL and/or BoundR are ignored).             |
//|     *  1, which corresponds to the first derivative boundary     |
//|           condition                                              |
//|     *  2, which corresponds to the second derivative boundary    |
//|           condition                                              |
//|     *  by default, BoundType=0 is used                           |
//| PROBLEMS WITH PERIODIC BOUNDARY CONDITIONS:                      |
//| Problems with periodic boundary conditions have                  |
//| Y[first_point]=Y[last_point].                                    |
//| However, this subroutine doesn't require you to specify equal    |
//| values for the first and last points - it automatically forces   |
//| them to be equal by copying Y[first_point] (corresponds to the   |
//| leftmost, minimal X[]) to Y[last_point]. However it is           |
//| recommended to pass consistent values of Y[], i.e. to make       |
//| Y[first_point]=Y[last_point].                                    |
//+------------------------------------------------------------------+
static void CSpline1D::Spline1DGridDiff2Cubic(double &cx[],double &cy[],
                                              const int n,const int boundltype,
                                              const double boundl,const int boundrtype,
                                              const double boundr,double &d1[],double &d2[])
  {
//--- create variables
   int    i=0;
   int    ylen=0;
   double delta=0;
   double delta2=0;
   double delta3=0;
   double s0=0;
   double s1=0;
   double s2=0;
   double s3=0;
   int    i_=0;
//--- create arrays
   double a1[];
   double a2[];
   double a3[];
   double b[];
   double dt[];
   int    p[];
   double x[];
   double y[];
//--- copy arrays
   ArrayCopy(x,cx);
   ArrayCopy(y,cy);
//--- check correctness of boundary conditions
   if(!CAp::Assert(((boundltype==-1 || boundltype==0) || boundltype==1) || boundltype==2,__FUNCTION__+": incorrect BoundLType!"))
      return;
//--- check
   if(!CAp::Assert(((boundrtype==-1 || boundrtype==0) || boundrtype==1) || boundrtype==2,__FUNCTION__+": incorrect BoundRType!"))
      return;
//--- check
   if(!CAp::Assert((boundrtype==-1 && boundltype==-1) || (boundrtype!=-1 && boundltype!=-1),__FUNCTION__+": incorrect BoundLType/BoundRType!"))
      return;
//--- check
   if(boundltype==1 || boundltype==2)
     {
      //--- check
      if(!CAp::Assert(CMath::IsFinite(boundl),__FUNCTION__+": BoundL is infinite or NAN!"))
         return;
     }
//--- check
   if(boundrtype==1 || boundrtype==2)
     {
      //--- check
      if(!CAp::Assert(CMath::IsFinite(boundr),__FUNCTION__+": BoundR is infinite or NAN!"))
         return;
     }
//--- check lengths of arguments
   if(!CAp::Assert(n>=2,__FUNCTION__+": N<2!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(x)>=n,__FUNCTION__+": Length(X)<N!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(y)>=n,__FUNCTION__+": Length(Y)<N!"))
      return;
//--- check and sort points
   ylen=n;
//--- check
   if(boundltype==-1)
      ylen=n-1;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(x,n),__FUNCTION__+": X contains infinite or NAN values!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(y,ylen),__FUNCTION__+": Y contains infinite or NAN values!"))
      return;
//--- function call
   HeapSortPPoints(x,y,p,n);
//--- check
   if(!CAp::Assert(CApServ::AreDistinct(x,n),__FUNCTION__+": at least two consequent points are too close!"))
      return;
//--- Now we've checked and preordered everything,
//--- so we can call internal function.
//--- After this call we will calculate second derivatives
//--- (manually,by converting to the power basis)
   Spline1DGridDiffCubicInternal(x,y,n,boundltype,boundl,boundrtype,boundr,d1,a1,a2,a3,b,dt);
//--- allocation
   ArrayResizeAL(d2,n);
   delta=0;
   s2=0;
   s3=0;
//--- calculation
   for(i=0;i<=n-2;i++)
     {
      //--- We convert from Hermite basis to the power basis.
      //--- Si is coefficient before x^i.
      //--- Inside this cycle we need just S2,
      //--- because we calculate S'' exactly at spline node,
      //--- (only x^2 matters at x=0),but after iterations
      //--- will be over,we will need other coefficients
      //--- to calculate spline value at the last node.
      delta=x[i+1]-x[i];
      delta2=CMath::Sqr(delta);
      delta3=delta*delta2;
      s0=y[i];
      s1=d1[i];
      s2=(3*(y[i+1]-y[i])-2*d1[i]*delta-d1[i+1]*delta)/delta2;
      s3=(2*(y[i]-y[i+1])+d1[i]*delta+d1[i+1]*delta)/delta3;
      d2[i]=2*s2;
     }
   d2[n-1]=2*s2+6*s3*delta;
//--- Remember that HeapSortPPoints() call?
//--- Now we have to reorder them back.
   if(CAp::Len(dt)<n)
      ArrayResizeAL(dt,n);
//--- copy
   for(i=0;i<=n-1;i++)
      dt[p[i]]=d1[i];
   for(i_=0;i_<=n-1;i_++)
      d1[i_]=dt[i_];
   for(i=0;i<=n-1;i++)
      dt[p[i]]=d2[i];
   for(i_=0;i_<=n-1;i_++)
      d2[i_]=dt[i_];
  }
//+------------------------------------------------------------------+
//| This function solves following problem: given table y[] of       |
//| function values at old nodes x[]  and new nodes  x2[], it        |
//| calculates and returns table of function values y2[] (calculated |
//| at x2[]).                                                        |
//| This function yields same result as Spline1DBuildCubic() call    |
//| followed  by sequence of Spline1DDiff() calls, but it can be     |
//| several times faster when called for ordered X[] and X2[].       |
//| INPUT PARAMETERS:                                                |
//|     X           -   old spline nodes                             |
//|     Y           -   function values                              |
//|     X2           -  new spline nodes                             |
//| OPTIONAL PARAMETERS:                                             |
//|     N           -   points count:                                |
//|                     * N>=2                                       |
//|                     * if given, only first N points from X/Y are |
//|                       used                                       |
//|                     * if not given, automatically detected from  |
//|                       X/Y sizes (len(X) must be equal to len(Y)) |
//|     BoundLType  -   boundary condition type for the left boundary|
//|     BoundL      -   left boundary condition (first or second     |
//|                     derivative, depending on the BoundLType)     |
//|     BoundRType  -   boundary condition type for the right        |
//|                     boundary                                     |
//|     BoundR      -   right boundary condition (first or second    |
//|                     derivative, depending on the BoundRType)     |
//|     N2          -   new points count:                            |
//|                     * N2>=2                                      |
//|                     * if given, only first N2 points from X2 are |
//|                       used                                       |
//|                     * if not given, automatically detected from  |
//|                       X2 size                                    |
//| OUTPUT PARAMETERS:                                               |
//|     F2          -   function values at X2[]                      |
//| ORDER OF POINTS                                                  |
//| Subroutine automatically sorts points, so caller  may pass       |
//| unsorted array. Function  values  are correctly reordered on     |
//| return, so F2[I] is always equal to S(X2[I]) independently of    |
//| points order.                                                    |
//| SETTING BOUNDARY VALUES:                                         |
//| The BoundLType/BoundRType parameters can have the following      |
//| values:                                                          |
//|     * -1, which corresonds to the periodic (cyclic) boundary     |
//|           conditions. In this case:                              |
//|           * both BoundLType and BoundRType must be equal to -1.  |
//|           * BoundL/BoundR are ignored                            |
//|           * Y[last] is ignored (it is assumed to be equal to     |
//|           Y[first]).                                             |
//|     *  0, which corresponds to the parabolically terminated      |
//|           spline (BoundL and/or BoundR are ignored).             |
//|     *  1, which corresponds to the first derivative boundary     |
//|           condition                                              |
//|     *  2, which corresponds to the second derivative boundary    |
//|           condition                                              |
//|     *  by default, BoundType=0 is used                           |
//| PROBLEMS WITH PERIODIC BOUNDARY CONDITIONS:                      |
//| Problems with periodic boundary conditions have                  |
//| Y[first_point]=Y[last_point]. However, this subroutine doesn't   |
//| require you to specify equal values for the first and last       |
//| points - it automatically forces them to be equal by copying     |
//| Y[first_point] (corresponds to the leftmost, minimal X[]) to     |
//| Y[last_point]. However it is recommended to pass consistent      |
//| values of Y[], i.e. to make Y[first_point]=Y[last_point].        |
//+------------------------------------------------------------------+
static void CSpline1D::Spline1DConvCubic(double &cx[],double &cy[],
                                         const int n,const int boundltype,
                                         const double boundl,const int boundrtype,
                                         const double boundr,double &cx2[],
                                         const int n2,double &y2[])
  {
//--- create variables
   int    i=0;
   int    ylen=0;
   double t=0;
   double t2=0;
   int    i_=0;
//--- create arrays
   double a1[];
   double a2[];
   double a3[];
   double b[];
   double d[];
   double dt[];
   double d1[];
   double d2[];
   int    p[];
   int    p2[];
   double x[];
   double y[];
   double x2[];
//--- copy arrays
   ArrayCopy(x,cx);
   ArrayCopy(y,cy);
   ArrayCopy(x2,cx2);
//--- check correctness of boundary conditions
   if(!CAp::Assert(((boundltype==-1 || boundltype==0) || boundltype==1) || boundltype==2,__FUNCTION__+": incorrect BoundLType!"))
      return;
//--- check
   if(!CAp::Assert(((boundrtype==-1 || boundrtype==0) || boundrtype==1) || boundrtype==2,__FUNCTION__+": incorrect BoundRType!"))
      return;
//--- check
   if(!CAp::Assert((boundrtype==-1 && boundltype==-1) || (boundrtype!=-1 && boundltype!=-1),__FUNCTION__+": incorrect BoundLType/BoundRType!"))
      return;
//--- check
   if(boundltype==1 || boundltype==2)
     {
      //--- check
      if(!CAp::Assert(CMath::IsFinite(boundl),__FUNCTION__+": BoundL is infinite or NAN!"))
         return;
     }
//--- check
   if(boundrtype==1 || boundrtype==2)
     {
      //--- check
      if(!CAp::Assert(CMath::IsFinite(boundr),__FUNCTION__+": BoundR is infinite or NAN!"))
         return;
     }
//--- check lengths of arguments
   if(!CAp::Assert(n>=2,__FUNCTION__+": N<2!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(x)>=n,__FUNCTION__+": Length(X)<N!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(y)>=n,__FUNCTION__+": Length(Y)<N!"))
      return;
//--- check
   if(!CAp::Assert(n2>=2,__FUNCTION__+": N2<2!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(x2)>=n2,__FUNCTION__+": Length(X2)<N2!"))
      return;
//--- check and sort X/Y
   ylen=n;
//--- check
   if(boundltype==-1)
      ylen=n-1;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(x,n),__FUNCTION__+": X contains infinite or NAN values!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(y,ylen),__FUNCTION__+": Y contains infinite or NAN values!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(x2,n2),__FUNCTION__+": X2 contains infinite or NAN values!"))
      return;
//--- function call
   HeapSortPPoints(x,y,p,n);
//--- check
   if(!CAp::Assert(CApServ::AreDistinct(x,n),__FUNCTION__+": at least two consequent points are too close!"))
      return;
//--- set up DT (we will need it below)
   ArrayResizeAL(dt,MathMax(n,n2));
//--- sort X2:
//--- * use fake array DT because HeapSortPPoints() needs both integer AND real arrays
//--- * if we have periodic problem,wrap points
//--- * sort them,store permutation at P2
   if(boundrtype==-1 && boundltype==-1)
     {
      for(i=0;i<=n2-1;i++)
        {
         t=x2[i];
         CApServ::ApPeriodicMap(t,x[0],x[n-1],t2);
         x2[i]=t;
        }
     }
//--- function call
   HeapSortPPoints(x2,dt,p2,n2);
//--- Now we've checked and preordered everything,so we:
//--- * call internal GridDiff() function to get Hermite form of spline
//--- * convert using internal Conv() function
//--- * convert Y2 back to original order
   Spline1DGridDiffCubicInternal(x,y,n,boundltype,boundl,boundrtype,boundr,d,a1,a2,a3,b,dt);
   Spline1DConvDiffInternal(x,y,d,n,x2,n2,y2,true,d1,false,d2,false);
//--- check
   if(!CAp::Assert(CAp::Len(dt)>=n2,__FUNCTION__+": internal error!"))
      return;
//--- copy
   for(i=0;i<=n2-1;i++)
      dt[p2[i]]=y2[i];
   for(i_=0;i_<=n2-1;i_++)
      y2[i_]=dt[i_];
  }
//+------------------------------------------------------------------+
//| This function solves following problem: given table y[] of       |
//| function values at old nodes x[] and new nodes x2[], it          |
//| calculates and returns table of function values y2[] and         |
//| derivatives d2[] (calculated at x2[]).                           |
//| This function yields same result as Spline1DBuildCubic() call    |
//| followed by sequence of Spline1DDiff() calls, but it can be      |
//| several times faster when called for ordered X[] and X2[].       |
//| INPUT PARAMETERS:                                                |
//|     X           -   old spline nodes                             |
//|     Y           -   function values                              |
//|     X2           -  new spline nodes                             |
//| OPTIONAL PARAMETERS:                                             |
//|     N           -   points count:                                |
//|                     * N>=2                                       |
//|                     * if given, only first N points from X/Y are |
//|                       used                                       |
//|                     * if not given, automatically detected from  |
//|                       X/Y sizes (len(X) must be equal to len(Y)) |
//|     BoundLType  -   boundary condition type for the left boundary|
//|     BoundL      -   left boundary condition (first or second     |
//|                     derivative, depending on the BoundLType)     |
//|     BoundRType  -   boundary condition type for the right        |
//|                     boundary                                     |
//|     BoundR      -   right boundary condition (first or second    |
//|                     derivative, depending on the BoundRType)     |
//|     N2          -   new points count:                            |
//|                     * N2>=2                                      |
//|                     * if given, only first N2 points from X2 are |
//|                       used                                       |
//|                     * if not given, automatically detected from  |
//|                       X2 size                                    |
//| OUTPUT PARAMETERS:                                               |
//|     F2          -   function values at X2[]                      |
//|     D2          -   first derivatives at X2[]                    |
//| ORDER OF POINTS                                                  |
//| Subroutine automatically sorts points, so caller may pass        |
//| unsorted array. Function  values  are correctly reordered on     |
//| return, so F2[I] is always equal to S(X2[I]) independently of    |
//| points order.                                                    |
//| SETTING BOUNDARY VALUES:                                         |
//| The BoundLType/BoundRType parameters can have the following      |
//| values:                                                          |
//|     * -1, which corresonds to the periodic (cyclic) boundary     |
//|           conditions. In this case:                              |
//|           * both BoundLType and BoundRType must be equal to -1.  |
//|           * BoundL/BoundR are ignored                            |
//|           * Y[last] is ignored (it is assumed to be equal to     |
//|             Y[first]).                                           |
//|     *  0, which corresponds to the parabolically terminated      |
//|           spline (BoundL and/or BoundR are ignored).             |
//|     *  1, which corresponds to the first derivative boundary     |
//|           condition                                              |
//|     *  2, which corresponds to the second derivative boundary    |
//|           condition                                              |
//|     *  by default, BoundType=0 is used                           |
//| PROBLEMS WITH PERIODIC BOUNDARY CONDITIONS:                      |
//| Problems with periodic boundary conditions have                  |
//| Y[first_point]=Y[last_point]. However, this subroutine doesn't   |
//| require you to specify equal values for the first and last       |
//| points - it automatically forces them to be equal by copying     |
//| Y[first_point] (corresponds to the leftmost, minimal X[]) to     |
//| Y[last_point]. However it is recommended to pass consistent      |
//| values of Y[], i.e. to make Y[first_point]=Y[last_point].        |
//+------------------------------------------------------------------+
static void CSpline1D::Spline1DConvDiffCubic(double &cx[],double &cy[],
                                             const int n,const int boundltype,
                                             const double boundl,const int boundrtype,
                                             const double boundr,double &cx2[],
                                             const int n2,double &y2[],double &d2[])
  {
//--- create variables
   int    i=0;
   int    ylen=0;
   double t=0;
   double t2=0;
   int    i_=0;
//--- create arrays
   double a1[];
   double a2[];
   double a3[];
   double b[];
   double d[];
   double dt[];
   double rt1[];
   int    p[];
   int    p2[];
   double x[];
   double y[];
   double x2[];
//--- copy arrays
   ArrayCopy(x,cx);
   ArrayCopy(y,cy);
   ArrayCopy(x2,cx2);
//--- check correctness of boundary conditions
   if(!CAp::Assert(((boundltype==-1 || boundltype==0) || boundltype==1) || boundltype==2,__FUNCTION__+": incorrect BoundLType!"))
      return;
//--- check
   if(!CAp::Assert(((boundrtype==-1 || boundrtype==0) || boundrtype==1) || boundrtype==2,__FUNCTION__+": incorrect BoundRType!"))
      return;
//--- check
   if(!CAp::Assert((boundrtype==-1 && boundltype==-1) || (boundrtype!=-1 && boundltype!=-1),__FUNCTION__+": incorrect BoundLType/BoundRType!"))
      return;
//--- check
   if(boundltype==1 || boundltype==2)
     {
      //--- check
      if(!CAp::Assert(CMath::IsFinite(boundl),__FUNCTION__+": BoundL is infinite or NAN!"))
         return;
     }
//--- check
   if(boundrtype==1 || boundrtype==2)
     {
      //--- check
      if(!CAp::Assert(CMath::IsFinite(boundr),__FUNCTION__+": BoundR is infinite or NAN!"))
         return;
     }
//--- check lengths of arguments
   if(!CAp::Assert(n>=2,__FUNCTION__+": N<2!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(x)>=n,__FUNCTION__+": Length(X)<N!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(y)>=n,__FUNCTION__+": Length(Y)<N!"))
      return;
//--- check
   if(!CAp::Assert(n2>=2,__FUNCTION__+"Spline1DConvDiffCubic: N2<2!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(x2)>=n2,__FUNCTION__+": Length(X2)<N2!"))
      return;
//--- check and sort X/Y
   ylen=n;
//--- check
   if(boundltype==-1)
      ylen=n-1;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(x,n),__FUNCTION__+": X contains infinite or NAN values!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(y,ylen),__FUNCTION__+": Y contains infinite or NAN values!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(x2,n2),__FUNCTION__+": X2 contains infinite or NAN values!"))
      return;
//--- function call
   HeapSortPPoints(x,y,p,n);
//--- check
   if(!CAp::Assert(CApServ::AreDistinct(x,n),__FUNCTION__+": at least two consequent points are too close!"))
      return;
//--- set up DT (we will need it below)
   ArrayResizeAL(dt,MathMax(n,n2));
//--- sort X2:
//--- * use fake array DT because HeapSortPPoints() needs both integer AND real arrays
//--- * if we have periodic problem,wrap points
//--- * sort them,store permutation at P2
   if(boundrtype==-1 && boundltype==-1)
     {
      for(i=0;i<=n2-1;i++)
        {
         t=x2[i];
         CApServ::ApPeriodicMap(t,x[0],x[n-1],t2);
         x2[i]=t;
        }
     }
//--- function call
   HeapSortPPoints(x2,dt,p2,n2);
//--- Now we've checked and preordered everything,so we:
//--- * call internal GridDiff() function to get Hermite form of spline
//--- * convert using internal Conv() function
//--- * convert Y2 back to original order
   Spline1DGridDiffCubicInternal(x,y,n,boundltype,boundl,boundrtype,boundr,d,a1,a2,a3,b,dt);
   Spline1DConvDiffInternal(x,y,d,n,x2,n2,y2,true,d2,true,rt1,false);
//--- check
   if(!CAp::Assert(CAp::Len(dt)>=n2,__FUNCTION__+": internal error!"))
      return;
//--- copy
   for(i=0;i<=n2-1;i++)
      dt[p2[i]]=y2[i];
   for(i_=0;i_<=n2-1;i_++)
      y2[i_]=dt[i_];
   for(i=0;i<=n2-1;i++)
      dt[p2[i]]=d2[i];
   for(i_=0;i_<=n2-1;i_++)
      d2[i_]=dt[i_];
  }
//+------------------------------------------------------------------+
//| This function solves following problem: given table y[] of       |
//| function values at old nodes x[] and new nodes  x2[], it         |
//| calculates and returns table of function values y2[], first and  |
//| second derivatives d2[] and dd2[] (calculated at x2[]).          |
//| This function yields same result as Spline1DBuildCubic() call    |
//| followed  by sequence of Spline1DDiff() calls, but it can be     |
//| several times faster when called for ordered X[] and X2[].       |
//| INPUT PARAMETERS:                                                |
//|     X           -   old spline nodes                             |
//|     Y           -   function values                              |
//|     X2           -  new spline nodes                             |
//| OPTIONAL PARAMETERS:                                             |
//|     N           -   points count:                                |
//|                     * N>=2                                       |
//|                     * if given, only first N points from X/Y are |
//|                       used                                       |
//|                     * if not given, automatically detected from  |
//|                       X/Y sizes (len(X) must be equal to len(Y)) |
//|     BoundLType  -   boundary condition type for the left boundary|
//|     BoundL      -   left boundary condition (first or second     |
//|                     derivative, depending on the BoundLType)     |
//|     BoundRType  -   boundary condition type for the right        |
//|                     boundary                                     |
//|     BoundR      -   right boundary condition (first or second    |
//|                     derivative, depending on the BoundRType)     |
//|     N2          -   new points count:                            |
//|                     * N2>=2                                      |
//|                     * if given, only first N2 points from X2 are |
//|                       used                                       |
//|                     * if not given, automatically detected from  |
//|                       X2 size                                    |
//| OUTPUT PARAMETERS:                                               |
//|     F2          -   function values at X2[]                      |
//|     D2          -   first derivatives at X2[]                    |
//|     DD2         -   second derivatives at X2[]                   |
//| ORDER OF POINTS                                                  |
//| Subroutine automatically sorts points, so caller may pass        |
//| unsorted array. Function values are correctly reordered on       |
//| return, so F2[I] is always equal to S(X2[I]) independently of    |
//| points order.                                                    |
//| SETTING BOUNDARY VALUES:                                         |
//| The BoundLType/BoundRType parameters can have the following      |
//| values:                                                          |
//|     * -1, which corresonds to the periodic (cyclic) boundary     |
//|           conditions. In this case:                              |
//|           * both BoundLType and BoundRType must be equal to -1.  |
//|           * BoundL/BoundR are ignored                            |
//|           * Y[last] is ignored (it is assumed to be equal to     |
//|             Y[first]).                                           |
//|     *  0, which corresponds to the parabolically terminated      |
//|           spline (BoundL and/or BoundR are ignored).             |
//|     *  1, which corresponds to the first derivative boundary     |
//|           condition                                              |
//|     *  2, which corresponds to the second derivative boundary    |
//|           condition                                              |
//|     *  by default, BoundType=0 is used                           |
//| PROBLEMS WITH PERIODIC BOUNDARY CONDITIONS:                      |
//| Problems with periodic boundary conditions have                  |
//| Y[first_point]=Y[last_point]. However, this subroutine doesn't   |
//| require you to specify equal values for the first and last       |
//| points - it automatically forces them to be equal by copying     |
//| Y[first_point] (corresponds to the leftmost, minimal X[]) to     |
//| Y[last_point]. However it is recommended to pass consistent      |
//| values of Y[], i.e. to make Y[first_point]=Y[last_point].        |
//+------------------------------------------------------------------+
static void CSpline1D::Spline1DConvDiff2Cubic(double &cx[],double &cy[],
                                              const int n,const int boundltype,
                                              const double boundl,
                                              const int boundrtype,
                                              const double boundr,double &cx2[],
                                              const int n2,double &y2[],
                                              double &d2[],double &dd2[])
  {
//--- create variables
   int    i=0;
   int    ylen=0;
   double t=0;
   double t2=0;
   int    i_=0;
//--- create arrays
   double a1[];
   double a2[];
   double a3[];
   double b[];
   double d[];
   double dt[];
   int    p[];
   int    p2[];
   double x[];
   double y[];
   double x2[];
//--- copy arrays
   ArrayCopy(x,cx);
   ArrayCopy(y,cy);
   ArrayCopy(x2,cx2);
//--- check correctness of boundary conditions
   if(!CAp::Assert(((boundltype==-1 || boundltype==0) || boundltype==1) || boundltype==2,__FUNCTION__+": incorrect BoundLType!"))
      return;
//--- check
   if(!CAp::Assert(((boundrtype==-1 || boundrtype==0) || boundrtype==1) || boundrtype==2,__FUNCTION__+": incorrect BoundRType!"))
      return;
//--- check
   if(!CAp::Assert((boundrtype==-1 && boundltype==-1) || (boundrtype!=-1 && boundltype!=-1),__FUNCTION__+": incorrect BoundLType/BoundRType!"))
      return;
//--- check
   if(boundltype==1 || boundltype==2)
     {
      //--- check
      if(!CAp::Assert(CMath::IsFinite(boundl),__FUNCTION__+": BoundL is infinite or NAN!"))
         return;
     }
//--- check
   if(boundrtype==1 || boundrtype==2)
     {
      //--- check
      if(!CAp::Assert(CMath::IsFinite(boundr),__FUNCTION__+": BoundR is infinite or NAN!"))
         return;
     }
//--- check lengths of arguments
   if(!CAp::Assert(n>=2,__FUNCTION__+": N<2!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(x)>=n,__FUNCTION__+": Length(X)<N!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(y)>=n,__FUNCTION__+": Length(Y)<N!"))
      return;
//--- check
   if(!CAp::Assert(n2>=2,__FUNCTION__+": N2<2!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(x2)>=n2,__FUNCTION__+": Length(X2)<N2!"))
      return;
//--- check and sort X/Y
   ylen=n;
//--- check
   if(boundltype==-1)
      ylen=n-1;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(x,n),__FUNCTION__+": X contains infinite or NAN values!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(y,ylen),__FUNCTION__+": Y contains infinite or NAN values!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(x2,n2),__FUNCTION__+": X2 contains infinite or NAN values!"))
      return;
//--- function call
   HeapSortPPoints(x,y,p,n);
//--- check
   if(!CAp::Assert(CApServ::AreDistinct(x,n),__FUNCTION__+": at least two consequent points are too close!"))
      return;
//--- set up DT (we will need it below)
   ArrayResizeAL(dt,MathMax(n,n2));
//--- sort X2:
//--- * use fake array DT because HeapSortPPoints() needs both integer AND real arrays
//--- * if we have periodic problem,wrap points
//--- * sort them,store permutation at P2
   if(boundrtype==-1 && boundltype==-1)
     {
      for(i=0;i<=n2-1;i++)
        {
         t=x2[i];
         CApServ::ApPeriodicMap(t,x[0],x[n-1],t2);
         x2[i]=t;
        }
     }
//--- function call
   HeapSortPPoints(x2,dt,p2,n2);
//--- Now we've checked and preordered everything,so we:
//--- * call internal GridDiff() function to get Hermite form of spline
//--- * convert using internal Conv() function
//--- * convert Y2 back to original order
   Spline1DGridDiffCubicInternal(x,y,n,boundltype,boundl,boundrtype,boundr,d,a1,a2,a3,b,dt);
   Spline1DConvDiffInternal(x,y,d,n,x2,n2,y2,true,d2,true,dd2,true);
//--- check
   if(!CAp::Assert(CAp::Len(dt)>=n2,__FUNCTION__+": internal error!"))
      return;
//--- copy
   for(i=0;i<=n2-1;i++)
      dt[p2[i]]=y2[i];
   for(i_=0;i_<=n2-1;i_++)
      y2[i_]=dt[i_];
   for(i=0;i<=n2-1;i++)
      dt[p2[i]]=d2[i];
   for(i_=0;i_<=n2-1;i_++)
      d2[i_]=dt[i_];
   for(i=0;i<=n2-1;i++)
      dt[p2[i]]=dd2[i];
   for(i_=0;i_<=n2-1;i_++)
      dd2[i_]=dt[i_];
  }
//+------------------------------------------------------------------+
//| This subroutine builds Catmull-Rom spline interpolant.           |
//| INPUT PARAMETERS:                                                |
//|     X           -   spline nodes, array[0..N-1].                 |
//|     Y           -   function values, array[0..N-1].              |
//| OPTIONAL PARAMETERS:                                             |
//|     N           -   points count:                                |
//|                     * N>=2                                       |
//|                     * if given, only first N points are used to  |
//|                       build spline                               |
//|                     * if not given, automatically detected from  |
//|                       X/Y sizes (len(X) must be equal to len(Y)) |
//|     BoundType   -   boundary condition type:                     |
//|                     * -1 for periodic boundary condition         |
//|                     *  0 for parabolically terminated spline     |
//|                        (default)                                 |
//|     Tension     -   tension parameter:                           |
//|                     * tension=0   corresponds to classic         |
//|                       Catmull-Rom spline (default)               |
//|                     * 0<tension<1 corresponds to more general    |
//|                       form - cardinal spline                     |
//| OUTPUT PARAMETERS:                                               |
//|     C           -   spline interpolant                           |
//| ORDER OF POINTS                                                  |
//| Subroutine automatically sorts points, so caller may pass        |
//| unsorted array.                                                  |
//| PROBLEMS WITH PERIODIC BOUNDARY CONDITIONS:                      |
//| Problems with periodic boundary conditions have                  |
//| Y[first_point]=Y[last_point]. However, this subroutine doesn't   |
//| require you to specify equal values for the first and last       |
//| points - it automatically forces them to be equal by copying     |
//| Y[first_point] (corresponds to the leftmost, minimal X[]) to     |
//| Y[last_point]. However it is recommended to pass consistent      |
//| values of Y[], i.e. to make Y[first_point]=Y[last_point].        |
//+------------------------------------------------------------------+
static void CSpline1D::Spline1DBuildCatmullRom(double &cx[],double &cy[],
                                               const int n,const int boundtype,
                                               const double tension,
                                               CSpline1DInterpolant &c)
  {
//--- create a variable
   int i=0;
//--- create arrays
   double d[];
   double x[];
   double y[];
//--- copy arrays
   ArrayCopy(x,cx);
   ArrayCopy(y,cy);
//--- check
   if(!CAp::Assert(n>=2,__FUNCTION__+": N<2!"))
      return;
//--- check
   if(!CAp::Assert(boundtype==-1 || boundtype==0,__FUNCTION__+": incorrect BoundType!"))
      return;
//--- check
   if(!CAp::Assert((double)(tension)>=0.0,__FUNCTION__+": Tension<0!"))
      return;
//--- check
   if(!CAp::Assert((double)(tension)<=(double)(1),__FUNCTION__+": Tension>1!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(x)>=n,__FUNCTION__+": Length(X)<N!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(y)>=n,__FUNCTION__+": Length(Y)<N!"))
      return;
//--- check and sort points
   if(!CAp::Assert(CApServ::IsFiniteVector(x,n),__FUNCTION__+": X contains infinite or NAN values!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(y,n),__FUNCTION__+": Y contains infinite or NAN values!"))
      return;
//--- function call
   HeapSortPoints(x,y,n);
//--- check
   if(!CAp::Assert(CApServ::AreDistinct(x,n),__FUNCTION__+": at least two consequent points are too close!"))
      return;
//--- Special cases:
//--- * N=2,parabolic terminated boundary condition on both ends
//--- * N=2,periodic boundary condition
   if(n==2 && boundtype==0)
     {
      //--- Just linear spline
      Spline1DBuildLinear(x,y,n,c);
      return;
     }
   if(n==2 && boundtype==-1)
     {
      //--- Same as cubic spline with periodic conditions
      Spline1DBuildCubic(x,y,n,-1,0.0,-1,0.0,c);
      return;
     }
//--- Periodic or non-periodic boundary conditions
   if(boundtype==-1)
     {
      //--- Periodic boundary conditions
      y[n-1]=y[0];
      //--- allocation
      ArrayResizeAL(d,n);
      d[0]=(y[1]-y[n-2])/(2*(x[1]-x[0]+x[n-1]-x[n-2]));
      for(i=1;i<=n-2;i++)
         d[i]=(1-tension)*(y[i+1]-y[i-1])/(x[i+1]-x[i-1]);
      d[n-1]=d[0];
      //--- Now problem is reduced to the cubic Hermite spline
      Spline1DBuildHermite(x,y,d,n,c);
      c.m_periodic=true;
     }
   else
     {
      //--- Non-periodic boundary conditions
      ArrayResizeAL(d,n);
      for(i=1;i<=n-2;i++)
         d[i]=(1-tension)*(y[i+1]-y[i-1])/(x[i+1]-x[i-1]);
      d[0]=2*(y[1]-y[0])/(x[1]-x[0])-d[1];
      d[n-1]=2*(y[n-1]-y[n-2])/(x[n-1]-x[n-2])-d[n-2];
      //--- Now problem is reduced to the cubic Hermite spline
      Spline1DBuildHermite(x,y,d,n,c);
     }
  }
//+------------------------------------------------------------------+
//| This subroutine builds Hermite spline interpolant.               |
//| INPUT PARAMETERS:                                                |
//|     X           -   spline nodes, array[0..N-1]                  |
//|     Y           -   function values, array[0..N-1]               |
//|     D           -   derivatives, array[0..N-1]                   |
//|     N           -   points count (optional):                     |
//|                     * N>=2                                       |
//|                     * if given, only first N points are used to  |
//|                       build spline                               |
//|                     * if not given, automatically detected from  |
//|                       X/Y sizes (len(X) must be equal to len(Y)) |
//| OUTPUT PARAMETERS:                                               |
//|     C           -   spline interpolant.                          |
//| ORDER OF POINTS                                                  |
//| Subroutine automatically sorts points, so caller may pass        |
//| unsorted array.                                                  |
//+------------------------------------------------------------------+
static void CSpline1D::Spline1DBuildHermite(double &cx[],double &cy[],
                                            double &cd[],const int n,
                                            CSpline1DInterpolant &c)
  {
//--- create variables
   int    i=0;
   double delta=0;
   double delta2=0;
   double delta3=0;
//--- create arrays
   double x[];
   double y[];
   double d[];
//--- copy arrays
   ArrayCopy(x,cx);
   ArrayCopy(y,cy);
   ArrayCopy(d,cd);
//--- check
   if(!CAp::Assert(n>=2,__FUNCTION__+": N<2!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(x)>=n,__FUNCTION__+": Length(X)<N!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(y)>=n,__FUNCTION__+": Length(Y)<N!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(d)>=n,__FUNCTION__+": Length(D)<N!"))
      return;
//--- check and sort points
   if(!CAp::Assert(CApServ::IsFiniteVector(x,n),__FUNCTION__+": X contains infinite or NAN values!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(y,n),__FUNCTION__+": Y contains infinite or NAN values!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(d,n),__FUNCTION__+": D contains infinite or NAN values!"))
      return;
   HeapSortDPoints(x,y,d,n);
//--- check
   if(!CAp::Assert(CApServ::AreDistinct(x,n),__FUNCTION__+": at least two consequent points are too close!"))
      return;
//--- Build
   ArrayResizeAL(c.m_x,n);
   ArrayResizeAL(c.m_c,4*(n-1));
//--- change values
   c.m_periodic=false;
   c.m_k=3;
   c.m_n=n;
//--- copy
   for(i=0;i<=n-1;i++)
      c.m_x[i]=x[i];
//--- calculation
   for(i=0;i<=n-2;i++)
     {
      delta=x[i+1]-x[i];
      delta2=CMath::Sqr(delta);
      delta3=delta*delta2;
      c.m_c[4*i+0]=y[i];
      c.m_c[4*i+1]=d[i];
      c.m_c[4*i+2]=(3*(y[i+1]-y[i])-2*d[i]*delta-d[i+1]*delta)/delta2;
      c.m_c[4*i+3]=(2*(y[i]-y[i+1])+d[i]*delta+d[i+1]*delta)/delta3;
     }
  }
//+------------------------------------------------------------------+
//| This subroutine builds Akima spline interpolant                  |
//| INPUT PARAMETERS:                                                |
//|     X           -   spline nodes, array[0..N-1]                  |
//|     Y           -   function values, array[0..N-1]               |
//|     N           -   points count (optional):                     |
//|                     * N>=5                                       |
//|                     * if given, only first N points are used to  |
//|                       build spline                               |
//|                     * if not given, automatically detected from  |
//|                       X/Y sizes (len(X) must be equal to len(Y)) |
//| OUTPUT PARAMETERS:                                               |
//|     C           -   spline interpolant                           |
//| ORDER OF POINTS                                                  |
//| Subroutine automatically sorts points, so caller may pass        |
//| unsorted array.                                                  |
//+------------------------------------------------------------------+
static void CSpline1D::Spline1DBuildAkima(double &cx[],double &cy[],
                                          const int n,CSpline1DInterpolant &c)
  {
//--- create a variable
   int i=0;
//--- create arrays
   double d[];
   double w[];
   double diff[];
   double x[];
   double y[];
//--- copy arrays
   ArrayCopy(x,cx);
   ArrayCopy(y,cy);
//--- check
   if(!CAp::Assert(n>=5,__FUNCTION__+": N<5!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(x)>=n,__FUNCTION__+": Length(X)<N!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(y)>=n,__FUNCTION__+": Length(Y)<N!"))
      return;
//--- check and sort points
   if(!CAp::Assert(CApServ::IsFiniteVector(x,n),__FUNCTION__+": X contains infinite or NAN values!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(y,n),__FUNCTION__+": Y contains infinite or NAN values!"))
      return;
//--- function call
   HeapSortPoints(x,y,n);
//--- check
   if(!CAp::Assert(CApServ::AreDistinct(x,n),__FUNCTION__+": at least two consequent points are too close!"))
      return;
//--- Prepare W (weights),Diff (divided differences)
   ArrayResizeAL(w,n-1);
   ArrayResizeAL(diff,n-1);
   for(i=0;i<=n-2;i++)
      diff[i]=(y[i+1]-y[i])/(x[i+1]-x[i]);
   for(i=1;i<=n-2;i++)
      w[i]=MathAbs(diff[i]-diff[i-1]);
//--- Prepare Hermite interpolation scheme
   ArrayResizeAL(d,n);
   for(i=2;i<=n-3;i++)
     {
      //--- check
      if(MathAbs(w[i-1])+MathAbs(w[i+1])!=0.0)
         d[i]=(w[i+1]*diff[i-1]+w[i-1]*diff[i])/(w[i+1]+w[i-1]);
      else
         d[i]=((x[i+1]-x[i])*diff[i-1]+(x[i]-x[i-1])*diff[i])/(x[i+1]-x[i-1]);
     }
//--- change values
   d[0]=DiffThreePoint(x[0],x[0],y[0],x[1],y[1],x[2],y[2]);
   d[1]=DiffThreePoint(x[1],x[0],y[0],x[1],y[1],x[2],y[2]);
   d[n-2]=DiffThreePoint(x[n-2],x[n-3],y[n-3],x[n-2],y[n-2],x[n-1],y[n-1]);
   d[n-1]=DiffThreePoint(x[n-1],x[n-3],y[n-3],x[n-2],y[n-2],x[n-1],y[n-1]);
//--- Build Akima spline using Hermite interpolation scheme
   Spline1DBuildHermite(x,y,d,n,c);
  }
//+------------------------------------------------------------------+
//| This subroutine calculates the value of the spline at the given  |
//| point X.                                                         |
//| INPUT PARAMETERS:                                                |
//|     C   -   spline interpolant                                   |
//|     X   -   point                                                |
//| Result:                                                          |
//|     S(x)                                                         |
//+------------------------------------------------------------------+
static double CSpline1D::Spline1DCalc(CSpline1DInterpolant &c,double x)
  {
//--- create variables
   int    l=0;
   int    r=0;
   int    m=0;
   double t=0;
//--- check
   if(!CAp::Assert(c.m_k==3,__FUNCTION__+": internal error"))
      return(EMPTY_VALUE);
//--- check
   if(!CAp::Assert(!CInfOrNaN::IsInfinity(x),__FUNCTION__+": infinite X!"))
      return(EMPTY_VALUE);
//--- special case: NaN
   if(CInfOrNaN::IsNaN(x))
      return(CInfOrNaN::NaN());
//--- correct if periodic
   if(c.m_periodic)
      CApServ::ApPeriodicMap(x,c.m_x[0],c.m_x[c.m_n-1],t);
//--- Binary search in the [ x[0],...,x[n-2] ] (x[n-1] is not included)
   l=0;
   r=c.m_n-2+1;
   while(l!=r-1)
     {
      m=(l+r)/2;
      //--- check
      if(c.m_x[m]>=x)
         r=m;
      else
         l=m;
     }
//--- Interpolation
   x=x-c.m_x[l];
   m=4*l;
//--- return result
   return(c.m_c[m]+x*(c.m_c[m+1]+x*(c.m_c[m+2]+x*c.m_c[m+3])));
  }
//+------------------------------------------------------------------+
//| This subroutine differentiates the spline.                       |
//| INPUT PARAMETERS:                                                |
//|     C   -   spline interpolant.                                  |
//|     X   -   point                                                |
//| Result:                                                          |
//|     S   -   S(x)                                                 |
//|     DS  -   S'(x)                                                |
//|     D2S -   S''(x)                                               |
//+------------------------------------------------------------------+
static void CSpline1D::Spline1DDiff(CSpline1DInterpolant &c,double x,
                                    double &s,double &ds,double &d2s)
  {
//--- create variables
   int    l=0;
   int    r=0;
   int    m=0;
   double t=0;
//--- initialization
   s=0;
   ds=0;
   d2s=0;
//--- check
   if(!CAp::Assert(c.m_k==3,__FUNCTION__+": internal error"))
      return;
//--- check
   if(!CAp::Assert(!CInfOrNaN::IsInfinity(x),__FUNCTION__+": infinite X!"))
      return;
//--- special case: NaN
   if(CInfOrNaN::IsNaN(x))
     {
      //--- change values
      s=CInfOrNaN::NaN();
      ds=CInfOrNaN::NaN();
      d2s=CInfOrNaN::NaN();
      //--- exit the function
      return;
     }
//--- correct if periodic
   if(c.m_periodic)
      CApServ::ApPeriodicMap(x,c.m_x[0],c.m_x[c.m_n-1],t);
//--- Binary search
   l=0;
   r=c.m_n-2+1;
   while(l!=r-1)
     {
      m=(l+r)/2;
      //--- check
      if(c.m_x[m]>=x)
         r=m;
      else
         l=m;
     }
//--- Differentiation
   x=x-c.m_x[l];
   m=4*l;
   s=c.m_c[m]+x*(c.m_c[m+1]+x*(c.m_c[m+2]+x*c.m_c[m+3]));
   ds=c.m_c[m+1]+2*x*c.m_c[m+2]+3*CMath::Sqr(x)*c.m_c[m+3];
   d2s=2*c.m_c[m+2]+6*x*c.m_c[m+3];
  }
//+------------------------------------------------------------------+
//| This subroutine makes the copy of the spline.                    |
//| INPUT PARAMETERS:                                                |
//|     C   -   spline interpolant.                                  |
//| Result:                                                          |
//|     CC  -   spline copy                                          |
//+------------------------------------------------------------------+
static void CSpline1D::Spline1DCopy(CSpline1DInterpolant &c,CSpline1DInterpolant &cc)
  {
//--- create a variable
   int i_=0;
//--- change values
   cc.m_periodic=c.m_periodic;
   cc.m_n=c.m_n;
   cc.m_k=c.m_k;
//--- allocation
   ArrayResizeAL(cc.m_x,cc.m_n);
//--- copy
   for(i_=0;i_<=cc.m_n-1;i_++)
      cc.m_x[i_]=c.m_x[i_];
//--- allocation
   ArrayResizeAL(cc.m_c,(cc.m_k+1)*(cc.m_n-1));
//--- copy
   for(i_=0;i_<=(cc.m_k+1)*(cc.m_n-1)-1;i_++)
      cc.m_c[i_]=c.m_c[i_];
  }
//+------------------------------------------------------------------+
//| This subroutine unpacks the spline into the coefficients table.  |
//| INPUT PARAMETERS:                                                |
//|     C   -   spline interpolant.                                  |
//|     X   -   point                                                |
//| Result:                                                          |
//|     Tbl -   coefficients table, unpacked format, array[0..N-2,   |
//|             0..5].                                               |
//|             For I = 0...N-2:                                     |
//|                 Tbl[I,0] = X[i]                                  |
//|                 Tbl[I,1] = X[i+1]                                |
//|                 Tbl[I,2] = C0                                    |
//|                 Tbl[I,3] = C1                                    |
//|                 Tbl[I,4] = C2                                    |
//|                 Tbl[I,5] = C3                                    |
//|             On [x[i], x[i+1]] spline is equals to:               |
//|                 S(x) = C0 + C1*t + C2*t^2 + C3*t^3               |
//|                 t = x-x[i]                                       |
//+------------------------------------------------------------------+
static void CSpline1D::Spline1DUnpack(CSpline1DInterpolant &c,int &n,
                                      CMatrixDouble &tbl)
  {
//--- create variables
   int i=0;
   int j=0;
//--- allocation
   tbl.Resize(c.m_n-2+1,2+c.m_k+1);
//--- initialization
   n=c.m_n;
//--- Fill
   for(i=0;i<=n-2;i++)
     {
      tbl[i].Set(0,c.m_x[i]);
      tbl[i].Set(1,c.m_x[i+1]);
      for(j=0;j<=c.m_k;j++)
         tbl[i].Set(2+j,c.m_c[(c.m_k+1)*i+j]);
     }
  }
//+------------------------------------------------------------------+
//| This subroutine performs linear transformation of the spline     |
//| argument.                                                        |
//| INPUT PARAMETERS:                                                |
//|     C   -   spline interpolant.                                  |
//|     A, B-   transformation coefficients: x = A*t + B             |
//| Result:                                                          |
//|     C   -   transformed spline                                   |
//+------------------------------------------------------------------+
static void CSpline1D::Spline1DLinTransX(CSpline1DInterpolant &c,const double a,
                                         const double b)
  {
//--- create variables
   int    i=0;
   int    j=0;
   int    n=0;
   double v=0;
   double dv=0;
   double d2v=0;
//--- create arrays
   double x[];
   double y[];
   double d[];
//--- initialization
   n=c.m_n;
//--- Special case: A=0
   if(a==0.0)
     {
      v=Spline1DCalc(c,b);
      for(i=0;i<=n-2;i++)
        {
         c.m_c[(c.m_k+1)*i]=v;
         for(j=1;j<=c.m_k;j++)
            c.m_c[(c.m_k+1)*i+j]=0;
        }
      //--- exit the function
      return;
     }
//--- General case: A<>0.
//--- Unpack,X,Y,dY/dX.
//--- Scale and pack again.
   if(!CAp::Assert(c.m_k==3,__FUNCTION__+": internal error"))
      return;
//--- allocation
   ArrayResizeAL(x,n);
   ArrayResizeAL(y,n);
   ArrayResizeAL(d,n);
//--- calculation
   for(i=0;i<=n-1;i++)
     {
      x[i]=c.m_x[i];
      Spline1DDiff(c,x[i],v,dv,d2v);
      x[i]=(x[i]-b)/a;
      y[i]=v;
      d[i]=a*dv;
     }
//--- function call
   Spline1DBuildHermite(x,y,d,n,c);
  }
//+------------------------------------------------------------------+
//| This subroutine performs linear transformation of the spline.    |
//| INPUT PARAMETERS:                                                |
//|     C   -   spline interpolant.                                  |
//|     A,B-   transformation coefficients: S2(x)=A*S(x) + B         |
//| Result:                                                          |
//|     C   -   transformed spline                                   |
//+------------------------------------------------------------------+
static void CSpline1D::Spline1DLinTransY(CSpline1DInterpolant &c,const double a,
                                         const double b)
  {
//--- create variables
   int i=0;
   int j=0;
   int n=0;
//--- initialization
   n=c.m_n;
//--- calculation
   for(i=0;i<=n-2;i++)
     {
      c.m_c[(c.m_k+1)*i]=a*c.m_c[(c.m_k+1)*i]+b;
      for(j=1;j<=c.m_k;j++)
         c.m_c[(c.m_k+1)*i+j]=a*c.m_c[(c.m_k+1)*i+j];
     }
  }
//+------------------------------------------------------------------+
//| This subroutine integrates the spline.                           |
//| INPUT PARAMETERS:                                                |
//|     C   -   spline interpolant.                                  |
//|     X   -   right bound of the integration interval [a, x],      |
//|             here 'a' denotes min(x[])                            |
//| Result:                                                          |
//|     integral(S(t)dt,a,x)                                         |
//+------------------------------------------------------------------+
static double CSpline1D::Spline1DIntegrate(CSpline1DInterpolant &c,double x)
  {
//--- create variables
   double result=0;
   int    n=0;
   int    i=0;
   int    j=0;
   int    l=0;
   int    r=0;
   int    m=0;
   double w=0;
   double v=0;
   double t=0;
   double intab=0;
   double additionalterm=0;
//--- initialization
   n=c.m_n;
//--- Periodic splines require special treatment. We make
//--- following transformation:
//---     integral(S(t)dt,A,X)=integral(S(t)dt,A,Z)+AdditionalTerm
//--- here X may lie outside of [A,B],Z lies strictly in [A,B],
//--- AdditionalTerm is equals to integral(S(t)dt,A,B) times some
//--- integer number (may be zero).
   if(c.m_periodic && (x<c.m_x[0] || x>c.m_x[c.m_n-1]))
     {
      //--- compute integral(S(x)dx,A,B)
      intab=0;
      for(i=0;i<=c.m_n-2;i++)
        {
         w=c.m_x[i+1]-c.m_x[i];
         m=(c.m_k+1)*i;
         intab=intab+c.m_c[m]*w;
         v=w;
         for(j=1;j<=c.m_k;j++)
           {
            v=v*w;
            intab=intab+c.m_c[m+j]*v/(j+1);
           }
        }
      //--- map X into [A,B]
      CApServ::ApPeriodicMap(x,c.m_x[0],c.m_x[c.m_n-1],t);
      additionalterm=t*intab;
     }
   else
      additionalterm=0;
//--- Binary search in the [ x[0],...,x[n-2] ] (x[n-1] is not included)
   l=0;
   r=n-2+1;
   while(l!=r-1)
     {
      m=(l+r)/2;
      //--- check
      if(c.m_x[m]>=x)
         r=m;
      else
         l=m;
     }
//--- Integration
   result=0;
   for(i=0;i<=l-1;i++)
     {
      w=c.m_x[i+1]-c.m_x[i];
      m=(c.m_k+1)*i;
      result=result+c.m_c[m]*w;
      v=w;
      //--- calculation
      for(j=1;j<=c.m_k;j++)
        {
         v=v*w;
         result=result+c.m_c[m+j]*v/(j+1);
        }
     }
//--- change values
   w=x-c.m_x[l];
   m=(c.m_k+1)*l;
   v=w;
   result=result+c.m_c[m]*w;
//--- calculation
   for(j=1;j<=c.m_k;j++)
     {
      v=v*w;
      result=result+c.m_c[m+j]*v/(j+1);
     }
//--- return result
   return(result+additionalterm);
  }
//+------------------------------------------------------------------+
//| Internal version of Spline1DConvDiff                             |
//| Converts from Hermite spline given by grid XOld to new grid X2   |
//| INPUT PARAMETERS:                                                |
//|     XOld    -   old grid                                         |
//|     YOld    -   values at old grid                               |
//|     DOld    -   first derivative at old grid                     |
//|     N       -   grid size                                        |
//|     X2      -   new grid                                         |
//|     N2      -   new grid size                                    |
//|     Y       -   possibly preallocated output array               |
//|                 (reallocate if too small)                        |
//|     NeedY   -   do we need Y?                                    |
//|     D1      -   possibly preallocated output array               |
//|                 (reallocate if too small)                        |
//|     NeedD1  -   do we need D1?                                   |
//|     D2      -   possibly preallocated output array               |
//|                 (reallocate if too small)                        |
//|     NeedD2  -   do we need D1?                                   |
//| OUTPUT ARRAYS:                                                   |
//|     Y       -   values, if needed                                |
//|     D1      -   first derivative, if needed                      |
//|     D2      -   second derivative, if needed                     |
//+------------------------------------------------------------------+
static void CSpline1D::Spline1DConvDiffInternal(double &xold[],double &yold[],
                                                double &dold[],const int n,
                                                double &x2[],const int n2,
                                                double &y[],const bool needy,
                                                double &d1[],const bool needd1,
                                                double &d2[],const bool needd2)
  {
//--- create variables
   int    intervalindex=0;
   int    pointindex=0;
   bool   havetoadvance;
   double c0=0;
   double c1=0;
   double c2=0;
   double c3=0;
   double a=0;
   double b=0;
   double w=0;
   double w2=0;
   double w3=0;
   double fa=0;
   double fb=0;
   double da=0;
   double db=0;
   double t=0;
//--- Prepare space
   if(needy && CAp::Len(y)<n2)
      ArrayResizeAL(y,n2);
//--- check
   if(needd1 && CAp::Len(d1)<n2)
      ArrayResizeAL(d1,n2);
//--- check
   if(needd2 && CAp::Len(d2)<n2)
      ArrayResizeAL(d2,n2);
//--- These assignments aren't actually needed
//--- (variables are initialized in the loop below),
//--- but without them compiler will complain about uninitialized locals
   c0=0;
   c1=0;
   c2=0;
   c3=0;
   a=0;
   b=0;
//--- Cycle
   intervalindex=-1;
   pointindex=0;
//--- calculation
   while(true)
     {
      //--- are we ready to exit?
      if(pointindex>=n2)
         break;
      t=x2[pointindex];
      //--- do we need to advance interval?
      havetoadvance=false;
      //--- check
      if(intervalindex==-1)
         havetoadvance=true;
      else
        {
         //--- check
         if(intervalindex<n-2)
            havetoadvance=t>=b;
        }
      //--- check
      if(havetoadvance)
        {
         //--- change values
         intervalindex=intervalindex+1;
         a=xold[intervalindex];
         b=xold[intervalindex+1];
         w=b-a;
         w2=w*w;
         w3=w*w2;
         fa=yold[intervalindex];
         fb=yold[intervalindex+1];
         da=dold[intervalindex];
         db=dold[intervalindex+1];
         c0=fa;
         c1=da;
         c2=(3*(fb-fa)-2*da*w-db*w)/w2;
         c3=(2*(fa-fb)+da*w+db*w)/w3;
         continue;
        }
      //--- Calculate spline and its derivatives using power basis
      t=t-a;
      if(needy)
         y[pointindex]=c0+t*(c1+t*(c2+t*c3));
      //--- check
      if(needd1)
         d1[pointindex]=c1+2*t*c2+3*t*t*c3;
      //--- check
      if(needd2)
         d2[pointindex]=2*c2+6*t*c3;
      //--- change value
      pointindex=pointindex+1;
     }
  }
//+------------------------------------------------------------------+
//| Internal subroutine. Heap sort.                                  |
//+------------------------------------------------------------------+
static void CSpline1D::HeapSortDPoints(double &x[],double &y[],double &d[],
                                       const int n)
  {
//--- create variables
   int i=0;
   int i_=0;
//--- create arrays
   double rbuf[];
   int    ibuf[];
   double rbuf2[];
   int    ibuf2[];
//--- allocation
   ArrayResizeAL(ibuf,n);
   ArrayResizeAL(rbuf,n);
   for(i=0;i<=n-1;i++)
      ibuf[i]=i;
//--- function call
   CTSort::TagSortFastI(x,ibuf,rbuf2,ibuf2,n);
//--- copy
   for(i=0;i<=n-1;i++)
      rbuf[i]=y[ibuf[i]];
   for(i_=0;i_<=n-1;i_++)
      y[i_]=rbuf[i_];
   for(i=0;i<=n-1;i++)
      rbuf[i]=d[ibuf[i]];
   for(i_=0;i_<=n-1;i_++)
      d[i_]=rbuf[i_];
  }
//+------------------------------------------------------------------+
//| Internal version of Spline1DGridDiffCubic.                       |
//| Accepts pre-ordered X/Y, temporary arrays (which may be          |
//| preallocated, if you want to save time, or not) and output array |
//| (which may be preallocated too).                                 |
//| Y is passed as var-parameter because we may need to force last   |
//| element  to be equal to the first one (if periodic boundary      |
//| conditions are specified).                                       |
//+------------------------------------------------------------------+
static void CSpline1D::Spline1DGridDiffCubicInternal(double &x[],double &y[],
                                                     const int n,const int boundltype,
                                                     const double boundl,
                                                     const int boundrtype,
                                                     const double boundr,
                                                     double &d[],double &a1[],
                                                     double &a2[],double &a3[],
                                                     double &b[],double &dt[])
  {
//--- create variables
   int i=0;
   int i_=0;
//--- allocate arrays
   if(CAp::Len(d)<n)
      ArrayResizeAL(d,n);
//--- check
   if(CAp::Len(a1)<n)
      ArrayResizeAL(a1,n);
//--- check
   if(CAp::Len(a2)<n)
      ArrayResizeAL(a2,n);
//--- check
   if(CAp::Len(a3)<n)
      ArrayResizeAL(a3,n);
//--- check
   if(CAp::Len(b)<n)
      ArrayResizeAL(b,n);
//--- check
   if(CAp::Len(dt)<n)
      ArrayResizeAL(dt,n);
//--- Special cases:
//--- * N=2,parabolic terminated boundary condition on both ends
//--- * N=2,periodic boundary condition
   if((n==2 && boundltype==0) && boundrtype==0)
     {
      //--- change values
      d[0]=(y[1]-y[0])/(x[1]-x[0]);
      d[1]=d[0];
      //--- exit the function
      return;
     }
//--- check
   if((n==2 && boundltype==-1) && boundrtype==-1)
     {
      //--- change values
      d[0]=0;
      d[1]=0;
      //--- exit the function
      return;
     }
//--- Periodic and non-periodic boundary conditions are
//--- two separate classes
   if(boundrtype==-1 && boundltype==-1)
     {
      //--- Periodic boundary conditions
      y[n-1]=y[0];
      //--- Boundary conditions at N-1 points
      //--- (one point less because last point is the same as first point).
      a1[0]=x[1]-x[0];
      a2[0]=2*(x[1]-x[0]+x[n-1]-x[n-2]);
      a3[0]=x[n-1]-x[n-2];
      b[0]=3*(y[n-1]-y[n-2])/(x[n-1]-x[n-2])*(x[1]-x[0])+3*(y[1]-y[0])/(x[1]-x[0])*(x[n-1]-x[n-2]);
      //--- calculation
      for(i=1;i<=n-2;i++)
        {
         //--- Altough last point is [N-2],we use X[N-1] and Y[N-1]
         //--- (because of periodicity)
         a1[i]=x[i+1]-x[i];
         a2[i]=2*(x[i+1]-x[i-1]);
         a3[i]=x[i]-x[i-1];
         b[i]=3*(y[i]-y[i-1])/(x[i]-x[i-1])*(x[i+1]-x[i])+3*(y[i+1]-y[i])/(x[i+1]-x[i])*(x[i]-x[i-1]);
        }
      //--- Solve,add last point (with index N-1)
      SolveCyclicTridiagonal(a1,a2,a3,b,n-1,dt);
      for(i_=0;i_<=n-2;i_++)
         d[i_]=dt[i_];
      d[n-1]=d[0];
     }
   else
     {
      //--- Non-periodic boundary condition.
      //--- Left boundary conditions.
      if(boundltype==0)
        {
         //--- change values
         a1[0]=0;
         a2[0]=1;
         a3[0]=1;
         b[0]=2*(y[1]-y[0])/(x[1]-x[0]);
        }
      //--- check
      if(boundltype==1)
        {
         //--- change values
         a1[0]=0;
         a2[0]=1;
         a3[0]=0;
         b[0]=boundl;
        }
      //--- check
      if(boundltype==2)
        {
         //--- change values
         a1[0]=0;
         a2[0]=2;
         a3[0]=1;
         b[0]=3*(y[1]-y[0])/(x[1]-x[0])-0.5*boundl*(x[1]-x[0]);
        }
      //--- Central conditions
      for(i=1;i<=n-2;i++)
        {
         a1[i]=x[i+1]-x[i];
         a2[i]=2*(x[i+1]-x[i-1]);
         a3[i]=x[i]-x[i-1];
         b[i]=3*(y[i]-y[i-1])/(x[i]-x[i-1])*(x[i+1]-x[i])+3*(y[i+1]-y[i])/(x[i+1]-x[i])*(x[i]-x[i-1]);
        }
      //--- Right boundary conditions
      if(boundrtype==0)
        {
         //--- change values
         a1[n-1]=1;
         a2[n-1]=1;
         a3[n-1]=0;
         b[n-1]=2*(y[n-1]-y[n-2])/(x[n-1]-x[n-2]);
        }
      //--- check
      if(boundrtype==1)
        {
         //--- change values
         a1[n-1]=0;
         a2[n-1]=1;
         a3[n-1]=0;
         b[n-1]=boundr;
        }
      //--- check
      if(boundrtype==2)
        {
         //--- change values
         a1[n-1]=1;
         a2[n-1]=2;
         a3[n-1]=0;
         b[n-1]=3*(y[n-1]-y[n-2])/(x[n-1]-x[n-2])+0.5*boundr*(x[n-1]-x[n-2]);
        }
      //--- Solve
      SolveTridiagonal(a1,a2,a3,b,n,d);
     }
  }
//+------------------------------------------------------------------+
//| Internal subroutine. Heap sort.                                  |
//+------------------------------------------------------------------+
static void CSpline1D::HeapSortPoints(double &x[],double &y[],const int n)
  {
//--- create arrays
   double bufx[];
   double bufy[];
//--- function call
   CTSort::TagSortFastR(x,y,bufx,bufy,n);
  }
//+------------------------------------------------------------------+
//| Internal subroutine. Heap sort.                                  |
//| Accepts:                                                         |
//|     X, Y    -   points                                           |
//|     P       -   empty or preallocated array                      |
//| Returns:                                                         |
//|     X, Y    -   sorted by X                                      |
//|     P       -   array of permutations; I-th position of output   |
//| arrays X/Y contains(X[P[I]],Y[P[I]])                             |
//+------------------------------------------------------------------+
static void CSpline1D::HeapSortPPoints(double &x[],double &y[],int &p[],
                                       const int n)
  {
//--- create variables
   int i=0;
   int i_=0;
//--- create arrays
   double rbuf[];
   int    ibuf[];
//--- check
   if(CAp::Len(p)<n)
      ArrayResizeAL(p,n);
//--- allocation
   ArrayResizeAL(rbuf,n);
//--- initialization
   for(i=0;i<=n-1;i++)
      p[i]=i;
//--- function call
   CTSort::TagSortFastI(x,p,rbuf,ibuf,n);
//--- copy
   for(i=0;i<=n-1;i++)
      rbuf[i]=y[p[i]];
   for(i_=0;i_<=n-1;i_++)
      y[i_]=rbuf[i_];
  }
//+------------------------------------------------------------------+
//| Internal subroutine. Tridiagonal solver. Solves                  |
//| ( B[0] C[0]                      )                               |
//| ( A[1] B[1] C[1]                 )                               |
//| (      A[2] B[2] C[2]            )                               |
//| (            ..........          ) * X=D                         |
//| (            ..........          )                               |
//| (           A[N-2] B[N-2] C[N-2] )                               |
//| (                  A[N-1] B[N-1] )                               |
//+------------------------------------------------------------------+
static void CSpline1D::SolveTridiagonal(double &a[],double &cb[],double &c[],
                                        double &cd[],const int n,double &x[])
  {
//--- create variables
   int    k=0;
   double t=0;
//--- create arrays
   double d[];
   double b[];
//--- copy arrays
   ArrayCopy(d,cd);
   ArrayCopy(b,cb);
//--- check
   if(CAp::Len(x)<n)
      ArrayResizeAL(x,n);
//--- calculation
   for(k=1;k<=n-1;k++)
     {
      t=a[k]/b[k-1];
      b[k]=b[k]-t*c[k-1];
      d[k]=d[k]-t*d[k-1];
     }
   x[n-1]=d[n-1]/b[n-1];
   for(k=n-2;k>=0;k--)
      x[k]=(d[k]-c[k]*x[k+1])/b[k];
  }
//+------------------------------------------------------------------+
//| Internal subroutine. Cyclic tridiagonal solver. Solves           |
//| ( B[0] C[0]                 A[0] )                               |
//| ( A[1] B[1] C[1]                 )                               |
//| (      A[2] B[2] C[2]            )                               |
//| (            ..........          ) * X=D                         |
//| (            ..........          )                               |
//| (           A[N-2] B[N-2] C[N-2] )                               |
//| ( C[N-1]           A[N-1] B[N-1] )                               |
//+------------------------------------------------------------------+
static void CSpline1D::SolveCyclicTridiagonal(double &a[],double &cb[],
                                              double &c[],double &d[],
                                              const int n,double &x[])
  {
//--- create variables
   int    k=0;
   double alpha=0;
   double beta=0;
   double gamma=0;
//--- create arrays
   double y[];
   double z[];
   double u[];
   double b[];
//--- copy array
   ArrayCopy(b,cb);
//--- check
   if(CAp::Len(x)<n)
      ArrayResizeAL(x,n);
//--- change values
   beta=a[0];
   alpha=c[n-1];
   gamma=-b[0];
   b[0]=2*b[0];
   b[n-1]=b[n-1]-alpha*beta/gamma;
//--- allocation
   ArrayResizeAL(u,n);
//--- initialization
   for(k=0;k<=n-1;k++)
      u[k]=0;
   u[0]=gamma;
   u[n-1]=alpha;
//--- function call
   SolveTridiagonal(a,b,c,d,n,y);
//--- function call
   SolveTridiagonal(a,b,c,u,n,z);
//--- calculation
   for(k=0;k<=n-1;k++)
      x[k]=y[k]-(y[0]+beta/gamma*y[n-1])/(1+z[0]+beta/gamma*z[n-1])*z[k];
  }
//+------------------------------------------------------------------+
//| Internal subroutine. Three-point differentiation                 |
//+------------------------------------------------------------------+
static double CSpline1D::DiffThreePoint(double t,const double x0,const double f0,
                                        double x1,const double f1,double x2,
                                        const double f2)
  {
//--- create variables
   double a=0;
   double b=0;
//--- change values
   t=t-x0;
   x1=x1-x0;
   x2=x2-x0;
   a=(f2-f0-x2/x1*(f1-f0))/(CMath::Sqr(x2)-x1*x2);
   b=(f1-f0-a*CMath::Sqr(x1))/x1;
//--- return result
   return(2*a*t+b);
  }
//+------------------------------------------------------------------+
//| Polynomial fitting report:                                       |
//|     TaskRCond       reciprocal of task's condition number        |
//|     RMSError        RMS error                                    |
//|     AvgError        average error                                |
//|     AvgRelError     average relative error (for non-zero Y[I])   |
//|     MaxError        maximum error                                |
//+------------------------------------------------------------------+
class CPolynomialFitReport
  {
public:
   //--- variables
   double            m_taskrcond;
   double            m_rmserror;
   double            m_avgerror;
   double            m_avgrelerror;
   double            m_maxerror;
   //--- constructor, destructor
                     CPolynomialFitReport(void);
                    ~CPolynomialFitReport(void);
   //--- copy
   void              Copy(CPolynomialFitReport &obj);
  };
//+------------------------------------------------------------------+
//| Constructor without parameters                                   |
//+------------------------------------------------------------------+
CPolynomialFitReport::CPolynomialFitReport(void)
  {

  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CPolynomialFitReport::~CPolynomialFitReport(void)
  {

  }
//+------------------------------------------------------------------+
//| Copy                                                             |
//+------------------------------------------------------------------+
void CPolynomialFitReport::Copy(CPolynomialFitReport &obj)
  {
//--- copy variables
   m_taskrcond=obj.m_taskrcond;
   m_rmserror=obj.m_rmserror;
   m_avgerror=obj.m_avgerror;
   m_avgrelerror=obj.m_avgrelerror;
   m_maxerror=obj.m_maxerror;
  }
//+------------------------------------------------------------------+
//| Polynomial fitting report:                                       |
//|     TaskRCond       reciprocal of task's condition number        |
//|     RMSError        RMS error                                    |
//|     AvgError        average error                                |
//|     AvgRelError     average relative error (for non-zero Y[I])   |
//|     MaxError        maximum error                                |
//+------------------------------------------------------------------+
class CPolynomialFitReportShell
  {
private:
   CPolynomialFitReport m_innerobj;
public:
   //--- constructors, destructor
                     CPolynomialFitReportShell(void);
                     CPolynomialFitReportShell(CPolynomialFitReport &obj);
                    ~CPolynomialFitReportShell(void);
   //--- methods
   double            GetTaskRCond(void);
   void              SetTaskRCond(const double d);
   double            GetRMSError(void);
   void              SetRMSError(const double d);
   double            GetAvgError(void);
   void              SetAvgError(const double d);
   double            GetAvgRelError(void);
   void              SetAvgRelError(const double d);
   double            GetMaxError(void);
   void              SetMaxError(const double d);
   CPolynomialFitReport *GetInnerObj(void);
  };
//+------------------------------------------------------------------+
//| Constructor without parameters                                   |
//+------------------------------------------------------------------+
CPolynomialFitReportShell::CPolynomialFitReportShell(void)
  {

  }
//+------------------------------------------------------------------+
//| Copy constructor                                                 |
//+------------------------------------------------------------------+
CPolynomialFitReportShell::CPolynomialFitReportShell(CPolynomialFitReport &obj)
  {
//--- copy
   m_innerobj.Copy(obj);
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CPolynomialFitReportShell::~CPolynomialFitReportShell(void)
  {

  }
//+------------------------------------------------------------------+
//| Returns the value of the variable taskrcond                      |
//+------------------------------------------------------------------+
double CPolynomialFitReportShell::GetTaskRCond(void)
  {
//--- return result
   return(m_innerobj.m_taskrcond);
  }
//+------------------------------------------------------------------+
//| Changing the value of the variable taskrcond                     |
//+------------------------------------------------------------------+
void CPolynomialFitReportShell::SetTaskRCond(const double d)
  {
//--- change value
   m_innerobj.m_taskrcond=d;
  }
//+------------------------------------------------------------------+
//| Returns the value of the variable rmserror                       |
//+------------------------------------------------------------------+
double CPolynomialFitReportShell::GetRMSError(void)
  {
//--- return result
   return(m_innerobj.m_rmserror);
  }
//+------------------------------------------------------------------+
//| Changing the value of the variable rmserror                      |
//+------------------------------------------------------------------+
void CPolynomialFitReportShell::SetRMSError(const double d)
  {
//--- change value
   m_innerobj.m_rmserror=d;
  }
//+------------------------------------------------------------------+
//| Returns the value of the variable avgerror                       |
//+------------------------------------------------------------------+
double CPolynomialFitReportShell::GetAvgError(void)
  {
//--- return result
   return(m_innerobj.m_avgerror);
  }
//+------------------------------------------------------------------+
//| Changing the value of the variable avgerror                      |
//+------------------------------------------------------------------+
void CPolynomialFitReportShell::SetAvgError(const double d)
  {
//--- change value
   m_innerobj.m_avgerror=d;
  }
//+------------------------------------------------------------------+
//| Returns the value of the variable avgrelerror                    |
//+------------------------------------------------------------------+
double CPolynomialFitReportShell::GetAvgRelError(void)
  {
//--- return result
   return(m_innerobj.m_avgrelerror);
  }
//+------------------------------------------------------------------+
//| Changing the value of the variable avgrelerror                   |
//+------------------------------------------------------------------+
void CPolynomialFitReportShell::SetAvgRelError(const double d)
  {
//--- change value
   m_innerobj.m_avgrelerror=d;
  }
//+------------------------------------------------------------------+
//| Returns the value of the variable maxerror                       |
//+------------------------------------------------------------------+
double CPolynomialFitReportShell::GetMaxError(void)
  {
//--- return result
   return(m_innerobj.m_maxerror);
  }
//+------------------------------------------------------------------+
//| Changing the value of the variable maxerror                      |
//+------------------------------------------------------------------+
void CPolynomialFitReportShell::SetMaxError(const double d)
  {
//--- change value
   m_innerobj.m_maxerror=d;
  }
//+------------------------------------------------------------------+
//| Return object of class                                           |
//+------------------------------------------------------------------+
CPolynomialFitReport *CPolynomialFitReportShell::GetInnerObj(void)
  {
//--- return result
   return(GetPointer(m_innerobj));
  }
//+------------------------------------------------------------------+
//| Barycentric fitting report:                                      |
//|     RMSError        RMS error                                    |
//|     AvgError        average error                                |
//|     AvgRelError     average relative error (for non-zero Y[I])   |
//|     MaxError        maximum error                                |
//|     TaskRCond       reciprocal of task's condition number        |
//+------------------------------------------------------------------+
class CBarycentricFitReport
  {
public:
   //--- variables
   double            m_taskrcond;
   int               m_dbest;
   double            m_rmserror;
   double            m_avgerror;
   double            m_avgrelerror;
   double            m_maxerror;
   //--- constructor, destructor
                     CBarycentricFitReport(void);
                    ~CBarycentricFitReport(void);
   //--- copy
   void              Copy(CBarycentricFitReport &obj);
  };
//+------------------------------------------------------------------+
//| Constructor without parameters                                   |
//+------------------------------------------------------------------+
CBarycentricFitReport::CBarycentricFitReport(void)
  {

  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CBarycentricFitReport::~CBarycentricFitReport(void)
  {

  }
//+------------------------------------------------------------------+
//| Copy                                                             |
//+------------------------------------------------------------------+
void CBarycentricFitReport::Copy(CBarycentricFitReport &obj)
  {
//--- copy variables
   m_taskrcond=obj.m_taskrcond;
   m_dbest=obj.m_dbest;
   m_rmserror=obj.m_rmserror;
   m_avgerror=obj.m_avgerror;
   m_avgrelerror=obj.m_avgrelerror;
   m_maxerror=obj.m_maxerror;
  }
//+------------------------------------------------------------------+
//| Barycentric fitting report:                                      |
//|     RMSError        RMS error                                    |
//|     AvgError        average error                                |
//|     AvgRelError     average relative error (for non-zero Y[I])   |
//|     MaxError        maximum error                                |
//|     TaskRCond       reciprocal of task's condition number        |
//+------------------------------------------------------------------+
class CBarycentricFitReportShell
  {
private:
   CBarycentricFitReport m_innerobj;
public:
   //--- constructors, destructor
                     CBarycentricFitReportShell(void);
                     CBarycentricFitReportShell(CBarycentricFitReport &obj);
                    ~CBarycentricFitReportShell(void);
   //--- methods
   double            GetTaskRCond(void);
   void              SetTaskRCond(const double d);
   int               GetDBest(void);
   void              SetDBest(const int i);
   double            GetRMSError(void);
   void              SetRMSError(const double d);
   double            GetAvgError(void);
   void              SetAvgError(const double d);
   double            GetAvgRelError(void);
   void              SetAvgRelError(const double d);
   double            GetMaxError(void);
   void              SetMaxError(const double d);
   CBarycentricFitReport *GetInnerObj(void);
  };
//+------------------------------------------------------------------+
//| Constructor without parameters                                   |
//+------------------------------------------------------------------+
CBarycentricFitReportShell::CBarycentricFitReportShell(void)
  {

  }
//+------------------------------------------------------------------+
//| Copy constructor                                                 |
//+------------------------------------------------------------------+
CBarycentricFitReportShell::CBarycentricFitReportShell(CBarycentricFitReport &obj)
  {
//--- copy
   m_innerobj.Copy(obj);
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CBarycentricFitReportShell::~CBarycentricFitReportShell(void)
  {

  }
//+------------------------------------------------------------------+
//| Returns the value of the variable taskrcond                      |
//+------------------------------------------------------------------+
double CBarycentricFitReportShell::GetTaskRCond(void)
  {
//--- return result
   return(m_innerobj.m_taskrcond);
  }
//+------------------------------------------------------------------+
//| Changing the value of the variable taskrcond                     |
//+------------------------------------------------------------------+
void CBarycentricFitReportShell::SetTaskRCond(const double d)
  {
//--- change value
   m_innerobj.m_taskrcond=d;
  }
//+------------------------------------------------------------------+
//| Returns the value of the variable dbest                          |
//+------------------------------------------------------------------+
int CBarycentricFitReportShell::GetDBest(void)
  {
//--- return result
   return(m_innerobj.m_dbest);
  }
//+------------------------------------------------------------------+
//| Changing the value of the variable dbest                         |
//+------------------------------------------------------------------+
void CBarycentricFitReportShell::SetDBest(const int i)
  {
//--- change value
   m_innerobj.m_dbest=i;
  }
//+------------------------------------------------------------------+
//| Returns the value of the variable rmserror                       |
//+------------------------------------------------------------------+
double CBarycentricFitReportShell::GetRMSError(void)
  {
//--- return result
   return(m_innerobj.m_rmserror);
  }
//+------------------------------------------------------------------+
//| Changing the value of the variable rmserror                      |
//+------------------------------------------------------------------+
void CBarycentricFitReportShell::SetRMSError(const double d)
  {
//--- change value
   m_innerobj.m_rmserror=d;
  }
//+------------------------------------------------------------------+
//| Returns the value of the variable avgerror                       |
//+------------------------------------------------------------------+
double CBarycentricFitReportShell::GetAvgError(void)
  {
//--- return result
   return(m_innerobj.m_avgerror);
  }
//+------------------------------------------------------------------+
//| Changing the value of the variable avgerror                      |
//+------------------------------------------------------------------+
void CBarycentricFitReportShell::SetAvgError(const double d)
  {
//--- change value
   m_innerobj.m_avgerror=d;
  }
//+------------------------------------------------------------------+
//| Returns the value of the variable avgrelerror                    |
//+------------------------------------------------------------------+
double CBarycentricFitReportShell::GetAvgRelError(void)
  {
//--- return result
   return(m_innerobj.m_avgrelerror);
  }
//+------------------------------------------------------------------+
//| Changing the value of the variable avgrelerror                   |
//+------------------------------------------------------------------+
void CBarycentricFitReportShell::SetAvgRelError(const double d)
  {
//--- change value
   m_innerobj.m_avgrelerror=d;
  }
//+------------------------------------------------------------------+
//| Returns the value of the variable maxerror                       |
//+------------------------------------------------------------------+
double CBarycentricFitReportShell::GetMaxError(void)
  {
//--- return result
   return(m_innerobj.m_maxerror);
  }
//+------------------------------------------------------------------+
//| Changing the value of the variable maxerror                      |
//+------------------------------------------------------------------+
void CBarycentricFitReportShell::SetMaxError(const double d)
  {
//--- change value
   m_innerobj.m_maxerror=d;
  }
//+------------------------------------------------------------------+
//| Return object of class                                           |
//+------------------------------------------------------------------+
CBarycentricFitReport *CBarycentricFitReportShell::GetInnerObj(void)
  {
//--- return result
   return(GetPointer(m_innerobj));
  }
//+------------------------------------------------------------------+
//| Spline fitting report:                                           |
//|     RMSError        RMS error                                    |
//|     AvgError        average error                                |
//|     AvgRelError     average relative error (for non-zero Y[I])   |
//|     MaxError        maximum error                                |
//| Fields below are filled by obsolete functions (Spline1DFitCubic, |
//| Spline1DFitHermite). Modern fitting functions do NOT fill these  | 
//| fields:                                                          |
//|     TaskRCond       reciprocal of task's condition number        |
//+------------------------------------------------------------------+
class CSpline1DFitReport
  {
public:
   //--- variables
   double            m_taskrcond;
   double            m_rmserror;
   double            m_avgerror;
   double            m_avgrelerror;
   double            m_maxerror;
   //--- constructor, destructor
                     CSpline1DFitReport(void);
                    ~CSpline1DFitReport(void);
   //--- copy
   void              Copy(CSpline1DFitReport &obj);
  };
//+------------------------------------------------------------------+
//| Constructor without parameters                                   |
//+------------------------------------------------------------------+
CSpline1DFitReport::CSpline1DFitReport(void)
  {

  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CSpline1DFitReport::~CSpline1DFitReport(void)
  {

  }
//+------------------------------------------------------------------+
//| Copy                                                             |
//+------------------------------------------------------------------+
void CSpline1DFitReport::Copy(CSpline1DFitReport &obj)
  {
//--- copy variables
   m_taskrcond=obj.m_taskrcond;
   m_rmserror=obj.m_rmserror;
   m_avgerror=obj.m_avgerror;
   m_avgrelerror=obj.m_avgrelerror;
   m_maxerror=obj.m_maxerror;
  }
//+------------------------------------------------------------------+
//| Spline fitting report:                                           |
//|     RMSError        RMS error                                    |
//|     AvgError        average error                                |
//|     AvgRelError     average relative error (for non-zero Y[I])   |
//|     MaxError        maximum error                                |
//| Fields below are filled by obsolete functions (Spline1DFitCubic, |
//| Spline1DFitHermite). Modern fitting functions do NOT fill these  | 
//| fields:                                                          |
//|     TaskRCond       reciprocal of task's condition number        |
//+------------------------------------------------------------------+
class CSpline1DFitReportShell
  {
private:
   CSpline1DFitReport m_innerobj;
public:
   //--- constructors, destructor
                     CSpline1DFitReportShell(void);
                     CSpline1DFitReportShell(CSpline1DFitReport &obj);
                    ~CSpline1DFitReportShell(void);
   //--- methods
   double            GetTaskRCond(void);
   void              SetTaskRCond(const double d);
   double            GetRMSError(void);
   void              SetRMSError(const double d);
   double            GetAvgError(void);
   void              SetAvgError(const double d);
   double            GetAvgRelError(void);
   void              SetAvgRelError(const double d);
   double            GetMaxError(void);
   void              SetMaxError(const double d);
   CSpline1DFitReport *GetInnerObj(void);
  };
//+------------------------------------------------------------------+
//| Constructor without parameters                                   |
//+------------------------------------------------------------------+
CSpline1DFitReportShell::CSpline1DFitReportShell(void)
  {

  }
//+------------------------------------------------------------------+
//| Copy constructor                                                 |
//+------------------------------------------------------------------+
CSpline1DFitReportShell::CSpline1DFitReportShell(CSpline1DFitReport &obj)
  {
//--- copy
   m_innerobj.Copy(obj);
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CSpline1DFitReportShell::~CSpline1DFitReportShell(void)
  {

  }
//+------------------------------------------------------------------+
//| Returns the value of the variable taskrcond                      |
//+------------------------------------------------------------------+
double CSpline1DFitReportShell::GetTaskRCond(void)
  {
//--- return result
   return(m_innerobj.m_taskrcond);
  }
//+------------------------------------------------------------------+
//| Changing the value of the variable taskrcond                     |
//+------------------------------------------------------------------+
void CSpline1DFitReportShell::SetTaskRCond(const double d)
  {
//--- change value
   m_innerobj.m_taskrcond=d;
  }
//+------------------------------------------------------------------+
//| Returns the value of the variable rmserror                       |
//+------------------------------------------------------------------+
double CSpline1DFitReportShell::GetRMSError(void)
  {
//--- return result
   return(m_innerobj.m_rmserror);
  }
//+------------------------------------------------------------------+
//| Changing the value of the variable rmserror                      |
//+------------------------------------------------------------------+
void CSpline1DFitReportShell::SetRMSError(const double d)
  {
//--- change value
   m_innerobj.m_rmserror=d;
  }
//+------------------------------------------------------------------+
//| Returns the value of the variable avgerror                       |
//+------------------------------------------------------------------+
double CSpline1DFitReportShell::GetAvgError(void)
  {
//--- return result
   return(m_innerobj.m_avgerror);
  }
//+------------------------------------------------------------------+
//| Changing the value of the variable avgerror                      |
//+------------------------------------------------------------------+
void CSpline1DFitReportShell::SetAvgError(const double d)
  {
//--- change value
   m_innerobj.m_avgerror=d;
  }
//+------------------------------------------------------------------+
//| Returns the value of the variable avgrelerror                    |
//+------------------------------------------------------------------+
double CSpline1DFitReportShell::GetAvgRelError(void)
  {
//--- return result
   return(m_innerobj.m_avgrelerror);
  }
//+------------------------------------------------------------------+
//| Changing the value of the variable avgrelerror                   |
//+------------------------------------------------------------------+
void CSpline1DFitReportShell::SetAvgRelError(const double d)
  {
//--- change value
   m_innerobj.m_avgrelerror=d;
  }
//+------------------------------------------------------------------+
//| Returns the value of the variable maxerror                       |
//+------------------------------------------------------------------+
double CSpline1DFitReportShell::GetMaxError(void)
  {
//--- return result
   return(m_innerobj.m_maxerror);
  }
//+------------------------------------------------------------------+
//| Changing the value of the variable maxerror                      |
//+------------------------------------------------------------------+
void CSpline1DFitReportShell::SetMaxError(const double d)
  {
//--- change value
   m_innerobj.m_maxerror=d;
  }
//+------------------------------------------------------------------+
//| Return object of class                                           |
//+------------------------------------------------------------------+
CSpline1DFitReport *CSpline1DFitReportShell::GetInnerObj(void)
  {
//--- return result
   return(GetPointer(m_innerobj));
  }
//+------------------------------------------------------------------+
//| Least squares fitting report:                                    |
//|     TaskRCond       reciprocal of task's condition number        |
//|     IterationsCount number of internal iterations                |
//|     RMSError        RMS error                                    |
//|     AvgError        average error                                |
//|     AvgRelError     average relative error (for non-zero Y[I])   |
//|     MaxError        maximum error                                |
//|     WRMSError       weighted RMS error                           |
//+------------------------------------------------------------------+
class CLSFitReport
  {
public:
   //--- variables
   double            m_taskrcond;
   int               m_iterationscount;
   double            m_rmserror;
   double            m_avgerror;
   double            m_avgrelerror;
   double            m_maxerror;
   double            m_wrmserror;
   //--- constructor, destructor
                     CLSFitReport(void);
                    ~CLSFitReport(void);
   //--- copy
   void              Copy(CLSFitReport &obj);
  };
//+------------------------------------------------------------------+
//| Constructor without parameters                                   |
//+------------------------------------------------------------------+
CLSFitReport::CLSFitReport(void)
  {

  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CLSFitReport::~CLSFitReport(void)
  {

  }
//+------------------------------------------------------------------+
//| Copy                                                             |
//+------------------------------------------------------------------+
void CLSFitReport::Copy(CLSFitReport &obj)
  {
//--- copy variables
   m_taskrcond=obj.m_taskrcond;
   m_iterationscount=obj.m_iterationscount;
   m_rmserror=obj.m_rmserror;
   m_avgerror=obj.m_avgerror;
   m_avgrelerror=obj.m_avgrelerror;
   m_maxerror=obj.m_maxerror;
   m_wrmserror=obj.m_wrmserror;
  }
//+------------------------------------------------------------------+
//| Least squares fitting report:                                    |
//|     TaskRCond       reciprocal of task's condition number        |
//|     IterationsCount number of internal iterations                |
//|     RMSError        RMS error                                    |
//|     AvgError        average error                                |
//|     AvgRelError     average relative error (for non-zero Y[I])   |
//|     MaxError        maximum error                                |
//|     WRMSError       weighted RMS error                           |
//+------------------------------------------------------------------+
class CLSFitReportShell
  {
private:
   CLSFitReport      m_innerobj;
public:
   //--- constructors, destructor
                     CLSFitReportShell(void);
                     CLSFitReportShell(CLSFitReport &obj);
                    ~CLSFitReportShell(void);
   //--- methods
   double            GetTaskRCond(void);
   void              SetTaskRCond(const double d);
   int               GetIterationsCount(void);
   void              SetIterationsCount(const int i);
   double            GetRMSError(void);
   void              SetRMSError(const double d);
   double            GetAvgError(void);
   void              SetAvgError(const double d);
   double            GetAvgRelError(void);
   void              SetAvgRelError(const double d);
   double            GetMaxError(void);
   void              SetMaxError(const double d);
   double            GetWRMSError(void);
   void              SetWRMSError(const double d);
   CLSFitReport     *GetInnerObj(void);
  };
//+------------------------------------------------------------------+
//| Constructor without parameters                                   |
//+------------------------------------------------------------------+
CLSFitReportShell::CLSFitReportShell(void)
  {

  }
//+------------------------------------------------------------------+
//| Copy constructor                                                 |
//+------------------------------------------------------------------+
CLSFitReportShell::CLSFitReportShell(CLSFitReport &obj)
  {
//--- copy
   m_innerobj.Copy(obj);
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CLSFitReportShell::~CLSFitReportShell(void)
  {

  }
//+------------------------------------------------------------------+
//| Returns the value of the variable taskrcond                      |
//+------------------------------------------------------------------+
double CLSFitReportShell::GetTaskRCond(void)
  {
//--- return result
   return(m_innerobj.m_taskrcond);
  }
//+------------------------------------------------------------------+
//| Changing the value of the variable taskrcond                     |
//+------------------------------------------------------------------+
void CLSFitReportShell::SetTaskRCond(const double d)
  {
//--- change value
   m_innerobj.m_taskrcond=d;
  }
//+------------------------------------------------------------------+
//| Returns the value of the variable iterationscount                |
//+------------------------------------------------------------------+
int CLSFitReportShell::GetIterationsCount(void)
  {
//--- return result
   return(m_innerobj.m_iterationscount);
  }
//+------------------------------------------------------------------+
//| Changing the value of the variable iterationscount               |
//+------------------------------------------------------------------+
void CLSFitReportShell::SetIterationsCount(const int i)
  {
//--- change value
   m_innerobj.m_iterationscount=i;
  }
//+------------------------------------------------------------------+
//| Returns the value of the variable rmserror                       |
//+------------------------------------------------------------------+
double CLSFitReportShell::GetRMSError(void)
  {
//--- return result
   return(m_innerobj.m_rmserror);
  }
//+------------------------------------------------------------------+
//| Changing the value of the variable rmserror                      |
//+------------------------------------------------------------------+
void CLSFitReportShell::SetRMSError(const double d)
  {
//--- change value
   m_innerobj.m_rmserror=d;
  }
//+------------------------------------------------------------------+
//| Returns the value of the variable avgerror                       |
//+------------------------------------------------------------------+
double CLSFitReportShell::GetAvgError(void)
  {
//--- return result
   return(m_innerobj.m_avgerror);
  }
//+------------------------------------------------------------------+
//| Changing the value of the variable avgerror                      |
//+------------------------------------------------------------------+
void CLSFitReportShell::SetAvgError(const double d)
  {
//--- change value
   m_innerobj.m_avgerror=d;
  }
//+------------------------------------------------------------------+
//| Returns the value of the variable avgrelerror                    |
//+------------------------------------------------------------------+
double CLSFitReportShell::GetAvgRelError(void)
  {
//--- return result
   return(m_innerobj.m_avgrelerror);
  }
//+------------------------------------------------------------------+
//| Changing the value of the variable avgrelerror                   |
//+------------------------------------------------------------------+
void CLSFitReportShell::SetAvgRelError(const double d)
  {
//--- change value
   m_innerobj.m_avgrelerror=d;
  }
//+------------------------------------------------------------------+
//| Returns the value of the variable maxerror                       |
//+------------------------------------------------------------------+
double CLSFitReportShell::GetMaxError(void)
  {
//--- return result
   return(m_innerobj.m_maxerror);
  }
//+------------------------------------------------------------------+
//| Changing the value of the variable maxerror                      |
//+------------------------------------------------------------------+
void CLSFitReportShell::SetMaxError(const double d)
  {
//--- change value
   m_innerobj.m_maxerror=d;
  }
//+------------------------------------------------------------------+
//| Returns the value of the variable wrmserror                      |
//+------------------------------------------------------------------+
double CLSFitReportShell::GetWRMSError(void)
  {
//--- return result
   return(m_innerobj.m_wrmserror);
  }
//+------------------------------------------------------------------+
//| Changing the value of the variable wrmserror                     |
//+------------------------------------------------------------------+
void CLSFitReportShell::SetWRMSError(const double d)
  {
//--- change value
   m_innerobj.m_wrmserror=d;
  }
//+------------------------------------------------------------------+
//| Return object of class                                           |
//+------------------------------------------------------------------+
CLSFitReport *CLSFitReportShell::GetInnerObj(void)
  {
//--- return result
   return(GetPointer(m_innerobj));
  }
//+------------------------------------------------------------------+
//| Nonlinear fitter.                                                |
//| You should use ALGLIB functions to work with fitter.             |
//| Never try to access its fields directly!                         |
//+------------------------------------------------------------------+
class CLSFitState
  {
public:
   //--- variables
   int               m_optalgo;
   int               m_m;
   int               m_k;
   double            m_f;
   double            m_epsf;
   double            m_epsx;
   int               m_maxits;
   double            m_stpmax;
   bool              m_xrep;
   int               m_npoints;
   int               m_nweights;
   int               m_wkind;
   int               m_wits;
   bool              m_xupdated;
   bool              m_needf;
   bool              m_needfg;
   bool              m_needfgh;
   int               m_pointindex;
   int               m_repiterationscount;
   int               m_repterminationtype;
   double            m_reprmserror;
   double            m_repavgerror;
   double            m_repavgrelerror;
   double            m_repmaxerror;
   double            m_repwrmserror;
   CMinLMState       m_optstate;
   CMinLMReport      m_optrep;
   int               m_prevnpt;
   int               m_prevalgo;
   RCommState        m_rstate;
   //--- arrays
   double            m_s[];
   double            m_bndl[];
   double            m_bndu[];
   double            m_tasky[];
   double            m_w[];
   double            m_x[];
   double            m_c[];
   double            m_g[];
   //--- matrices
   CMatrixDouble     m_taskx;
   CMatrixDouble     m_h;
   //--- constructor, destructor
                     CLSFitState(void);
                    ~CLSFitState(void);
   //--- copy
   void              Copy(CLSFitState &obj);
  };
//+------------------------------------------------------------------+
//| Constructor without parameters                                   |
//+------------------------------------------------------------------+
CLSFitState::CLSFitState(void)
  {

  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CLSFitState::~CLSFitState(void)
  {

  }
//+------------------------------------------------------------------+
//| Copy                                                             |
//+------------------------------------------------------------------+
void CLSFitState::Copy(CLSFitState &obj)
  {
//--- copy variables
   m_optalgo=obj.m_optalgo;
   m_m=obj.m_m;
   m_k=obj.m_k;
   m_f=obj.m_f;
   m_epsf=obj.m_epsf;
   m_epsx=obj.m_epsx;
   m_maxits=obj.m_maxits;
   m_stpmax=obj.m_stpmax;
   m_xrep=obj.m_xrep;
   m_npoints=obj.m_npoints;
   m_nweights=obj.m_nweights;
   m_wkind=obj.m_wkind;
   m_wits=obj.m_wits;
   m_xupdated=obj.m_xupdated;
   m_needf=obj.m_needf;
   m_needfg=obj.m_needfg;
   m_needfgh=obj.m_needfgh;
   m_pointindex=obj.m_pointindex;
   m_repiterationscount=obj.m_repiterationscount;
   m_repterminationtype=obj.m_repterminationtype;
   m_reprmserror=obj.m_reprmserror;
   m_repavgerror=obj.m_repavgerror;
   m_repavgrelerror=obj.m_repavgrelerror;
   m_repmaxerror=obj.m_repmaxerror;
   m_repwrmserror=obj.m_repwrmserror;
   m_prevnpt=obj.m_prevnpt;
   m_prevalgo=obj.m_prevalgo;
   m_optstate.Copy(obj.m_optstate);
   m_optrep.Copy(obj.m_optrep);
   m_rstate.Copy(obj.m_rstate);
//--- copy arrays
   ArrayCopy(m_s,obj.m_s);
   ArrayCopy(m_bndl,obj.m_bndl);
   ArrayCopy(m_bndu,obj.m_bndu);
   ArrayCopy(m_tasky,obj.m_tasky);
   ArrayCopy(m_w,obj.m_w);
   ArrayCopy(m_x,obj.m_x);
   ArrayCopy(m_c,obj.m_c);
   ArrayCopy(m_g,obj.m_g);
//--- copy matrices
   m_taskx=obj.m_taskx;
   m_h=obj.m_h;
  }
//+------------------------------------------------------------------+
//| Nonlinear fitter.                                                |
//| You should use ALGLIB functions to work with fitter.             |
//| Never try to access its fields directly!                         |
//+------------------------------------------------------------------+
class CLSFitStateShell
  {
private:
   CLSFitState       m_innerobj;
public:
   //--- constructors, destructor
                     CLSFitStateShell(void);
                     CLSFitStateShell(CLSFitState &obj);
                    ~CLSFitStateShell(void);
   //--- methods
   bool              GetNeedF(void);
   void              SetNeedF(const bool b);
   bool              GetNeedFG(void);
   void              SetNeedFG(const bool b);
   bool              GetNeedFGH(void);
   void              SetNeedFGH(const bool b);
   bool              GetXUpdated(void);
   void              SetXUpdated(const bool b);
   double            GetF(void);
   void              SetF(const double d);
   CLSFitState      *GetInnerObj(void);
  };
//+------------------------------------------------------------------+
//| Constructor without parameters                                   |
//+------------------------------------------------------------------+
CLSFitStateShell::CLSFitStateShell(void)
  {

  }
//+------------------------------------------------------------------+
//| Copy constructor                                                 |
//+------------------------------------------------------------------+
CLSFitStateShell::CLSFitStateShell(CLSFitState &obj)
  {
//--- copy
   m_innerobj.Copy(obj);
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CLSFitStateShell::~CLSFitStateShell(void)
  {

  }
//+------------------------------------------------------------------+
//| Returns the value of the variable needf                          |
//+------------------------------------------------------------------+
bool CLSFitStateShell::GetNeedF(void)
  {
//--- return result
   return(m_innerobj.m_needf);
  }
//+------------------------------------------------------------------+
//| Changing the value of the variable needf                         |
//+------------------------------------------------------------------+
void CLSFitStateShell::SetNeedF(const bool b)
  {
//--- change value
   m_innerobj.m_needf=b;
  }
//+------------------------------------------------------------------+
//| Returns the value of the variable needfg                         |
//+------------------------------------------------------------------+
bool CLSFitStateShell::GetNeedFG(void)
  {
//--- return result
   return(m_innerobj.m_needfg);
  }
//+------------------------------------------------------------------+
//| Changing the value of the variable needfg                        |
//+------------------------------------------------------------------+
void CLSFitStateShell::SetNeedFG(const bool b)
  {
//--- change value
   m_innerobj.m_needfg=b;
  }
//+------------------------------------------------------------------+
//| Returns the value of the variable needfgh                        |
//+------------------------------------------------------------------+
bool CLSFitStateShell::GetNeedFGH(void)
  {
//--- return result
   return(m_innerobj.m_needfgh);
  }
//+------------------------------------------------------------------+
//| Changing the value of the variable needfgh                       |
//+------------------------------------------------------------------+
void CLSFitStateShell::SetNeedFGH(const bool b)
  {
//--- change value
   m_innerobj.m_needfgh=b;
  }
//+------------------------------------------------------------------+
//| Returns the value of the variable xupdated                       |
//+------------------------------------------------------------------+
bool CLSFitStateShell::GetXUpdated(void)
  {
//--- return result
   return(m_innerobj.m_xupdated);
  }
//+------------------------------------------------------------------+
//| Changing the value of the variable xupdated                      |
//+------------------------------------------------------------------+
void CLSFitStateShell::SetXUpdated(const bool b)
  {
//--- change value
   m_innerobj.m_xupdated=b;
  }
//+------------------------------------------------------------------+
//| Returns the value of the variable f                              |
//+------------------------------------------------------------------+
double CLSFitStateShell::GetF(void)
  {
//--- return result
   return(m_innerobj.m_f);
  }
//+------------------------------------------------------------------+
//| Changing the value of the variable f                             |
//+------------------------------------------------------------------+
void CLSFitStateShell::SetF(const double d)
  {
//--- change value
   m_innerobj.m_f=d;
  }
//+------------------------------------------------------------------+
//| Return object of class                                           |
//+------------------------------------------------------------------+
CLSFitState *CLSFitStateShell::GetInnerObj(void)
  {
//--- return result
   return(GetPointer(m_innerobj));
  }
//+------------------------------------------------------------------+
//| Least squares fitting                                            |
//+------------------------------------------------------------------+
class CLSFit
  {
private:
   //--- private methods
   static void       Spline1DFitInternal(const int st,double &cx[],double &cy[],double &cw[],const int n,double &cxc[],double &cyc[],int &dc[],const int k,const int m,int &info,CSpline1DInterpolant &s,CSpline1DFitReport &rep);
   static void       LSFitLinearInternal(double &y[],double &w[],CMatrixDouble &fmatrix,const int n,const int m,int &info,double &c[],CLSFitReport &rep);
   static void       LSFitClearRequestFields(CLSFitState &state);
   static void       BarycentricCalcBasis(CBarycentricInterpolant &b,const double t,double &y[]);
   static void       InternalChebyshevFit(double &x[],double &y[],double &w[],const int n,double &cxc[],double &cyc[],int &dc[],const int k,const int m,int &info,double &c[],CLSFitReport &rep);
   static void       BarycentricFitWCFixedD(double &cx[],double &cy[],double &cw[],const int n,double &cxc[],double &cyc[],int &dc[],const int k,const int m,const int d,int &info,CBarycentricInterpolant &b,CBarycentricFitReport &rep);
   //--- auxiliary functions for LSFitIteration
   static void       Func_lbl_rcomm(CLSFitState &state,int n,int m,int k,int i,int j,double v,double vv,double relcnt);
   static bool       Func_lbl_7(CLSFitState &state,int &n,int &m,int &k,int &i,int &j,double &v,double &vv,double &relcnt);
   static bool       Func_lbl_11(CLSFitState &state,int &n,int &m,int &k,int &i,int &j,double &v,double &vv,double &relcnt);
   static bool       Func_lbl_14(CLSFitState &state,int &n,int &m,int &k,int &i,int &j,double &v,double &vv,double &relcnt);
   static bool       Func_lbl_16(CLSFitState &state,int &n,int &m,int &k,int &i,int &j,double &v,double &vv,double &relcnt);
   static bool       Func_lbl_21(CLSFitState &state,int &n,int &m,int &k,int &i,int &j,double &v,double &vv,double &relcnt);
   static bool       Func_lbl_24(CLSFitState &state,int &n,int &m,int &k,int &i,int &j,double &v,double &vv,double &relcnt);
   static bool       Func_lbl_26(CLSFitState &state,int &n,int &m,int &k,int &i,int &j,double &v,double &vv,double &relcnt);
   static bool       Func_lbl_29(CLSFitState &state,int &n,int &m,int &k,int &i,int &j,double &v,double &vv,double &relcnt);
   static bool       Func_lbl_31(CLSFitState &state,int &n,int &m,int &k,int &i,int &j,double &v,double &vv,double &relcnt);
   static bool       Func_lbl_38(CLSFitState &state,int &n,int &m,int &k,int &i,int &j,double &v,double &vv,double &relcnt);
public:
   //--- class constant
   static const int  m_rfsmax;
   //--- constructor, destructor
                     CLSFit(void);
                    ~CLSFit(void);
   //--- public methods
   static void       PolynomialFit(double &x[],double &y[],const int n,const int m,int &info,CBarycentricInterpolant &p,CPolynomialFitReport &rep);
   static void       PolynomialFitWC(double &cx[],double &cy[],double &cw[],const int n,double &cxc[],double &cyc[],int &dc[],const int k,const int m,int &info,CBarycentricInterpolant &p,CPolynomialFitReport &rep);
   static void       BarycentricFitFloaterHormannWC(double &x[],double &y[],double &w[],const int n,double &xc[],double &yc[],int &dc[],const int k,const int m,int &info,CBarycentricInterpolant &b,CBarycentricFitReport &rep);
   static void       BarycentricFitFloaterHormann(double &x[],double &y[],const int n,const int m,int &info,CBarycentricInterpolant &b,CBarycentricFitReport &rep);
   static void       Spline1DFitPenalized(double &cx[],double &cy[],const int n,const int m,const double rho,int &info,CSpline1DInterpolant &s,CSpline1DFitReport &rep);
   static void       Spline1DFitPenalizedW(double &cx[],double &cy[],double &cw[],const int n,const int m,double rho,int &info,CSpline1DInterpolant &s,CSpline1DFitReport &rep);
   static void       Spline1DFitCubicWC(double &x[],double &y[],double &w[],const int n,double &xc[],double &yc[],int &dc[],const int k,const int m,int &info,CSpline1DInterpolant &s,CSpline1DFitReport &rep);
   static void       Spline1DFitHermiteWC(double &x[],double &y[],double &w[],const int n,double &xc[],double &yc[],int &dc[],const int k,const int m,int &info,CSpline1DInterpolant &s,CSpline1DFitReport &rep);
   static void       Spline1DFitCubic(double &x[],double &y[],const int n,const int m,int &info,CSpline1DInterpolant &s,CSpline1DFitReport &rep);
   static void       Spline1DFitHermite(double &x[],double &y[],const int n,const int m,int &info,CSpline1DInterpolant &s,CSpline1DFitReport &rep);
   static void       LSFitLinearW(double &y[],double &w[],CMatrixDouble &fmatrix,const int n,const int m,int &info,double &c[],CLSFitReport &rep);
   static void       LSFitLinearWC(double &cy[],double &w[],CMatrixDouble &fmatrix,CMatrixDouble &ccmatrix,const int n,const int m,const int k,int &info,double &c[],CLSFitReport &rep);
   static void       LSFitLinear(double &y[],CMatrixDouble &fmatrix,const int n,const int m,int &info,double &c[],CLSFitReport &rep);
   static void       LSFitLinearC(double &cy[],CMatrixDouble &fmatrix,CMatrixDouble &cmatrix,const int n,const int m,const int k,int &info,double &c[],CLSFitReport &rep);
   static void       LSFitCreateWF(CMatrixDouble &x,double &y[],double &w[],double &c[],const int n,const int m,const int k,const double diffstep,CLSFitState &state);
   static void       LSFitCreateF(CMatrixDouble &x,double &y[],double &c[],const int n,const int m,const int k,const double diffstep,CLSFitState &state);
   static void       LSFitCreateWFG(CMatrixDouble &x,double &y[],double &w[],double &c[],const int n,const int m,const int k,bool cheapfg,CLSFitState &state);
   static void       LSFitCreateFG(CMatrixDouble &x,double &y[],double &c[],const int n,const int m,const int k,const bool cheapfg,CLSFitState &state);
   static void       LSFitCreateWFGH(CMatrixDouble &x,double &y[],double &w[],double &c[],const int n,const int m,const int k,CLSFitState &state);
   static void       LSFitCreateFGH(CMatrixDouble &x,double &y[],double &c[],const int n,const int m,const int k,CLSFitState &state);
   static void       LSFitSetCond(CLSFitState &state,const double epsf,const double epsx,const int maxits);
   static void       LSFitSetStpMax(CLSFitState &state,const double stpmax);
   static void       LSFitSetXRep(CLSFitState &state,const bool needxrep);
   static void       LSFitSetScale(CLSFitState &state,double &s[]);
   static void       LSFitSetBC(CLSFitState &state,double &bndl[],double &bndu[]);
   static void       LSFitResults(CLSFitState &state,int &info,double &c[],CLSFitReport &rep);
   static void       LSFitScaleXY(double &x[],double &y[],double &w[],const int n,double &xc[],double &yc[],int &dc[],const int k,double &xa,double &xb,double &sa,double &sb,double &xoriginal[],double &yoriginal[]);
   static bool       LSFitIteration(CLSFitState &state);
  };
//+------------------------------------------------------------------+
//| Initialize constant                                              |
//+------------------------------------------------------------------+
const int CLSFit::m_rfsmax=10;
//+------------------------------------------------------------------+
//| Constructor without parameters                                   |
//+------------------------------------------------------------------+
CLSFit::CLSFit(void)
  {

  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CLSFit::~CLSFit(void)
  {

  }
//+------------------------------------------------------------------+
//| Fitting by polynomials in barycentric form. This function        |
//| provides simple unterface for unconstrained unweighted fitting.  |
//| See PolynomialFitWC() if you need constrained fitting.           |
//| Task is linear, so linear least squares solver is used.          |
//| Complexity of this computational scheme is O(N*M^2), mostly      |
//| dominated by least squares solver                                |
//| SEE ALSO:                                                        |
//|     PolynomialFitWC()                                            |
//| INPUT PARAMETERS:                                                |
//|     X   -   points, array[0..N-1].                               |
//|     Y   -   function values, array[0..N-1].                      |
//|     N   -   number of points, N>0                                |
//|             * if given, only leading N elements of X/Y are used  |
//|             * if not given, automatically determined from sizes  |
//|               of X/Y                                             |
//|     M   -   number of basis functions (= polynomial_degree + 1), |
//|             M>=1                                                 |
//| OUTPUT PARAMETERS:                                               |
//|     Info-   same format as in LSFitLinearW() subroutine:         |
//|             * Info>0    task is solved                           |
//|             * Info<=0   an error occured:                        |
//|                         -4 means inconvergence of internal SVD   |
//|     P   -   interpolant in barycentric form.                     |
//|     Rep -   report, same format as in LSFitLinearW() subroutine. |
//|             Following fields are set:                            |
//|             * RMSError      rms error on the (X,Y).              |
//|             * AvgError      average error on the (X,Y).          |
//|             * AvgRelError   average relative error on the        |
//|                             non-zero Y                           |
//|             * MaxError      maximum error                        |
//|                             NON-WEIGHTED ERRORS ARE CALCULATED   |
//| NOTES:                                                           |
//|     you can convert P from barycentric form to the power or      |
//|     Chebyshev basis with PolynomialBar2Pow() or                  |
//|     PolynomialBar2Cheb() functions from POLINT subpackage.       |
//+------------------------------------------------------------------+
static void CLSFit::PolynomialFit(double &x[],double &y[],const int n,
                                  const int m,int &info,
                                  CBarycentricInterpolant &p,
                                  CPolynomialFitReport &rep)
  {
//--- create a variable
   int i=0;
//--- create arrays
   double w[];
   double xc[];
   double yc[];
   int    dc[];
//--- initialization
   info=0;
//--- check
   if(!CAp::Assert(n>0,__FUNCTION__+": N<=0!"))
      return;
//--- check
   if(!CAp::Assert(m>0,__FUNCTION__+": M<=0!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(x)>=n,__FUNCTION__+": Length(X)<N!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(y)>=n,__FUNCTION__+": Length(Y)<N!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(x,n),__FUNCTION__+": X contains infinite or NaN values!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(y,n),__FUNCTION__+": Y contains infinite or NaN values!"))
      return;
//--- allocation
   ArrayResizeAL(w,n);
//--- initialization
   for(i=0;i<=n-1;i++)
      w[i]=1;
//--- function call
   PolynomialFitWC(x,y,w,n,xc,yc,dc,0,m,info,p,rep);
  }
//+------------------------------------------------------------------+
//| Weighted  fitting by polynomials in barycentric form, with       |
//| constraints  on function values or first derivatives.            |
//| Small regularizing term is used when solving constrained tasks   |
//| (to improve stability).                                          |
//| Task is linear, so linear least squares solver is used.          |
//| Complexity of this computational scheme is O(N*M^2), mostly      |
//| dominated by least squares solver                                |
//| SEE ALSO:                                                        |
//|     PolynomialFit()                                              |
//| INPUT PARAMETERS:                                                |
//|     X   -   points, array[0..N-1].                               |
//|     Y   -   function values, array[0..N-1].                      |
//|     W   -   weights, array[0..N-1]                               |
//|             Each summand in square sum of approximation          |
//|             deviations from given values is multiplied by the    |
//|             square of corresponding weight. Fill it by 1's if you|
//|             don't want to solve weighted task.                   |
//|     N   -   number of points, N>0.                               |
//|             * if given, only leading N elements of X/Y/W are used|
//|             * if not given, automatically determined from sizes  |
//|               of X/Y/W                                           |
//|     XC  -   points where polynomial values/derivatives are       |
//|             constrained, array[0..K-1].                          |
//|     YC  -   values of constraints, array[0..K-1]                 |
//|     DC  -   array[0..K-1], types of constraints:                 |
//|             * DC[i]=0   means that P(XC[i])=YC[i]                |
//|             * DC[i]=1   means that P'(XC[i])=YC[i]               |
//|             SEE BELOW FOR IMPORTANT INFORMATION ON CONSTRAINTS   |
//|     K   -   number of constraints, 0<=K<M.                       |
//|             K=0 means no constraints (XC/YC/DC are not used in   |
//|             such cases)                                          |
//|     M   -   number of basis functions (= polynomial_degree + 1), |
//|             M>=1                                                 |
//| OUTPUT PARAMETERS:                                               |
//|     Info-   same format as in LSFitLinearW() subroutine:         |
//|             * Info>0    task is solved                           |
//|             * Info<=0   an error occured:                        |
//|                         -4 means inconvergence of internal SVD   |
//|                         -3 means inconsistent constraints        |
//|     P   -   interpolant in barycentric form.                     |
//|     Rep -   report, same format as in LSFitLinearW() subroutine. |
//|             Following fields are set:                            |
//|             * RMSError      rms error on the (X,Y).              |
//|             * AvgError      average error on the (X,Y).          |
//|             * AvgRelError   average relative error on the        |
//|                             non-zero Y                           |
//|             * MaxError      maximum error                        |
//|                             NON-WEIGHTED ERRORS ARE CALCULATED   |
//| IMPORTANT:                                                       |
//|     this subroitine doesn't calculate task's condition number    |
//|     for K<>0.                                                    |
//| NOTES:                                                           |
//|     you can convert P from barycentric form to the power or      |
//|     Chebyshev basis with PolynomialBar2Pow() or                  |
//|     PolynomialBar2Cheb() functions from POLINT subpackage.       |
//| SETTING CONSTRAINTS - DANGERS AND OPPORTUNITIES:                 |
//| Setting constraints can lead to undesired results, like          |
//| ill-conditioned behavior, or inconsistency being detected.       |
//| From the other side, it allows us to improve quality of the fit. |
//| Here we summarize our experience with constrained regression     |
//| splines:                                                         |
//| * even simple constraints can be inconsistent, see Wikipedia     |
//|   article on this subject:                                       |
//|   http://en.wikipedia.org/wiki/Birkhoff_interpolation            |
//| * the greater is M (given fixed constraints), the more chances   |
//|   that constraints will be consistent                            |
//| * in the general case, consistency of constraints is NOT         |
//|   GUARANTEED.                                                    |
//| * in the one special cases, however, we can guarantee            |
//|   consistency. This case  is:  M>1  and constraints on the       |
//|   function values (NOT DERIVATIVES)                              |
//| Our final recommendation is to use constraints WHEN AND ONLY when|
//| you can't solve your task without them. Anything beyond special  |
//| cases given above is not guaranteed and may result in            |
//| inconsistency.                                                   |
//+------------------------------------------------------------------+
static void CLSFit::PolynomialFitWC(double &cx[],double &cy[],double &cw[],
                                    const int n,double &cxc[],double &cyc[],
                                    int &dc[],const int k,const int m,
                                    int &info,CBarycentricInterpolant &p,
                                    CPolynomialFitReport &rep)
  {
//--- create variables
   double xa=0;
   double xb=0;
   double sa=0;
   double sb=0;
   int    i=0;
   int    j=0;
   double u=0;
   double v=0;
   double s=0;
   int    relcnt=0;
//--- create arrays
   double xoriginal[];
   double yoriginal[];
   double y2[];
   double w2[];
   double tmp[];
   double tmp2[];
   double bx[];
   double by[];
   double bw[];
   double x[];
   double y[];
   double w[];
   double xc[];
   double yc[];
//--- object of class
   CLSFitReport lrep;
//--- copy arrays
   ArrayCopy(x,cx);
   ArrayCopy(y,cy);
   ArrayCopy(w,cw);
   ArrayCopy(xc,cxc);
   ArrayCopy(yc,cyc);
//--- initialization
   info=0;
//--- check
   if(!CAp::Assert(n>0,__FUNCTION__+": N<=0!"))
      return;
//--- check
   if(!CAp::Assert(m>0,__FUNCTION__+": M<=0!"))
      return;
//--- check
   if(!CAp::Assert(k>=0,__FUNCTION__+": K<0!"))
      return;
//--- check
   if(!CAp::Assert(k<m,__FUNCTION__+": K>=M!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(x)>=n,__FUNCTION__+": Length(X)<N!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(y)>=n,__FUNCTION__+": Length(Y)<N!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(w)>=n,__FUNCTION__+": Length(W)<N!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(xc)>=k,__FUNCTION__+": Length(XC)<K!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(yc)>=k,__FUNCTION__+": Length(YC)<K!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(dc)>=k,__FUNCTION__+": Length(DC)<K!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(x,n),__FUNCTION__+": X contains infinite or NaN values!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(y,n),__FUNCTION__+": Y contains infinite or NaN values!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(w,n),__FUNCTION__+": X contains infinite or NaN values!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(xc,k),__FUNCTION__+": XC contains infinite or NaN values!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(yc,k),__FUNCTION__+": YC contains infinite or NaN values!"))
      return;
   for(i=0;i<=k-1;i++)
     {
      //--- check
      if(!CAp::Assert(dc[i]==0 || dc[i]==1,__FUNCTION__+": one of DC[] is not 0 or 1!"))
         return;
     }
//--- Scale X,Y,XC,YC.
//--- Solve scaled problem using internal Chebyshev fitting function.
   LSFitScaleXY(x,y,w,n,xc,yc,dc,k,xa,xb,sa,sb,xoriginal,yoriginal);
   InternalChebyshevFit(x,y,w,n,xc,yc,dc,k,m,info,tmp,lrep);
//--- check
   if(info<0)
      return;
//--- Generate barycentric model and scale it
//--- * BX,BY store barycentric model nodes
//--- * FMatrix is reused (remember - it is at least MxM,what we need)
//--- Model intialization is done in O(M^2). In principle,it can be
//--- done in O(M*log(M)),but before it we solved task with O(N*M^2)
//--- complexity,so it is only a small amount of total time spent.
   ArrayResizeAL(bx,m);
   ArrayResizeAL(by,m);
   ArrayResizeAL(bw,m);
   ArrayResizeAL(tmp2,m);
   s=1;
//--- calculation
   for(i=0;i<=m-1;i++)
     {
      //--- check
      if(m!=1)
         u=MathCos(M_PI*i/(m-1));
      else
         u=0;
      v=0;
      for(j=0;j<=m-1;j++)
        {
         //--- check
         if(j==0)
            tmp2[j]=1;
         else
           {
            //--- check
            if(j==1)
               tmp2[j]=u;
            else
               tmp2[j]=2*u*tmp2[j-1]-tmp2[j-2];
           }
         v=v+tmp[j]*tmp2[j];
        }
      //--- change values
      bx[i]=u;
      by[i]=v;
      bw[i]=s;
      //--- check
      if(i==0 || i==m-1)
         bw[i]=0.5*bw[i];
      s=-s;
     }
//--- function call
   CRatInt::BarycentricBuildXYW(bx,by,bw,m,p);
//--- function call
   CRatInt::BarycentricLinTransX(p,2/(xb-xa),-((xa+xb)/(xb-xa)));
//--- function call
   CRatInt::BarycentricLinTransY(p,sb-sa,sa);
//--- Scale absolute errors obtained from LSFitLinearW.
//--- Relative error should be calculated separately
//--- (because of shifting/scaling of the task)
   rep.m_taskrcond=lrep.m_taskrcond;
   rep.m_rmserror=lrep.m_rmserror*(sb-sa);
   rep.m_avgerror=lrep.m_avgerror*(sb-sa);
   rep.m_maxerror=lrep.m_maxerror*(sb-sa);
   rep.m_avgrelerror=0;
   relcnt=0;
//--- calculation
   for(i=0;i<=n-1;i++)
     {
      //--- check
      if(yoriginal[i]!=0.0)
        {
         rep.m_avgrelerror=rep.m_avgrelerror+MathAbs(CRatInt::BarycentricCalc(p,xoriginal[i])-yoriginal[i])/MathAbs(yoriginal[i]);
         relcnt=relcnt+1;
        }
     }
//--- check
   if(relcnt!=0)
      rep.m_avgrelerror=rep.m_avgrelerror/relcnt;
  }
//+------------------------------------------------------------------+
//| Weghted rational least squares fitting using Floater-Hormann     |
//| rational functions with optimal D chosen from [0,9], with        |
//| constraints and individual weights.                              |
//| Equidistant grid with M node on [min(x),max(x)] is used to build |
//| basis functions. Different values of D are tried, optimal D      |
//| (least WEIGHTED root mean square error) is chosen. Task is       |
//| linear, so linear least squares solver is used. Complexity of    |
//| this computational scheme is O(N*M^2) (mostly dominated by the   |
//| least squares solver).                                           |
//| SEE ALSO                                                         |
//| * BarycentricFitFloaterHormann(), "lightweight" fitting without  |
//|   invididual weights and constraints.                            |
//| INPUT PARAMETERS:                                                |
//|     X   -   points, array[0..N-1].                               |
//|     Y   -   function values, array[0..N-1].                      |
//|     W   -   weights, array[0..N-1]                               |
//|             Each summand in square sum of approximation          |
//|             deviations from given values is multiplied by the    |
//|             square of corresponding weight. Fill it by 1's if    |
//|             you don't want to solve weighted task.               |
//|     N   -   number of points, N>0.                               |
//|     XC  -   points where function values/derivatives are         |
//|             constrained, array[0..K-1].                          |
//|     YC  -   values of constraints, array[0..K-1]                 |
//|     DC  -   array[0..K-1], types of constraints:                 |
//|             * DC[i]=0   means that S(XC[i])=YC[i]                |
//|             * DC[i]=1   means that S'(XC[i])=YC[i]               |
//|             SEE BELOW FOR IMPORTANT INFORMATION ON CONSTRAINTS   |
//|     K   -   number of constraints, 0<=K<M.                       |
//|             K=0 means no constraints (XC/YC/DC are not used in   |
//|             such cases)                                          |
//|     M   -   number of basis functions ( = number_of_nodes),      |
//|             M>=2.                                                |
//| OUTPUT PARAMETERS:                                               |
//|     Info-   same format as in LSFitLinearWC() subroutine.        |
//|             * Info>0    task is solved                           |
//|             * Info<=0   an error occured:                        |
//|                         -4 means inconvergence of internal SVD   |
//|                         -3 means inconsistent constraints        |
//|                         -1 means another errors in parameters    |
//|                            passed (N<=0, for example)            |
//|     B   -   barycentric interpolant.                             |
//|     Rep -   report, same format as in LSFitLinearWC() subroutine.|
//|             Following fields are set:                            |
//|             * DBest         best value of the D parameter        |
//|             * RMSError      rms error on the (X,Y).              |
//|             * AvgError      average error on the (X,Y).          |
//|             * AvgRelError   average relative error on the        |
//|                             non-zero Y                           |
//|             * MaxError      maximum error                        |
//|                             NON-WEIGHTED ERRORS ARE CALCULATED   |
//| IMPORTANT:                                                       |
//|     this subroutine doesn't calculate task's condition number    |
//|     for K<>0.                                                    |
//| SETTING CONSTRAINTS - DANGERS AND OPPORTUNITIES:                 |
//| Setting constraints can lead to undesired results, like          |
//| ill-conditioned behavior, or inconsistency being detected. From  |
//| the other side, it allows us to improve quality of the fit. Here |
//| we summarize our experience with constrained barycentric         |
//| interpolants:                                                    |
//| * excessive constraints can be inconsistent. Floater-Hormann     |
//|   basis functions aren't as flexible as splines (although they   |
//|   are very smooth).                                              |
//| * the more evenly constraints are spread across [min(x),max(x)], |
//|   the more chances that they will be consistent                  |
//| * the greater is M (given  fixed  constraints), the more chances |
//|   that constraints will be consistent                            |
//| * in the general case, consistency of constraints IS NOT         |
//|   GUARANTEED.                                                    |
//| * in the several special cases, however, we CAN guarantee        |
//|   consistency.                                                   |
//| * one of this cases is constraints on the function VALUES at the |
//|   interval boundaries. Note that consustency of the constraints  |
//|   on the function DERIVATIVES is NOT guaranteed (you can use in  |
//|   such cases cubic splines which are more flexible).             |
//| * another special case is ONE constraint on the function value   |
//|   (OR, but not AND, derivative) anywhere in the interval         |
//| Our final recommendation is to use constraints WHEN AND ONLY     |
//| WHEN you can't solve your task without them. Anything beyond     |
//| special cases given above is not guaranteed and may result in    |
//| inconsistency.                                                   |
//+------------------------------------------------------------------+
static void CLSFit::BarycentricFitFloaterHormannWC(double &x[],double &y[],
                                                   double &w[],const int n,
                                                   double &xc[],double &yc[],
                                                   int &dc[],const int k,
                                                   const int m,int &info,
                                                   CBarycentricInterpolant &b,
                                                   CBarycentricFitReport &rep)
  {
//--- create variables
   int    d=0;
   int    i=0;
   double wrmscur=0;
   double wrmsbest=0;
   int    locinfo=0;
//--- objects of classes
   CBarycentricInterpolant locb;
   CBarycentricFitReport locrep;
//--- initialization
   info=0;
//--- check
   if(!CAp::Assert(n>0,__FUNCTION__+": N<=0!"))
      return;
//--- check
   if(!CAp::Assert(m>0,__FUNCTION__+": M<=0!"))
      return;
//--- check
   if(!CAp::Assert(k>=0,__FUNCTION__+": K<0!"))
      return;
//--- check
   if(!CAp::Assert(k<m,__FUNCTION__+": K>=M!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(x)>=n,__FUNCTION__+": Length(X)<N!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(y)>=n,__FUNCTION__+": Length(Y)<N!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(w)>=n,__FUNCTION__+": Length(W)<N!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(xc)>=k,__FUNCTION__+": Length(XC)<K!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(yc)>=k,__FUNCTION__+": Length(YC)<K!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(dc)>=k,__FUNCTION__+": Length(DC)<K!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(x,n),__FUNCTION__+": X contains infinite or NaN values!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(y,n),__FUNCTION__+": Y contains infinite or NaN values!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(w,n),__FUNCTION__+": X contains infinite or NaN values!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(xc,k),__FUNCTION__+": XC contains infinite or NaN values!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(yc,k),__FUNCTION__+": YC contains infinite or NaN values!"))
      return;
   for(i=0;i<=k-1;i++)
     {
      //--- check
      if(!CAp::Assert(dc[i]==0 || dc[i]==1,__FUNCTION__+": one of DC[] is not 0 or 1!"))
         return;
     }
//--- Find optimal D
//--- Info is -3 by default (degenerate constraints).
//--- If LocInfo will always be equal to -3,Info will remain equal to -3.
//--- If at least once LocInfo will be -4,Info will be -4.
   wrmsbest=CMath::m_maxrealnumber;
   rep.m_dbest=-1;
   info=-3;
//--- calculation
   for(d=0;d<=MathMin(9,n-1);d++)
     {
      //--- function call
      BarycentricFitWCFixedD(x,y,w,n,xc,yc,dc,k,m,d,locinfo,locb,locrep);
      //--- check
      if(!CAp::Assert((locinfo==-4 || locinfo==-3) || locinfo>0,__FUNCTION__+": unexpected result from BarycentricFitWCFixedD!"))
         return;
      //--- check
      if(locinfo>0)
        {
         //--- Calculate weghted RMS
         wrmscur=0;
         for(i=0;i<=n-1;i++)
            wrmscur=wrmscur+CMath::Sqr(w[i]*(y[i]-CRatInt::BarycentricCalc(locb,x[i])));
         wrmscur=MathSqrt(wrmscur/n);
         //--- check
         if(wrmscur<wrmsbest || rep.m_dbest<0)
           {
            //--- function call
            CRatInt::BarycentricCopy(locb,b);
            //--- change values
            rep.m_dbest=d;
            info=1;
            rep.m_rmserror=locrep.m_rmserror;
            rep.m_avgerror=locrep.m_avgerror;
            rep.m_avgrelerror=locrep.m_avgrelerror;
            rep.m_maxerror=locrep.m_maxerror;
            rep.m_taskrcond=locrep.m_taskrcond;
            wrmsbest=wrmscur;
           }
        }
      else
        {
         //--- check
         if(locinfo!=-3 && info<0)
            info=locinfo;
        }
     }
  }
//+------------------------------------------------------------------+
//| Rational least squares fitting using Floater-Hormann rational    |
//| functions with optimal D chosen from [0,9].                      |
//| Equidistant grid with M node on [min(x),max(x)] is used to build |
//| basis functions. Different values of D are tried, optimal D      |
//| (least root mean square error) is chosen.  Task is linear, so    |
//| linear least squares solver is used. Complexity of this          |
//| computational scheme is O(N*M^2) (mostly dominated by the least  |
//| squares solver).                                                 |
//| INPUT PARAMETERS:                                                |
//|     X   -   points, array[0..N-1].                               |
//|     Y   -   function values, array[0..N-1].                      |
//|     N   -   number of points, N>0.                               |
//|     M   -   number of basis functions ( = number_of_nodes), M>=2.|
//| OUTPUT PARAMETERS:                                               |
//|     Info-   same format as in LSFitLinearWC() subroutine.        |
//|             * Info>0    task is solved                           |
//|             * Info<=0   an error occured:                        |
//|                         -4 means inconvergence of internal SVD   |
//|                         -3 means inconsistent constraints        |
//|     B   -   barycentric interpolant.                             |
//|     Rep -   report, same format as in LSFitLinearWC() subroutine.|
//|             Following fields are set:                            |
//|             * DBest         best value of the D parameter        |
//|             * RMSError      rms error on the (X,Y).              |
//|             * AvgError      average error on the (X,Y).          |
//|             * AvgRelError   average relative error on the        |
//|                             non-zero Y                           |
//|             * MaxError      maximum error                        |
//|                             NON-WEIGHTED ERRORS ARE CALCULATED   |
//+------------------------------------------------------------------+
static void CLSFit::BarycentricFitFloaterHormann(double &x[],double &y[],
                                                 const int n,const int m,
                                                 int &info,CBarycentricInterpolant &b,
                                                 CBarycentricFitReport &rep)
  {
//--- create arrays
   double w[];
   double xc[];
   double yc[];
   int    dc[];
//--- create a variable
   int i=0;
//--- initialization
   info=0;
//--- check
   if(!CAp::Assert(n>0,__FUNCTION__+": N<=0!"))
      return;
//--- check
   if(!CAp::Assert(m>0,__FUNCTION__+": M<=0!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(x)>=n,__FUNCTION__+": Length(X)<N!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(y)>=n,__FUNCTION__+": Length(Y)<N!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(x,n),__FUNCTION__+": X contains infinite or NaN values!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(y,n),__FUNCTION__+": Y contains infinite or NaN values!"))
      return;
//--- allocation
   ArrayResizeAL(w,n);
//--- initialization
   for(i=0;i<=n-1;i++)
      w[i]=1;
//--- function call
   BarycentricFitFloaterHormannWC(x,y,w,n,xc,yc,dc,0,m,info,b,rep);
  }
//+------------------------------------------------------------------+
//| Rational least squares fitting using Floater-Hormann rational    |
//| functions with optimal D chosen from [0,9].                      |
//| Equidistant grid with M node on [min(x),max(x)] is used to build |
//| basis functions. Different values of D are tried, optimal D      |
//| (least root mean square error) is chosen. Task is linear, so     |
//| linear least squares solver is used. Complexity of this          |
//| computational scheme is O(N*M^2) (mostly dominated by the least  |
//| squares solver).                                                 |
//| INPUT PARAMETERS:                                                |
//|     X   -   points, array[0..N-1].                               |
//|     Y   -   function values, array[0..N-1].                      |
//|     N   -   number of points, N>0.                               |
//|     M   -   number of basis functions ( = number_of_nodes), M>=2.|
//| OUTPUT PARAMETERS:                                               |
//|     Info-   same format as in LSFitLinearWC() subroutine.        |
//|             * Info>0    task is solved                           |
//|             * Info<=0   an error occured:                        |
//|                         -4 means inconvergence of internal SVD   |
//|                         -3 means inconsistent constraints        |
//|     B   -   barycentric interpolant.                             |
//|     Rep -   report, same format as in LSFitLinearWC() subroutine.|
//|             Following fields are set:                            |
//|             * DBest         best value of the D parameter        |
//|             * RMSError      rms error on the (X,Y).              |
//|             * AvgError      average error on the (X,Y).          |
//|             * AvgRelError   average relative error on the        |
//|                             non-zero Y                           |
//|             * MaxError      maximum error                        |
//|                             NON-WEIGHTED ERRORS ARE CALCULATED   |
//+------------------------------------------------------------------+
static void CLSFit::Spline1DFitPenalized(double &cx[],double &cy[],
                                         const int n,const int m,
                                         const double rho,int &info,
                                         CSpline1DInterpolant &s,
                                         CSpline1DFitReport &rep)
  {
//--- create a variable
   int i=0;
//--- create arrays
   double w[];
   double x[];
   double y[];
//--- copy arrays
   ArrayCopy(x,cx);
   ArrayCopy(y,cy);
//--- initialization
   info=0;
//--- check
   if(!CAp::Assert(n>=1,__FUNCTION__+": N<1!"))
      return;
//--- check
   if(!CAp::Assert(m>=4,__FUNCTION__+": M<4!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(x)>=n,__FUNCTION__+": Length(X)<N!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(y)>=n,__FUNCTION__+": Length(Y)<N!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(x,n),__FUNCTION__+": X contains infinite or NAN values!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(y,n),__FUNCTION__+": Y contains infinite or NAN values!"))
      return;
//--- check
   if(!CAp::Assert(CMath::IsFinite(rho),__FUNCTION__+": Rho is infinite!"))
      return;
//--- allocation
   ArrayResizeAL(w,n);
//--- initialization
   for(i=0;i<=n-1;i++)
      w[i]=1;
//--- function call
   Spline1DFitPenalizedW(x,y,w,n,m,rho,info,s,rep);
  }
//+------------------------------------------------------------------+
//| Weighted fitting by penalized cubic spline.                      |
//| Equidistant grid with M nodes on [min(x,xc),max(x,xc)] is used to|
//| build basis functions. Basis functions are cubic splines with    |
//| natural boundary conditions. Problem is regularized by adding    |
//| non-linearity penalty to the usual least squares penalty         |
//| function:                                                        |
//|     S(x) = arg min { LS + P }, where                             |
//|     LS   = SUM { w[i]^2*(y[i] - S(x[i]))^2 } - least squares     |
//|            penalty                                               |
//|     P    = C*10^rho*integral{ S''(x)^2*dx } - non-linearity      |
//|            penalty                                               |
//|     rho  - tunable constant given by user                        |
//|     C    - automatically determined scale parameter,             |
//|            makes penalty invariant with respect to scaling of X, |
//|            Y, W.                                                 |
//| INPUT PARAMETERS:                                                |
//|     X   -   points, array[0..N-1].                               |
//|     Y   -   function values, array[0..N-1].                      |
//|     W   -   weights, array[0..N-1]                               |
//|             Each summand in square sum of approximation          |
//|             deviations from given values is multiplied by the    |
//|             square of corresponding weight. Fill it by 1's if    |
//|             you don't want to solve weighted problem.            |
//|     N   -   number of points (optional):                         |
//|             * N>0                                                |
//|             * if given, only first N elements of X/Y/W are       |
//|               processed                                          |
//|             * if not given, automatically determined from X/Y/W  |
//|               sizes                                              |
//|     M   -   number of basis functions ( = number_of_nodes), M>=4.|
//|     Rho -   regularization constant passed by user. It penalizes |
//|             nonlinearity in the regression spline. It is         |
//|             logarithmically scaled, i.e. actual value of         |
//|             regularization constant is calculated as 10^Rho. It  |
//|             is automatically scaled so that:                     |
//|             * Rho=2.0 corresponds to moderate amount of          |
//|               nonlinearity                                       |
//|             * generally, it should be somewhere in the           |
//|               [-8.0,+8.0]                                        |
//|             If you do not want to penalize nonlineary,           |
//|             pass small Rho. Values as low as -15 should work.    |
//| OUTPUT PARAMETERS:                                               |
//|     Info-   same format as in LSFitLinearWC() subroutine.        |
//|             * Info>0    task is solved                           |
//|             * Info<=0   an error occured:                        |
//|                         -4 means inconvergence of internal SVD   |
//|                            or Cholesky decomposition; problem    |
//|                            may be too ill-conditioned (very      |
//|                            rare)                                 |
//|     S   -   spline interpolant.                                  |
//|     Rep -   Following fields are set:                            |
//|             * RMSError      rms error on the (X,Y).              |
//|             * AvgError      average error on the (X,Y).          |
//|             * AvgRelError   average relative error on the        |
//|                             non-zero Y                           |
//|             * MaxError      maximum error                        |
//|                             NON-WEIGHTED ERRORS ARE CALCULATED   |
//| IMPORTANT:                                                       |
//|     this subroitine doesn't calculate task's condition number    |
//|     for K<>0.                                                    |
//| NOTE 1: additional nodes are added to the spline outside of the  |
//| fitting interval to force linearity when x<min(x,xc) or          |
//| x>max(x,xc). It is done for consistency - we penalize            |
//| non-linearity at [min(x,xc),max(x,xc)], so it is natural to      |
//| force linearity outside of this interval.                        |
//| NOTE 2: function automatically sorts points, so caller may pass  |
//| unsorted array.                                                  |
//+------------------------------------------------------------------+
static void CLSFit::Spline1DFitPenalizedW(double &cx[],double &cy[],
                                          double &cw[],const int n,
                                          const int m,double rho,
                                          int &info,CSpline1DInterpolant &s,
                                          CSpline1DFitReport &rep)
  {
//--- create variables
   int    i=0;
   int    j=0;
   int    b=0;
   double v=0;
   double relcnt=0;
   double xa=0;
   double xb=0;
   double sa=0;
   double sb=0;
   double pdecay=0;
   double tdecay=0;
   double fdmax=0;
   double admax=0;
   double fa=0;
   double ga=0;
   double fb=0;
   double gb=0;
   double lambdav=0;
   int    i_=0;
   int    i1_=0;
//--- create arrays
   double xoriginal[];
   double yoriginal[];
   double fcolumn[];
   double y2[];
   double w2[];
   double xc[];
   double yc[];
   int    dc[];
   double bx[];
   double by[];
   double bd1[];
   double bd2[];
   double tx[];
   double ty[];
   double td[];
   double rightpart[];
   double c[];
   double tmp0[];
   double x[];
   double y[];
   double w[];
//--- create matrix
   CMatrixDouble fmatrix;
   CMatrixDouble amatrix;
   CMatrixDouble d2matrix;
   CMatrixDouble nmatrix;
//--- objects of classes
   CSpline1DInterpolant bs;
   CFblsLinCgState      cgstate;
//--- copy arrays
   ArrayCopy(x,cx);
   ArrayCopy(y,cy);
   ArrayCopy(w,cw);
//--- initialization
   info=0;
//--- check
   if(!CAp::Assert(n>=1,__FUNCTION__+": N<1!"))
      return;
//--- check
   if(!CAp::Assert(m>=4,__FUNCTION__+": M<4!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(x)>=n,__FUNCTION__+": Length(X)<N!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(y)>=n,__FUNCTION__+": Length(Y)<N!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(w)>=n,__FUNCTION__+": Length(W)<N!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(x,n),__FUNCTION__+": X contains infinite or NAN values!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(y,n),__FUNCTION__+": Y contains infinite or NAN values!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(w,n),__FUNCTION__+": Y contains infinite or NAN values!"))
      return;
//--- check
   if(!CAp::Assert(CMath::IsFinite(rho),__FUNCTION__+": Rho is infinite!"))
      return;
//--- Prepare LambdaV
   v=-(MathLog(CMath::m_machineepsilon)/MathLog(10));
//--- check
   if(rho<-v)
      rho=-v;
//--- check
   if(rho>v)
      rho=v;
   lambdav=MathPow(10,rho);
//--- Sort X,Y,W
   CSpline1D::HeapSortDPoints(x,y,w,n);
//--- Scale X,Y,XC,YC
   LSFitScaleXY(x,y,w,n,xc,yc,dc,0,xa,xb,sa,sb,xoriginal,yoriginal);
//--- Allocate space
   fmatrix.Resize(n,m);
   amatrix.Resize(m,m);
   d2matrix.Resize(m,m);
   ArrayResizeAL(bx,m);
   ArrayResizeAL(by,m);
   ArrayResizeAL(fcolumn,n);
   nmatrix.Resize(m,m);
   ArrayResizeAL(rightpart,m);
   ArrayResizeAL(tmp0,MathMax(m,n));
   ArrayResizeAL(c,m);
//--- Fill:
//--- * FMatrix by values of basis functions
//--- * TmpAMatrix by second derivatives of I-th function at J-th point
//--- * CMatrix by constraints
   fdmax=0;
   for(b=0;b<=m-1;b++)
     {
      //--- Prepare I-th basis function
      for(j=0;j<=m-1;j++)
        {
         bx[j]=(double)(2*j)/(double)(m-1)-1;
         by[j]=0;
        }
      by[b]=1;
      //--- function call
      CSpline1D::Spline1DGridDiff2Cubic(bx,by,m,2,0.0,2,0.0,bd1,bd2);
      //--- function call
      CSpline1D::Spline1DBuildCubic(bx,by,m,2,0.0,2,0.0,bs);
      //--- Calculate B-th column of FMatrix
      //--- Update FDMax (maximum column norm)
      CSpline1D::Spline1DConvCubic(bx,by,m,2,0.0,2,0.0,x,n,fcolumn);
      for(i_=0;i_<=n-1;i_++)
         fmatrix[i_].Set(b,fcolumn[i_]);
      v=0;
      for(i=0;i<=n-1;i++)
         v=v+CMath::Sqr(w[i]*fcolumn[i]);
      fdmax=MathMax(fdmax,v);
      //--- Fill temporary with second derivatives of basis function
      for(i_=0;i_<=m-1;i_++)
         d2matrix[b].Set(i_,bd2[i_]);
     }
//--- * calculate penalty matrix A
//--- * calculate max of diagonal elements of A
//--- * calculate PDecay - coefficient before penalty matrix
   for(i=0;i<=m-1;i++)
     {
      for(j=i;j<=m-1;j++)
        {
         //--- calculate integral(B_i''*B_j'') where B_i and B_j are
         //--- i-th and j-th basis splines.
         //--- B_i and B_j are piecewise linear functions.
         v=0;
         for(b=0;b<=m-2;b++)
           {
            //--- change values
            fa=d2matrix[i][b];
            fb=d2matrix[i][b+1];
            ga=d2matrix[j][b];
            gb=d2matrix[j][b+1];
            v=v+(bx[b+1]-bx[b])*(fa*ga+(fa*(gb-ga)+ga*(fb-fa))/2+(fb-fa)*(gb-ga)/3);
           }
         amatrix[i].Set(j,v);
         amatrix[j].Set(i,v);
        }
     }
//--- change values
   admax=0;
   for(i=0;i<=m-1;i++)
      admax=MathMax(admax,MathAbs(amatrix[i][i]));
   pdecay=lambdav*fdmax/admax;
//--- Calculate TDecay for Tikhonov regularization
   tdecay=fdmax*(1+pdecay)*10*CMath::m_machineepsilon;
//--- Prepare system
//--- NOTE: FMatrix is spoiled during this process
   for(i=0;i<=n-1;i++)
     {
      v=w[i];
      for(i_=0;i_<=m-1;i_++)
         fmatrix[i].Set(i_,v*fmatrix[i][i_]);
     }
//--- function call
   CAblas::RMatrixGemm(m,m,n,1.0,fmatrix,0,0,1,fmatrix,0,0,0,0.0,nmatrix,0,0);
   for(i=0;i<=m-1;i++)
     {
      for(j=0;j<=m-1;j++)
         nmatrix[i].Set(j,nmatrix[i][j]+pdecay*amatrix[i][j]);
     }
//--- calculation
   for(i=0;i<=m-1;i++)
      nmatrix[i].Set(i,nmatrix[i][i]+tdecay);
   for(i=0;i<=m-1;i++)
      rightpart[i]=0;
//--- change values
   for(i=0;i<=n-1;i++)
     {
      v=y[i]*w[i];
      for(i_=0;i_<=m-1;i_++)
         rightpart[i_]=rightpart[i_]+v*fmatrix[i][i_];
     }
//--- Solve system
   if(!CTrFac::SPDMatrixCholesky(nmatrix,m,true))
     {
      info=-4;
      return;
     }
//--- function call
   CFbls::FblsCholeskySolve(nmatrix,1.0,m,true,rightpart,tmp0);
//--- copy
   for(i_=0;i_<=m-1;i_++)
      c[i_]=rightpart[i_];
//--- add nodes to force linearity outside of the fitting interval
   CSpline1D::Spline1DGridDiffCubic(bx,c,m,2,0.0,2,0.0,bd1);
//--- allocation
   ArrayResizeAL(tx,m+2);
   ArrayResizeAL(ty,m+2);
   ArrayResizeAL(td,m+2);
//--- copy
   i1_=-1;
   for(i_=1;i_<=m;i_++)
      tx[i_]=bx[i_+i1_];
   i1_=-1;
   for(i_=1;i_<=m;i_++)
      ty[i_]=rightpart[i_+i1_];
   i1_=-1;
   for(i_=1;i_<=m;i_++)
      td[i_]=bd1[i_+i1_];
//--- change values
   tx[0]=tx[1]-(tx[2]-tx[1]);
   ty[0]=ty[1]-td[1]*(tx[2]-tx[1]);
   td[0]=td[1];
   tx[m+1]=tx[m]+(tx[m]-tx[m-1]);
   ty[m+1]=ty[m]+td[m]*(tx[m]-tx[m-1]);
   td[m+1]=td[m];
//--- function call
   CSpline1D::Spline1DBuildHermite(tx,ty,td,m+2,s);
//--- function call
   CSpline1D::Spline1DLinTransX(s,2/(xb-xa),-((xa+xb)/(xb-xa)));
//--- function call
   CSpline1D::Spline1DLinTransY(s,sb-sa,sa);
//--- change value
   info=1;
//--- Fill report
   rep.m_rmserror=0;
   rep.m_avgerror=0;
   rep.m_avgrelerror=0;
   rep.m_maxerror=0;
   relcnt=0;
//--- function call
   CSpline1D::Spline1DConvCubic(bx,rightpart,m,2,0.0,2,0.0,x,n,fcolumn);
//--- calculation
   for(i=0;i<=n-1;i++)
     {
      //--- change values
      v=(sb-sa)*fcolumn[i]+sa;
      rep.m_rmserror=rep.m_rmserror+CMath::Sqr(v-yoriginal[i]);
      rep.m_avgerror=rep.m_avgerror+MathAbs(v-yoriginal[i]);
      //--- check
      if(yoriginal[i]!=0.0)
        {
         rep.m_avgrelerror=rep.m_avgrelerror+MathAbs(v-yoriginal[i])/MathAbs(yoriginal[i]);
         relcnt=relcnt+1;
        }
      rep.m_maxerror=MathMax(rep.m_maxerror,MathAbs(v-yoriginal[i]));
     }
//--- change values
   rep.m_rmserror=MathSqrt(rep.m_rmserror/n);
   rep.m_avgerror=rep.m_avgerror/n;
//--- check
   if(relcnt!=0.0)
      rep.m_avgrelerror=rep.m_avgrelerror/relcnt;
  }
//+------------------------------------------------------------------+
//| Weighted fitting by cubic spline, with constraints on function   |
//| values or derivatives.                                           |
//| Equidistant grid with M-2 nodes on [min(x,xc),max(x,xc)] is used |
//| to build basis functions. Basis functions are cubic splines with |
//| continuous second derivatives and non-fixed first derivatives at |
//| interval ends. Small regularizing term is used when solving      |
//| constrained tasks (to improve stability).                        |
//| Task is linear, so linear least squares solver is used.          |
//| Complexity of this computational scheme is O(N*M^2), mostly      |
//| dominated by least squares solver                                |
//| SEE ALSO                                                         |
//|     Spline1DFitHermiteWC()  -   fitting by Hermite splines (more |
//|                                 flexible, less smooth)           |
//|     Spline1DFitCubic()      -   "lightweight" fitting by cubic   |
//|                                 splines, without invididual      |
//|                                 weights and constraints          |
//| INPUT PARAMETERS:                                                |
//|     X   -   points, array[0..N-1].                               |
//|     Y   -   function values, array[0..N-1].                      |
//|     W   -   weights, array[0..N-1]                               |
//|             Each summand in square sum of approximation          |
//|             deviations from given values is multiplied by the    |
//|             square of corresponding weight. Fill it by 1's if you|
//|             don't want to solve weighted task.                   |
//|     N   -   number of points (optional):                         |
//|             * N>0                                                |
//|             * if given, only first N elements of X/Y/W are       |
//|               processed                                          |
//|             * if not given, automatically determined from X/Y/W  |
//|               sizes                                              |
//|     XC  -   points where spline values/derivatives are           |
//|             constrained, array[0..K-1].                          |
//|     YC  -   values of constraints, array[0..K-1]                 |
//|     DC  -   array[0..K-1], types of constraints:                 |
//|             * DC[i]=0   means that S(XC[i])=YC[i]                |
//|             * DC[i]=1   means that S'(XC[i])=YC[i]               |
//|             SEE BELOW FOR IMPORTANT INFORMATION ON CONSTRAINTS   |
//|     K   -   number of constraints (optional):                    |
//|             * 0<=K<M.                                            |
//|             * K=0 means no constraints (XC/YC/DC are not used)   |
//|             * if given, only first K elements of XC/YC/DC are    |
//|               used                                               |
//|             * if not given, automatically determined from        |
//|               XC/YC/DC                                           |
//|     M   -   number of basis functions ( = number_of_nodes+2),    |
//|             M>=4.                                                |
//| OUTPUT PARAMETERS:                                               |
//|     Info-   same format as in LSFitLinearWC() subroutine.        |
//|             * Info>0    task is solved                           |
//|             * Info<=0   an error occured:                        |
//|                         -4 means inconvergence of internal SVD   |
//|                         -3 means inconsistent constraints        |
//|     S   -   spline interpolant.                                  |
//|     Rep -   report, same format as in LSFitLinearWC() subroutine.|
//|             Following fields are set:                            |
//|             * RMSError      rms error on the (X,Y).              |
//|             * AvgError      average error on the (X,Y).          |
//|             * AvgRelError   average relative error on the        |
//|                             non-zero Y                           |
//|             * MaxError      maximum error                        |
//|                             NON-WEIGHTED ERRORS ARE CALCULATED   |
//| IMPORTANT:                                                       |
//|     this subroitine doesn't calculate task's condition number    |
//|     for K<>0.                                                    |
//| ORDER OF POINTS                                                  |
//| Subroutine automatically sorts points, so caller may pass        |
//| unsorted array.                                                  |
//| SETTING CONSTRAINTS - DANGERS AND OPPORTUNITIES:                 |
//| Setting constraints can lead  to undesired  results, like        |
//| ill-conditioned behavior, or inconsistency being detected. From  |
//| the other side, it allows us to improve quality of the fit.      |
//| Here we summarize our experience with constrained regression     |
//| splines:                                                         |
//| * excessive constraints can be inconsistent. Splines are         |
//|   piecewise cubic functions, and it is easy to create an         |
//|   example, where large number of constraints  concentrated in    |
//|   small area will result in inconsistency. Just because spline   |
//|   is not flexible enough to satisfy all of them. And same        |
//|   constraints spread across the [min(x),max(x)] will be          |
//|   perfectly consistent.                                          |
//| * the more evenly constraints are spread across [min(x),max(x)], |
//|   the more chances that they will be consistent                  |
//| * the greater is M (given fixed constraints), the more chances   |
//|   that constraints will be consistent                            |
//| * in the general case, consistency of constraints IS NOT         |
//|   GUARANTEED.                                                    |
//| * in the several special cases, however, we CAN guarantee        |
//|   consistency.                                                   |
//| * one of this cases is constraints on the function values        |
//|   AND/OR its derivatives at the interval boundaries.             |
//| * another special case is ONE constraint on the function value   |
//|   (OR, but not AND, derivative) anywhere in the interval         |
//| Our final recommendation is to use constraints WHEN AND ONLY WHEN|
//| you can't solve your task without them. Anything beyond special  |
//| cases given above is not guaranteed and may result in            |
//| inconsistency.                                                   |
//+------------------------------------------------------------------+
static void CLSFit::Spline1DFitCubicWC(double &x[],double &y[],double &w[],
                                       const int n,double &xc[],double &yc[],
                                       int &dc[],const int k,const int m,
                                       int &info,CSpline1DInterpolant &s,
                                       CSpline1DFitReport &rep)
  {
//--- create a variable
   int i=0;
//--- initialization
   info=0;
//--- check
   if(!CAp::Assert(n>=1,__FUNCTION__+": N<1!"))
      return;
//--- check
   if(!CAp::Assert(m>=4,__FUNCTION__+": M<4!"))
      return;
//--- check
   if(!CAp::Assert(k>=0,__FUNCTION__+": K<0!"))
      return;
//--- check
   if(!CAp::Assert(k<m,__FUNCTION__+": K>=M!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(x)>=n,__FUNCTION__+": Length(X)<N!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(y)>=n,__FUNCTION__+": Length(Y)<N!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(w)>=n,__FUNCTION__+": Length(W)<N!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(xc)>=k,__FUNCTION__+": Length(XC)<K!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(yc)>=k,__FUNCTION__+": Length(YC)<K!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(dc)>=k,__FUNCTION__+": Length(DC)<K!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(x,n),__FUNCTION__+": X contains infinite or NAN values!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(y,n),__FUNCTION__+": Y contains infinite or NAN values!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(w,n),__FUNCTION__+": Y contains infinite or NAN values!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(xc,k),__FUNCTION__+": X contains infinite or NAN values!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(yc,k),__FUNCTION__+": Y contains infinite or NAN values!"))
      return;
   for(i=0;i<=k-1;i++)
     {
      //--- check
      if(!CAp::Assert(dc[i]==0 || dc[i]==1,__FUNCTION__+": DC[i] is neither 0 or 1!"))
         return;
     }
//--- function call
   Spline1DFitInternal(0,x,y,w,n,xc,yc,dc,k,m,info,s,rep);
  }
//+------------------------------------------------------------------+
//| Weighted fitting by Hermite spline, with constraints on function |
//| values or first derivatives.                                     |
//| Equidistant grid with M nodes on [min(x,xc),max(x,xc)] is used to|
//| build basis functions. Basis functions are Hermite splines. Small|
//| regularizing term is used when solving constrained tasks (to     |
//| improve stability).                                              |
//| Task is linear, so linear least squares solver is used.          |
//| Complexity of this computational scheme is O(N*M^2), mostly      |
//| dominated by least squares solver                                |
//| SEE ALSO                                                         |
//|     Spline1DFitCubicWC()    -   fitting by Cubic splines (less   |
//|                                 flexible, more smooth)           |
//|     Spline1DFitHermite()    -   "lightweight" Hermite fitting,   |
//|                                 without invididual weights and   |
//|                                 constraints                      |
//| INPUT PARAMETERS:                                                |
//|     X   -   points, array[0..N-1].                               |
//|     Y   -   function values, array[0..N-1].                      |
//|     W   -   weights, array[0..N-1]                               |
//|             Each summand in square sum of approximation          |
//|             deviations from given values is multiplied by the    |
//|             square of corresponding weight. Fill it by 1's if    |
//|             you don't want to solve weighted task.               |
//|     N   -   number of points (optional):                         |
//|             * N>0                                                |
//|             * if given, only first N elements of X/Y/W are       |
//|               processed                                          |
//|             * if not given, automatically determined from X/Y/W  |
//|               sizes                                              |
//|     XC  -   points where spline values/derivatives are           |
//|             constrained, array[0..K-1].                          |
//|     YC  -   values of constraints, array[0..K-1]                 |
//|     DC  -   array[0..K-1], types of constraints:                 |
//|             * DC[i]=0   means that S(XC[i])=YC[i]                |
//|             * DC[i]=1   means that S'(XC[i])=YC[i]               |
//|             SEE BELOW FOR IMPORTANT INFORMATION ON CONSTRAINTS   |
//|     K   -   number of constraints (optional):                    |
//|             * 0<=K<M.                                            |
//|             * K=0 means no constraints (XC/YC/DC are not used)   |
//|             * if given, only first K elements of XC/YC/DC are    |
//|               used                                               |
//|             * if not given, automatically determined from        |
//|               XC/YC/DC                                           |
//|     M   -   number of basis functions (= 2 * number of nodes),   |
//|             M>=4,                                                |
//|             M IS EVEN!                                           |
//| OUTPUT PARAMETERS:                                               |
//|     Info-   same format as in LSFitLinearW() subroutine:         |
//|             * Info>0    task is solved                           |
//|             * Info<=0   an error occured:                        |
//|                         -4 means inconvergence of internal SVD   |
//|                         -3 means inconsistent constraints        |
//|                         -2 means odd M was passed (which is not  |
//|                            supported)                            |
//|                         -1 means another errors in parameters    |
//|                            passed (N<=0, for example)            |
//|     S   -   spline interpolant.                                  |
//|     Rep -   report, same format as in LSFitLinearW() subroutine. |
//|             Following fields are set:                            |
//|             * RMSError      rms error on the (X,Y).              |
//|             * AvgError      average error on the (X,Y).          |
//|             * AvgRelError   average relative error on the        |
//|                             non-zero Y                           |
//|             * MaxError      maximum error                        |
//|                             NON-WEIGHTED ERRORS ARE CALCULATED   |
//| IMPORTANT:                                                       |
//|     this subroitine doesn't calculate task's condition number    |
//|     for K<>0.                                                    |
//| IMPORTANT:                                                       |
//|     this subroitine supports only even M's                       |
//| ORDER OF POINTS                                                  |
//| ubroutine automatically sorts points, so caller may pass         |
//| unsorted array.                                                  |
//| SETTING CONSTRAINTS - DANGERS AND OPPORTUNITIES:                 |
//| Setting constraints can lead to undesired results, like          |
//| ill-conditioned behavior, or inconsistency being detected. From  |
//| the other side, it allows us to improve quality of the fit. Here |
//| we summarize our experience  with constrained regression splines:|
//| * excessive constraints can be inconsistent. Splines are         |
//|   piecewise cubic functions, and it is easy to create an example,|
//|   where large number of constraints concentrated in small area   |
//|   will result in inconsistency. Just because spline is not       |
//|   flexible enough to satisfy all of them. And same constraints   |
//|   spread across the [min(x),max(x)] will be perfectly consistent.|
//| * the more evenly constraints are spread across [min(x),max(x)], |
//|   the more chances that they will be consistent                  |
//| * the greater is M (given  fixed  constraints), the more chances |
//|   that constraints will be consistent                            |
//| * in the general case, consistency of constraints is NOT         |
//|   GUARANTEED.                                                    |
//| * in the several special cases, however, we can guarantee        |
//|   consistency.                                                   |
//| * one of this cases is M>=4 and constraints on the function      |
//|   value (AND/OR its derivative) at the interval boundaries.      |
//| * another special case is M>=4 and ONE constraint on the         |
//|   function value (OR, BUT NOT AND, derivative) anywhere in       |
//|   [min(x),max(x)]                                                |
//| Our final recommendation is to use constraints WHEN AND ONLY when|
//| you can't solve your task without them. Anything beyond  special |
//| cases given above is not guaranteed and may result in            |
//| inconsistency.                                                   |
//+------------------------------------------------------------------+
static void CLSFit::Spline1DFitHermiteWC(double &x[],double &y[],double &w[],
                                         const int n,double &xc[],double &yc[],
                                         int &dc[],const int k,const int m,
                                         int &info,CSpline1DInterpolant &s,
                                         CSpline1DFitReport &rep)
  {
//--- create a variable
   int i=0;
//--- initialization
   info=0;
//--- check
   if(!CAp::Assert(n>=1,__FUNCTION__+": N<1!"))
      return;
//--- check
   if(!CAp::Assert(m>=4,__FUNCTION__+": M<4!"))
      return;
//--- check
   if(!CAp::Assert(m%2==0,__FUNCTION__+": M is odd!"))
      return;
//--- check
   if(!CAp::Assert(k>=0,__FUNCTION__+": K<0!"))
      return;
//--- check
   if(!CAp::Assert(k<m,__FUNCTION__+": K>=M!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(x)>=n,__FUNCTION__+": Length(X)<N!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(y)>=n,__FUNCTION__+": Length(Y)<N!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(w)>=n,__FUNCTION__+": Length(W)<N!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(xc)>=k,__FUNCTION__+": Length(XC)<K!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(yc)>=k,__FUNCTION__+": Length(YC)<K!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(dc)>=k,__FUNCTION__+": Length(DC)<K!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(x,n),__FUNCTION__+": X contains infinite or NAN values!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(y,n),__FUNCTION__+": Y contains infinite or NAN values!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(w,n),__FUNCTION__+": Y contains infinite or NAN values!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(xc,k),__FUNCTION__+": X contains infinite or NAN values!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(yc,k),__FUNCTION__+": Y contains infinite or NAN values!"))
      return;
   for(i=0;i<=k-1;i++)
     {
      //--- check
      if(!CAp::Assert(dc[i]==0 || dc[i]==1,__FUNCTION__+": DC[i] is neither 0 or 1!"))
         return;
     }
//--- function call
   Spline1DFitInternal(1,x,y,w,n,xc,yc,dc,k,m,info,s,rep);
  }
//+------------------------------------------------------------------+
//| Least squares fitting by cubic spline.                           |
//| This subroutine is "lightweight" alternative for more complex    |
//| and feature - rich Spline1DFitCubicWC(). See Spline1DFitCubicWC()|
//| for more information about subroutine parameters (we don't       |
//| duplicate it here because of length)                             |
//+------------------------------------------------------------------+
static void CLSFit::Spline1DFitCubic(double &x[],double &y[],const int n,
                                     const int m,int &info,
                                     CSpline1DInterpolant &s,
                                     CSpline1DFitReport &rep)
  {
//--- create a variable
   int i=0;
//--- create arrays
   double w[];
   double xc[];
   double yc[];
   int    dc[];
//--- initialization
   info=0;
//--- check
   if(!CAp::Assert(n>=1,__FUNCTION__+": N<1!"))
      return;
//--- check
   if(!CAp::Assert(m>=4,__FUNCTION__+": M<4!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(x)>=n,__FUNCTION__+": Length(X)<N!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(y)>=n,__FUNCTION__+": Length(Y)<N!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(x,n),__FUNCTION__+": X contains infinite or NAN values!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(y,n),__FUNCTION__+": Y contains infinite or NAN values!"))
      return;
//--- allocation
   ArrayResizeAL(w,n);
//--- initialization
   for(i=0;i<=n-1;i++)
      w[i]=1;
//--- function call
   Spline1DFitCubicWC(x,y,w,n,xc,yc,dc,0,m,info,s,rep);
  }
//+------------------------------------------------------------------+
//| Least squares fitting by Hermite spline.                         |
//| This subroutine is "lightweight" alternative for more complex    |
//| and feature - rich Spline1DFitHermiteWC(). See                   |
//| Spline1DFitHermiteWC() description for more information about    |
//| subroutine parameters (we don't duplicate it here because of     |
//| length).                                                         |
//+------------------------------------------------------------------+
static void CLSFit::Spline1DFitHermite(double &x[],double &y[],const int n,
                                       const int m,int &info,
                                       CSpline1DInterpolant &s,
                                       CSpline1DFitReport &rep)
  {
//--- create a variable
   int i=0;
//--- create arrays
   double w[];
   double xc[];
   double yc[];
   int    dc[];
//--- initialization
   info=0;
//--- check
   if(!CAp::Assert(n>=1,__FUNCTION__+": N<1!"))
      return;
//--- check
   if(!CAp::Assert(m>=4,__FUNCTION__+": M<4!"))
      return;
//--- check
   if(!CAp::Assert(m%2==0,__FUNCTION__+": M is odd!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(x)>=n,__FUNCTION__+": Length(X)<N!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(y)>=n,__FUNCTION__+": Length(Y)<N!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(x,n),__FUNCTION__+": X contains infinite or NAN values!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(y,n),__FUNCTION__+": Y contains infinite or NAN values!"))
      return;
//--- allocation
   ArrayResizeAL(w,n);
//--- initialization
   for(i=0;i<=n-1;i++)
      w[i]=1;
//--- function call
   Spline1DFitHermiteWC(x,y,w,n,xc,yc,dc,0,m,info,s,rep);
  }
//+------------------------------------------------------------------+
//| Weighted linear least squares fitting.                           |
//| QR decomposition is used to reduce task to MxM, then triangular  |
//| solver or SVD-based solver is used depending on condition number |
//| of the system. It allows to maximize speed and retain decent     |
//| accuracy.                                                        |
//| INPUT PARAMETERS:                                                |
//|     Y       -   array[0..N-1] Function values in N points.       |
//|     W       -   array[0..N-1] Weights corresponding to function  |
//|                 values. Each summand in square sum of            |
//|                 approximation deviations from given values is    |
//|                 multiplied by the square of corresponding weight.|
//|     FMatrix -   a table of basis functions values,               |
//|                 array[0..N-1, 0..M-1]. FMatrix[I, J] - value of  |
//|                 J-th basis function in I-th point.               |
//|     N       -   number of points used. N>=1.                     |
//|     M       -   number of basis functions, M>=1.                 |
//| OUTPUT PARAMETERS:                                               |
//|     Info    -   error code:                                      |
//|                 * -4    internal SVD decomposition subroutine    |
//|                         failed (very rare and for degenerate     |
//|                         systems only)                            |
//|                 * -1    incorrect N/M were specified             |
//|                 *  1    task is solved                           |
//|     C       -   decomposition coefficients, array[0..M-1]        |
//|     Rep     -   fitting report. Following fields are set:        |
//|                 * Rep.TaskRCond     reciprocal of condition      |
//|                                     number                       |
//|                 * RMSError          rms error on the (X,Y).      |
//|                 * AvgError          average error on the (X,Y).  |
//|                 * AvgRelError       average relative error on the|
//|                                     non-zero Y                   |
//|                 * MaxError          maximum error                |
//|                                     NON-WEIGHTED ERRORS ARE      |
//|                                     CALCULATED                   |
//+------------------------------------------------------------------+
static void CLSFit::LSFitLinearW(double &y[],double &w[],CMatrixDouble &fmatrix,
                                 const int n,const int m,int &info,
                                 double &c[],CLSFitReport &rep)
  {
//--- initialization
   info=0;
//--- check
   if(!CAp::Assert(n>=1,__FUNCTION__+": N<1!"))
      return;
//--- check
   if(!CAp::Assert(m>=1,__FUNCTION__+": M<1!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(y)>=n,__FUNCTION__+": length(Y)<N!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(y,n),__FUNCTION__+": Y contains infinite or NaN values!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(w)>=n,__FUNCTION__+": length(W)<N!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(w,n),__FUNCTION__+": W contains infinite or NaN values!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Rows(fmatrix)>=n,__FUNCTION__+": rows(FMatrix)<N!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Cols(fmatrix)>=m,__FUNCTION__+": cols(FMatrix)<M!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteMatrix(fmatrix,n,m),__FUNCTION__+": FMatrix contains infinite or NaN values!"))
      return;
//--- function call
   LSFitLinearInternal(y,w,fmatrix,n,m,info,c,rep);
  }
//+------------------------------------------------------------------+
//| Weighted constained linear least squares fitting.                |
//| This is variation of LSFitLinearW(), which searchs for           |
//| min|A*x=b| given that K additional constaints C*x=bc are         |
//| satisfied. It reduces original task to modified one: min|B*y-d|  |
//| WITHOUT constraints, then LSFitLinearW() is called.              |
//| INPUT PARAMETERS:                                                |
//|     Y       -   array[0..N-1] Function values in  N  points.     |
//|     W       -   array[0..N-1] Weights corresponding to function  |
//|                 values. Each summand in square sum of            |
//|                 approximation deviations from given values is    |
//|                 multiplied by the square of corresponding        |
//|                 weight.                                          |
//|     FMatrix -   a table of basis functions values,               |
//|                 array[0..N-1, 0..M-1]. FMatrix[I,J] - value of   |
//|                 J-th basis function in I-th point.               |
//|     CMatrix -   a table of constaints, array[0..K-1,0..M].       |
//|                 I-th row of CMatrix corresponds to I-th linear   |
//|                 constraint: CMatrix[I,0]*C[0] + ... +            |
//|                 + CMatrix[I,M-1]*C[M-1] = CMatrix[I,M]           |
//|     N       -   number of points used. N>=1.                     |
//|     M       -   number of basis functions, M>=1.                 |
//|     K       -   number of constraints, 0 <= K < M                |
//|                 K=0 corresponds to absence of constraints.       |
//| OUTPUT PARAMETERS:                                               |
//|     Info    -   error code:                                      |
//|                 * -4    internal SVD decomposition subroutine    |
//|                         failed (very rare and for degenerate     |
//|                         systems only)                            |
//|                 * -3    either too many constraints (M or more), |
//|                         degenerate constraints (some constraints |
//|                         are repetead twice) or inconsistent      |
//|                         constraints were specified.              |
//|                 *  1    task is solved                           |
//|     C       -   decomposition coefficients, array[0..M-1]        |
//|     Rep     -   fitting report. Following fields are set:        |
//|                 * RMSError          rms error on the (X,Y).      |
//|                 * AvgError          average error on the (X,Y).  |
//|                 * AvgRelError       average relative error on the|
//|                                     non-zero Y                   |
//|                 * MaxError          maximum error                |
//|                                     NON-WEIGHTED ERRORS ARE      |
//|                                     CALCULATED                   |
//| IMPORTANT:                                                       |
//|     this subroitine doesn't calculate task's condition number    |
//|     for K<>0.                                                    |
//+------------------------------------------------------------------+
static void CLSFit::LSFitLinearWC(double &cy[],double &w[],CMatrixDouble &fmatrix,
                                  CMatrixDouble &ccmatrix,const int n,
                                  const int m,const int k,int &info,
                                  double &c[],CLSFitReport &rep)
  {
//--- create variables
   int    i=0;
   int    j=0;
   double v=0;
   int    i_=0;
//--- create arrays
   double tau[];
   double tmp[];
   double c0[];
   double y[];
//--- create matrix
   CMatrixDouble q;
   CMatrixDouble f2;
   CMatrixDouble cmatrix;
//--- copy array
   ArrayCopy(y,cy);
//--- copy matrix
   cmatrix=ccmatrix;
//--- initialization
   info=0;
//--- check
   if(!CAp::Assert(n>=1,__FUNCTION__+": N<1!"))
      return;
//--- check
   if(!CAp::Assert(m>=1,__FUNCTION__+": M<1!"))
      return;
//--- check
   if(!CAp::Assert(k>=0,__FUNCTION__+": K<0!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(y)>=n,__FUNCTION__+": length(Y)<N!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(y,n),__FUNCTION__+": Y contains infinite or NaN values!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(w)>=n,__FUNCTION__+": length(W)<N!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(w,n),__FUNCTION__+": W contains infinite or NaN values!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Rows(fmatrix)>=n,__FUNCTION__+": rows(FMatrix)<N!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Cols(fmatrix)>=m,__FUNCTION__+": cols(FMatrix)<M!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteMatrix(fmatrix,n,m),__FUNCTION__+": FMatrix contains infinite or NaN values!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Rows(cmatrix)>=k,__FUNCTION__+": rows(CMatrix)<K!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Cols(cmatrix)>=m+1 || k==0,__FUNCTION__+": cols(CMatrix)<M+1!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteMatrix(cmatrix,k,m+1),__FUNCTION__+": CMatrix contains infinite or NaN values!"))
      return;
//--- check
   if(k>=m)
     {
      info=-3;
      return;
     }
//--- Solve
   if(k==0)
     {
      //--- no constraints
      LSFitLinearInternal(y,w,fmatrix,n,m,info,c,rep);
     }
   else
     {
      //--- First,find general form solution of constraints system:
      //--- * factorize C=L*Q
      //--- * unpack Q
      //--- * fill upper part of C with zeros (for RCond)
      //--- We got C=C0+Q2'*y where Q2 is lower M-K rows of Q.
      COrtFac::RMatrixLQ(cmatrix,k,m,tau);
      COrtFac::RMatrixLQUnpackQ(cmatrix,k,m,tau,m,q);
      for(i=0;i<=k-1;i++)
        {
         for(j=i+1;j<=m-1;j++)
            cmatrix[i].Set(j,0.0);
        }
      //--- check
      if(CRCond::RMatrixLURCondInf(cmatrix,k)<1000*CMath::m_machineepsilon)
        {
         info=-3;
         return;
        }
      //--- allocation
      ArrayResizeAL(tmp,k);
      //--- calculation
      for(i=0;i<=k-1;i++)
        {
         //--- check
         if(i>0)
           {
            v=0.0;
            for(i_=0;i_<=i-1;i_++)
               v+=cmatrix[i][i_]*tmp[i_];
           }
         else
            v=0;
         //--- change values
         tmp[i]=(cmatrix[i][m]-v)/cmatrix[i][i];
        }
      //--- allocation
      ArrayResizeAL(c0,m);
      //--- calculation
      for(i=0;i<=m-1;i++)
         c0[i]=0;
      for(i=0;i<=k-1;i++)
        {
         v=tmp[i];
         for(i_=0;i_<=m-1;i_++)
            c0[i_]=c0[i_]+v*q[i][i_];
        }
      //--- Second,prepare modified matrix F2=F*Q2' and solve modified task
      ArrayResizeAL(tmp,MathMax(n,m)+1);
      f2.Resize(n,m-k);
      //--- function call
      CBlas::MatrixVectorMultiply(fmatrix,0,n-1,0,m-1,false,c0,0,m-1,-1.0,y,0,n-1,1.0);
      //--- function call
      CBlas::MatrixMatrixMultiply(fmatrix,0,n-1,0,m-1,false,q,k,m-1,0,m-1,true,1.0,f2,0,n-1,0,m-k-1,0.0,tmp);
      //--- function call
      LSFitLinearInternal(y,w,f2,n,m-k,info,tmp,rep);
      rep.m_taskrcond=-1;
      //--- check
      if(info<=0)
         return;
      //--- then,convert back to original answer: C=C0 + Q2'*Y0
      ArrayResizeAL(c,m);
      for(i_=0;i_<=m-1;i_++)
         c[i_]=c0[i_];
      //--- function call
      CBlas::MatrixVectorMultiply(q,k,m-1,0,m-1,true,tmp,0,m-k-1,1.0,c,0,m-1,1.0);
     }
  }
//+------------------------------------------------------------------+
//| Linear least squares fitting.                                    |
//| QR decomposition is used to reduce task to MxM, then triangular  |
//| solver or SVD-based solver is used depending on condition number |
//| of the system. It allows to maximize speed and retain decent     |
//| accuracy.                                                        |
//| INPUT PARAMETERS:                                                |
//|     Y       -   array[0..N-1] Function values in  N  points.     |
//|     FMatrix -   a table of basis functions values,               |
//|                 array[0..N-1, 0..M-1].                           |
//|                 FMatrix[I, J] - value of J-th basis function in  |
//|                 I-th point.                                      |
//|     N       -   number of points used. N>=1.                     |
//|     M       -   number of basis functions, M>=1.                 |
//| OUTPUT PARAMETERS:                                               |
//|     Info    -   error code:                                      |
//|                 * -4    internal SVD decomposition subroutine    |
//|                         failed (very rare and for degenerate     |
//|                         systems only)                            |
//|                 *  1    task is solved                           |
//|     C       -   decomposition coefficients, array[0..M-1]        |
//|     Rep     -   fitting report. Following fields are set:        |
//|                 * Rep.TaskRCond     reciprocal of condition      |
//|                                     number                       |
//|                 * RMSError          rms error on the (X,Y).      |
//|                 * AvgError          average error on the (X,Y).  |
//|                 * AvgRelError       average relative error on the|
//|                                     non-zero Y                   |
//|                 * MaxError          maximum error                |
//|                                     NON-WEIGHTED ERRORS ARE      |
//|                                     CALCULATED                   |
//+------------------------------------------------------------------+
static void CLSFit::LSFitLinear(double &y[],CMatrixDouble &fmatrix,
                                const int n,const int m,int &info,
                                double &c[],CLSFitReport &rep)
  {
//--- create a variable
   int i=0;
//--- create array
   double w[];
//--- initialization
   info=0;
//--- check
   if(!CAp::Assert(n>=1,__FUNCTION__+": N<1!"))
      return;
//--- check
   if(!CAp::Assert(m>=1,__FUNCTION__+": M<1!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(y)>=n,__FUNCTION__+": length(Y)<N!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(y,n),__FUNCTION__+": Y contains infinite or NaN values!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Rows(fmatrix)>=n,__FUNCTION__+": rows(FMatrix)<N!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Cols(fmatrix)>=m,__FUNCTION__+": cols(FMatrix)<M!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteMatrix(fmatrix,n,m),__FUNCTION__+": FMatrix contains infinite or NaN values!"))
      return;
//--- allocation
   ArrayResizeAL(w,n);
//--- initialization
   for(i=0;i<=n-1;i++)
      w[i]=1;
//--- function call
   LSFitLinearInternal(y,w,fmatrix,n,m,info,c,rep);
  }
//+------------------------------------------------------------------+
//| Constained linear least squares fitting.                         |
//| This is variation of LSFitLinear(), which searchs for min|A*x=b| |
//| given that K additional constaints C*x=bc are satisfied. It      |
//| reduces original task to modified one: min|B*y-d| WITHOUT        |
//| constraints, then LSFitLinear() is called.                       |
//| INPUT PARAMETERS:                                                |
//|     Y       -   array[0..N-1] Function values in N points.       |
//|     FMatrix -   a table of basis functions values,               |
//|                 array[0..N-1, 0..M-1]. FMatrix[I,J] - value of   |
//|                 J-th basis function in I-th point.               |
//|     CMatrix -   a table of constaints, array[0..K-1,0..M].       |
//|                 I-th row of CMatrix corresponds to I-th linear   |
//|                 constraint: CMatrix[I,0]*C[0] + ... +            |
//|                 + CMatrix[I,M-1]*C[M-1] = CMatrix[I,M]           |
//|     N       -   number of points used. N>=1.                     |
//|     M       -   number of basis functions, M>=1.                 |
//|     K       -   number of constraints, 0 <= K < M                |
//|                 K=0 corresponds to absence of constraints.       |
//| OUTPUT PARAMETERS:                                               |
//|     Info    -   error code:                                      |
//|                 * -4    internal SVD decomposition subroutine    |
//|                         failed (very rare and for degenerate     |
//|                         systems only)                            |
//|                 * -3    either too many constraints (M or more), |
//|                         degenerate constraints (some constraints |
//|                         are repetead twice) or inconsistent      |
//|                         constraints were specified.              |
//|                 *  1    task is solved                           |
//|     C       -   decomposition coefficients, array[0..M-1]        |
//|     Rep     -   fitting report. Following fields are set:        |
//|                 * RMSError          rms error on the (X,Y).      |
//|                 * AvgError          average error on the (X,Y).  |
//|                 * AvgRelError       average relative error on the|
//|                                     non-zero Y                   |
//|                 * MaxError          maximum error                |
//|                                     NON-WEIGHTED ERRORS ARE      |
//|                                     CALCULATED                   |
//| IMPORTANT:                                                       |
//|     this subroitine doesn't calculate task's condition number    |
//|     for K<>0.                                                    |
//+------------------------------------------------------------------+
static void CLSFit::LSFitLinearC(double &cy[],CMatrixDouble &fmatrix,
                                 CMatrixDouble &cmatrix,const int n,
                                 const int m,const int k,int &info,
                                 double &c[],CLSFitReport &rep)
  {
//--- create a variable
   int i=0;
//--- create arrays
   double w[];
   double y[];
//--- copy array
   ArrayCopy(y,cy);
//--- initialization
   info=0;
//--- check
   if(!CAp::Assert(n>=1,__FUNCTION__+": N<1!"))
      return;
//--- check
   if(!CAp::Assert(m>=1,__FUNCTION__+": M<1!"))
      return;
//--- check
   if(!CAp::Assert(k>=0,__FUNCTION__+": K<0!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(y)>=n,__FUNCTION__+": length(Y)<N!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(y,n),__FUNCTION__+": Y contains infinite or NaN values!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Rows(fmatrix)>=n,__FUNCTION__+": rows(FMatrix)<N!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Cols(fmatrix)>=m,__FUNCTION__+": cols(FMatrix)<M!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteMatrix(fmatrix,n,m),__FUNCTION__+": FMatrix contains infinite or NaN values!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Rows(cmatrix)>=k,__FUNCTION__+": rows(CMatrix)<K!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Cols(cmatrix)>=m+1 || k==0,__FUNCTION__+": cols(CMatrix)<M+1!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteMatrix(cmatrix,k,m+1),__FUNCTION__+": CMatrix contains infinite or NaN values!"))
      return;
//--- allocation
   ArrayResizeAL(w,n);
//--- initialization
   for(i=0;i<=n-1;i++)
      w[i]=1;
//--- function call
   LSFitLinearWC(y,w,fmatrix,cmatrix,n,m,k,info,c,rep);
  }
//+------------------------------------------------------------------+
//| Weighted nonlinear least squares fitting using function values   |
//| only.                                                            |
//| Combination of numerical differentiation and secant updates is   |
//| used to obtain function Jacobian.                                |
//| Nonlinear task min(F(c)) is solved, where                        |
//|     F(c) = (w[0]*(f(c,x[0])-y[0]))^2 + ... +                     |
//|     + (w[n-1]*(f(c,x[n-1])-y[n-1]))^2,                           |
//|     * N is a number of points,                                   |
//|     * M is a dimension of a space points belong to,              |
//|     * K is a dimension of a space of parameters being fitted,    |
//|     * w is an N-dimensional vector of weight coefficients,       |
//|     * x is a set of N points, each of them is an M-dimensional   |
//|       vector,                                                    |
//|     * c is a K-dimensional vector of parameters being fitted     |
//| This subroutine uses only f(c,x[i]).                             |
//| INPUT PARAMETERS:                                                |
//|     X       -   array[0..N-1,0..M-1], points (one row = one      |
//|                 point)                                           |
//|     Y       -   array[0..N-1], function values.                  |
//|     W       -   weights, array[0..N-1]                           |
//|     C       -   array[0..K-1], initial approximation to the      |
//|                 solution,                                        |
//|     N       -   number of points, N>1                            |
//|     M       -   dimension of space                               |
//|     K       -   number of parameters being fitted                |
//|     DiffStep-   numerical differentiation step;                  |
//|                 should not be very small or large;               |
//|                 large = loss of accuracy                         |
//|                 small = growth of round-off errors               |
//| OUTPUT PARAMETERS:                                               |
//|     State   -   structure which stores algorithm state           |
//+------------------------------------------------------------------+
static void CLSFit::LSFitCreateWF(CMatrixDouble &x,double &y[],double &w[],
                                  double &c[],const int n,const int m,
                                  const int k,const double diffstep,
                                  CLSFitState &state)
  {
//--- create variables
   int i=0;
   int i_=0;
//--- check
   if(!CAp::Assert(n>=1,__FUNCTION__+": N<1!"))
      return;
//--- check
   if(!CAp::Assert(m>=1,__FUNCTION__+": M<1!"))
      return;
//--- check
   if(!CAp::Assert(k>=1,__FUNCTION__+": K<1!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(c)>=k,__FUNCTION__+": length(C)<K!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(c,k),__FUNCTION__+": C contains infinite or NaN values!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(y)>=n,__FUNCTION__+": length(Y)<N!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(y,n),__FUNCTION__+": Y contains infinite or NaN values!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(w)>=n,__FUNCTION__+": length(W)<N!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(w,n),__FUNCTION__+": W contains infinite or NaN values!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Rows(x)>=n,__FUNCTION__+": rows(X)<N!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Cols(x)>=m,__FUNCTION__+": cols(X)<M!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteMatrix(x,n,m),__FUNCTION__+": X contains infinite or NaN values!"))
      return;
//--- check
   if(!CAp::Assert(CMath::IsFinite(diffstep),__FUNCTION__+": DiffStep is not finite!"))
      return;
//--- check
   if(!CAp::Assert((double)(diffstep)>0.0,__FUNCTION__+": DiffStep<=0!"))
      return;
//--- initialization
   state.m_npoints=n;
   state.m_nweights=n;
   state.m_wkind=1;
   state.m_m=m;
   state.m_k=k;
//--- function call
   LSFitSetCond(state,0.0,0.0,0);
//--- function call
   LSFitSetStpMax(state,0.0);
//--- function call
   LSFitSetXRep(state,false);
//--- allocation
   state.m_taskx.Resize(n,m);
   ArrayResizeAL(state.m_tasky,n);
   ArrayResizeAL(state.m_w,n);
   ArrayResizeAL(state.m_c,k);
   ArrayResizeAL(state.m_x,m);
//--- copy
   for(i_=0;i_<=k-1;i_++)
      state.m_c[i_]=c[i_];
   for(i_=0;i_<=n-1;i_++)
      state.m_w[i_]=w[i_];
   for(i=0;i<=n-1;i++)
     {
      for(i_=0;i_<=m-1;i_++)
         state.m_taskx[i].Set(i_,x[i][i_]);
      state.m_tasky[i]=y[i];
     }
//--- allocation
   ArrayResizeAL(state.m_s,k);
   ArrayResizeAL(state.m_bndl,k);
   ArrayResizeAL(state.m_bndu,k);
//--- change values
   for(i=0;i<=k-1;i++)
     {
      state.m_s[i]=1.0;
      state.m_bndl[i]=CInfOrNaN::NegativeInfinity();
      state.m_bndu[i]=CInfOrNaN::PositiveInfinity();
     }
//--- change values
   state.m_optalgo=0;
   state.m_prevnpt=-1;
   state.m_prevalgo=-1;
//--- function call
   CMinLM::MinLMCreateV(k,n,state.m_c,diffstep,state.m_optstate);
//--- function call
   LSFitClearRequestFields(state);
//--- allocation
   ArrayResizeAL(state.m_rstate.ia,5);
   ArrayResizeAL(state.m_rstate.ra,3);
   state.m_rstate.stage=-1;
  }
//+------------------------------------------------------------------+
//| Nonlinear least squares fitting using function values only.      |
//| Combination of numerical differentiation and secant updates is   |
//| used to obtain function Jacobian.                                |
//| Nonlinear task min(F(c)) is solved, where                        |
//|     F(c) = (f(c,x[0])-y[0])^2 + ... + (f(c,x[n-1])-y[n-1])^2,    |
//|     * N is a number of points,                                   |
//|     * M is a dimension of a space points belong to,              |
//|     * K is a dimension of a space of parameters being fitted,    |
//|     * w is an N-dimensional vector of weight coefficients,       |
//|     * x is a set of N points, each of them is an M-dimensional   |
//|       vector,                                                    |
//|     * c is a K-dimensional vector of parameters being fitted     |
//| This subroutine uses only f(c,x[i]).                             |
//| INPUT PARAMETERS:                                                |
//|     X       -   array[0..N-1,0..M-1], points (one row = one      |
//|                 point)                                           |
//|     Y       -   array[0..N-1], function values.                  |
//|     C       -   array[0..K-1], initial approximation to the      |
//|                 solution,                                        |
//|     N       -   number of points, N>1                            |
//|     M       -   dimension of space                               |
//|     K       -   number of parameters being fitted                |
//|     DiffStep-   numerical differentiation step;                  |
//|                 should not be very small or large;               |
//|                 large = loss of accuracy                         |
//|                 small = growth of round-off errors               |
//| OUTPUT PARAMETERS:                                               |
//|     State   -   structure which stores algorithm state           |
//+------------------------------------------------------------------+
static void CLSFit::LSFitCreateF(CMatrixDouble &x,double &y[],double &c[],
                                 const int n,const int m,const int k,
                                 const double diffstep,CLSFitState &state)
  {
//--- create variables
   int i=0;
   int i_=0;
//--- check
   if(!CAp::Assert(n>=1,__FUNCTION__+": N<1!"))
      return;
//--- check
   if(!CAp::Assert(m>=1,__FUNCTION__+": M<1!"))
      return;
//--- check
   if(!CAp::Assert(k>=1,__FUNCTION__+": K<1!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(c)>=k,__FUNCTION__+": length(C)<K!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(c,k),__FUNCTION__+": C contains infinite or NaN values!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(y)>=n,__FUNCTION__+": length(Y)<N!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(y,n),__FUNCTION__+": Y contains infinite or NaN values!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Rows(x)>=n,__FUNCTION__+": rows(X)<N!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Cols(x)>=m,__FUNCTION__+": cols(X)<M!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteMatrix(x,n,m),__FUNCTION__+": X contains infinite or NaN values!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Rows(x)>=n,__FUNCTION__+": rows(X)<N!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Cols(x)>=m,__FUNCTION__+": cols(X)<M!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteMatrix(x,n,m),__FUNCTION__+": X contains infinite or NaN values!"))
      return;
//--- check
   if(!CAp::Assert(CMath::IsFinite(diffstep),__FUNCTION__+": DiffStep is not finite!"))
      return;
//--- check
   if(!CAp::Assert((double)(diffstep)>0.0,__FUNCTION__+": DiffStep<=0!"))
      return;
//--- initialization
   state.m_npoints=n;
   state.m_wkind=0;
   state.m_m=m;
   state.m_k=k;
//--- function call
   LSFitSetCond(state,0.0,0.0,0);
//--- function call
   LSFitSetStpMax(state,0.0);
//--- function call
   LSFitSetXRep(state,false);
//--- allocation
   state.m_taskx.Resize(n,m);
   ArrayResizeAL(state.m_tasky,n);
   ArrayResizeAL(state.m_c,k);
   ArrayResizeAL(state.m_x,m);
//--- copy
   for(i_=0;i_<=k-1;i_++)
      state.m_c[i_]=c[i_];
   for(i=0;i<=n-1;i++)
     {
      for(i_=0;i_<=m-1;i_++)
         state.m_taskx[i].Set(i_,x[i][i_]);
      state.m_tasky[i]=y[i];
     }
//--- allocation
   ArrayResizeAL(state.m_s,k);
   ArrayResizeAL(state.m_bndl,k);
   ArrayResizeAL(state.m_bndu,k);
//--- change values
   for(i=0;i<=k-1;i++)
     {
      state.m_s[i]=1.0;
      state.m_bndl[i]=CInfOrNaN::NegativeInfinity();
      state.m_bndu[i]=CInfOrNaN::PositiveInfinity();
     }
//--- change values
   state.m_optalgo=0;
   state.m_prevnpt=-1;
   state.m_prevalgo=-1;
//--- function call
   CMinLM::MinLMCreateV(k,n,state.m_c,diffstep,state.m_optstate);
//--- function call
   LSFitClearRequestFields(state);
//--- allocation
   ArrayResizeAL(state.m_rstate.ia,5);
   ArrayResizeAL(state.m_rstate.ra,3);
   state.m_rstate.stage=-1;
  }
//+------------------------------------------------------------------+
//| Weighted nonlinear least squares fitting using gradient only.    |
//| Nonlinear task min(F(c)) is solved, where                        |
//|     F(c) = (w[0]*(f(c,x[0])-y[0]))^2 + ... +                     |
//|     + (w[n-1]*(f(c,x[n-1])-y[n-1]))^2,                           |
//|     * N is a number of points,                                   |
//|     * M is a dimension of a space points belong to,              |
//|     * K is a dimension of a space of parameters being fitted,    |
//|     * w is an N-dimensional vector of weight coefficients,       |
//|     * x is a set of N points, each of them is an M-dimensional   |
//|       vector,                                                    |
//|     * c is a K-dimensional vector of parameters being fitted     |
//| This subroutine uses only f(c,x[i]) and its gradient.            |
//| INPUT PARAMETERS:                                                |
//|     X       -   array[0..N-1,0..M-1], points (one row = one      |
//|                 point)                                           |
//|     Y       -   array[0..N-1], function values.                  |
//|     W       -   weights, array[0..N-1]                           |
//|     C       -   array[0..K-1], initial approximation to the      |
//|                 solution,                                        |
//|     N       -   number of points, N>1                            |
//|     M       -   dimension of space                               |
//|     K       -   number of parameters being fitted                |
//|     CheapFG -   boolean flag, which is:                          |
//|                 * True if both function and gradient calculation |
//|                        complexity are less than O(M^2). An       |
//|                        improved algorithm can be used which      |
//|                        corresponds to FGJ scheme from MINLM unit.|
//|                 * False otherwise.                               |
//|                        Standard Jacibian-bases                   |
//|                        Levenberg-Marquardt algo will be used (FJ |
//|                        scheme).                                  |
//| OUTPUT PARAMETERS:                                               |
//|     State   -   structure which stores algorithm state           |
//| See also:                                                        |
//|     LSFitResults                                                 |
//|     LSFitCreateFG (fitting without weights)                      |
//|     LSFitCreateWFGH (fitting using Hessian)                      |
//|     LSFitCreateFGH (fitting using Hessian, without weights)      |
//+------------------------------------------------------------------+
static void CLSFit::LSFitCreateWFG(CMatrixDouble &x,double &y[],double &w[],
                                   double &c[],const int n,const int m,
                                   const int k,bool cheapfg,CLSFitState &state)
  {
//--- create variables
   int i=0;
   int i_=0;
//--- check
   if(!CAp::Assert(n>=1,__FUNCTION__+": N<1!"))
      return;
//--- check
   if(!CAp::Assert(m>=1,__FUNCTION__+": M<1!"))
      return;
//--- check
   if(!CAp::Assert(k>=1,__FUNCTION__+": K<1!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(c)>=k,__FUNCTION__+": length(C)<K!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(c,k),__FUNCTION__+": C contains infinite or NaN values!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(y)>=n,__FUNCTION__+": length(Y)<N!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(y,n),__FUNCTION__+": Y contains infinite or NaN values!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(w)>=n,__FUNCTION__+": length(W)<N!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(w,n),__FUNCTION__+": W contains infinite or NaN values!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Rows(x)>=n,__FUNCTION__+": rows(X)<N!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Cols(x)>=m,__FUNCTION__+": cols(X)<M!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteMatrix(x,n,m),__FUNCTION__+": X contains infinite or NaN values!"))
      return;
//--- initialization
   state.m_npoints=n;
   state.m_nweights=n;
   state.m_wkind=1;
   state.m_m=m;
   state.m_k=k;
//--- function call
   LSFitSetCond(state,0.0,0.0,0);
//--- function call
   LSFitSetStpMax(state,0.0);
//--- function call
   LSFitSetXRep(state,false);
//--- allocation
   state.m_taskx.Resize(n,m);
   ArrayResizeAL(state.m_tasky,n);
   ArrayResizeAL(state.m_w,n);
   ArrayResizeAL(state.m_c,k);
   ArrayResizeAL(state.m_x,m);
   ArrayResizeAL(state.m_g,k);
//--- copy
   for(i_=0;i_<=k-1;i_++)
      state.m_c[i_]=c[i_];
   for(i_=0;i_<=n-1;i_++)
      state.m_w[i_]=w[i_];
   for(i=0;i<=n-1;i++)
     {
      for(i_=0;i_<=m-1;i_++)
         state.m_taskx[i].Set(i_,x[i][i_]);
      state.m_tasky[i]=y[i];
     }
//--- allocation
   ArrayResizeAL(state.m_s,k);
   ArrayResizeAL(state.m_bndl,k);
   ArrayResizeAL(state.m_bndu,k);
//--- change values
   for(i=0;i<=k-1;i++)
     {
      state.m_s[i]=1.0;
      state.m_bndl[i]=CInfOrNaN::NegativeInfinity();
      state.m_bndu[i]=CInfOrNaN::PositiveInfinity();
     }
//--- change values
   state.m_optalgo=1;
   state.m_prevnpt=-1;
   state.m_prevalgo=-1;
//--- check
   if(cheapfg)
      CMinLM::MinLMCreateVGJ(k,n,state.m_c,state.m_optstate);
   else
      CMinLM::MinLMCreateVJ(k,n,state.m_c,state.m_optstate);
//--- function call
   LSFitClearRequestFields(state);
//--- allocation
   ArrayResizeAL(state.m_rstate.ia,5);
   ArrayResizeAL(state.m_rstate.ra,3);
   state.m_rstate.stage=-1;
  }
//+------------------------------------------------------------------+
//| Nonlinear least squares fitting using gradient only, without     |
//| individual weights.                                              |
//| Nonlinear task min(F(c)) is solved, where                        |
//|     F(c) = ((f(c,x[0])-y[0]))^2 + ... + ((f(c,x[n-1])-y[n-1]))^2,|
//|     * N is a number of points,                                   |
//|     * M is a dimension of a space points belong to,              |
//|     * K is a dimension of a space of parameters being fitted,    |
//|     * x is a set of N points, each of them is an M-dimensional   |
//|       vector,                                                    |
//|     * c is a K-dimensional vector of parameters being fitted     |
//| This subroutine uses only f(c,x[i]) and its gradient.            |
//| INPUT PARAMETERS:                                                |
//|     X       -   array[0..N-1,0..M-1], points (one row = one      |
//|                 point)                                           |
//|     Y       -   array[0..N-1], function values.                  |
//|     C       -   array[0..K-1], initial approximation to the      |
//|                 solution,                                        |
//|     N       -   number of points, N>1                            |
//|     M       -   dimension of space                               |
//|     K       -   number of parameters being fitted                |
//|     CheapFG -   boolean flag, which is:                          |
//|                 * True  if both function and gradient calculation|
//|                         complexity are less than O(M^2). An      |
//|                         improved algorithm can be used which     |
//|                         corresponds to FGJ scheme from MINLM     |
//|                         unit.                                    |
//|                 * False otherwise.                               |
//|                         Standard Jacibian-bases                  |
//|                         Levenberg-Marquardt algo will be used    |
//|                         (FJ scheme).                             |
//| OUTPUT PARAMETERS:                                               |
//|     State   -   structure which stores algorithm state           |
//+------------------------------------------------------------------+
static void CLSFit::LSFitCreateFG(CMatrixDouble &x,double &y[],double &c[],
                                  const int n,const int m,const int k,
                                  const bool cheapfg,CLSFitState &state)
  {
//--- create variables
   int i=0;
   int i_=0;
//--- check
   if(!CAp::Assert(n>=1,__FUNCTION__+": N<1!"))
      return;
//--- check
   if(!CAp::Assert(m>=1,__FUNCTION__+": M<1!"))
      return;
//--- check
   if(!CAp::Assert(k>=1,__FUNCTION__+": K<1!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(c)>=k,__FUNCTION__+": length(C)<K!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(c,k),__FUNCTION__+": C contains infinite or NaN values!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(y)>=n,__FUNCTION__+": length(Y)<N!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(y,n),__FUNCTION__+": Y contains infinite or NaN values!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Rows(x)>=n,__FUNCTION__+": rows(X)<N!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Cols(x)>=m,__FUNCTION__+": cols(X)<M!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteMatrix(x,n,m),__FUNCTION__+": X contains infinite or NaN values!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Rows(x)>=n,__FUNCTION__+": rows(X)<N!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Cols(x)>=m,__FUNCTION__+": cols(X)<M!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteMatrix(x,n,m),__FUNCTION__+": X contains infinite or NaN values!"))
      return;
//--- initialization
   state.m_npoints=n;
   state.m_wkind=0;
   state.m_m=m;
   state.m_k=k;
   LSFitSetCond(state,0.0,0.0,0);
   LSFitSetStpMax(state,0.0);
   LSFitSetXRep(state,false);
//--- allocation
   state.m_taskx.Resize(n,m);
   ArrayResizeAL(state.m_tasky,n);
   ArrayResizeAL(state.m_c,k);
   ArrayResizeAL(state.m_x,m);
   ArrayResizeAL(state.m_g,k);
//--- copy
   for(i_=0;i_<=k-1;i_++)
      state.m_c[i_]=c[i_];
   for(i=0;i<=n-1;i++)
     {
      for(i_=0;i_<=m-1;i_++)
         state.m_taskx[i].Set(i_,x[i][i_]);
      state.m_tasky[i]=y[i];
     }
//--- allocation
   ArrayResizeAL(state.m_s,k);
   ArrayResizeAL(state.m_bndl,k);
   ArrayResizeAL(state.m_bndu,k);
//--- change values
   for(i=0;i<=k-1;i++)
     {
      state.m_s[i]=1.0;
      state.m_bndl[i]=CInfOrNaN::NegativeInfinity();
      state.m_bndu[i]=CInfOrNaN::PositiveInfinity();
     }
//--- change values
   state.m_optalgo=1;
   state.m_prevnpt=-1;
   state.m_prevalgo=-1;
//--- check
   if(cheapfg)
      CMinLM::MinLMCreateVGJ(k,n,state.m_c,state.m_optstate);
   else
      CMinLM::MinLMCreateVJ(k,n,state.m_c,state.m_optstate);
//--- function call
   LSFitClearRequestFields(state);
//--- allocation
   ArrayResizeAL(state.m_rstate.ia,5);
   ArrayResizeAL(state.m_rstate.ra,3);
   state.m_rstate.stage=-1;
  }
//+------------------------------------------------------------------+
//| Weighted nonlinear least squares fitting using gradient/Hessian. |
//| Nonlinear task min(F(c)) is solved, where                        |
//|     F(c) = (w[0]*(f(c,x[0])-y[0]))^2 + ... +                     |
//|     (w[n-1]*(f(c,x[n-1])-y[n-1]))^2,                             |
//|     * N is a number of points,                                   |
//|     * M is a dimension of a space points belong to,              |
//|     * K is a dimension of a space of parameters being fitted,    |
//|     * w is an N-dimensional vector of weight coefficients,       |
//|     * x is a set of N points, each of them is an M-dimensional   |
//|     vector,                                                      |
//|     * c is a K-dimensional vector of parameters being fitted     |
//| This subroutine uses f(c,x[i]), its gradient and its Hessian.    |
//| INPUT PARAMETERS:                                                |
//|     X       -   array[0..N-1,0..M-1], points (one row = one      |
//|                 point)                                           |
//|     Y       -   array[0..N-1], function values.                  |
//|     W       -   weights, array[0..N-1]                           |
//|     C       -   array[0..K-1], initial approximation to the      |
//|                 solution,                                        |
//|     N       -   number of points, N>1                            |
//|     M       -   dimension of space                               |
//|     K       -   number of parameters being fitted                |
//| OUTPUT PARAMETERS:                                               |
//|     State   -   structure which stores algorithm state           |
//+------------------------------------------------------------------+
static void CLSFit::LSFitCreateWFGH(CMatrixDouble &x,double &y[],double &w[],
                                    double &c[],const int n,const int m,
                                    const int k,CLSFitState &state)
  {
//--- create variables
   int i=0;
   int i_=0;
//--- check
   if(!CAp::Assert(n>=1,__FUNCTION__+": N<1!"))
      return;
//--- check
   if(!CAp::Assert(m>=1,__FUNCTION__+": M<1!"))
      return;
//--- check
   if(!CAp::Assert(k>=1,__FUNCTION__+": K<1!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(c)>=k,__FUNCTION__+": length(C)<K!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(c,k),__FUNCTION__+": C contains infinite or NaN values!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(y)>=n,__FUNCTION__+": length(Y)<N!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(y,n),__FUNCTION__+": Y contains infinite or NaN values!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(w)>=n,__FUNCTION__+": length(W)<N!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(w,n),__FUNCTION__+": W contains infinite or NaN values!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Rows(x)>=n,__FUNCTION__+": rows(X)<N!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Cols(x)>=m,__FUNCTION__+": cols(X)<M!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteMatrix(x,n,m),__FUNCTION__+": X contains infinite or NaN values!"))
      return;
//--- initialization
   state.m_npoints=n;
   state.m_nweights=n;
   state.m_wkind=1;
   state.m_m=m;
   state.m_k=k;
//--- function call
   LSFitSetCond(state,0.0,0.0,0);
//--- function call
   LSFitSetStpMax(state,0.0);
//--- function call
   LSFitSetXRep(state,false);
//--- allocation
   state.m_taskx.Resize(n,m);
   ArrayResizeAL(state.m_tasky,n);
   ArrayResizeAL(state.m_w,n);
   ArrayResizeAL(state.m_c,k);
   state.m_h.Resize(k,k);
   ArrayResizeAL(state.m_x,m);
   ArrayResizeAL(state.m_g,k);
//--- copy
   for(i_=0;i_<=k-1;i_++)
      state.m_c[i_]=c[i_];
   for(i_=0;i_<=n-1;i_++)
      state.m_w[i_]=w[i_];
   for(i=0;i<=n-1;i++)
     {
      for(i_=0;i_<=m-1;i_++)
         state.m_taskx[i].Set(i_,x[i][i_]);
      state.m_tasky[i]=y[i];
     }
//--- allocation
   ArrayResizeAL(state.m_s,k);
   ArrayResizeAL(state.m_bndl,k);
   ArrayResizeAL(state.m_bndu,k);
//--- change values
   for(i=0;i<=k-1;i++)
     {
      state.m_s[i]=1.0;
      state.m_bndl[i]=CInfOrNaN::NegativeInfinity();
      state.m_bndu[i]=CInfOrNaN::PositiveInfinity();
     }
//--- change values
   state.m_optalgo=2;
   state.m_prevnpt=-1;
   state.m_prevalgo=-1;
//--- function call
   CMinLM::MinLMCreateFGH(k,state.m_c,state.m_optstate);
//--- function call
   LSFitClearRequestFields(state);
//--- allocation
   ArrayResizeAL(state.m_rstate.ia,5);
   ArrayResizeAL(state.m_rstate.ra,3);
   state.m_rstate.stage=-1;
  }
//+------------------------------------------------------------------+
//| Nonlinear least squares fitting using gradient/Hessian, without  |
//| individial weights.                                              |
//| Nonlinear task min(F(c)) is solved, where                        |
//|     F(c) = ((f(c,x[0])-y[0]))^2 + ... +                          |
//|     ((f(c,x[n-1])-y[n-1]))^2,                                    |
//|     * N is a number of points,                                   |
//|     * M is a dimension of a space points belong to,              |
//|     * K is a dimension of a space of parameters being fitted,    |
//|     * x is a set of N points, each of them is an M-dimensional   |
//|       vector,                                                    |
//|     * c is a K-dimensional vector of parameters being fitted     |
//| This subroutine uses f(c,x[i]), its gradient and its Hessian.    |
//| INPUT PARAMETERS:                                                |
//|     X       -   array[0..N-1,0..M-1], points (one row = one      |
//|                 point)                                           |
//|     Y       -   array[0..N-1], function values.                  |
//|     C       -   array[0..K-1], initial approximation to the      |
//|                 solution,                                        |
//|     N       -   number of points, N>1                            |
//|     M       -   dimension of space                               |
//|     K       -   number of parameters being fitted                |
//| OUTPUT PARAMETERS:                                               |
//|     State   -   structure which stores algorithm state           |
//+------------------------------------------------------------------+
static void CLSFit::LSFitCreateFGH(CMatrixDouble &x,double &y[],double &c[],
                                   const int n,const int m,const int k,
                                   CLSFitState &state)
  {
//--- create variables
   int i=0;
   int i_=0;
//--- check
   if(!CAp::Assert(n>=1,__FUNCTION__+": N<1!"))
      return;
//--- check
   if(!CAp::Assert(m>=1,__FUNCTION__+": M<1!"))
      return;
//--- check
   if(!CAp::Assert(k>=1,__FUNCTION__+": K<1!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(c)>=k,__FUNCTION__+": length(C)<K!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(c,k),__FUNCTION__+": C contains infinite or NaN values!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(y)>=n,__FUNCTION__+": length(Y)<N!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteVector(y,n),__FUNCTION__+": Y contains infinite or NaN values!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Rows(x)>=n,__FUNCTION__+": rows(X)<N!"))
      return;
//--- check
   if(!CAp::Assert(CAp::Cols(x)>=m,__FUNCTION__+": cols(X)<M!"))
      return;
//--- check
   if(!CAp::Assert(CApServ::IsFiniteMatrix(x,n,m),__FUNCTION__+": X contains infinite or NaN values!"))
      return;
//--- initialization
   state.m_npoints=n;
   state.m_wkind=0;
   state.m_m=m;
   state.m_k=k;
//--- function call
   LSFitSetCond(state,0.0,0.0,0);
//--- function call
   LSFitSetStpMax(state,0.0);
//--- function call
   LSFitSetXRep(state,false);
//--- allocation
   state.m_taskx.Resize(n,m);
   ArrayResizeAL(state.m_tasky,n);
   ArrayResizeAL(state.m_c,k);
   state.m_h.Resize(k,k);
   ArrayResizeAL(state.m_x,m);
   ArrayResizeAL(state.m_g,k);
//--- copy
   for(i_=0;i_<=k-1;i_++)
      state.m_c[i_]=c[i_];
   for(i=0;i<=n-1;i++)
     {
      for(i_=0;i_<=m-1;i_++)
         state.m_taskx[i].Set(i_,x[i][i_]);
      state.m_tasky[i]=y[i];
     }
//--- allocation
   ArrayResizeAL(state.m_s,k);
   ArrayResizeAL(state.m_bndl,k);
   ArrayResizeAL(state.m_bndu,k);
//--- change values
   for(i=0;i<=k-1;i++)
     {
      state.m_s[i]=1.0;
      state.m_bndl[i]=CInfOrNaN::NegativeInfinity();
      state.m_bndu[i]=CInfOrNaN::PositiveInfinity();
     }
//--- change values
   state.m_optalgo=2;
   state.m_prevnpt=-1;
   state.m_prevalgo=-1;
//--- function call
   CMinLM::MinLMCreateFGH(k,state.m_c,state.m_optstate);
//--- function call
   LSFitClearRequestFields(state);
//--- allocation
   ArrayResizeAL(state.m_rstate.ia,5);
   ArrayResizeAL(state.m_rstate.ra,3);
   state.m_rstate.stage=-1;
  }
//+------------------------------------------------------------------+
//| Stopping conditions for nonlinear least squares fitting.         |
//| INPUT PARAMETERS:                                                |
//|     State   -   structure which stores algorithm state           |
//|     EpsF    -   stopping criterion. Algorithm stops if           |
//|                 |F(k+1)-F(k)| <= EpsF*max{|F(k)|, |F(k+1)|, 1}   |
//|     EpsX    -   >=0                                              |
//|                 The subroutine finishes its work if on k+1-th    |
//|                 iteration the condition |v|<=EpsX is fulfilled,  |
//|                 where:                                           |
//|                 * |.| means Euclidian norm                       |
//|                 * v - scaled step vector, v[i]=dx[i]/s[i]        |
//|                 * dx - ste pvector, dx=X(k+1)-X(k)               |
//|                 * s - scaling coefficients set by LSFitSetScale()|
//|     MaxIts  -   maximum number of iterations. If MaxIts=0, the   |
//|                 number of iterations is unlimited. Only          |
//|                 Levenberg-Marquardt iterations are counted       |
//|                 (L-BFGS/CG iterations are NOT counted because    |
//|                 their cost is very low compared to that of LM).  |
//| NOTE                                                             |
//| Passing EpsF=0, EpsX=0 and MaxIts=0 (simultaneously) will lead to|
//| automatic stopping criterion selection (according to the scheme  |
//| used by MINLM unit).                                             |
//+------------------------------------------------------------------+
static void CLSFit::LSFitSetCond(CLSFitState &state,const double epsf,
                                 const double epsx,const int maxits)
  {
//--- check
   if(!CAp::Assert(CMath::IsFinite(epsf),__FUNCTION__+": EpsF is not finite!"))
      return;
//--- check
   if(!CAp::Assert((double)(epsf)>=0.0,__FUNCTION__+": negative EpsF!"))
      return;
//--- check
   if(!CAp::Assert(CMath::IsFinite(epsx),__FUNCTION__+": EpsX is not finite!"))
      return;
//--- check
   if(!CAp::Assert((double)(epsx)>=0.0,__FUNCTION__+": negative EpsX!"))
      return;
//--- check
   if(!CAp::Assert(maxits>=0,__FUNCTION__+": negative MaxIts!"))
      return;
//--- change values
   state.m_epsf=epsf;
   state.m_epsx=epsx;
   state.m_maxits=maxits;
  }
//+------------------------------------------------------------------+
//| This function sets maximum step length                           |
//| INPUT PARAMETERS:                                                |
//|     State   -   structure which stores algorithm state           |
//|     StpMax  -   maximum step length, >=0. Set StpMax to 0.0, if  |
//|                 you don't want to limit step length.             |
//| Use this subroutine when you optimize target function which      |
//| contains exp() or other fast growing functions, and optimization |
//| algorithm makes too large steps which leads to overflow. This    |
//| function allows us to reject steps that are too large (and       |
//| therefore expose us to the possible overflow) without actually   |
//| calculating function value at the x+stp*d.                       |
//| NOTE: non-zero StpMax leads to moderate performance degradation  |
//| because intermediate step of preconditioned L-BFGS optimization  |
//| is incompatible with limits on step size.                        |
//+------------------------------------------------------------------+
static void CLSFit::LSFitSetStpMax(CLSFitState &state,const double stpmax)
  {
//--- check
   if(!CAp::Assert(stpmax>=0.0,__FUNCTION__+": StpMax<0!"))
      return;
//--- change value
   state.m_stpmax=stpmax;
  }
//+------------------------------------------------------------------+
//| This function turns on/off reporting.                            |
//| INPUT PARAMETERS:                                                |
//|     State   -   structure which stores algorithm state           |
//|     NeedXRep-   whether iteration reports are needed or not      |
//| When reports are needed, State.C (current parameters) and State. |
//| F (current value of fitting function) are reported.              |
//+------------------------------------------------------------------+
static void CLSFit::LSFitSetXRep(CLSFitState &state,const bool needxrep)
  {
//--- change value
   state.m_xrep=needxrep;
  }
//+------------------------------------------------------------------+
//| This function sets scaling coefficients for underlying optimizer.|
//| ALGLIB optimizers use scaling matrices to test stopping          |
//| conditions (step size and gradient are scaled before comparison  |
//| with tolerances). Scale of the I-th variable is a translation    |
//| invariant measure of:                                            |
//| a) "how large" the variable is                                   |
//| b) how large the step should be to make significant changes in   |
//| the function                                                     |
//| Generally, scale is NOT considered to be a form of               |
//| preconditioner. But LM optimizer is unique in that it uses       |
//| scaling matrix both in the stopping condition tests and as       |
//| Marquardt damping factor.                                        |
//| Proper scaling is very important for the algorithm performance.  |
//| It is less important for the quality of results, but still has   |
//| some influence (it is easier to converge when variables are      |
//| properly scaled, so premature stopping is possible when very     |
//| badly scalled variables are combined with relaxed stopping       |
//| conditions).                                                     |
//| INPUT PARAMETERS:                                                |
//|     State   -   structure stores algorithm state                 |
//|     S       -   array[N], non-zero scaling coefficients          |
//|                 S[i] may be negative, sign doesn't matter.       |
//+------------------------------------------------------------------+
static void CLSFit::LSFitSetScale(CLSFitState &state,double &s[])
  {
//--- create a variable
   int i=0;
//--- check
   if(!CAp::Assert(CAp::Len(s)>=state.m_k,__FUNCTION__+": Length(S)<K"))
      return;
   for(i=0;i<=state.m_k-1;i++)
     {
      //--- check
      if(!CAp::Assert(CMath::IsFinite(s[i]),__FUNCTION__+": S contains infinite or NAN elements"))
         return;
      //--- check
      if(!CAp::Assert((double)(s[i])!=0.0,__FUNCTION__+": S contains infinite or NAN elements"))
         return;
      //--- change values
      state.m_s[i]=s[i];
     }
  }
//+------------------------------------------------------------------+
//| This function sets boundary constraints for underlying optimizer |
//| Boundary constraints are inactive by default (after initial      |
//| creation). They are preserved until explicitly turned off with   |
//| another SetBC() call.                                            |
//| INPUT PARAMETERS:                                                |
//|     State   -   structure stores algorithm state                 |
//|     BndL    -   lower bounds, array[K].                          |
//|                 If some (all) variables are unbounded, you may   |
//|                 specify very small number or -INF (latter is     |
//|                 recommended because it will allow solver to use  |
//|                 better algorithm).                               |
//|     BndU    -   upper bounds, array[K].                          |
//|                 If some (all) variables are unbounded, you may   |
//|                 specify very large number or +INF (latter is     |
//|                 recommended because it will allow solver to use  |
//|                 better algorithm).                               |
//| NOTE 1: it is possible to specify BndL[i]=BndU[i]. In this case  |
//| I-th variable will be "frozen" at X[i]=BndL[i]=BndU[i].          |
//| NOTE 2: unlike other constrained optimization algorithms, this   |
//| solver has following useful properties:                          |
//| * bound constraints are always satisfied exactly                 |
//| * function is evaluated only INSIDE area specified by bound      |
//|   constraints                                                    |
//+------------------------------------------------------------------+
static void CLSFit::LSFitSetBC(CLSFitState &state,double &bndl[],
                               double &bndu[])
  {
//--- create variables
   int i=0;
   int k=0;
//--- initialization
   k=state.m_k;
//--- check
   if(!CAp::Assert(CAp::Len(bndl)>=k,__FUNCTION__+": Length(BndL)<K"))
      return;
//--- check
   if(!CAp::Assert(CAp::Len(bndu)>=k,__FUNCTION__+": Length(BndU)<K"))
      return;
   for(i=0;i<=k-1;i++)
     {
      //--- check
      if(!CAp::Assert(CMath::IsFinite(bndl[i]) || CInfOrNaN::IsNegativeInfinity(bndl[i]),__FUNCTION__+": BndL contains NAN or +INF"))
         return;
      //--- check
      if(!CAp::Assert(CMath::IsFinite(bndu[i]) || CInfOrNaN::IsPositiveInfinity(bndu[i]),__FUNCTION__+": BndU contains NAN or -INF"))
         return;
      //--- check
      if(CMath::IsFinite(bndl[i]) && CMath::IsFinite(bndu[i]))
        {
         //--- check
         if(!CAp::Assert(bndl[i]<=bndu[i],__FUNCTION__+": BndL[i]>BndU[i]"))
            return;
        }
      //--- change values
      state.m_bndl[i]=bndl[i];
      state.m_bndu[i]=bndu[i];
     }
  }
//+------------------------------------------------------------------+
//| Nonlinear least squares fitting results.                         |
//| Called after return from LSFitFit().                             |
//| INPUT PARAMETERS:                                                |
//|     State   -   algorithm state                                  |
//| OUTPUT PARAMETERS:                                               |
//|     Info    -   completetion code:                               |
//|                     *  1    relative function improvement is no  |
//|                             more than EpsF.                      |
//|                     *  2    relative step is no more than EpsX.  |
//|                     *  4    gradient norm is no more than EpsG   |
//|                     *  5    MaxIts steps was taken               |
//|                     *  7    stopping conditions are too          |
//|                             stringent, further improvement is    |
//|                             impossible                           |
//|     C       -   array[0..K-1], solution                          |
//|     Rep     -   optimization report. Following fields are set:   |
//|                 * Rep.TerminationType completetion code:         |
//|                 * RMSError          rms error on the (X,Y).      |
//|                 * AvgError          average error on the (X,Y).  |
//|                 * AvgRelError       average relative error on the|
//|                                     non-zero Y                   |
//|                 * MaxError          maximum error                |
//|                                     NON-WEIGHTED ERRORS ARE      |
//|                                     CALCULATED                   |
//|                 * WRMSError         weighted rms error on the    |
//|                                     (X,Y).                       |
//+------------------------------------------------------------------+
static void CLSFit::LSFitResults(CLSFitState &state,int &info,double &c[],
                                 CLSFitReport &rep)
  {
//--- create a variable
   int i_=0;
//--- initialization
   info=state.m_repterminationtype;
//--- check
   if(info>0)
     {
      //--- allocation
      ArrayResizeAL(c,state.m_k);
      for(i_=0;i_<=state.m_k-1;i_++)
         c[i_]=state.m_c[i_];
      //--- change values
      rep.m_rmserror=state.m_reprmserror;
      rep.m_wrmserror=state.m_repwrmserror;
      rep.m_avgerror=state.m_repavgerror;
      rep.m_avgrelerror=state.m_repavgrelerror;
      rep.m_maxerror=state.m_repmaxerror;
      rep.m_iterationscount=state.m_repiterationscount;
     }
  }
//+------------------------------------------------------------------+
//| Internal subroutine: automatic scaling for LLS tasks.            |
//| NEVER CALL IT DIRECTLY!                                          |
//| Maps abscissas to [-1,1], standartizes ordinates and             |
//| correspondingly scales constraints. It also scales weights so    |
//| that max(W[i])=1                                                 |
//| Transformations performed:                                       |
//| * X, XC         [XA,XB] => [-1,+1]                               |
//|                 transformation makes min(X)=-1, max(X)=+1        |
//| * Y             [SA,SB] => [0,1]                                 |
//|                 transformation makes mean(Y)=0, stddev(Y)=1      |
//| * YC            transformed accordingly to SA, SB, DC[I]         |
//+------------------------------------------------------------------+
static void CLSFit::LSFitScaleXY(double &x[],double &y[],double &w[],
                                 const int n,double &xc[],double &yc[],
                                 int &dc[],const int k,double &xa,
                                 double &xb,double &sa,double &sb,
                                 double &xoriginal[],double &yoriginal[])
  {
//--- create variables
   double xmin=0;
   double xmax=0;
   int    i=0;
   double mx=0;
   int    i_=0;
//--- initialization
   xa=0;
   xb=0;
   sa=0;
   sb=0;
//--- check
   if(!CAp::Assert(n>=1,__FUNCTION__+": incorrect N"))
      return;
//--- check
   if(!CAp::Assert(k>=0,__FUNCTION__+": incorrect K"))
      return;
//--- Calculate xmin/xmax.
//--- Force xmin<>xmax.
   xmin=x[0];
   xmax=x[0];
   for(i=1;i<=n-1;i++)
     {
      xmin=MathMin(xmin,x[i]);
      xmax=MathMax(xmax,x[i]);
     }
   for(i=0;i<=k-1;i++)
     {
      xmin=MathMin(xmin,xc[i]);
      xmax=MathMax(xmax,xc[i]);
     }
//--- check
   if(xmin==xmax)
     {
      //--- check
      if(xmin==0.0)
        {
         xmin=-1;
         xmax=1;
        }
      else
        {
         //--- check
         if(xmin>0.0)
            xmin=0.5*xmin;
         else
            xmax=0.5*xmax;
        }
     }
//--- Transform abscissas: map [XA,XB] to [0,1]
//--- Store old X[] in XOriginal[] (it will be used
//--- to calculate relative error).
   ArrayResizeAL(xoriginal,n);
   for(i_=0;i_<=n-1;i_++)
      xoriginal[i_]=x[i_];
//--- change values
   xa=xmin;
   xb=xmax;
   for(i=0;i<=n-1;i++)
      x[i]=2*(x[i]-0.5*(xa+xb))/(xb-xa);
//--- calculation
   for(i=0;i<=k-1;i++)
     {
      //--- check
      if(!CAp::Assert(dc[i]>=0,__FUNCTION__+": internal error!"))
         return;
      xc[i]=2*(xc[i]-0.5*(xa+xb))/(xb-xa);
      yc[i]=yc[i]*MathPow(0.5*(xb-xa),dc[i]);
     }
//--- Transform function values: map [SA,SB] to [0,1]
//--- SA=mean(Y),
//--- SB=SA+stddev(Y).
//--- Store old Y[] in YOriginal[] (it will be used
//--- to calculate relative error).
   ArrayResizeAL(yoriginal,n);
   for(i_=0;i_<=n-1;i_++)
      yoriginal[i_]=y[i_];
   sa=0;
   for(i=0;i<=n-1;i++)
      sa=sa+y[i];
   sa=sa/n;
//--- change value
   sb=0;
   for(i=0;i<=n-1;i++)
      sb=sb+CMath::Sqr(y[i]-sa);
   sb=MathSqrt(sb/n)+sa;
//--- check
   if(sb==sa)
      sb=2*sa;
//--- check
   if(sb==sa)
      sb=sa+1;
   for(i=0;i<=n-1;i++)
      y[i]=(y[i]-sa)/(sb-sa);
   for(i=0;i<=k-1;i++)
     {
      //--- check
      if(dc[i]==0)
         yc[i]=(yc[i]-sa)/(sb-sa);
      else
         yc[i]=yc[i]/(sb-sa);
     }
//--- Scale weights
   mx=0;
   for(i=0;i<=n-1;i++)
      mx=MathMax(mx,MathAbs(w[i]));
//--- check
   if(mx!=0.0)
     {
      for(i=0;i<=n-1;i++)
         w[i]=w[i]/mx;
     }
  }
//+------------------------------------------------------------------+
//| Internal spline fitting subroutine                               |
//+------------------------------------------------------------------+
static void CLSFit::Spline1DFitInternal(const int st,double &cx[],double &cy[],
                                        double &cw[],const int n,double &cxc[],
                                        double &cyc[],int &dc[],const int k,
                                        const int m,int &info,
                                        CSpline1DInterpolant &s,
                                        CSpline1DFitReport &rep)
  {
//--- create variables
   double v0=0;
   double v1=0;
   double v2=0;
   double mx=0;
   int    i=0;
   int    j=0;
   int    relcnt=0;
   double xa=0;
   double xb=0;
   double sa=0;
   double sb=0;
   double bl=0;
   double br=0;
   double decay=0;
   int    i_=0;
//--- create arrays
   double y2[];
   double w2[];
   double sx[];
   double sy[];
   double sd[];
   double tmp[];
   double xoriginal[];
   double yoriginal[];
   double x[];
   double y[];
   double w[];
   double xc[];
   double yc[];
//--- create matrix
   CMatrixDouble fmatrix;
   CMatrixDouble cmatrix;
//--- objects of classes
   CLSFitReport         lrep;
   CSpline1DInterpolant s2;
//--- copy arrays
   ArrayCopy(x,cx);
   ArrayCopy(y,cy);
   ArrayCopy(w,cw);
   ArrayCopy(xc,cxc);
   ArrayCopy(yc,cyc);
//--- initialization
   info=0;
//--- check
   if(!CAp::Assert(st==0 || st==1,__FUNCTION__+": internal error!"))
      return;
//--- check
   if(st==0 && m<4)
     {
      info=-1;
      return;
     }
//--- check
   if(st==1 && m<4)
     {
      info=-1;
      return;
     }
//--- check
   if((n<1 || k<0) || k>=m)
     {
      info=-1;
      return;
     }
   for(i=0;i<=k-1;i++)
     {
      info=0;
      //--- check
      if(dc[i]<0)
         info=-1;
      //--- check
      if(dc[i]>1)
         info=-1;
      //--- check
      if(info<0)
         return;
     }
//--- check
   if(st==1 && m%2!=0)
     {
      //--- Hermite fitter must have even number of basis functions
      info=-2;
      return;
     }
//--- weight decay for correct handling of task which becomes
//--- degenerate after constraints are applied
   decay=10000*CMath::m_machineepsilon;
//--- Scale X,Y,XC,YC
   LSFitScaleXY(x,y,w,n,xc,yc,dc,k,xa,xb,sa,sb,xoriginal,yoriginal);
//--- allocate space,initialize:
//--- * SX     -   grid for basis functions
//--- * SY     -   values of basis functions at grid points
//--- * FMatrix-   values of basis functions at X[]
//--- * CMatrix-   values (derivatives) of basis functions at XC[]
   ArrayResizeAL(y2,n+m);
   ArrayResizeAL(w2,n+m);
   fmatrix.Resize(n+m,m);
//--- check
   if(k>0)
      cmatrix.Resize(k,m+1);
//--- check
   if(st==0)
     {
      //--- allocate space for cubic spline
      ArrayResizeAL(sx,m-2);
      ArrayResizeAL(sy,m-2);
      for(j=0;j<=m-2-1;j++)
         sx[j]=(double)(2*j)/(double)(m-2-1)-1;
     }
//--- check
   if(st==1)
     {
      //--- allocate space for Hermite spline
      ArrayResizeAL(sx,m/2);
      ArrayResizeAL(sy,m/2);
      ArrayResizeAL(sd,m/2);
      for(j=0;j<=m/2-1;j++)
         sx[j]=(double)(2*j)/(double)(m/2-1)-1;
     }
//--- Prepare design and constraints matrices:
//--- * fill constraints matrix
//--- * fill first N rows of design matrix with values
//--- * fill next M rows of design matrix with regularizing term
//--- * append M zeros to Y
//--- * append M elements,mean(abs(W)) each,to W
   for(j=0;j<=m-1;j++)
     {
      //--- prepare Jth basis function
      if(st==0)
        {
         //--- cubic spline basis
         for(i=0;i<=m-2-1;i++)
            sy[i]=0;
         bl=0;
         br=0;
         //--- check
         if(j<m-2)
            sy[j]=1;
         //--- check
         if(j==m-2)
            bl=1;
         //--- check
         if(j==m-1)
            br=1;
         //--- function call
         CSpline1D::Spline1DBuildCubic(sx,sy,m-2,1,bl,1,br,s2);
        }
      //--- check
      if(st==1)
        {
         //--- Hermite basis
         for(i=0;i<=m/2-1;i++)
           {
            sy[i]=0;
            sd[i]=0;
           }
         //--- check
         if(j%2==0)
            sy[j/2]=1;
         else
            sd[j/2]=1;
         //--- function call
         CSpline1D::Spline1DBuildHermite(sx,sy,sd,m/2,s2);
        }
      //--- values at X[],XC[]
      for(i=0;i<=n-1;i++)
         fmatrix[i].Set(j,CSpline1D::Spline1DCalc(s2,x[i]));
      for(i=0;i<=k-1;i++)
        {
         //--- check
         if(!CAp::Assert(dc[i]>=0 && dc[i]<=2,__FUNCTION__+": internal error!"))
            return;
         //--- function call
         CSpline1D::Spline1DDiff(s2,xc[i],v0,v1,v2);
         //--- check
         if(dc[i]==0)
            cmatrix[i].Set(j,v0);
         //--- check
         if(dc[i]==1)
            cmatrix[i].Set(j,v1);
         //--- check
         if(dc[i]==2)
            cmatrix[i].Set(j,v2);
        }
     }
//--- calculation
   for(i=0;i<=k-1;i++)
      cmatrix[i].Set(m,yc[i]);
   for(i=0;i<=m-1;i++)
     {
      for(j=0;j<=m-1;j++)
        {
         //--- check
         if(i==j)
            fmatrix[n+i].Set(j,decay);
         else
            fmatrix[n+i].Set(j,0);
        }
     }
//--- allocation
   ArrayResizeAL(y2,n+m);
   ArrayResizeAL(w2,n+m);
//--- copy
   for(i_=0;i_<=n-1;i_++)
      y2[i_]=y[i_];
   for(i_=0;i_<=n-1;i_++)
      w2[i_]=w[i_];
//--- change value
   mx=0;
   for(i=0;i<=n-1;i++)
      mx=mx+MathAbs(w[i]);
   mx=mx/n;
   for(i=0;i<=m-1;i++)
     {
      y2[n+i]=0;
      w2[n+i]=mx;
     }
//--- Solve constrained task
   if(k>0)
     {
      //--- solve using regularization
      LSFitLinearWC(y2,w2,fmatrix,cmatrix,n+m,m,k,info,tmp,lrep);
     }
   else
     {
      //--- no constraints,no regularization needed
      LSFitLinearWC(y,w,fmatrix,cmatrix,n,m,k,info,tmp,lrep);
     }
//--- check
   if(info<0)
      return;
//--- Generate spline and scale it
   if(st==0)
     {
      //--- cubic spline basis
      for(i_=0;i_<=m-2-1;i_++)
         sy[i_]=tmp[i_];
      //--- function call
      CSpline1D::Spline1DBuildCubic(sx,sy,m-2,1,tmp[m-2],1,tmp[m-1],s);
     }
//--- check
   if(st==1)
     {
      //--- Hermite basis
      for(i=0;i<=m/2-1;i++)
        {
         sy[i]=tmp[2*i];
         sd[i]=tmp[2*i+1];
        }
      //--- function call
      CSpline1D::Spline1DBuildHermite(sx,sy,sd,m/2,s);
     }
//--- function call
   CSpline1D::Spline1DLinTransX(s,2/(xb-xa),-((xa+xb)/(xb-xa)));
//--- function call
   CSpline1D::Spline1DLinTransY(s,sb-sa,sa);
//--- Scale absolute errors obtained from LSFitLinearW.
//--- Relative error should be calculated separately
//--- (because of shifting/scaling of the task)
   rep.m_taskrcond=lrep.m_taskrcond;
   rep.m_rmserror=lrep.m_rmserror*(sb-sa);
   rep.m_avgerror=lrep.m_avgerror*(sb-sa);
   rep.m_maxerror=lrep.m_maxerror*(sb-sa);
   rep.m_avgrelerror=0;
   relcnt=0;
   for(i=0;i<=n-1;i++)
     {
      //--- check
      if(yoriginal[i]!=0.0)
        {
         rep.m_avgrelerror=rep.m_avgrelerror+MathAbs(CSpline1D::Spline1DCalc(s,xoriginal[i])-yoriginal[i])/MathAbs(yoriginal[i]);
         relcnt=relcnt+1;
        }
     }
//--- check
   if(relcnt!=0)
      rep.m_avgrelerror=rep.m_avgrelerror/relcnt;
  }
//+------------------------------------------------------------------+
//| Internal fitting subroutine                                      |
//+------------------------------------------------------------------+
static void CLSFit::LSFitLinearInternal(double &y[],double &w[],
                                        CMatrixDouble &fmatrix,const int n,
                                        const int m,int &info,double &c[],
                                        CLSFitReport &rep)
  {
//--- create variables
   double threshold=0;
   int    i=0;
   int    j=0;
   double v=0;
   int    relcnt=0;
   int    i_=0;
//--- create arrays
   double b[];
   double wmod[];
   double tau[];
   double sv[];
   double tmp[];
   double utb[];
   double sutb[];
//--- create matrix
   CMatrixDouble ft;
   CMatrixDouble q;
   CMatrixDouble l;
   CMatrixDouble r;
   CMatrixDouble u;
   CMatrixDouble vt;
//--- initialization
   info=0;
//--- check
   if(n<1 || m<1)
     {
      info=-1;
      return;
     }
//--- initialization
   info=1;
   threshold=MathSqrt(CMath::m_machineepsilon);
//--- Degenerate case,needs special handling
   if(n<m)
     {
      //--- Create design matrix.
      ft.Resize(n,m);
      ArrayResizeAL(b,n);
      ArrayResizeAL(wmod,n);
      for(j=0;j<=n-1;j++)
        {
         v=w[j];
         for(i_=0;i_<=m-1;i_++)
            ft[j].Set(i_,v*fmatrix[j][i_]);
         //--- change values
         b[j]=w[j]*y[j];
         wmod[j]=1;
        }
      //--- LQ decomposition and reduction to M=N
      ArrayResizeAL(c,m);
      for(i=0;i<=m-1;i++)
         c[i]=0;
      rep.m_taskrcond=0;
      //--- function call
      COrtFac::RMatrixLQ(ft,n,m,tau);
      //--- function call
      COrtFac::RMatrixLQUnpackQ(ft,n,m,tau,n,q);
      //--- function call
      COrtFac::RMatrixLQUnpackL(ft,n,m,l);
      //--- function call
      LSFitLinearInternal(b,wmod,l,n,n,info,tmp,rep);
      //--- check
      if(info<=0)
         return;
      //--- calculation
      for(i=0;i<=n-1;i++)
        {
         v=tmp[i];
         for(i_=0;i_<=m-1;i_++)
            c[i_]=c[i_]+v*q[i][i_];
        }
      //--- exit the function
      return;
     }
//--- N>=M. Generate design matrix and reduce to N=M using
//--- QR decomposition.
   ft.Resize(n,m);
   ArrayResizeAL(b,n);
   for(j=0;j<=n-1;j++)
     {
      v=w[j];
      for(i_=0;i_<=m-1;i_++)
         ft[j].Set(i_,v*fmatrix[j][i_]);
      b[j]=w[j]*y[j];
     }
//--- function call
   COrtFac::RMatrixQR(ft,n,m,tau);
//--- function call
   COrtFac::RMatrixQRUnpackQ(ft,n,m,tau,m,q);
//--- function call
   COrtFac::RMatrixQRUnpackR(ft,n,m,r);
//--- allocation
   ArrayResizeAL(tmp,m);
   for(i=0;i<=m-1;i++)
      tmp[i]=0;
//--- calculation
   for(i=0;i<=n-1;i++)
     {
      v=b[i];
      for(i_=0;i_<=m-1;i_++)
         tmp[i_]=tmp[i_]+v*q[i][i_];
     }
//--- allocation
   ArrayResizeAL(b,m);
//--- copy
   for(i_=0;i_<=m-1;i_++)
      b[i_]=tmp[i_];
//--- R contains reduced MxM design upper triangular matrix,
//--- B contains reduced Mx1 right part.
//--- Determine system condition number and decide
//--- should we use triangular solver (faster) or
//--- SVD-based solver (more stable).
//--- We can use LU-based RCond estimator for this task.
   rep.m_taskrcond=CRCond::RMatrixLURCondInf(r,m);
//--- check
   if(rep.m_taskrcond>threshold)
     {
      //--- use QR-based solver
      ArrayResizeAL(c,m);
      c[m-1]=b[m-1]/r[m-1][m-1];
      //--- calculation
      for(i=m-2;i>=0;i--)
        {
         v=0.0;
         for(i_=i+1;i_<=m-1;i_++)
            v+=r[i][i_]*c[i_];
         c[i]=(b[i]-v)/r[i][i];
        }
     }
   else
     {
      //--- use SVD-based solver
      if(!CSingValueDecompose::RMatrixSVD(r,m,m,1,1,2,sv,u,vt))
        {
         info=-4;
         return;
        }
      //--- allocation
      ArrayResizeAL(utb,m);
      ArrayResizeAL(sutb,m);
      for(i=0;i<=m-1;i++)
         utb[i]=0;
      //--- calculation
      for(i=0;i<=m-1;i++)
        {
         v=b[i];
         for(i_=0;i_<=m-1;i_++)
            utb[i_]=utb[i_]+v*u[i][i_];
        }
      //--- check
      if(sv[0]>0.0)
        {
         rep.m_taskrcond=sv[m-1]/sv[0];
         for(i=0;i<=m-1;i++)
           {
            //--- check
            if(sv[i]>threshold*sv[0])
               sutb[i]=utb[i]/sv[i];
            else
               sutb[i]=0;
           }
        }
      else
        {
         //--- change values
         rep.m_taskrcond=0;
         for(i=0;i<=m-1;i++)
            sutb[i]=0;
        }
      //--- allocation
      ArrayResizeAL(c,m);
      for(i=0;i<=m-1;i++)
         c[i]=0;
      //--- calculation
      for(i=0;i<=m-1;i++)
        {
         v=sutb[i];
         for(i_=0;i_<=m-1;i_++)
            c[i_]=c[i_]+v*vt[i][i_];
        }
     }
//--- calculate errors
   rep.m_rmserror=0;
   rep.m_avgerror=0;
   rep.m_avgrelerror=0;
   rep.m_maxerror=0;
   relcnt=0;
//--- calculation
   for(i=0;i<=n-1;i++)
     {
      v=0.0;
      for(i_=0;i_<=m-1;i_++)
         v+=fmatrix[i][i_]*c[i_];
      //--- change values
      rep.m_rmserror=rep.m_rmserror+CMath::Sqr(v-y[i]);
      rep.m_avgerror=rep.m_avgerror+MathAbs(v-y[i]);
      //--- check
      if(y[i]!=0.0)
        {
         rep.m_avgrelerror=rep.m_avgrelerror+MathAbs(v-y[i])/MathAbs(y[i]);
         relcnt=relcnt+1;
        }
      rep.m_maxerror=MathMax(rep.m_maxerror,MathAbs(v-y[i]));
     }
//--- change values
   rep.m_rmserror=MathSqrt(rep.m_rmserror/n);
   rep.m_avgerror=rep.m_avgerror/n;
//--- check
   if(relcnt!=0)
      rep.m_avgrelerror=rep.m_avgrelerror/relcnt;
  }
//+------------------------------------------------------------------+
//| Internal subroutine                                              |
//+------------------------------------------------------------------+
static void CLSFit::LSFitClearRequestFields(CLSFitState &state)
  {
//--- change values
   state.m_needf=false;
   state.m_needfg=false;
   state.m_needfgh=false;
   state.m_xupdated=false;
  }
//+------------------------------------------------------------------+
//| Internal subroutine, calculates barycentric basis functions.     |
//| Used for efficient simultaneous calculation of N basis functions.|
//+------------------------------------------------------------------+
static void CLSFit::BarycentricCalcBasis(CBarycentricInterpolant &b,
                                         const double t,double &y[])
  {
//--- create variables
   double s2=0;
   double s=0;
   double v=0;
   int    i=0;
   int    j=0;
   int    i_=0;
//--- special case: N=1
   if(b.m_n==1)
     {
      y[0]=1;
      return;
     }
//--- Here we assume that task is normalized,i.m_e.:
//--- 1. abs(Y[i])<=1
//--- 2. abs(W[i])<=1
//--- 3. X[] is ordered
//--- First,we decide: should we use "safe" formula (guarded
//--- against overflow) or fast one?
   s=MathAbs(t-b.m_x[0]);
   for(i=0;i<=b.m_n-1;i++)
     {
      v=b.m_x[i];
      //--- check
      if(v==t)
        {
         for(j=0;j<=b.m_n-1;j++)
            y[j]=0;
         y[i]=1;
         //--- exit the function
         return;
        }
      //--- change value
      v=MathAbs(t-v);
      //--- check
      if(v<s)
         s=v;
     }
   s2=0;
//--- calculation
   for(i=0;i<=b.m_n-1;i++)
     {
      v=s/(t-b.m_x[i]);
      v=v*b.m_w[i];
      y[i]=v;
      s2=s2+v;
     }
//--- change values
   v=1/s2;
   for(i_=0;i_<=b.m_n-1;i_++)
      y[i_]=v*y[i_];
  }
//+------------------------------------------------------------------+
//| This is internal function for Chebyshev fitting.                 |
//| It assumes that input data are normalized:                       |
//| * X/XC belong to [-1,+1],                                        |
//| * mean(Y)=0, stddev(Y)=1.                                        |
//| It does not checks inputs for errors.                            |
//| This function is used to fit general (shifted) Chebyshev models, | 
//| power basis models or barycentric models.                        |
//| INPUT PARAMETERS:                                                |
//|     X   -   points, array[0..N-1].                               |
//|     Y   -   function values, array[0..N-1].                      |
//|     W   -   weights, array[0..N-1]                               |
//|     N   -   number of points, N>0.                               |
//|     XC  -   points where polynomial values/derivatives are       |
//|             constrained, array[0..K-1].                          |
//|     YC  -   values of constraints, array[0..K-1]                 |
//|     DC  -   array[0..K-1], types of constraints:                 |
//|             * DC[i]=0   means that P(XC[i])=YC[i]                |
//|             * DC[i]=1   means that P'(XC[i])=YC[i]               |
//|     K   -   number of constraints, 0<=K<M.                       |
//|             K=0 means no constraints (XC/YC/DC are not used in   |
//|             such cases)                                          |
//|     M   -   number of basis functions (= polynomial_degree + 1), |
//|             M>=1                                                 |
//| OUTPUT PARAMETERS:                                               |
//|     Info-   same format as in LSFitLinearW() subroutine:         |
//|             * Info>0    task is solved                           |
//|             * Info<=0   an error occured:                        |
//|                         -4 means inconvergence of internal SVD   |
//|                         -3 means inconsistent constraints        |
//|     C   -   interpolant in Chebyshev form; [-1,+1] is used as    |
//|             base interval                                        |
//|     Rep -   report, same format as in LSFitLinearW() subroutine. |
//|             Following fields are set:                            |
//|             * RMSError      rms error on the (X,Y).              |
//|             * AvgError      average error on the (X,Y).          |
//|             * AvgRelError   average relative error on the        |
//|                             non-zero Y                           |
//|             * MaxError      maximum error                        |
//|                             NON-WEIGHTED ERRORS ARE CALCULATED   |
//| IMPORTANT:                                                       |
//|     this subroitine doesn't calculate task's condition number for|
//|     K<>0.                                                        |
//+------------------------------------------------------------------+
static void CLSFit::InternalChebyshevFit(double &x[],double &y[],double &w[],
                                         const int n,double &cxc[],double &cyc[],
                                         int &dc[],const int k,const int m,
                                         int &info,double &c[],CLSFitReport &rep)
  {
//--- create variables
   int    i=0;
   int    j=0;
   double mx=0;
   double decay=0;
   int    i_=0;
//--- create arrays
   double y2[];
   double w2[];
   double tmp[];
   double tmp2[];
   double tmpdiff[];
   double bx[];
   double by[];
   double bw[];
   double xc[];
   double yc[];
//--- create matrix
   CMatrixDouble fmatrix;
   CMatrixDouble cmatrix;
//--- copy arrays
   ArrayCopy(xc,cxc);
   ArrayCopy(yc,cyc);
//--- initialization
   info=0;
//--- weight decay for correct handling of task which becomes
//--- degenerate after constraints are applied
   decay=10000*CMath::m_machineepsilon;
//--- allocate space,initialize/fill:
//--- * FMatrix-   values of basis functions at X[]
//--- * CMatrix-   values (derivatives) of basis functions at XC[]
//--- * fill constraints matrix
//--- * fill first N rows of design matrix with values
//--- * fill next M rows of design matrix with regularizing term
//--- * append M zeros to Y
//--- * append M elements,mean(abs(W)) each,to W
   ArrayResizeAL(y2,n+m);
   ArrayResizeAL(w2,n+m);
   ArrayResizeAL(tmp,m);
   ArrayResizeAL(tmpdiff,m);
   fmatrix.Resize(n+m,m);
//--- check
   if(k>0)
      cmatrix.Resize(k,m+1);
//--- Fill design matrix,Y2,W2:
//--- * first N rows with basis functions for original points
//--- * next M rows with decay terms
   for(i=0;i<=n-1;i++)
     {
      //--- prepare Ith row
      //--- use Tmp for calculations to avoid multidimensional arrays overhead
      for(j=0;j<=m-1;j++)
        {
         //--- check
         if(j==0)
            tmp[j]=1;
         else
           {
            //--- check
            if(j==1)
               tmp[j]=x[i];
            else
               tmp[j]=2*x[i]*tmp[j-1]-tmp[j-2];
           }
        }
      //--- copy
      for(i_=0;i_<=m-1;i_++)
         fmatrix[i].Set(i_,tmp[i_]);
     }
//--- calculation
   for(i=0;i<=m-1;i++)
     {
      for(j=0;j<=m-1;j++)
        {
         //--- check
         if(i==j)
            fmatrix[n+i].Set(j,decay);
         else
            fmatrix[n+i].Set(j,0);
        }
     }
//--- copy
   for(i_=0;i_<=n-1;i_++)
      y2[i_]=y[i_];
   for(i_=0;i_<=n-1;i_++)
      w2[i_]=w[i_];
//--- change value
   mx=0;
   for(i=0;i<=n-1;i++)
      mx=mx+MathAbs(w[i]);
   mx=mx/n;
//--- change values
   for(i=0;i<=m-1;i++)
     {
      y2[n+i]=0;
      w2[n+i]=mx;
     }
//--- fill constraints matrix
   for(i=0;i<=k-1;i++)
     {
      //--- prepare Ith row
      //--- use Tmp for basis function values,
      //--- TmpDiff for basos function derivatives
      for(j=0;j<=m-1;j++)
        {
         //--- check
         if(j==0)
           {
            tmp[j]=1;
            tmpdiff[j]=0;
           }
         else
           {
            //--- check
            if(j==1)
              {
               tmp[j]=xc[i];
               tmpdiff[j]=1;
              }
            else
              {
               tmp[j]=2*xc[i]*tmp[j-1]-tmp[j-2];
               tmpdiff[j]=2*(tmp[j-1]+xc[i]*tmpdiff[j-1])-tmpdiff[j-2];
              }
           }
        }
      //--- check
      if(dc[i]==0)
        {
         for(i_=0;i_<=m-1;i_++)
            cmatrix[i].Set(i_,tmp[i_]);
        }
      //--- check
      if(dc[i]==1)
        {
         for(i_=0;i_<=m-1;i_++)
            cmatrix[i].Set(i_,tmpdiff[i_]);
        }
      cmatrix[i].Set(m,yc[i]);
     }
//--- Solve constrained task
   if(k>0)
     {
      //--- solve using regularization
      LSFitLinearWC(y2,w2,fmatrix,cmatrix,n+m,m,k,info,c,rep);
     }
   else
     {
      //--- no constraints,no regularization needed
      LSFitLinearWC(y,w,fmatrix,cmatrix,n,m,0,info,c,rep);
     }
//--- check
   if(info<0)
      return;
  }
//+------------------------------------------------------------------+
//| Internal Floater-Hormann fitting subroutine for fixed D          |
//+------------------------------------------------------------------+
static void CLSFit::BarycentricFitWCFixedD(double &cx[],double &cy[],
                                           double &cw[],const int n,
                                           double &cxc[],double &cyc[],
                                           int &dc[],const int k,const int m,
                                           const int d,int &info,
                                           CBarycentricInterpolant &b,
                                           CBarycentricFitReport &rep)
  {
//--- create variables
   double v0=0;
   double v1=0;
   double mx=0;
   int    i=0;
   int    j=0;
   int    relcnt=0;
   double xa=0;
   double xb=0;
   double sa=0;
   double sb=0;
   double decay=0;
   int    i_=0;
//--- create arrays
   double y2[];
   double w2[];
   double sx[];
   double sy[];
   double sbf[];
   double xoriginal[];
   double yoriginal[];
   double tmp[];
   double x[];
   double y[];
   double w[];
   double xc[];
   double yc[];
//--- create matrix
   CMatrixDouble fmatrix;
   CMatrixDouble cmatrix;
//--- objects of classes
   CLSFitReport            lrep;
   CBarycentricInterpolant b2;
//--- copy arrays
   ArrayCopy(x,cx);
   ArrayCopy(y,cy);
   ArrayCopy(w,cw);
   ArrayCopy(xc,cxc);
   ArrayCopy(yc,cyc);
//--- initialization
   info=0;
//--- check
   if(((n<1 || m<2) || k<0) || k>=m)
     {
      info=-1;
      return;
     }
   for(i=0;i<=k-1;i++)
     {
      info=0;
      //--- check
      if(dc[i]<0)
         info=-1;
      //--- check
      if(dc[i]>1)
         info=-1;
      //--- check
      if(info<0)
         return;
     }
//--- weight decay for correct handling of task which becomes
//--- degenerate after constraints are applied
   decay=10000*CMath::m_machineepsilon;
//--- Scale X,Y,XC,YC
   LSFitScaleXY(x,y,w,n,xc,yc,dc,k,xa,xb,sa,sb,xoriginal,yoriginal);
//--- allocate space,initialize:
//--- * FMatrix-   values of basis functions at X[]
//--- * CMatrix-   values (derivatives) of basis functions at XC[]
   ArrayResizeAL(y2,n+m);
   ArrayResizeAL(w2,n+m);
   fmatrix.Resize(n+m,m);
//--- check
   if(k>0)
      cmatrix.Resize(k,m+1);
//--- allocation
   ArrayResizeAL(y2,n+m);
   ArrayResizeAL(w2,n+m);
//--- Prepare design and constraints matrices:
//--- * fill constraints matrix
//--- * fill first N rows of design matrix with values
//--- * fill next M rows of design matrix with regularizing term
//--- * append M zeros to Y
//--- * append M elements,mean(abs(W)) each,to W
   ArrayResizeAL(sx,m);
   ArrayResizeAL(sy,m);
   ArrayResizeAL(sbf,m);
   for(j=0;j<=m-1;j++)
      sx[j]=(double)(2*j)/(double)(m-1)-1;
   for(i=0;i<=m-1;i++)
      sy[i]=1;
//--- function call
   CRatInt::BarycentricBuildFloaterHormann(sx,sy,m,d,b2);
//--- change value
   mx=0;
//--- calculation
   for(i=0;i<=n-1;i++)
     {
      //--- function call
      BarycentricCalcBasis(b2,x[i],sbf);
      for(i_=0;i_<=m-1;i_++)
         fmatrix[i].Set(i_,sbf[i_]);
      //--- change values
      y2[i]=y[i];
      w2[i]=w[i];
      mx=mx+MathAbs(w[i])/n;
     }
//--- calculation
   for(i=0;i<=m-1;i++)
     {
      for(j=0;j<=m-1;j++)
        {
         //--- check
         if(i==j)
            fmatrix[n+i].Set(j,decay);
         else
            fmatrix[n+i].Set(j,0);
        }
      //--- change values
      y2[n+i]=0;
      w2[n+i]=mx;
     }
//--- check
   if(k>0)
     {
      for(j=0;j<=m-1;j++)
        {
         for(i=0;i<=m-1;i++)
            sy[i]=0;
         sy[j]=1;
         //--- function call
         CRatInt::BarycentricBuildFloaterHormann(sx,sy,m,d,b2);
         //--- calculation
         for(i=0;i<=k-1;i++)
           {
            //--- check
            if(!CAp::Assert(dc[i]>=0 && dc[i]<=1,__FUNCTION__+": internal error!"))
               return;
            //--- function call
            CRatInt::BarycentricDiff1(b2,xc[i],v0,v1);
            //--- check
            if(dc[i]==0)
               cmatrix[i].Set(j,v0);
            //--- check
            if(dc[i]==1)
               cmatrix[i].Set(j,v1);
           }
        }
      for(i=0;i<=k-1;i++)
         cmatrix[i].Set(m,yc[i]);
     }
//--- Solve constrained task
   if(k>0)
     {
      //--- solve using regularization
      LSFitLinearWC(y2,w2,fmatrix,cmatrix,n+m,m,k,info,tmp,lrep);
     }
   else
     {
      //--- no constraints,no regularization needed
      LSFitLinearWC(y,w,fmatrix,cmatrix,n,m,k,info,tmp,lrep);
     }
//--- check
   if(info<0)
      return;
//--- Generate interpolant and scale it
   for(i_=0;i_<=m-1;i_++)
      sy[i_]=tmp[i_];
//--- function call
   CRatInt::BarycentricBuildFloaterHormann(sx,sy,m,d,b);
//--- function call
   CRatInt::BarycentricLinTransX(b,2/(xb-xa),-((xa+xb)/(xb-xa)));
//--- function call
   CRatInt::BarycentricLinTransY(b,sb-sa,sa);
//--- Scale absolute errors obtained from LSFitLinearW.
//--- Relative error should be calculated separately
//--- (because of shifting/scaling of the task)
   rep.m_taskrcond=lrep.m_taskrcond;
   rep.m_rmserror=lrep.m_rmserror*(sb-sa);
   rep.m_avgerror=lrep.m_avgerror*(sb-sa);
   rep.m_maxerror=lrep.m_maxerror*(sb-sa);
   rep.m_avgrelerror=0;
   relcnt=0;
   for(i=0;i<=n-1;i++)
     {
      //--- check
      if(yoriginal[i]!=0.0)
        {
         rep.m_avgrelerror=rep.m_avgrelerror+MathAbs(CRatInt::BarycentricCalc(b,xoriginal[i])-yoriginal[i])/MathAbs(yoriginal[i]);
         relcnt=relcnt+1;
        }
     }
//--- check
   if(relcnt!=0)
      rep.m_avgrelerror=rep.m_avgrelerror/relcnt;
  }
//+------------------------------------------------------------------+
//| NOTES:                                                           |
//| 1. this algorithm is somewhat unusual because it works with      |
//|    parameterized function f(C,X), where X is a function argument |
//|    (we have many points which are characterized by different     |
//|    argument values), and C is a parameter to fit.                |
//|    For example, if we want to do linear fit by f(c0,c1,x) =      |
//|    = c0*x+c1, then x will be argument, and {c0,c1} will be       |
//|    parameters.                                                   |
//|    It is important to understand that this algorithm finds       |
//|    minimum in the space of function PARAMETERS (not arguments),  | 
//|    so it needs derivatives of f() with respect to C, not X.      |
//|    In the example above it will need f=c0*x+c1 and               |
//|    {df/dc0,df/dc1} = {x,1} instead of {df/dx} = {c0}.            |
//| 2. Callback functions accept C as the first parameter, and X as  |
//|    the second                                                    |
//| 3. If state was created with LSFitCreateFG(), algorithm needs    |
//|    just function and its gradient, but if state was created with |
//|    LSFitCreateFGH(), algorithm will need function, gradient and  |
//|    Hessian.                                                      |
//|    According to the said above, there ase several versions of    |
//|    this function, which accept different sets of callbacks.      |
//|    This flexibility opens way to subtle errors - you may create  |
//|    state with LSFitCreateFGH() (optimization using Hessian), but |
//|    call function which does not accept Hessian. So when algorithm|
//|    will request Hessian, there will be no callback to call. In   |
//|    this case exception will be thrown.                           |
//|    Be careful to avoid such errors because there is no way to    |
//|    find them at compile time - you can see them at runtime only. |
//+------------------------------------------------------------------+
static bool CLSFit::LSFitIteration(CLSFitState &state)
  {
//--- create variables
   int    n=0;
   int    m=0;
   int    k=0;
   int    i=0;
   int    j=0;
   double v=0;
   double vv=0;
   double relcnt=0;
   int    i_=0;
//--- Reverse communication preparations
//--- I know it looks ugly,but it works the same way
//--- anywhere from C++ to Python.
//
//--- This code initializes locals by:
//--- * random values determined during code
//---   generation - on first subroutine call
//--- * values from previous call - on subsequent calls
   if(state.m_rstate.stage>=0)
     {
      //--- initialization
      n=state.m_rstate.ia[0];
      m=state.m_rstate.ia[1];
      k=state.m_rstate.ia[2];
      i=state.m_rstate.ia[3];
      j=state.m_rstate.ia[4];
      v=state.m_rstate.ra[0];
      vv=state.m_rstate.ra[1];
      relcnt=state.m_rstate.ra[2];
     }
   else
     {
      //--- initialization
      n=-983;
      m=-989;
      k=-834;
      i=900;
      j=-287;
      v=364;
      vv=214;
      relcnt=-338;
     }
//--- check
   if(state.m_rstate.stage==0)
     {
      //--- change value
      state.m_needf=false;
      //--- check
      if(state.m_wkind==1)
         vv=state.m_w[i];
      else
         vv=1.0;
      //--- change values
      state.m_optstate.m_fi[i]=vv*(state.m_f-state.m_tasky[i]);
      i=i+1;
      //--- function call, return result
      return(Func_lbl_11(state,n,m,k,i,j,v,vv,relcnt));
     }
//--- check
   if(state.m_rstate.stage==1)
     {
      //--- change value
      state.m_needf=false;
      //--- check
      if(state.m_wkind==1)
         vv=state.m_w[i];
      else
         vv=1.0;
      //--- change values
      state.m_optstate.m_f=state.m_optstate.m_f+CMath::Sqr(vv*(state.m_f-state.m_tasky[i]));
      i=i+1;
      //--- function call, return result
      return(Func_lbl_16(state,n,m,k,i,j,v,vv,relcnt));
     }
//--- check
   if(state.m_rstate.stage==2)
     {
      //--- change value
      state.m_needfg=false;
      //--- check
      if(state.m_wkind==1)
         vv=state.m_w[i];
      else
         vv=1.0;
      //--- change values
      state.m_optstate.m_f=state.m_optstate.m_f+CMath::Sqr(vv*(state.m_f-state.m_tasky[i]));
      v=CMath::Sqr(vv)*2*(state.m_f-state.m_tasky[i]);
      for(i_=0;i_<=k-1;i_++)
         state.m_optstate.m_g[i_]=state.m_optstate.m_g[i_]+v*state.m_g[i_];
      i=i+1;
      //--- function call, return result
      return(Func_lbl_21(state,n,m,k,i,j,v,vv,relcnt));
     }
//--- check
   if(state.m_rstate.stage==3)
     {
      //--- change value
      state.m_needfg=false;
      //--- check
      if(state.m_wkind==1)
         vv=state.m_w[i];
      else
         vv=1.0;
      //--- change values
      state.m_optstate.m_fi[i]=vv*(state.m_f-state.m_tasky[i]);
      for(i_=0;i_<=k-1;i_++)
         state.m_optstate.m_j[i].Set(i_,vv*state.m_g[i_]);
      i=i+1;
      //--- function call, return result
      return(Func_lbl_26(state,n,m,k,i,j,v,vv,relcnt));
     }
//--- check
   if(state.m_rstate.stage==4)
     {
      //--- change value
      state.m_needfgh=false;
      //--- check
      if(state.m_wkind==1)
         vv=state.m_w[i];
      else
         vv=1.0;
      //--- change values
      state.m_optstate.m_f=state.m_optstate.m_f+CMath::Sqr(vv*(state.m_f-state.m_tasky[i]));
      v=CMath::Sqr(vv)*2*(state.m_f-state.m_tasky[i]);
      for(i_=0;i_<=k-1;i_++)
         state.m_optstate.m_g[i_]=state.m_optstate.m_g[i_]+v*state.m_g[i_];
      //--- calculation
      for(j=0;j<=k-1;j++)
        {
         v=2*CMath::Sqr(vv)*state.m_g[j];
         for(i_=0;i_<=k-1;i_++)
            state.m_optstate.m_h[j].Set(i_,state.m_optstate.m_h[j][i_]+v*state.m_g[i_]);
         v=2*CMath::Sqr(vv)*(state.m_f-state.m_tasky[i]);
         for(i_=0;i_<=k-1;i_++)
            state.m_optstate.m_h[j].Set(i_,state.m_optstate.m_h[j][i_]+v*state.m_h[j][i_]);
        }
      i=i+1;
      //--- function call, return result
      return(Func_lbl_31(state,n,m,k,i,j,v,vv,relcnt));
     }
//--- check
   if(state.m_rstate.stage==5)
     {
      //--- change value
      state.m_xupdated=false;
      //--- function call, return result
      return(Func_lbl_7(state,n,m,k,i,j,v,vv,relcnt));
     }
//--- check
   if(state.m_rstate.stage==6)
     {
      //--- change values
      state.m_needf=false;
      v=state.m_f;
      //--- check
      if(state.m_wkind==1)
         vv=state.m_w[i];
      else
         vv=1.0;
      //--- change values
      state.m_reprmserror=state.m_reprmserror+CMath::Sqr(v-state.m_tasky[i]);
      state.m_repwrmserror=state.m_repwrmserror+CMath::Sqr(vv*(v-state.m_tasky[i]));
      state.m_repavgerror=state.m_repavgerror+MathAbs(v-state.m_tasky[i]);
      //--- check
      if(state.m_tasky[i]!=0.0)
        {
         state.m_repavgrelerror=state.m_repavgrelerror+MathAbs(v-state.m_tasky[i])/MathAbs(state.m_tasky[i]);
         relcnt=relcnt+1;
        }
      //--- change values
      state.m_repmaxerror=MathMax(state.m_repmaxerror,MathAbs(v-state.m_tasky[i]));
      i=i+1;
      //--- function call, return result
      return(Func_lbl_38(state,n,m,k,i,j,v,vv,relcnt));
     }
//--- Routine body
//--- init
   if(state.m_wkind==1)
     {
      //--- check
      if(!CAp::Assert(state.m_npoints==state.m_nweights,__FUNCTION__+": number of points is not equal to the number of weights"))
         return(false);
     }
//--- change values
   n=state.m_npoints;
   m=state.m_m;
   k=state.m_k;
//--- function call
   CMinLM::MinLMSetCond(state.m_optstate,0.0,state.m_epsf,state.m_epsx,state.m_maxits);
//--- function call
   CMinLM::MinLMSetStpMax(state.m_optstate,state.m_stpmax);
//--- function call
   CMinLM::MinLMSetXRep(state.m_optstate,state.m_xrep);
//--- function call
   CMinLM::MinLMSetScale(state.m_optstate,state.m_s);
//--- function call
   CMinLM::MinLMSetBC(state.m_optstate,state.m_bndl,state.m_bndu);
//--- Optimize
   return(Func_lbl_7(state,n,m,k,i,j,v,vv,relcnt));
  }
//+------------------------------------------------------------------+
//| Auxiliary function for LSFitIteration. Is a product to get rid of|
//| the operator unconditional jump goto.                            |
//+------------------------------------------------------------------+
static void CLSFit::Func_lbl_rcomm(CLSFitState &state,int n,int m,int k,int i,
                                   int j,double v,double vv,double relcnt)
  {
//--- Saving state
   state.m_rstate.ia[0]=n;
   state.m_rstate.ia[1]=m;
   state.m_rstate.ia[2]=k;
   state.m_rstate.ia[3]=i;
   state.m_rstate.ia[4]=j;
   state.m_rstate.ra[0]=v;
   state.m_rstate.ra[1]=vv;
   state.m_rstate.ra[2]=relcnt;
  }
//+------------------------------------------------------------------+
//| Auxiliary function for LSFitIteration. Is a product to get rid of|
//| the operator unconditional jump goto.                            |
//+------------------------------------------------------------------+
static bool CLSFit::Func_lbl_7(CLSFitState &state,int &n,int &m,int &k,int &i,
                               int &j,double &v,double &vv,double &relcnt)
  {
//--- check
   if(!CMinLM::MinLMIteration(state.m_optstate))
     {
      //--- function call
      CMinLM::MinLMResults(state.m_optstate,state.m_c,state.m_optrep);
      //--- change values
      state.m_repterminationtype=state.m_optrep.m_terminationtype;
      state.m_repiterationscount=state.m_optrep.m_iterationscount;
      //--- calculate errors
      if(state.m_repterminationtype<=0)
         return(false);
      //--- change values
      state.m_reprmserror=0;
      state.m_repwrmserror=0;
      state.m_repavgerror=0;
      state.m_repavgrelerror=0;
      state.m_repmaxerror=0;
      relcnt=0;
      i=0;
      //--- function call, return result
      return(Func_lbl_38(state,n,m,k,i,j,v,vv,relcnt));
     }
//--- check
   if(!state.m_optstate.m_needfi)
     {
      //--- check
      if(!state.m_optstate.m_needf)
         return(Func_lbl_14(state,n,m,k,i,j,v,vv,relcnt));
      //--- calculate F=sum (wi*(f(xi,c)-yi))^2
      state.m_optstate.m_f=0;
      i=0;
      //--- function call, return result
      return(Func_lbl_16(state,n,m,k,i,j,v,vv,relcnt));
     }
//--- calculate f[]=wi*(f(xi,c)-yi)
   i=0;
//--- function call, return result
   return(Func_lbl_11(state,n,m,k,i,j,v,vv,relcnt));
  }
//+------------------------------------------------------------------+
//| Auxiliary function for LSFitIteration. Is a product to get rid of|
//| the operator unconditional jump goto.                            |
//+------------------------------------------------------------------+
static bool CLSFit::Func_lbl_11(CLSFitState &state,int &n,int &m,int &k,int &i,
                                int &j,double &v,double &vv,double &relcnt)
  {
//--- check
   if(i>n-1)
      return(Func_lbl_7(state,n,m,k,i,j,v,vv,relcnt));
//--- copy
   for(int i_=0;i_<=k-1;i_++)
      state.m_c[i_]=state.m_optstate.m_x[i_];
   for(int i_=0;i_<=m-1;i_++)
      state.m_x[i_]=state.m_taskx[i][i_];
   state.m_pointindex=i;
//--- function call
   LSFitClearRequestFields(state);
//--- change values
   state.m_needf=true;
   state.m_rstate.stage=0;
//--- Saving state
   Func_lbl_rcomm(state,n,m,k,i,j,v,vv,relcnt);
//--- return result
   return(true);
  }
//+------------------------------------------------------------------+
//| Auxiliary function for LSFitIteration. Is a product to get rid of|
//| the operator unconditional jump goto.                            |
//+------------------------------------------------------------------+
static bool CLSFit::Func_lbl_14(CLSFitState &state,int &n,int &m,int &k,int &i,
                                int &j,double &v,double &vv,double &relcnt)
  {
//--- check
   if(!state.m_optstate.m_needfg)
     {
      //--- check
      if(!state.m_optstate.m_needfij)
         return(Func_lbl_24(state,n,m,k,i,j,v,vv,relcnt));
      //--- calculate Fi/jac(Fi)
      i=0;
      //--- function call, return result
      return(Func_lbl_26(state,n,m,k,i,j,v,vv,relcnt));
     }
//--- calculate F/gradF
   state.m_optstate.m_f=0;
   for(i=0;i<=k-1;i++)
      state.m_optstate.m_g[i]=0;
   i=0;
//--- function call, return result
   return(Func_lbl_21(state,n,m,k,i,j,v,vv,relcnt));
  }
//+------------------------------------------------------------------+
//| Auxiliary function for LSFitIteration. Is a product to get rid of|
//| the operator unconditional jump goto.                            |
//+------------------------------------------------------------------+
static bool CLSFit::Func_lbl_16(CLSFitState &state,int &n,int &m,int &k,int &i,
                                int &j,double &v,double &vv,double &relcnt)
  {
//--- check
   if(i>n-1)
      return(Func_lbl_7(state,n,m,k,i,j,v,vv,relcnt));
//--- copy
   for(int i_=0;i_<=k-1;i_++)
      state.m_c[i_]=state.m_optstate.m_x[i_];
   for(int i_=0;i_<=m-1;i_++)
      state.m_x[i_]=state.m_taskx[i][i_];
   state.m_pointindex=i;
//--- function call
   LSFitClearRequestFields(state);
//--- change values
   state.m_needf=true;
   state.m_rstate.stage=1;
//--- Saving state
   Func_lbl_rcomm(state,n,m,k,i,j,v,vv,relcnt);
//--- return result
   return(true);
  }
//+------------------------------------------------------------------+
//| Auxiliary function for LSFitIteration. Is a product to get rid of|
//| the operator unconditional jump goto.                            |
//+------------------------------------------------------------------+
static bool CLSFit::Func_lbl_21(CLSFitState &state,int &n,int &m,int &k,int &i,
                                int &j,double &v,double &vv,double &relcnt)
  {
//--- check
   if(i>n-1)
      return(Func_lbl_7(state,n,m,k,i,j,v,vv,relcnt));
//--- copy
   for(int i_=0;i_<=k-1;i_++)
      state.m_c[i_]=state.m_optstate.m_x[i_];
   for(int i_=0;i_<=m-1;i_++)
      state.m_x[i_]=state.m_taskx[i][i_];
   state.m_pointindex=i;
//--- function call
   LSFitClearRequestFields(state);
//--- change values
   state.m_needfg=true;
   state.m_rstate.stage=2;
//--- Saving state
   Func_lbl_rcomm(state,n,m,k,i,j,v,vv,relcnt);
//--- return result
   return(true);
  }
//+------------------------------------------------------------------+
//| Auxiliary function for LSFitIteration. Is a product to get rid of|
//| the operator unconditional jump goto.                            |
//+------------------------------------------------------------------+
static bool CLSFit::Func_lbl_24(CLSFitState &state,int &n,int &m,int &k,int &i,
                                int &j,double &v,double &vv,double &relcnt)
  {
//--- check
   if(!state.m_optstate.m_needfgh)
      return(Func_lbl_29(state,n,m,k,i,j,v,vv,relcnt));
//--- calculate F/grad(F)/hess(F)
   state.m_optstate.m_f=0;
   for(i=0;i<=k-1;i++)
      state.m_optstate.m_g[i]=0;
   for(i=0;i<=k-1;i++)
     {
      for(j=0;j<=k-1;j++)
         state.m_optstate.m_h[i].Set(j,0);
     }
//--- change value
   i=0;
//--- function call, return result
   return(Func_lbl_31(state,n,m,k,i,j,v,vv,relcnt));
  }
//+------------------------------------------------------------------+
//| Auxiliary function for LSFitIteration. Is a product to get rid of|
//| the operator unconditional jump goto.                            |
//+------------------------------------------------------------------+
static bool CLSFit::Func_lbl_26(CLSFitState &state,int &n,int &m,int &k,int &i,
                                int &j,double &v,double &vv,double &relcnt)
  {
//--- check
   if(i>n-1)
      return(Func_lbl_7(state,n,m,k,i,j,v,vv,relcnt));
//--- copy
   for(int i_=0;i_<=k-1;i_++)
      state.m_c[i_]=state.m_optstate.m_x[i_];
   for(int i_=0;i_<=m-1;i_++)
      state.m_x[i_]=state.m_taskx[i][i_];
   state.m_pointindex=i;
//--- function call
   LSFitClearRequestFields(state);
//--- change values
   state.m_needfg=true;
   state.m_rstate.stage=3;
//--- Saving state
   Func_lbl_rcomm(state,n,m,k,i,j,v,vv,relcnt);
//--- return result
   return(true);
  }
//+------------------------------------------------------------------+
//| Auxiliary function for LSFitIteration. Is a product to get rid of|
//| the operator unconditional jump goto.                            |
//+------------------------------------------------------------------+
static bool CLSFit::Func_lbl_29(CLSFitState &state,int &n,int &m,int &k,int &i,
                                int &j,double &v,double &vv,double &relcnt)
  {
//--- check
   if(!state.m_optstate.m_xupdated)
      return(Func_lbl_7(state,n,m,k,i,j,v,vv,relcnt));
//--- Report new iteration
   for(int i_=0;i_<=k-1;i_++)
      state.m_c[i_]=state.m_optstate.m_x[i_];
   state.m_f=state.m_optstate.m_f;
//--- function call
   LSFitClearRequestFields(state);
//--- change values
   state.m_xupdated=true;
   state.m_rstate.stage=5;
//--- Saving state
   Func_lbl_rcomm(state,n,m,k,i,j,v,vv,relcnt);
//--- return result
   return(true);
  }
//+------------------------------------------------------------------+
//| Auxiliary function for LSFitIteration. Is a product to get rid of|
//| the operator unconditional jump goto.                            |
//+------------------------------------------------------------------+
static bool CLSFit::Func_lbl_31(CLSFitState &state,int &n,int &m,int &k,int &i,
                                int &j,double &v,double &vv,double &relcnt)
  {
//--- check
   if(i>n-1)
      return(Func_lbl_7(state,n,m,k,i,j,v,vv,relcnt));
//--- copy
   for(int i_=0;i_<=k-1;i_++)
      state.m_c[i_]=state.m_optstate.m_x[i_];
   for(int i_=0;i_<=m-1;i_++)
      state.m_x[i_]=state.m_taskx[i][i_];
   state.m_pointindex=i;
//--- function call
   LSFitClearRequestFields(state);
//--- change values
   state.m_needfgh=true;
   state.m_rstate.stage=4;
//--- Saving state
   Func_lbl_rcomm(state,n,m,k,i,j,v,vv,relcnt);
//--- return result
   return(true);
  }
//+------------------------------------------------------------------+
//| Auxiliary function for LSFitIteration. Is a product to get rid of|
//| the operator unconditional jump goto.                            |
//+------------------------------------------------------------------+
static bool CLSFit::Func_lbl_38(CLSFitState &state,int &n,int &m,int &k,int &i,
                                int &j,double &v,double &vv,double &relcnt)
  {
//--- check
   if(i>n-1)
     {
      //--- change values
      state.m_reprmserror=MathSqrt(state.m_reprmserror/n);
      state.m_repwrmserror=MathSqrt(state.m_repwrmserror/n);
      state.m_repavgerror=state.m_repavgerror/n;
      //--- check
      if(relcnt!=0.0)
         state.m_repavgrelerror=state.m_repavgrelerror/relcnt;
      //--- return result
      return(false);
     }
//--- copy
   for(int i_=0;i_<=k-1;i_++)
      state.m_c[i_]=state.m_c[i_];
   for(int i_=0;i_<=m-1;i_++)
      state.m_x[i_]=state.m_taskx[i][i_];
   state.m_pointindex=i;
//--- function call
   LSFitClearRequestFields(state);
//--- change values
   state.m_needf=true;
   state.m_rstate.stage=6;
//--- Saving state
   Func_lbl_rcomm(state,n,m,k,i,j,v,vv,relcnt);
//--- return result
   return(true);
  }
//+------------------------------------------------------------------+
//| Parametric spline inteprolant: 2-dimensional curve.              |
//| You should not try to access its members directly - use          |
//| PSpline2XXXXXXXX() functions instead.                            |
//+------------------------------------------------------------------+
class CPSpline2Interpolant
  {
public:
   //--- variables
   int               m_n;
   bool              m_periodic;
   CSpline1DInterpolant m_x;
   CSpline1DInterpolant m_y;
   //--- array
   double            m_p[];
   //--- constructor, destructor
                     CPSpline2Interpolant(void);
                    ~CPSpline2Interpolant(void);
   //--- copy
   void              Copy(CPSpline2Interpolant &obj);
  };
//+------------------------------------------------------------------+
//| Constructor without parameters                                   |
//+------------------------------------------------------------------+
CPSpline2Interpolant::CPSpline2Interpolant(void)
  {

  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CPSpline2Interpolant::~CPSpline2Interpolant(void)
  {

  }
//+------------------------------------------------------------------+
//| Copy                                                             |
//+------------------------------------------------------------------+
void CPSpline2Interpolant::Copy(CPSpline2Interpolant &obj)
  {
//--- copy variables
   m_n=obj.m_n;
   m_periodic=obj.m_periodic;
   m_x.Copy(obj.m_x);
   m_y.Copy(obj.m_y);
//--- copy array
   ArrayCopy(m_p,obj.m_p);
  }
//+------------------------------------------------------------------+
//| Parametric spline inteprolant: 2-dimensional curve.              |
//| You should not try to access its members directly - use          |
//| PSpline2XXXXXXXX() functions instead.                            |
//+------------------------------------------------------------------+
class CPSpline2InterpolantShell
  {
private:
   CPSpline2Interpolant m_innerobj;
public:
   //--- constructors, destructor
                     CPSpline2InterpolantShell(void);
                     CPSpline2InterpolantShell(CPSpline2Interpolant &obj);
                    ~CPSpline2InterpolantShell(void);
   //--- method
   CPSpline2Interpolant *GetInnerObj(void);
  };
//+------------------------------------------------------------------+
//| Constructor without parameters                                   |
//+------------------------------------------------------------------+
CPSpline2InterpolantShell::CPSpline2InterpolantShell(void)
  {

  }
//+------------------------------------------------------------------+
//| Copy constructor                                                 |
//+------------------------------------------------------------------+
CPSpline2InterpolantShell::CPSpline2InterpolantShell(CPSpline2Interpolant &obj)
  {
//--- copy
   m_innerobj.Copy(obj);
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CPSpline2InterpolantShell::~CPSpline2InterpolantShell(void)
  {

  }
//+------------------------------------------------------------------+
//| Return object of class                                           |
//+------------------------------------------------------------------+
CPSpline2Interpolant *CPSpline2InterpolantShell::GetInnerObj(void)
  {
//--- return result
   return(GetPointer(m_innerobj));
  }
//+------------------------------------------------------------------+
//| Parametric spline inteprolant: 3-dimensional curve.              |
//| You should not try to access its members directly - use          |
//| PSpline3XXXXXXXX() functions instead.                            |
//+------------------------------------------------------------------+
class CPSpline3Interpolant
  {
public:
   //--- variables
   int               m_n;
   bool              m_periodic;
   CSpline1DInterpolant m_x;
   CSpline1DInterpolant m_y;
   CSpline1DInterpolant m_z;
   //--- array
   double            m_p[];
   //--- constructor, destructor
                     CPSpline3Interpolant(void);
                    ~CPSpline3Interpolant(void);
   //--- copy
   void              Copy(CPSpline3Interpolant &obj);
  };
//+------------------------------------------------------------------+
//| Constructor without parameters                                   |
//+------------------------------------------------------------------+
CPSpline3Interpolant::CPSpline3Interpolant(void)
  {

  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CPSpline3Interpolant::~CPSpline3Interpolant(void)
  {

  }
//+------------------------------------------------------------------+
//| Copy                                                             |
//+------------------------------------------------------------------+
void CPSpline3Interpolant::Copy(CPSpline3Interpolant &obj)
  {
//--- copy variables
   m_n=obj.m_n;
   m_periodic=obj.m_periodic;
   m_x.Copy(obj.m_x);
   m_y.Copy(obj.m_y);
   m_z.Copy(obj.m_z);
//--- copy array
   ArrayCopy(m_p,obj.m_p);
  }
//+------------------------------------------------------------------+
//| Parametric spline inteprolant: 3-dimensional curve.              |
//| You should not try to access its members directly - use          |
//| PSpline3XXXXXXXX() functions instead.                            |
//+------------------------------------------------------------------+
class CPSpline3InterpolantShell
  {
private:
   CPSpline3Interpolant m_innerobj;
public:
   //--- constructors, destructor
                     CPSpline3InterpolantShell(void);
                     CPSpline3InterpolantShell(CPSpline3Interpolant &obj);
                    ~CPSpline3InterpolantShell(void);
   //--- method
   CPSpline3Interpolant *GetInnerObj(void);
  };
//+------------------------------------------------------------------+
//| Constructor without parameters                                   |
//+------------------------------------------------------------------+
CPSpline3InterpolantShell::CPSpline3InterpolantShell(void)
  {

  }
//+------------------------------------------------------------------+
//| Copy constructor                                                 |
//+------------------------------------------------------------------+
CPSpline3InterpolantShell::CPSpline3InterpolantShell(CPSpline3Interpolant &obj)
  {
//--- copy
   m_innerobj.Copy(obj);
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CPSpline3InterpolantShell::~CPSpline3InterpolantShell(void)
  {

  }
//+------------------------------------------------------------------+
//| Return object of class                                           |
//+------------------------------------------------------------------+
CPSpline3Interpolant *CPSpline3InterpolantShell::GetInnerObj(void)
  {
//--- return result
   return(GetPointer(m_innerobj));
  }
//+------------------------------------------------------------------+
//| Parametric spline                                                |
//+------------------------------------------------------------------+
class CPSpline
  {
private:
   //--- private methods
   static void       PSpline2Par(CMatrixDouble &xy,const int n,const int pt,double &p[]);
   static void       PSpline3Par(CMatrixDouble &xy,const int n,const int pt,double &p[]);
public:
   //--- constructor, destructor
                     CPSpline(void);
                    ~CPSpline(void);
   //--- public methods
   static void       PSpline2Build(CMatrixDouble &cxy,const int n,const int st,const int pt,CPSpline2Interpolant &p);
   static void       PSpline3Build(CMatrixDouble &cxy,const int n,const int st,const int pt,CPSpline3Interpolant &p);
   static void       PSpline2BuildPeriodic(CMatrixDouble &cxy,const int n,const int st,const int pt,CPSpline2Interpolant &p);
   static void       PSpline3BuildPeriodic(CMatrixDouble &cxy,const int n,const int st,const int pt,CPSpline3Interpolant &p);
   static void       PSpline2ParameterValues(CPSpline2Interpolant &p,int &n,double &t[]);
   static void       PSpline3ParameterValues(CPSpline3Interpolant &p,int &n,double &t[]);
   static void       PSpline2Calc(CPSpline2Interpolant &p,double t,double &x,double &y);
   static void       PSpline3Calc(CPSpline3Interpolant &p,double t,double &x,double &y,double &z);
   static void       PSpline2Tangent(CPSpline2Interpolant &p,double t,double &x,double &y);
   static void       PSpline3Tangent(CPSpline3Interpolant &p,double t,double &x,double &y,double &z);
   static void       PSpline2Diff(CPSpline2Interpolant &p,double t,double &x,double &dx,double &y,double &dy);
   static void       PSpline3Diff(CPSpline3Interpolant &p,double t,double &x,double &dx,double &y,double &dy,double &z,double &dz);
   static void       PSpline2Diff2(CPSpline2Interpolant &p,double t,double &x,double &dx,double &d2x,double &y,double &dy,double &d2y);
   static void       PSpline3Diff2(CPSpline3Interpolant &p,double t,double &x,double &dx,double &d2x,double &y,double &dy,double &d2y,double &z,double &dz,double &d2z);
   static double     PSpline2ArcLength(CPSpline2Interpolant &p,const double a,const double b);
   static double     PSpline3ArcLength(CPSpline3Interpolant &p,const double a,const double b);
  };
//+------------------------------------------------------------------+
//| Constructor without parameters                                   |
//+------------------------------------------------------------------+
CPSpline::CPSpline(void)
  {

  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CPSpline::~CPSpline(void)
  {

  }
//+------------------------------------------------------------------+
//| This function builds non-periodic 2-dimensional parametric       |
//| spline which starts at (X[0],Y[0]) and ends at (X[N-1],Y[N-1]).  |
//| INPUT PARAMETERS:                                                |
//|     XY  -   points, array[0..N-1,0..1].                          |
//|             XY[I,0:1] corresponds to the Ith point.              |
//|             Order of points is important!                        |
//|     N   -   points count, N>=5 for Akima splines, N>=2 for other |
//|             types of splines.                                    |
//|     ST  -   spline type:                                         |
//|             * 0     Akima spline                                 |
//|             * 1     parabolically terminated Catmull-Rom spline  |
//|                     (Tension=0)                                  |
//|             * 2     parabolically terminated cubic spline        |
//|     PT  -   parameterization type:                               |
//|             * 0     uniform                                      |
//|             * 1     chord length                                 |
//|             * 2     centripetal                                  |
//| OUTPUT PARAMETERS:                                               |
//|     P   -   parametric spline interpolant                        |
//| NOTES:                                                           |
//| * this function assumes that there all consequent points are     |
//|   distinct. I.e. (x0,y0)<>(x1,y1), (x1,y1)<>(x2,y2),             |
//|   (x2,y2)<>(x3,y3) and so on. However, non-consequent points may |
//|   coincide, i.e. we can have (x0,y0) = (x2,y2).                  |
//+------------------------------------------------------------------+
static void CPSpline::PSpline2Build(CMatrixDouble &cxy,const int n,
                                    const int st,const int pt,
                                    CPSpline2Interpolant &p)
  {
//--- create a variable
   int i_=0;
//--- create array
   double tmp[];
//--- copy matrix
   CMatrixDouble xy;
   xy=cxy;
//--- check
   if(!CAp::Assert(st>=0 && st<=2,__FUNCTION__+": incorrect spline type!"))
      return;
//--- check
   if(!CAp::Assert(pt>=0 && pt<=2,__FUNCTION__+": incorrect parameterization type!"))
      return;
//--- check
   if(st==0)
     {
      //--- check
      if(!CAp::Assert(n>=5,__FUNCTION__+": N<5 (minimum value for Akima splines)!"))
         return;
     }
   else
     {
      //--- check
      if(!CAp::Assert(n>=2,__FUNCTION__+": N<2!"))
         return;
     }
//--- Prepare
   p.m_n=n;
   p.m_periodic=false;
//--- allocation
   ArrayResizeAL(tmp,n);
//--- Build parameterization,check that all parameters are distinct
   PSpline2Par(xy,n,pt,p.m_p);
//--- check
   if(!CAp::Assert(CApServ::AreDistinct(p.m_p,n),__FUNCTION__+": consequent points are too close!"))
      return;
//--- Build splines
   if(st==0)
     {
      //--- copy
      for(i_=0;i_<=n-1;i_++)
         tmp[i_]=xy[i_][0];
      //--- function call
      CSpline1D::Spline1DBuildAkima(p.m_p,tmp,n,p.m_x);
      //--- copy
      for(i_=0;i_<=n-1;i_++)
         tmp[i_]=xy[i_][1];
      //--- function call
      CSpline1D::Spline1DBuildAkima(p.m_p,tmp,n,p.m_y);
     }
//--- check
   if(st==1)
     {
      //--- copy
      for(i_=0;i_<=n-1;i_++)
         tmp[i_]=xy[i_][0];
      //--- function call
      CSpline1D::Spline1DBuildCatmullRom(p.m_p,tmp,n,0,0.0,p.m_x);
      //--- copy
      for(i_=0;i_<=n-1;i_++)
         tmp[i_]=xy[i_][1];
      //--- function call
      CSpline1D::Spline1DBuildCatmullRom(p.m_p,tmp,n,0,0.0,p.m_y);
     }
//--- check
   if(st==2)
     {
      //--- copy
      for(i_=0;i_<=n-1;i_++)
         tmp[i_]=xy[i_][0];
      //--- function call
      CSpline1D::Spline1DBuildCubic(p.m_p,tmp,n,0,0.0,0,0.0,p.m_x);
      //--- copy
      for(i_=0;i_<=n-1;i_++)
         tmp[i_]=xy[i_][1];
      //--- function call
      CSpline1D::Spline1DBuildCubic(p.m_p,tmp,n,0,0.0,0,0.0,p.m_y);
     }
  }
//+------------------------------------------------------------------+
//| This function builds non-periodic 3-dimensional parametric spline|
//| which starts at (X[0],Y[0],Z[0]) and ends at                     |
//| (X[N-1],Y[N-1],Z[N-1]).                                          |
//| Same as PSpline2Build() function, but for 3D, so we won't        |
//| duplicate its description here.                                  |
//+------------------------------------------------------------------+
static void CPSpline::PSpline3Build(CMatrixDouble &cxy,const int n,
                                    const int st,const int pt,
                                    CPSpline3Interpolant &p)
  {
//--- create a variable
   int i_=0;
//--- create array
   double tmp[];
//--- copy matrix
   CMatrixDouble xy;
   xy=cxy;
//--- check
   if(!CAp::Assert(st>=0 && st<=2,__FUNCTION__+": incorrect spline type!"))
      return;
//--- check
   if(!CAp::Assert(pt>=0 && pt<=2,__FUNCTION__+": incorrect parameterization type!"))
      return;
//--- check
   if(st==0)
     {
      //--- check
      if(!CAp::Assert(n>=5,__FUNCTION__+": N<5 (minimum value for Akima splines)!"))
         return;
     }
   else
     {
      //--- check
      if(!CAp::Assert(n>=2,__FUNCTION__+"PSpline3Build: N<2!"))
         return;
     }
//--- Prepare
   p.m_n=n;
   p.m_periodic=false;
//--- allocation
   ArrayResizeAL(tmp,n);
//--- Build parameterization,check that all parameters are distinct
   PSpline3Par(xy,n,pt,p.m_p);
//--- check
   if(!CAp::Assert(CApServ::AreDistinct(p.m_p,n),__FUNCTION__+": consequent points are too close!"))
      return;
//--- Build splines
   if(st==0)
     {
      //--- copy
      for(i_=0;i_<=n-1;i_++)
         tmp[i_]=xy[i_][0];
      //--- function call
      CSpline1D::Spline1DBuildAkima(p.m_p,tmp,n,p.m_x);
      //--- copy
      for(i_=0;i_<=n-1;i_++)
         tmp[i_]=xy[i_][1];
      //--- function call
      CSpline1D::Spline1DBuildAkima(p.m_p,tmp,n,p.m_y);
      //--- copy
      for(i_=0;i_<=n-1;i_++)
         tmp[i_]=xy[i_][2];
      //--- function call
      CSpline1D::Spline1DBuildAkima(p.m_p,tmp,n,p.m_z);
     }
//--- check
   if(st==1)
     {
      //--- copy
      for(i_=0;i_<=n-1;i_++)
         tmp[i_]=xy[i_][0];
      //--- function call
      CSpline1D::Spline1DBuildCatmullRom(p.m_p,tmp,n,0,0.0,p.m_x);
      //--- copy
      for(i_=0;i_<=n-1;i_++)
         tmp[i_]=xy[i_][1];
      //--- function call
      CSpline1D::Spline1DBuildCatmullRom(p.m_p,tmp,n,0,0.0,p.m_y);
      //--- copy
      for(i_=0;i_<=n-1;i_++)
         tmp[i_]=xy[i_][2];
      //--- function call
      CSpline1D::Spline1DBuildCatmullRom(p.m_p,tmp,n,0,0.0,p.m_z);
     }
//--- check
   if(st==2)
     {
      //--- copy
      for(i_=0;i_<=n-1;i_++)
         tmp[i_]=xy[i_][0];
      //--- function call
      CSpline1D::Spline1DBuildCubic(p.m_p,tmp,n,0,0.0,0,0.0,p.m_x);
      //--- copy
      for(i_=0;i_<=n-1;i_++)
         tmp[i_]=xy[i_][1];
      //--- function call
      CSpline1D::Spline1DBuildCubic(p.m_p,tmp,n,0,0.0,0,0.0,p.m_y);
      //--- copy
      for(i_=0;i_<=n-1;i_++)
         tmp[i_]=xy[i_][2];
      //--- function call
      CSpline1D::Spline1DBuildCubic(p.m_p,tmp,n,0,0.0,0,0.0,p.m_z);
     }
  }
//+------------------------------------------------------------------+
//| This function builds periodic 2-dimensional parametric spline    |
//| which starts at (X[0],Y[0]), goes through all points to          |
//| (X[N-1],Y[N-1]) and then back to (X[0],Y[0]).                    |
//| INPUT PARAMETERS:                                                |
//|     XY  -   points, array[0..N-1,0..1].                          |
//|             XY[I,0:1] corresponds to the Ith point.              |
//|             XY[N-1,0:1] must be different from XY[0,0:1].        |
//|             Order of points is important!                        |
//|     N   -   points count, N>=3 for other types of splines.       |
//|     ST  -   spline type:                                         |
//|             * 1     Catmull-Rom spline (Tension=0) with cyclic   |
//|                     boundary conditions                          |
//|             * 2     cubic spline with cyclic boundary conditions |
//|     PT  -   parameterization type:                               |
//|             * 0     uniform                                      |
//|             * 1     chord length                                 |
//|             * 2     centripetal                                  |
//| OUTPUT PARAMETERS:                                               |
//|     P   -   parametric spline interpolant                        |
//| NOTES:                                                           |
//| * this function assumes that there all consequent points are     |
//|   distinct. I.e. (x0,y0)<>(x1,y1), (x1,y1)<>(x2,y2),             |
//|   (x2,y2)<>(x3,y3) and so on. However, non-consequent points may |
//|   coincide, i.e. we can  have (x0,y0) = (x2,y2).                 |
//| * last point of sequence is NOT equal to the first point. You    |
//|   shouldn't make curve "explicitly periodic" by making them      |
//|   equal.                                                         |
//+------------------------------------------------------------------+
static void CPSpline::PSpline2BuildPeriodic(CMatrixDouble &cxy,const int n,
                                            const int st,const int pt,
                                            CPSpline2Interpolant &p)
  {
//--- create a variable
   int i_=0;
//--- create array
   double tmp[];
//--- create matrix
   CMatrixDouble xyp;
   CMatrixDouble xy;
//--- copy matrix
   xy=cxy;
//--- check
   if(!CAp::Assert(st>=1 && st<=2,__FUNCTION__+": incorrect spline type!"))
      return;
//--- check
   if(!CAp::Assert(pt>=0 && pt<=2,__FUNCTION__+": incorrect parameterization type!"))
      return;
//--- check
   if(!CAp::Assert(n>=3,__FUNCTION__+": N<3!"))
      return;
//--- Prepare
   p.m_n=n;
   p.m_periodic=true;
//--- allocation
   ArrayResizeAL(tmp,n+1);
   xyp.Resize(n+1,2);
//--- change values
   for(i_=0;i_<=n-1;i_++)
      xyp[i_].Set(0,xy[i_][0]);
   for(i_=0;i_<=n-1;i_++)
      xyp[i_].Set(1,xy[i_][1]);
   for(i_=0;i_<=1;i_++)
      xyp[n].Set(i_,xy[0][i_]);
//--- Build parameterization,check that all parameters are distinct
   PSpline2Par(xyp,n+1,pt,p.m_p);
//--- check
   if(!CAp::Assert(CApServ::AreDistinct(p.m_p,n+1),__FUNCTION__+": consequent (or first and last) points are too close!"))
      return;
//--- Build splines
   if(st==1)
     {
      //--- copy
      for(i_=0;i_<=n;i_++)
         tmp[i_]=xyp[i_][0];
      //--- function call
      CSpline1D::Spline1DBuildCatmullRom(p.m_p,tmp,n+1,-1,0.0,p.m_x);
      //--- copy
      for(i_=0;i_<=n;i_++)
         tmp[i_]=xyp[i_][1];
      //--- function call
      CSpline1D::Spline1DBuildCatmullRom(p.m_p,tmp,n+1,-1,0.0,p.m_y);
     }
//--- check
   if(st==2)
     {
      //--- copy
      for(i_=0;i_<=n;i_++)
         tmp[i_]=xyp[i_][0];
      //--- function call
      CSpline1D::Spline1DBuildCubic(p.m_p,tmp,n+1,-1,0.0,-1,0.0,p.m_x);
      //--- copy
      for(i_=0;i_<=n;i_++)
         tmp[i_]=xyp[i_][1];
      //--- function call
      CSpline1D::Spline1DBuildCubic(p.m_p,tmp,n+1,-1,0.0,-1,0.0,p.m_y);
     }
  }
//+------------------------------------------------------------------+
//| This function builds periodic 3-dimensional parametric spline    |
//| which starts at (X[0],Y[0],Z[0]), goes through all points to     |
//| (X[N-1],Y[N-1],Z[N-1]) and then back to (X[0],Y[0],Z[0]).        |
//| Same as PSpline2Build() function, but for 3D, so we won't        |
//| duplicate its description here.                                  |
//+------------------------------------------------------------------+
static void CPSpline::PSpline3BuildPeriodic(CMatrixDouble &cxy,const int n,
                                            const int st,const int pt,
                                            CPSpline3Interpolant &p)
  {
//--- create a variable
   int i_=0;
//--- create array
   double tmp[];
//--- create matrix
   CMatrixDouble xyp;
   CMatrixDouble xy;
//--- copy matrix
   xy=cxy;
//--- check
   if(!CAp::Assert(st>=1 && st<=2,__FUNCTION__+": incorrect spline type!"))
      return;
//--- check
   if(!CAp::Assert(pt>=0 && pt<=2,__FUNCTION__+": incorrect parameterization type!"))
      return;
//--- check
   if(!CAp::Assert(n>=3,__FUNCTION__+": N<3!"))
      return;
//--- Prepare
   p.m_n=n;
   p.m_periodic=true;
//--- allocation
   ArrayResizeAL(tmp,n+1);
   xyp.Resize(n+1,3);
//--- change values
   for(i_=0;i_<=n-1;i_++)
      xyp[i_].Set(0,xy[i_][0]);
   for(i_=0;i_<=n-1;i_++)
      xyp[i_].Set(1,xy[i_][1]);
   for(i_=0;i_<=n-1;i_++)
      xyp[i_].Set(2,xy[i_][2]);
   for(i_=0;i_<=2;i_++)
      xyp[n].Set(i_,xy[0][i_]);
//--- Build parameterization,check that all parameters are distinct
   PSpline3Par(xyp,n+1,pt,p.m_p);
//--- check
   if(!CAp::Assert(CApServ::AreDistinct(p.m_p,n+1),__FUNCTION__+": consequent (or first and last) points are too close!"))
      return;
//--- Build splines
   if(st==1)
     {
      //--- copy
      for(i_=0;i_<=n;i_++)
         tmp[i_]=xyp[i_][0];
      //--- function call
      CSpline1D::Spline1DBuildCatmullRom(p.m_p,tmp,n+1,-1,0.0,p.m_x);
      //--- copy
      for(i_=0;i_<=n;i_++)
         tmp[i_]=xyp[i_][1];
      //--- function call
      CSpline1D::Spline1DBuildCatmullRom(p.m_p,tmp,n+1,-1,0.0,p.m_y);
      //--- copy
      for(i_=0;i_<=n;i_++)
         tmp[i_]=xyp[i_][2];
      //--- function call
      CSpline1D::Spline1DBuildCatmullRom(p.m_p,tmp,n+1,-1,0.0,p.m_z);
     }
//--- check
   if(st==2)
     {
      //--- copy
      for(i_=0;i_<=n;i_++)
         tmp[i_]=xyp[i_][0];
      //--- function call
      CSpline1D::Spline1DBuildCubic(p.m_p,tmp,n+1,-1,0.0,-1,0.0,p.m_x);
      //--- copy
      for(i_=0;i_<=n;i_++)
         tmp[i_]=xyp[i_][1];
      //--- function call
      CSpline1D::Spline1DBuildCubic(p.m_p,tmp,n+1,-1,0.0,-1,0.0,p.m_y);
      //--- copy
      for(i_=0;i_<=n;i_++)
         tmp[i_]=xyp[i_][2];
      //--- function call
      CSpline1D::Spline1DBuildCubic(p.m_p,tmp,n+1,-1,0.0,-1,0.0,p.m_z);
     }
  }
//+------------------------------------------------------------------+
//| This function returns vector of parameter values correspoding to |
//| points.                                                          |
//| I.e. for P created from (X[0],Y[0])...(X[N-1],Y[N-1]) and        |
//| U=TValues(P) we have                                             |
//|     (X[0],Y[0]) = PSpline2Calc(P,U[0]),                          |
//|     (X[1],Y[1]) = PSpline2Calc(P,U[1]),                          |
//|     (X[2],Y[2]) = PSpline2Calc(P,U[2]),                          |
//|     ...                                                          |
//| INPUT PARAMETERS:                                                |
//|     P   -   parametric spline interpolant                        |
//| OUTPUT PARAMETERS:                                               |
//|     N   -   array size                                           |
//|     T   -   array[0..N-1]                                        |
//| NOTES:                                                           |
//| * for non-periodic splines U[0]=0, U[0]<U[1]<...<U[N-1], U[N-1]=1|
//| * for periodic splines     U[0]=0, U[0]<U[1]<...<U[N-1], U[N-1]<1|
//+------------------------------------------------------------------+
static void CPSpline::PSpline2ParameterValues(CPSpline2Interpolant &p,
                                              int &n,double &t[])
  {
//--- create a variable
   int i_=0;
//--- initialization
   n=0;
//--- check
   if(!CAp::Assert(p.m_n>=2,__FUNCTION__+": internal error!"))
      return;
//--- initialization
   n=p.m_n;
//--- allocation
   ArrayResizeAL(t,n);
//--- copy
   for(i_=0;i_<=n-1;i_++)
      t[i_]=p.m_p[i_];
   t[0]=0;
//--- check
   if(!p.m_periodic)
      t[n-1]=1;
  }
//+------------------------------------------------------------------+
//| This function returns vector of parameter values correspoding to |
//| points.                                                          |
//| Same as PSpline2ParameterValues(), but for 3D.                   |
//+------------------------------------------------------------------+
static void CPSpline::PSpline3ParameterValues(CPSpline3Interpolant &p,
                                              int &n,double &t[])
  {
//--- create a variable
   int i_=0;
//--- initialization
   n=0;
//--- check
   if(!CAp::Assert(p.m_n>=2,__FUNCTION__+": internal error!"))
      return;
//--- initialization
   n=p.m_n;
//--- allocation
   ArrayResizeAL(t,n);
//--- copy
   for(i_=0;i_<=n-1;i_++)
      t[i_]=p.m_p[i_];
   t[0]=0;
//--- check
   if(!p.m_periodic)
      t[n-1]=1;
  }
//+------------------------------------------------------------------+
//| This function calculates the value of the parametric spline for a| 
//| given value of parameter T                                       |
//| INPUT PARAMETERS:                                                |
//|     P   -   parametric spline interpolant                        |
//|     T   -   point:                                               |
//|             * T in [0,1] corresponds to interval spanned by      |
//|               points                                             |
//|             * for non-periodic splines T<0 (or T>1) correspond to|
//|               parts of the curve before the first (after the     |
//|               last) point                                        |
//|             * for periodic splines T<0 (or T>1) are projected    |
//|               into [0,1] by making T=T-floor(T).                 |
//| OUTPUT PARAMETERS:                                               |
//|     X   -   X-position                                           |
//|     Y   -   Y-position                                           |
//+------------------------------------------------------------------+
static void CPSpline::PSpline2Calc(CPSpline2Interpolant &p,double t,
                                   double &x,double &y)
  {
//--- initialization
   x=0;
   y=0;
//--- check
   if(p.m_periodic)
      t=t-(int)MathFloor(t);
//--- function call
   x=CSpline1D::Spline1DCalc(p.m_x,t);
//--- function call
   y=CSpline1D::Spline1DCalc(p.m_y,t);
  }
//+------------------------------------------------------------------+
//| This function calculates the value of the parametric spline for a|
//| given value of parameter T.                                      |
//| INPUT PARAMETERS:                                                |
//|     P   -   parametric spline interpolant                        |
//|     T   -   point:                                               |
//|             * T in [0,1] corresponds to interval spanned by      |
//|               points                                             |
//|             * for non-periodic splines T<0 (or T>1) correspond   |
//|               to parts of the curve before the first (after the  |
//|               last) point                                        |
//|             * for periodic splines T<0 (or T>1) are projected    |
//|               into [0,1] by making T=T-floor(T).                 |
//| OUTPUT PARAMETERS:                                               |
//|     X   -   X-position                                           |
//|     Y   -   Y-position                                           |
//|     Z   -   Z-position                                           |
//+------------------------------------------------------------------+
static void CPSpline::PSpline3Calc(CPSpline3Interpolant &p,double t,
                                   double &x,double &y,double &z)
  {
//--- initialization
   x=0;
   y=0;
   z=0;
//--- check
   if(p.m_periodic)
      t=t-(int)MathFloor(t);
//--- function call
   x=CSpline1D::Spline1DCalc(p.m_x,t);
//--- function call
   y=CSpline1D::Spline1DCalc(p.m_y,t);
//--- function call
   z=CSpline1D::Spline1DCalc(p.m_z,t);
  }
//+------------------------------------------------------------------+
//| This function calculates tangent vector for a given value of     |
//| parameter T                                                      |
//| INPUT PARAMETERS:                                                |
//|     P   -   parametric spline interpolant                        |
//|     T   -   point:                                               |
//|             * T in [0,1] corresponds to interval spanned by      |
//|               points                                             |
//|             * for non-periodic splines T<0 (or T>1) correspond to|
//|               parts of the curve before the first (after the     |
//|               last) point                                        |
//|             * for periodic splines T<0 (or T>1) are projected    |
//|               into [0,1] by making T=T-floor(T).                 |
//| OUTPUT PARAMETERS:                                               |
//|     X    -   X-component of tangent vector (normalized)          |
//|     Y    -   Y-component of tangent vector (normalized)          |
//| NOTE:                                                            |
//|     X^2+Y^2 is either 1 (for non-zero tangent vector) or 0.      |
//+------------------------------------------------------------------+
static void CPSpline::PSpline2Tangent(CPSpline2Interpolant &p,double t,
                                      double &x,double &y)
  {
//--- create variables
   double v=0;
   double v0=0;
   double v1=0;
//--- initialization
   x=0;
   y=0;
//--- check
   if(p.m_periodic)
      t=t-(int)MathFloor(t);
//--- function call
   PSpline2Diff(p,t,v0,x,v1,y);
//--- check
   if(x!=0.0 || y!=0.0)
     {
      //--- this code is a bit more complex than X^2+Y^2 to avoid
      //--- overflow for large values of X and Y.
      v=CApServ::SafePythag2(x,y);
      x=x/v;
      y=y/v;
     }
  }
//+------------------------------------------------------------------+
//| This function calculates tangent vector for a given value of     |
//| parameter T                                                      |
//| INPUT PARAMETERS:                                                |
//|     P   -   parametric spline interpolant                        |
//|     T   -   point:                                               |
//|             * T in [0,1] corresponds to interval spanned by      |
//|               points                                             |
//|             * for non-periodic splines T<0 (or T>1) correspond to|
//|               parts of the curve before the first (after the     |
//|               last) point                                        |
//|             * for periodic splines T<0 (or T>1) are projected    |
//|               into [0,1] by making T=T-floor(T).                 |
//| OUTPUT PARAMETERS:                                               |
//|     X    -   X-component of tangent vector (normalized)          |
//|     Y    -   Y-component of tangent vector (normalized)          |
//|     Z    -   Z-component of tangent vector (normalized)          |
//| NOTE:                                                            |
//|     X^2+Y^2+Z^2 is either 1 (for non-zero tangent vector) or 0.  |
//+------------------------------------------------------------------+
static void CPSpline::PSpline3Tangent(CPSpline3Interpolant &p,double t,
                                      double &x,double &y,double &z)
  {
//--- create variables
   double v=0;
   double v0=0;
   double v1=0;
   double v2=0;
//--- initialization
   x=0;
   y=0;
   z=0;
//--- check
   if(p.m_periodic)
      t=t-(int)MathFloor(t);
//--- function call
   PSpline3Diff(p,t,v0,x,v1,y,v2,z);
//--- check
   if((x!=0.0 || y!=0.0) || z!=0.0)
     {
      //--- function call
      v=CApServ::SafePythag3(x,y,z);
      //--- change values
      x=x/v;
      y=y/v;
      z=z/v;
     }
  }
//+------------------------------------------------------------------+
//| This function calculates derivative, i.e. it returns             |
//| (dX/dT,dY/dT).                                                   |
//| INPUT PARAMETERS:                                                |
//|     P   -   parametric spline interpolant                        |
//|     T   -   point:                                               |
//|             * T in [0,1] corresponds to interval spanned by      |
//|               points                                             |
//|             * for non-periodic splines T<0 (or T>1) correspond to|
//|               parts of the curve before the first (after the     |
//|               last) point                                        |
//|             * for periodic splines T<0 (or T>1) are projected    |
//|               into [0,1] by making T=T-floor(T).                 |
//| OUTPUT PARAMETERS:                                               |
//|     X   -   X-value                                              |
//|     DX  -   X-derivative                                         |
//|     Y   -   Y-value                                              |
//|     DY  -   Y-derivative                                         |
//+------------------------------------------------------------------+
static void CPSpline::PSpline2Diff(CPSpline2Interpolant &p,double t,
                                   double &x,double &dx,double &y,
                                   double &dy)
  {
//--- create a variable
   double d2s=0;
//--- change values
   x=0;
   dx=0;
   y=0;
   dy=0;
//--- check
   if(p.m_periodic)
      t=t-(int)MathFloor(t);
//--- function call
   CSpline1D::Spline1DDiff(p.m_x,t,x,dx,d2s);
//--- function call
   CSpline1D::Spline1DDiff(p.m_y,t,y,dy,d2s);
  }
//+------------------------------------------------------------------+
//| This function calculates derivative, i.e. it returns             |
//| (dX/dT,dY/dT,dZ/dT).                                             |
//| INPUT PARAMETERS:                                                |
//|     P   -   parametric spline interpolant                        |
//|     T   -   point:                                               |
//|             * T in [0,1] corresponds to interval spanned by      |
//|               points                                             |
//|             * for non-periodic splines T<0 (or T>1) correspond to|
//|               parts of the curve before the first (after the     |
//|               last) point                                        |
//|             * for periodic splines T<0 (or T>1) are projected    |
//|               into [0,1] by making T=T-floor(T).                 |
//| OUTPUT PARAMETERS:                                               |
//|     X   -   X-value                                              |
//|     DX  -   X-derivative                                         |
//|     Y   -   Y-value                                              |
//|     DY  -   Y-derivative                                         |
//|     Z   -   Z-value                                              |
//|     DZ  -   Z-derivative                                         |
//+------------------------------------------------------------------+
static void CPSpline::PSpline3Diff(CPSpline3Interpolant &p,double t,
                                   double &x,double &dx,double &y,
                                   double &dy,double &z,double &dz)
  {
//--- create a variable
   double d2s=0;
//--- initialization
   x=0;
   dx=0;
   y=0;
   dy=0;
   z=0;
   dz=0;
//--- check
   if(p.m_periodic)
      t=t-(int)MathFloor(t);
//--- function call
   CSpline1D::Spline1DDiff(p.m_x,t,x,dx,d2s);
//--- function call
   CSpline1D::Spline1DDiff(p.m_y,t,y,dy,d2s);
//--- function call
   CSpline1D::Spline1DDiff(p.m_z,t,z,dz,d2s);
  }
//+------------------------------------------------------------------+
//| This function calculates first and second derivative with respect|
//| to T.                                                            |
//| INPUT PARAMETERS:                                                |
//|     P   -   parametric spline interpolant                        |
//|     T   -   point:                                               |
//|             * T in [0,1] corresponds to interval spanned by      |
//|               points                                             |
//|             * for non-periodic splines T<0 (or T>1) correspond to|
//|               parts of the curve before the first (after the     |
//|               last) point                                        |
//|             * for periodic splines T<0 (or T>1) are projected    |
//|               into [0,1] by making T=T-floor(T).                 |
//| OUTPUT PARAMETERS:                                               |
//|     X   -   X-value                                              |
//|     DX  -   derivative                                           |
//|     D2X -   second derivative                                    |
//|     Y   -   Y-value                                              |
//|     DY  -   derivative                                           |
//|     D2Y -   second derivative                                    |
//+------------------------------------------------------------------+
static void CPSpline::PSpline2Diff2(CPSpline2Interpolant &p,double t,
                                    double &x,double &dx,double &d2x,
                                    double &y,double &dy,double &d2y)
  {
//--- initialization
   x=0;
   dx=0;
   d2x=0;
   y=0;
   dy=0;
   d2y=0;
//--- check
   if(p.m_periodic)
      t=t-(int)MathFloor(t);
//--- function call
   CSpline1D::Spline1DDiff(p.m_x,t,x,dx,d2x);
//--- function call
   CSpline1D::Spline1DDiff(p.m_y,t,y,dy,d2y);
  }
//+------------------------------------------------------------------+
//| This function calculates first and second derivative with respect|
//| to T.                                                            |
//| INPUT PARAMETERS:                                                |
//|     P   -   parametric spline interpolant                        |
//|     T   -   point:                                               |
//|             * T in [0,1] corresponds to interval spanned by      |
//|               points                                             |
//|             * for non-periodic splines T<0 (or T>1) correspond to|
//|               parts of the curve before the first (after the     |
//|               last) point                                        |
//|             * for periodic splines T<0 (or T>1) are projected    |
//|               into [0,1] by making T=T-floor(T).                 |
//| OUTPUT PARAMETERS:                                               |
//|     X   -   X-value                                              |
//|     DX  -   derivative                                           |
//|     D2X -   second derivative                                    |
//|     Y   -   Y-value                                              |
//|     DY  -   derivative                                           |
//|     D2Y -   second derivative                                    |
//|     Z   -   Z-value                                              |
//|     DZ  -   derivative                                           |
//|     D2Z -   second derivative                                    |
//+------------------------------------------------------------------+
static void CPSpline::PSpline3Diff2(CPSpline3Interpolant &p,double t,
                                    double &x,double &dx,double &d2x,
                                    double &y,double &dy,double &d2y,
                                    double &z,double &dz,double &d2z)
  {
//--- initialization
   x=0;
   dx=0;
   d2x=0;
   y=0;
   dy=0;
   d2y=0;
   z=0;
   dz=0;
   d2z=0;
//--- check
   if(p.m_periodic)
      t=t-(int)MathFloor(t);
//--- function call
   CSpline1D::Spline1DDiff(p.m_x,t,x,dx,d2x);
//--- function call
   CSpline1D::Spline1DDiff(p.m_y,t,y,dy,d2y);
//--- function call
   CSpline1D::Spline1DDiff(p.m_z,t,z,dz,d2z);
  }
//+------------------------------------------------------------------+
//| This function calculates arc length, i.e. length of curve between|
//| t=a and t=b.                                                     |
//| INPUT PARAMETERS:                                                |
//|     P   -   parametric spline interpolant                        |
//|     A,B -   parameter values corresponding to arc ends:          |
//|             * B>A will result in positive length returned        |
//|             * B<A will result in negative length returned        |
//| RESULT:                                                          |
//|     length of arc starting at T=A and ending at T=B.             |
//+------------------------------------------------------------------+
static double CPSpline::PSpline2ArcLength(CPSpline2Interpolant &p,const double a,
                                          const double b)
  {
//--- create variables
   double result=0;
   double sx=0;
   double dsx=0;
   double d2sx=0;
   double sy=0;
   double dsy=0;
   double d2sy=0;
//--- objects of classes
   CAutoGKState  state;
   CAutoGKReport rep;
//--- function call
   CAutoGK::AutoGKSmooth(a,b,state);
//--- cycle
   while(CAutoGK::AutoGKIteration(state))
     {
      CSpline1D::Spline1DDiff(p.m_x,state.m_x,sx,dsx,d2sx);
      CSpline1D::Spline1DDiff(p.m_y,state.m_x,sy,dsy,d2sy);
      state.m_f=CApServ::SafePythag2(dsx,dsy);
     }
//--- function call
   CAutoGK::AutoGKResults(state,result,rep);
//--- check
   if(!CAp::Assert(rep.m_terminationtype>0,__FUNCTION__+": internal error!"))
      return(EMPTY_VALUE);
//--- return result
   return(result);
  }
//+------------------------------------------------------------------+
//| This function calculates arc length, i.e. length of curve between|
//| t=a and t=b.                                                     |
//| INPUT PARAMETERS:                                                |
//|     P   -   parametric spline interpolant                        |
//|     A,B -   parameter values corresponding to arc ends:          |
//|             * B>A will result in positive length returned        |
//|             * B<A will result in negative length returned        |
//| RESULT:                                                          |
//|     length of arc starting at T=A and ending at T=B.             |
//+------------------------------------------------------------------+
static double CPSpline::PSpline3ArcLength(CPSpline3Interpolant &p,const double a,
                                          const double b)
  {
//--- create variables
   double result=0;
   double sx=0;
   double dsx=0;
   double d2sx=0;
   double sy=0;
   double dsy=0;
   double d2sy=0;
   double sz=0;
   double dsz=0;
   double d2sz=0;
//--- objects of classes
   CAutoGKState  state;
   CAutoGKReport rep;
//--- function call
   CAutoGK::AutoGKSmooth(a,b,state);
//--- cycle
   while(CAutoGK::AutoGKIteration(state))
     {
      CSpline1D::Spline1DDiff(p.m_x,state.m_x,sx,dsx,d2sx);
      CSpline1D::Spline1DDiff(p.m_y,state.m_x,sy,dsy,d2sy);
      CSpline1D::Spline1DDiff(p.m_z,state.m_x,sz,dsz,d2sz);
      state.m_f=CApServ::SafePythag3(dsx,dsy,dsz);
     }
//--- function call
   CAutoGK::AutoGKResults(state,result,rep);
//--- check
   if(!CAp::Assert(rep.m_terminationtype>0,__FUNCTION__+": internal error!"))
      return(EMPTY_VALUE);
//--- return result
   return(result);
  }
//+------------------------------------------------------------------+
//| Builds non-periodic parameterization for 2-dimensional spline    |
//+------------------------------------------------------------------+
static void CPSpline::PSpline2Par(CMatrixDouble &xy,const int n,const int pt,
                                  double &p[])
  {
//--- create variables
   double v=0;
   int    i=0;
   int    i_=0;
//--- check
   if(!CAp::Assert(pt>=0 && pt<=2,__FUNCTION__+": internal error!"))
      return;
//--- Build parameterization:
//--- * fill by non-normalized values
//--- * normalize them so we have P[0]=0,P[N-1]=1.
   ArrayResizeAL(p,n);
//--- check
   if(pt==0)
     {
      for(i=0;i<=n-1;i++)
         p[i]=i;
     }
//--- check
   if(pt==1)
     {
      p[0]=0;
      //--- calculation
      for(i=1;i<=n-1;i++)
         p[i]=p[i-1]+CApServ::SafePythag2(xy[i][0]-xy[i-1][0],xy[i][1]-xy[i-1][1]);
     }
//--- check
   if(pt==2)
     {
      p[0]=0;
      //--- calculation
      for(i=1;i<=n-1;i++)
         p[i]=p[i-1]+MathSqrt(CApServ::SafePythag2(xy[i][0]-xy[i-1][0],xy[i][1]-xy[i-1][1]));
     }
//--- change value
   v=1/p[n-1];
//--- calculation
   for(i_=0;i_<=n-1;i_++)
      p[i_]=v*p[i_];
  }
//+------------------------------------------------------------------+
//| Builds non-periodic parameterization for 3-dimensional spline    |
//+------------------------------------------------------------------+
static void CPSpline::PSpline3Par(CMatrixDouble &xy,const int n,const int pt,
                                  double &p[])
  {
//--- create variables
   double v=0;
   int    i=0;
   int    i_=0;
//--- check
   if(!CAp::Assert(pt>=0 && pt<=2,__FUNCTION__+": internal error!"))
      return;
//--- Build parameterization:
//--- * fill by non-normalized values
//--- * normalize them so we have P[0]=0,P[N-1]=1.
   ArrayResizeAL(p,n);
//--- check
   if(pt==0)
     {
      for(i=0;i<=n-1;i++)
         p[i]=i;
     }
//--- check
   if(pt==1)
     {
      p[0]=0;
      //--- calculation
      for(i=1;i<=n-1;i++)
         p[i]=p[i-1]+CApServ::SafePythag3(xy[i][0]-xy[i-1][0],xy[i][1]-xy[i-1][1],xy[i][2]-xy[i-1][2]);
     }
//--- check
   if(pt==2)
     {
      p[0]=0;
      //--- calculation
      for(i=1;i<=n-1;i++)
         p[i]=p[i-1]+MathSqrt(CApServ::SafePythag3(xy[i][0]-xy[i-1][0],xy[i][1]-xy[i-1][1],xy[i][2]-xy[i-1][2]));
     }
//--- change value
   v=1/p[n-1];
//--- calculation
   for(i_=0;i_<=n-1;i_++)
      p[i_]=v*p[i_];
  }
//+------------------------------------------------------------------+
//| 2-dimensional spline inteprolant                                 |
//+------------------------------------------------------------------+
class CSpline2DInterpolant
  {
public:
   //--- variable
   int               m_k;
   //--- array
   double            m_c[];
   //--- constructor, destructor
                     CSpline2DInterpolant(void);
                    ~CSpline2DInterpolant(void);
   //--- copy
   void              Copy(CSpline2DInterpolant &obj);
  };
//+------------------------------------------------------------------+
//| Constructor without parameters                                   |
//+------------------------------------------------------------------+
CSpline2DInterpolant::CSpline2DInterpolant(void)
  {

  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CSpline2DInterpolant::~CSpline2DInterpolant(void)
  {

  }
//+------------------------------------------------------------------+
//| Copy                                                             |
//+------------------------------------------------------------------+
void CSpline2DInterpolant::Copy(CSpline2DInterpolant &obj)
  {
//--- copy variable
   m_k=obj.m_k;
//--- copy array
   ArrayCopy(m_c,obj.m_c);
  }
//+------------------------------------------------------------------+
//| 2-dimensional spline inteprolant                                 |
//+------------------------------------------------------------------+
class CSpline2DInterpolantShell
  {
private:
   CSpline2DInterpolant m_innerobj;
public:
   //--- constructors, destructor
                     CSpline2DInterpolantShell(void);
                     CSpline2DInterpolantShell(CSpline2DInterpolant &obj);
                    ~CSpline2DInterpolantShell(void);
   //--- method
   CSpline2DInterpolant *GetInnerObj(void);
  };
//+------------------------------------------------------------------+
//| Constructor without parameters                                   |
//+------------------------------------------------------------------+
CSpline2DInterpolantShell::CSpline2DInterpolantShell(void)
  {

  }
//+------------------------------------------------------------------+
//| Copy constructor                                                 |
//+------------------------------------------------------------------+
CSpline2DInterpolantShell::CSpline2DInterpolantShell(CSpline2DInterpolant &obj)
  {
//--- copy
   m_innerobj.Copy(obj);
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CSpline2DInterpolantShell::~CSpline2DInterpolantShell(void)
  {

  }
//+------------------------------------------------------------------+
//| Return object of class                                           |
//+------------------------------------------------------------------+
CSpline2DInterpolant *CSpline2DInterpolantShell::GetInnerObj(void)
  {
//--- return result
   return(GetPointer(m_innerobj));
  }
//+------------------------------------------------------------------+
//| 2-dimensional spline interpolation                               |
//+------------------------------------------------------------------+
class CSpline2D
  {
private:
   //--- private method
   static void       BicubicCalcDerivatives(CMatrixDouble &a,double &x[],double &y[],const int m,const int n,CMatrixDouble &dx,CMatrixDouble &dy,CMatrixDouble &dxy);
public:
   //--- constructor, destructor
                     CSpline2D(void);
                    ~CSpline2D(void);
   //--- public methods
   static void       Spline2DBuildBilinear(double &cx[],double &cy[],CMatrixDouble &cf,const int m,const int n,CSpline2DInterpolant &c);
   static void       Spline2DBuildBicubic(double &cx[],double &cy[],CMatrixDouble &cf,const int m,const int n,CSpline2DInterpolant &c);
   static double     Spline2DCalc(CSpline2DInterpolant &c,const double x,const double y);
   static void       Spline2DDiff(CSpline2DInterpolant &c,const double x,const double y,double &f,double &fx,double &fy,double &fxy);
   static void       Spline2DUnpack(CSpline2DInterpolant &c,int &m,int &n,CMatrixDouble &tbl);
   static void       Spline2DLinTransXY(CSpline2DInterpolant &c,double ax,double bx,double ay,double by);
   static void       Spline2DLinTransF(CSpline2DInterpolant &c,const double a,const double b);
   static void       Spline2DCopy(CSpline2DInterpolant &c,CSpline2DInterpolant &cc);
   static void       Spline2DResampleBicubic(CMatrixDouble &a,const int oldheight,const int oldwidth,CMatrixDouble &b,const int newheight,const int newwidth);
   static void       Spline2DResampleBilinear(CMatrixDouble &a,const int oldheight,const int oldwidth,CMatrixDouble &b,const int newheight,const int newwidth);
  };
//+------------------------------------------------------------------+
//| Constructor without parameters                                   |
//+------------------------------------------------------------------+
CSpline2D::CSpline2D(void)
  {

  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CSpline2D::~CSpline2D(void)
  {

  }
//+------------------------------------------------------------------+
//| This subroutine builds bilinear spline coefficients table.       |
//| Input parameters:                                                |
//|     X   -   spline abscissas, array[0..N-1]                      |
//|     Y   -   spline ordinates, array[0..M-1]                      |
//|     F   -   function values, array[0..M-1,0..N-1]                |
//|     M,N -   grid size, M>=2, N>=2                                |
//| Output parameters:                                               |
//|     C   -   spline interpolant                                   |
//+------------------------------------------------------------------+
static void CSpline2D::Spline2DBuildBilinear(double &cx[],double &cy[],
                                             CMatrixDouble &cf,const int m,
                                             const int n,CSpline2DInterpolant &c)
  {
//--- create variables
   int    i=0;
   int    j=0;
   int    k=0;
   int    tblsize=0;
   int    shift=0;
   double t=0;
//--- create arrays
   double x[];
   double y[];
//--- create matrix
   CMatrixDouble dx;
   CMatrixDouble dy;
   CMatrixDouble dxy;
   CMatrixDouble f;
//--- copy arrays
   ArrayCopy(x,cx);
   ArrayCopy(y,cy);
//--- copy matrix
   f=cf;
//--- check
   if(!CAp::Assert(n>=2 && m>=2,__FUNCTION__+": N<2 or M<2!"))
      return;
//--- Sort points
   for(j=0;j<=n-1;j++)
     {
      k=j;
      for(i=j+1;i<=n-1;i++)
        {
         //--- check
         if(x[i]<x[k])
            k=i;
        }
      //--- check
      if(k!=j)
        {
         for(i=0;i<=m-1;i++)
           {
            //--- swap
            t=f[i][j];
            f[i].Set(j,f[i][k]);
            f[i].Set(k,t);
           }
         //--- swap
         t=x[j];
         x[j]=x[k];
         x[k]=t;
        }
     }
//--- calculation
   for(i=0;i<=m-1;i++)
     {
      k=i;
      for(j=i+1;j<=m-1;j++)
        {
         //--- check
         if(y[j]<y[k])
            k=j;
        }
      //--- check
      if(k!=i)
        {
         for(j=0;j<=n-1;j++)
           {
            //--- swap
            t=f[i][j];
            f[i].Set(j,f[k][j]);
            f[k].Set(j,t);
           }
         //--- swap
         t=y[i];
         y[i]=y[k];
         y[k]=t;
        }
     }
//--- Fill C:
//---  C[0]            -   length(C)
//---  C[1]            -   type(C):
//---                      -1=bilinear interpolant
//---                      -3=general cubic spline
//---                           (see BuildBicubicSpline)
//---  C[2]:
//---      N (x count)
//---  C[3]:
//---      M (y count)
//---  C[4]...C[4+N-1]:
//---      x[i],i=0...N-1
//---  C[4+N]...C[4+N+M-1]:
//---      y[i],i=0...M-1
//---  C[4+N+M]...C[4+N+M+(N*M-1)]:
//---      f(i,j) table. f(0,0),f(0,1),f(0,2) and so on...
   c.m_k=1;
   tblsize=4+n+m+n*m;
//--- allocation
   ArrayResizeAL(c.m_c,tblsize);
//--- change values
   c.m_c[0]=tblsize;
   c.m_c[1]=-1;
   c.m_c[2]=n;
   c.m_c[3]=m;
//--- copy
   for(i=0;i<=n-1;i++)
      c.m_c[4+i]=x[i];
   for(i=0;i<=m-1;i++)
      c.m_c[4+n+i]=y[i];
//--- calculation
   for(i=0;i<=m-1;i++)
     {
      for(j=0;j<=n-1;j++)
        {
         shift=i*n+j;
         c.m_c[4+n+m+shift]=f[i][j];
        }
     }
  }
//+------------------------------------------------------------------+
//| This subroutine builds bicubic spline coefficients table.        |
//| Input parameters:                                                |
//|     X   -   spline abscissas, array[0..N-1]                      |
//|     Y   -   spline ordinates, array[0..M-1]                      |
//|     F   -   function values, array[0..M-1,0..N-1]                |
//|     M,N -   grid size, M>=2, N>=2                                |
//| Output parameters:                                               |
//|     C   -   spline interpolant                                   |
//+------------------------------------------------------------------+
static void CSpline2D::Spline2DBuildBicubic(double &cx[],double &cy[],
                                            CMatrixDouble &cf,const int m,
                                            const int n,CSpline2DInterpolant &c)
  {
//--- create variables
   int    i=0;
   int    j=0;
   int    k=0;
   int    tblsize=0;
   int    shift=0;
   double t=0;
//--- create arrays
   double x[];
   double y[];
//--- create matrix
   CMatrixDouble dx;
   CMatrixDouble dy;
   CMatrixDouble dxy;
   CMatrixDouble f;
//--- copy arrays
   ArrayCopy(x,cx);
   ArrayCopy(y,cy);
//--- copy matrix
   f=cf;
//--- check
   if(!CAp::Assert(n>=2 && m>=2,__FUNCTION__+": N<2 or M<2!"))
      return;
//--- Sort points
   for(j=0;j<=n-1;j++)
     {
      k=j;
      for(i=j+1;i<=n-1;i++)
        {
         //--- check
         if(x[i]<x[k])
            k=i;
        }
      //--- check
      if(k!=j)
        {
         for(i=0;i<=m-1;i++)
           {
            //--- swap
            t=f[i][j];
            f[i].Set(j,f[i][k]);
            f[i].Set(k,t);
           }
         //--- swap
         t=x[j];
         x[j]=x[k];
         x[k]=t;
        }
     }
//--- calculation
   for(i=0;i<=m-1;i++)
     {
      k=i;
      for(j=i+1;j<=m-1;j++)
        {
         //--- check
         if(y[j]<y[k])
            k=j;
        }
      //--- check
      if(k!=i)
        {
         for(j=0;j<=n-1;j++)
           {
            //--- swap
            t=f[i][j];
            f[i].Set(j,f[k][j]);
            f[k].Set(j,t);
           }
         //--- swap
         t=y[i];
         y[i]=y[k];
         y[k]=t;
        }
     }
//--- Fill C:
//---  C[0]            -   length(C)
//---  C[1]            -   type(C):
//---                      -1=bilinear interpolant
//---                           (see BuildBilinearInterpolant)
//---                      -3=general cubic spline
//---  C[2]:
//---      N (x count)
//---  C[3]:
//---      M (y count)
//---  C[4]...C[4+N-1]:
//---      x[i],i=0...N-1
//---  C[4+N]...C[4+N+M-1]:
//---      y[i],i=0...M-1
//---  C[4+N+M]...C[4+N+M+(N*M-1)]:
//---      f(i,j) table. f(0,0),f(0,1),f(0,2) and so on...
//---  C[4+N+M+N*M]...C[4+N+M+(2*N*M-1)]:
//---      df(i,j)/dx table.
//---  C[4+N+M+2*N*M]...C[4+N+M+(3*N*M-1)]:
//---      df(i,j)/dy table.
//---  C[4+N+M+3*N*M]...C[4+N+M+(4*N*M-1)]:
//---      d2f(i,j)/dxdy table.
   c.m_k=3;
   tblsize=4+n+m+4*n*m;
//--- allocation
   ArrayResizeAL(c.m_c,tblsize);
//--- change values
   c.m_c[0]=tblsize;
   c.m_c[1]=-3;
   c.m_c[2]=n;
   c.m_c[3]=m;
//--- copy
   for(i=0;i<=n-1;i++)
      c.m_c[4+i]=x[i];
   for(i=0;i<=m-1;i++)
      c.m_c[4+n+i]=y[i];
//--- function call
   BicubicCalcDerivatives(f,x,y,m,n,dx,dy,dxy);
//--- change values
   for(i=0;i<=m-1;i++)
     {
      for(j=0;j<=n-1;j++)
        {
         shift=i*n+j;
         c.m_c[4+n+m+shift]=f[i][j];
         c.m_c[4+n+m+n*m+shift]=dx[i][j];
         c.m_c[4+n+m+2*n*m+shift]=dy[i][j];
         c.m_c[4+n+m+3*n*m+shift]=dxy[i][j];
        }
     }
  }
//+------------------------------------------------------------------+
//| This subroutine calculates the value of the bilinear or bicubic  |
//| spline at the given point X.                                     |
//| Input parameters:                                                |
//|     C   -   coefficients table.                                  |
//|             Built by BuildBilinearSpline or BuildBicubicSpline.  |
//|     X, Y-   point                                                |
//| Result:                                                          |
//|     S(x,y)                                                       |
//+------------------------------------------------------------------+
static double CSpline2D::Spline2DCalc(CSpline2DInterpolant &c,const double x,
                                      const double y)
  {
//--- create variables
   double v=0;
   double vx=0;
   double vy=0;
   double vxy=0;
//--- function call
   Spline2DDiff(c,x,y,v,vx,vy,vxy);
//--- return result
   return(v);
  }
//+------------------------------------------------------------------+
//| This subroutine calculates the value of the bilinear or bicubic  |
//| spline at the given point X and its derivatives.                 |
//| Input parameters:                                                |
//|     C   -   spline interpolant.                                  |
//|     X, Y-   point                                                |
//| Output parameters:                                               |
//|     F   -   S(x,y)                                               |
//|     FX  -   dS(x,y)/dX                                           |
//|     FY  -   dS(x,y)/dY                                           |
//|     FXY -   d2S(x,y)/dXdY                                        |
//+------------------------------------------------------------------+
static void CSpline2D::Spline2DDiff(CSpline2DInterpolant &c,const double x,
                                    const double y,double &f,double &fx,
                                    double &fy,double &fxy)
  {
//--- create variables
   int    n=0;
   int    m=0;
   double t=0;
   double dt=0;
   double u=0;
   double du=0;
   int    ix=0;
   int    iy=0;
   int    l=0;
   int    r=0;
   int    h=0;
   int    shift1=0;
   int    s1=0;
   int    s2=0;
   int    s3=0;
   int    s4=0;
   int    sf=0;
   int    sfx=0;
   int    sfy=0;
   int    sfxy=0;
   double y1=0;
   double y2=0;
   double y3=0;
   double y4=0;
   double v=0;
   double t0=0;
   double t1=0;
   double t2=0;
   double t3=0;
   double u0=0;
   double u1=0;
   double u2=0;
   double u3=0;
//--- initialization
   f=0;
   fx=0;
   fy=0;
   fxy=0;
//--- check
   if(!CAp::Assert((int)MathRound(c.m_c[1])==-1 || (int)MathRound(c.m_c[1])==-3,__FUNCTION__+": incorrect C!"))
      return;
//--- initialization
   n=(int)MathRound(c.m_c[2]);
   m=(int)MathRound(c.m_c[3]);
//--- Binary search in the [ x[0],...,x[n-2] ] (x[n-1] is not included)
   l=4;
   r=4+n-2+1;
   while(l!=r-1)
     {
      h=(l+r)/2;
      //--- check
      if(c.m_c[h]>=x)
         r=h;
      else
         l=h;
     }
//--- change values
   t=(x-c.m_c[l])/(c.m_c[l+1]-c.m_c[l]);
   dt=1.0/(c.m_c[l+1]-c.m_c[l]);
   ix=l-4;
//--- Binary search in the [ y[0],...,y[m-2] ] (y[m-1] is not included)
   l=4+n;
   r=4+n+(m-2)+1;
   while(l!=r-1)
     {
      h=(l+r)/2;
      //--- check
      if(c.m_c[h]>=y)
         r=h;
      else
         l=h;
     }
//--- change values
   u=(y-c.m_c[l])/(c.m_c[l+1]-c.m_c[l]);
   du=1.0/(c.m_c[l+1]-c.m_c[l]);
   iy=l-(4+n);
//--- Prepare F,dF/dX,dF/dY,d2F/dXdY
   f=0;
   fx=0;
   fy=0;
   fxy=0;
//--- Bilinear interpolation
   if((int)MathRound(c.m_c[1])==-1)
     {
      //--- calculation
      shift1=4+n+m;
      y1=c.m_c[shift1+n*iy+ix];
      y2=c.m_c[shift1+n*iy+(ix+1)];
      y3=c.m_c[shift1+n*(iy+1)+(ix+1)];
      y4=c.m_c[shift1+n*(iy+1)+ix];
      f=(1-t)*(1-u)*y1+t*(1-u)*y2+t*u*y3+(1-t)*u*y4;
      fx=(-((1-u)*y1)+(1-u)*y2+u*y3-u*y4)*dt;
      fy=(-((1-t)*y1)-t*y2+t*y3+(1-t)*y4)*du;
      fxy=(y1-y2+y3-y4)*du*dt;
      //--- exit the function
      return;
     }
//--- Bicubic interpolation
   if((int)MathRound(c.m_c[1])==-3)
     {
      //--- Prepare info
      t0=1;
      t1=t;
      t2=CMath::Sqr(t);
      t3=t*t2;
      u0=1;
      u1=u;
      u2=CMath::Sqr(u);
      u3=u*u2;
      sf=4+n+m;
      sfx=4+n+m+n*m;
      sfy=4+n+m+2*n*m;
      sfxy=4+n+m+3*n*m;
      s1=n*iy+ix;
      s2=n*iy+(ix+1);
      s3=n*(iy+1)+(ix+1);
      s4=n*(iy+1)+ix;
      //--- Calculate
      v=1*c.m_c[sf+s1];
      f=f+v*t0*u0;
      v=1*c.m_c[sfy+s1]/du;
      f=f+v*t0*u1;
      fy=fy+1*v*t0*u0*du;
      v=-(3*c.m_c[sf+s1])+3*c.m_c[sf+s4]-2*c.m_c[sfy+s1]/du-1*c.m_c[sfy+s4]/du;
      f=f+v*t0*u2;
      fy=fy+2*v*t0*u1*du;
      v=2*c.m_c[sf+s1]-2*c.m_c[sf+s4]+1*c.m_c[sfy+s1]/du+1*c.m_c[sfy+s4]/du;
      f=f+v*t0*u3;
      fy=fy+3*v*t0*u2*du;
      v=1*c.m_c[sfx+s1]/dt;
      f=f+v*t1*u0;
      fx=fx+1*v*t0*u0*dt;
      v=1*c.m_c[sfxy+s1]/(dt*du);
      f=f+v*t1*u1;
      fx=fx+1*v*t0*u1*dt;
      fy=fy+1*v*t1*u0*du;
      fxy=fxy+1*v*t0*u0*dt*du;
      v=-(3*c.m_c[sfx+s1]/dt)+3*c.m_c[sfx+s4]/dt-2*c.m_c[sfxy+s1]/(dt*du)-1*c.m_c[sfxy+s4]/(dt*du);
      f=f+v*t1*u2;
      fx=fx+1*v*t0*u2*dt;
      fy=fy+2*v*t1*u1*du;
      fxy=fxy+2*v*t0*u1*dt*du;
      v=2*c.m_c[sfx+s1]/dt-2*c.m_c[sfx+s4]/dt+1*c.m_c[sfxy+s1]/(dt*du)+1*c.m_c[sfxy+s4]/(dt*du);
      f=f+v*t1*u3;
      fx=fx+1*v*t0*u3*dt;
      fy=fy+3*v*t1*u2*du;
      fxy=fxy+3*v*t0*u2*dt*du;
      v=-(3*c.m_c[sf+s1])+3*c.m_c[sf+s2]-2*c.m_c[sfx+s1]/dt-1*c.m_c[sfx+s2]/dt;
      f=f+v*t2*u0;
      fx=fx+2*v*t1*u0*dt;
      v=-(3*c.m_c[sfy+s1]/du)+3*c.m_c[sfy+s2]/du-2*c.m_c[sfxy+s1]/(dt*du)-1*c.m_c[sfxy+s2]/(dt*du);
      f=f+v*t2*u1;
      fx=fx+2*v*t1*u1*dt;
      fy=fy+1*v*t2*u0*du;
      fxy=fxy+2*v*t1*u0*dt*du;
      v=9*c.m_c[sf+s1]-9*c.m_c[sf+s2]+9*c.m_c[sf+s3]-9*c.m_c[sf+s4]+6*c.m_c[sfx+s1]/dt+3*c.m_c[sfx+s2]/dt-3*c.m_c[sfx+s3]/dt-6*c.m_c[sfx+s4]/dt+6*c.m_c[sfy+s1]/du-6*c.m_c[sfy+s2]/du-3*c.m_c[sfy+s3]/du+3*c.m_c[sfy+s4]/du+4*c.m_c[sfxy+s1]/(dt*du)+2*c.m_c[sfxy+s2]/(dt*du)+1*c.m_c[sfxy+s3]/(dt*du)+2*c.m_c[sfxy+s4]/(dt*du);
      f=f+v*t2*u2;
      fx=fx+2*v*t1*u2*dt;
      fy=fy+2*v*t2*u1*du;
      fxy=fxy+4*v*t1*u1*dt*du;
      v=-(6*c.m_c[sf+s1])+6*c.m_c[sf+s2]-6*c.m_c[sf+s3]+6*c.m_c[sf+s4]-4*c.m_c[sfx+s1]/dt-2*c.m_c[sfx+s2]/dt+2*c.m_c[sfx+s3]/dt+4*c.m_c[sfx+s4]/dt-3*c.m_c[sfy+s1]/du+3*c.m_c[sfy+s2]/du+3*c.m_c[sfy+s3]/du-3*c.m_c[sfy+s4]/du-2*c.m_c[sfxy+s1]/(dt*du)-1*c.m_c[sfxy+s2]/(dt*du)-1*c.m_c[sfxy+s3]/(dt*du)-2*c.m_c[sfxy+s4]/(dt*du);
      f=f+v*t2*u3;
      fx=fx+2*v*t1*u3*dt;
      fy=fy+3*v*t2*u2*du;
      fxy=fxy+6*v*t1*u2*dt*du;
      v=2*c.m_c[sf+s1]-2*c.m_c[sf+s2]+1*c.m_c[sfx+s1]/dt+1*c.m_c[sfx+s2]/dt;
      f=f+v*t3*u0;
      fx=fx+3*v*t2*u0*dt;
      v=2*c.m_c[sfy+s1]/du-2*c.m_c[sfy+s2]/du+1*c.m_c[sfxy+s1]/(dt*du)+1*c.m_c[sfxy+s2]/(dt*du);
      f=f+v*t3*u1;
      fx=fx+3*v*t2*u1*dt;
      fy=fy+1*v*t3*u0*du;
      fxy=fxy+3*v*t2*u0*dt*du;
      v=-(6*c.m_c[sf+s1])+6*c.m_c[sf+s2]-6*c.m_c[sf+s3]+6*c.m_c[sf+s4]-3*c.m_c[sfx+s1]/dt-3*c.m_c[sfx+s2]/dt+3*c.m_c[sfx+s3]/dt+3*c.m_c[sfx+s4]/dt-4*c.m_c[sfy+s1]/du+4*c.m_c[sfy+s2]/du+2*c.m_c[sfy+s3]/du-2*c.m_c[sfy+s4]/du-2*c.m_c[sfxy+s1]/(dt*du)-2*c.m_c[sfxy+s2]/(dt*du)-1*c.m_c[sfxy+s3]/(dt*du)-1*c.m_c[sfxy+s4]/(dt*du);
      f=f+v*t3*u2;
      fx=fx+3*v*t2*u2*dt;
      fy=fy+2*v*t3*u1*du;
      fxy=fxy+6*v*t2*u1*dt*du;
      v=4*c.m_c[sf+s1]-4*c.m_c[sf+s2]+4*c.m_c[sf+s3]-4*c.m_c[sf+s4]+2*c.m_c[sfx+s1]/dt+2*c.m_c[sfx+s2]/dt-2*c.m_c[sfx+s3]/dt-2*c.m_c[sfx+s4]/dt+2*c.m_c[sfy+s1]/du-2*c.m_c[sfy+s2]/du-2*c.m_c[sfy+s3]/du+2*c.m_c[sfy+s4]/du+1*c.m_c[sfxy+s1]/(dt*du)+1*c.m_c[sfxy+s2]/(dt*du)+1*c.m_c[sfxy+s3]/(dt*du)+1*c.m_c[sfxy+s4]/(dt*du);
      f=f+v*t3*u3;
      fx=fx+3*v*t2*u3*dt;
      fy=fy+3*v*t3*u2*du;
      fxy=fxy+9*v*t2*u2*dt*du;
      //--- exit the function
      return;
     }
  }
//+------------------------------------------------------------------+
//| This subroutine unpacks two-dimensional spline into the          |
//| coefficients table                                               |
//| Input parameters:                                                |
//|     C   -   spline interpolant.                                  |
//| Result:                                                          |
//|     M, N-   grid size (x-axis and y-axis)                        |
//|     Tbl -   coefficients table, unpacked format,                 |
//|             [0..(N-1)*(M-1)-1, 0..19].                           |
//|             For I = 0...M-2, J=0..N-2:                           |
//|                 K =  I*(N-1)+J                                   |
//|                 Tbl[K,0] = X[j]                                  |
//|                 Tbl[K,1] = X[j+1]                                |
//|                 Tbl[K,2] = Y[i]                                  |
//|                 Tbl[K,3] = Y[i+1]                                |
//|                 Tbl[K,4] = C00                                   |
//|                 Tbl[K,5] = C01                                   |
//|                 Tbl[K,6] = C02                                   |
//|                 Tbl[K,7] = C03                                   |
//|                 Tbl[K,8] = C10                                   |
//|                 Tbl[K,9] = C11                                   |
//|                 ...                                              |
//|                 Tbl[K,19] = C33                                  |
//|             On each grid square spline is equals to:             |
//|                 S(x) = SUM(c[i,j]*(x^i)*(y^j), i=0..3, j=0..3)   |
//|                 t = x-x[j]                                       |
//|                 u = y-y[i]                                       |
//+------------------------------------------------------------------+
static void CSpline2D::Spline2DUnpack(CSpline2DInterpolant &c,int &m,
                                      int &n,CMatrixDouble &tbl)
  {
//--- create variables
   int    i=0;
   int    j=0;
   int    ci=0;
   int    cj=0;
   int    k=0;
   int    p=0;
   int    shift=0;
   int    s1=0;
   int    s2=0;
   int    s3=0;
   int    s4=0;
   int    sf=0;
   int    sfx=0;
   int    sfy=0;
   int    sfxy=0;
   double y1=0;
   double y2=0;
   double y3=0;
   double y4=0;
   double dt=0;
   double du=0;
//--- initialization
   m=0;
   n=0;
//--- check
   if(!CAp::Assert((int)MathRound(c.m_c[1])==-3 || (int)MathRound(c.m_c[1])==-1,__FUNCTION__+": incorrect C!"))
      return;
//--- initialization
   n=(int)MathRound(c.m_c[2]);
   m=(int)MathRound(c.m_c[3]);
//--- allocation
   tbl.Resize((n-1)*(m-1),20);
//--- Fill
   for(i=0;i<=m-2;i++)
     {
      for(j=0;j<=n-2;j++)
        {
         //--- calculation
         p=i*(n-1)+j;
         tbl[p].Set(0,c.m_c[4+j]);
         tbl[p].Set(1,c.m_c[4+j+1]);
         tbl[p].Set(2,c.m_c[4+n+i]);
         tbl[p].Set(3,c.m_c[4+n+i+1]);
         dt=1/(tbl[p][1]-tbl[p][0]);
         du=1/(tbl[p][3]-tbl[p][2]);
         //--- Bilinear interpolation
         if((int)MathRound(c.m_c[1])==-1)
           {
            for(k=4;k<=19;k++)
               tbl[p].Set(k,0);
            //--- calculation
            shift=4+n+m;
            y1=c.m_c[shift+n*i+j];
            y2=c.m_c[shift+n*i+(j+1)];
            y3=c.m_c[shift+n*(i+1)+(j+1)];
            y4=c.m_c[shift+n*(i+1)+j];
            tbl[p].Set(4,y1);
            tbl[p].Set(4+1*4+0,y2-y1);
            tbl[p].Set(4+0*4+1,y4-y1);
            tbl[p].Set(4+1*4+1,y3-y2-y4+y1);
           }
         //--- Bicubic interpolation
         if((int)MathRound(c.m_c[1])==-3)
           {
            //--- change values
            sf=4+n+m;
            sfx=4+n+m+n*m;
            sfy=4+n+m+2*n*m;
            sfxy=4+n+m+3*n*m;
            s1=n*i+j;
            s2=n*i+(j+1);
            s3=n*(i+1)+(j+1);
            s4=n*(i+1)+j;
            //--- change values
            tbl[p].Set(4+0*4+0,1*c.m_c[sf+s1]);
            tbl[p].Set(4+0*4+1,1*c.m_c[sfy+s1]/du);
            tbl[p].Set(4+0*4+2,-(3*c.m_c[sf+s1])+3*c.m_c[sf+s4]-2*c.m_c[sfy+s1]/du-1*c.m_c[sfy+s4]/du);
            tbl[p].Set(4+0*4+3,2*c.m_c[sf+s1]-2*c.m_c[sf+s4]+1*c.m_c[sfy+s1]/du+1*c.m_c[sfy+s4]/du);
            tbl[p].Set(4+1*4+0,1*c.m_c[sfx+s1]/dt);
            tbl[p].Set(4+1*4+1,1*c.m_c[sfxy+s1]/(dt*du));
            tbl[p].Set(4+1*4+2,-(3*c.m_c[sfx+s1]/dt)+3*c.m_c[sfx+s4]/dt-2*c.m_c[sfxy+s1]/(dt*du)-1*c.m_c[sfxy+s4]/(dt*du));
            tbl[p].Set(4+1*4+3,2*c.m_c[sfx+s1]/dt-2*c.m_c[sfx+s4]/dt+1*c.m_c[sfxy+s1]/(dt*du)+1*c.m_c[sfxy+s4]/(dt*du));
            tbl[p].Set(4+2*4+0,-(3*c.m_c[sf+s1])+3*c.m_c[sf+s2]-2*c.m_c[sfx+s1]/dt-1*c.m_c[sfx+s2]/dt);
            tbl[p].Set(4+2*4+1,-(3*c.m_c[sfy+s1]/du)+3*c.m_c[sfy+s2]/du-2*c.m_c[sfxy+s1]/(dt*du)-1*c.m_c[sfxy+s2]/(dt*du));
            tbl[p].Set(4+2*4+2,9*c.m_c[sf+s1]-9*c.m_c[sf+s2]+9*c.m_c[sf+s3]-9*c.m_c[sf+s4]+6*c.m_c[sfx+s1]/dt+3*c.m_c[sfx+s2]/dt-3*c.m_c[sfx+s3]/dt-6*c.m_c[sfx+s4]/dt+6*c.m_c[sfy+s1]/du-6*c.m_c[sfy+s2]/du-3*c.m_c[sfy+s3]/du+3*c.m_c[sfy+s4]/du+4*c.m_c[sfxy+s1]/(dt*du)+2*c.m_c[sfxy+s2]/(dt*du)+1*c.m_c[sfxy+s3]/(dt*du)+2*c.m_c[sfxy+s4]/(dt*du));
            tbl[p].Set(4+2*4+3,-(6*c.m_c[sf+s1])+6*c.m_c[sf+s2]-6*c.m_c[sf+s3]+6*c.m_c[sf+s4]-4*c.m_c[sfx+s1]/dt-2*c.m_c[sfx+s2]/dt+2*c.m_c[sfx+s3]/dt+4*c.m_c[sfx+s4]/dt-3*c.m_c[sfy+s1]/du+3*c.m_c[sfy+s2]/du+3*c.m_c[sfy+s3]/du-3*c.m_c[sfy+s4]/du-2*c.m_c[sfxy+s1]/(dt*du)-1*c.m_c[sfxy+s2]/(dt*du)-1*c.m_c[sfxy+s3]/(dt*du)-2*c.m_c[sfxy+s4]/(dt*du));
            tbl[p].Set(4+3*4+0,2*c.m_c[sf+s1]-2*c.m_c[sf+s2]+1*c.m_c[sfx+s1]/dt+1*c.m_c[sfx+s2]/dt);
            tbl[p].Set(4+3*4+1,2*c.m_c[sfy+s1]/du-2*c.m_c[sfy+s2]/du+1*c.m_c[sfxy+s1]/(dt*du)+1*c.m_c[sfxy+s2]/(dt*du));
            tbl[p].Set(4+3*4+2,-(6*c.m_c[sf+s1])+6*c.m_c[sf+s2]-6*c.m_c[sf+s3]+6*c.m_c[sf+s4]-3*c.m_c[sfx+s1]/dt-3*c.m_c[sfx+s2]/dt+3*c.m_c[sfx+s3]/dt+3*c.m_c[sfx+s4]/dt-4*c.m_c[sfy+s1]/du+4*c.m_c[sfy+s2]/du+2*c.m_c[sfy+s3]/du-2*c.m_c[sfy+s4]/du-2*c.m_c[sfxy+s1]/(dt*du)-2*c.m_c[sfxy+s2]/(dt*du)-1*c.m_c[sfxy+s3]/(dt*du)-1*c.m_c[sfxy+s4]/(dt*du));
            tbl[p].Set(4+3*4+3,4*c.m_c[sf+s1]-4*c.m_c[sf+s2]+4*c.m_c[sf+s3]-4*c.m_c[sf+s4]+2*c.m_c[sfx+s1]/dt+2*c.m_c[sfx+s2]/dt-2*c.m_c[sfx+s3]/dt-2*c.m_c[sfx+s4]/dt+2*c.m_c[sfy+s1]/du-2*c.m_c[sfy+s2]/du-2*c.m_c[sfy+s3]/du+2*c.m_c[sfy+s4]/du+1*c.m_c[sfxy+s1]/(dt*du)+1*c.m_c[sfxy+s2]/(dt*du)+1*c.m_c[sfxy+s3]/(dt*du)+1*c.m_c[sfxy+s4]/(dt*du));
           }
         //--- Rescale Cij
         for(ci=0;ci<=3;ci++)
           {
            for(cj=0;cj<=3;cj++)
               tbl[p].Set(4+ci*4+cj,tbl[p][4+ci*4+cj]*MathPow(dt,ci)*MathPow(du,cj));
           }
        }
     }
  }
//+------------------------------------------------------------------+
//| This subroutine performs linear transformation of the spline     |
//| argument.                                                        |
//| Input parameters:                                                |
//|     C       -   spline interpolant                               |
//|     AX, BX  -   transformation coefficients: x = A*t + B         |
//|     AY, BY  -   transformation coefficients: y = A*u + B         |
//| Result:                                                          |
//|     C   -   transformed spline                                   |
//+------------------------------------------------------------------+
static void CSpline2D::Spline2DLinTransXY(CSpline2DInterpolant &c,double ax,
                                          double bx,double ay,double by)
  {
//--- create variables
   int    i=0;
   int    j=0;
   int    n=0;
   int    m=0;
   double v=0;
   int    typec=0;
//--- create arrays
   double x[];
   double y[];
//--- create matrix
   CMatrixDouble f;
//--- initialization
   typec=(int)MathRound(c.m_c[1]);
//--- check
   if(!CAp::Assert(typec==-3 || typec==-1,__FUNCTION__+": incorrect C!"))
      return;
//--- initialization
   n=(int)MathRound(c.m_c[2]);
   m=(int)MathRound(c.m_c[3]);
//--- allocation
   ArrayResizeAL(x,n);
   ArrayResizeAL(y,m);
   f.Resize(m,n);
//--- copy
   for(j=0;j<=n-1;j++)
      x[j]=c.m_c[4+j];
   for(i=0;i<=m-1;i++)
      y[i]=c.m_c[4+n+i];
   for(i=0;i<=m-1;i++)
     {
      for(j=0;j<=n-1;j++)
         f[i].Set(j,c.m_c[4+n+m+i*n+j]);
     }
//--- Special case: AX=0 or AY=0
   if(ax==0.0)
     {
      //--- change values
      for(i=0;i<=m-1;i++)
        {
         v=Spline2DCalc(c,bx,y[i]);
         for(j=0;j<=n-1;j++)
            f[i].Set(j,v);
        }
      //--- check
      if(typec==-3)
         Spline2DBuildBicubic(x,y,f,m,n,c);
      //--- check
      if(typec==-1)
         Spline2DBuildBilinear(x,y,f,m,n,c);
      //--- change values
      ax=1;
      bx=0;
     }
//--- check
   if(ay==0.0)
     {
      //--- change values
      for(j=0;j<=n-1;j++)
        {
         v=Spline2DCalc(c,x[j],by);
         for(i=0;i<=m-1;i++)
            f[i].Set(j,v);
        }
      //--- check
      if(typec==-3)
         Spline2DBuildBicubic(x,y,f,m,n,c);
      //--- check
      if(typec==-1)
         Spline2DBuildBilinear(x,y,f,m,n,c);
      //--- change values
      ay=1;
      by=0;
     }
//--- General case: AX<>0,AY<>0
//--- Unpack,scale and pack again.
   for(j=0;j<=n-1;j++)
      x[j]=(x[j]-bx)/ax;
   for(i=0;i<=m-1;i++)
      y[i]=(y[i]-by)/ay;
//--- check
   if(typec==-3)
      Spline2DBuildBicubic(x,y,f,m,n,c);
//--- check
   if(typec==-1)
      Spline2DBuildBilinear(x,y,f,m,n,c);
  }
//+------------------------------------------------------------------+
//| This subroutine performs linear transformation of the spline.    |
//| Input parameters:                                                |
//|     C   -   spline interpolant.                                  |
//|     A, B-   transformation coefficients: S2(x,y) = A*S(x,y) + B  |
//| Output parameters:                                               |
//|     C   -   transformed spline                                   |
//+------------------------------------------------------------------+
static void CSpline2D::Spline2DLinTransF(CSpline2DInterpolant &c,const double a,
                                         const double b)
  {
//--- create variables
   int i=0;
   int j=0;
   int n=0;
   int m=0;
   int typec=0;
//--- create arrays
   double x[];
   double y[];
//--- create matrix
   CMatrixDouble f;
//--- initialization
   typec=(int)MathRound(c.m_c[1]);
//--- check
   if(!CAp::Assert(typec==-3 || typec==-1,__FUNCTION__+": incorrect C!"))
      return;
//--- initialization
   n=(int)MathRound(c.m_c[2]);
   m=(int)MathRound(c.m_c[3]);
//--- allocation
   ArrayResizeAL(x,n);
   ArrayResizeAL(y,m);
   f.Resize(m,n);
//--- copy
   for(j=0;j<=n-1;j++)
      x[j]=c.m_c[4+j];
   for(i=0;i<=m-1;i++)
      y[i]=c.m_c[4+n+i];
   for(i=0;i<=m-1;i++)
     {
      for(j=0;j<=n-1;j++)
         f[i].Set(j,a*c.m_c[4+n+m+i*n+j]+b);
     }
//--- check
   if(typec==-3)
      Spline2DBuildBicubic(x,y,f,m,n,c);
//--- check
   if(typec==-1)
      Spline2DBuildBilinear(x,y,f,m,n,c);
  }
//+------------------------------------------------------------------+
//| This subroutine makes the copy of the spline model.              |
//| Input parameters:                                                |
//|     C   -   spline interpolant                                   |
//| Output parameters:                                               |
//|     CC  -   spline copy                                          |
//+------------------------------------------------------------------+
static void CSpline2D::Spline2DCopy(CSpline2DInterpolant &c,CSpline2DInterpolant &cc)
  {
//--- create variables
   int n=0;
   int i_=0;
//--- check
   if(!CAp::Assert(c.m_k==1 || c.m_k==3,__FUNCTION__+": incorrect C!"))
      return;
//--- change values
   cc.m_k=c.m_k;
   n=(int)MathRound(c.m_c[0]);
//--- allocation
   ArrayResizeAL(cc.m_c,n);
//--- copy
   for(i_=0;i_<=n-1;i_++)
      cc.m_c[i_]=c.m_c[i_];
  }
//+------------------------------------------------------------------+
//| Bicubic spline resampling                                        |
//| Input parameters:                                                |
//|     A           -   function values at the old grid,             |
//|                     array[0..OldHeight-1, 0..OldWidth-1]         |
//|     OldHeight   -   old grid height, OldHeight>1                 |
//|     OldWidth    -   old grid width, OldWidth>1                   |
//|     NewHeight   -   new grid height, NewHeight>1                 |
//|     NewWidth    -   new grid width, NewWidth>1                   |
//| Output parameters:                                               |
//|     B           -   function values at the new grid,             |
//|                     array[0..NewHeight-1, 0..NewWidth-1]         |
//+------------------------------------------------------------------+
static void CSpline2D::Spline2DResampleBicubic(CMatrixDouble &a,const int oldheight,
                                               const int oldwidth,CMatrixDouble &b,
                                               const int newheight,const int newwidth)
  {
//--- create variables
   int i=0;
   int j=0;
   int mw=0;
   int mh=0;
//--- create arrays
   double x[];
   double y[];
//--- create matrix
   CMatrixDouble buf;
//--- object of class
   CSpline1DInterpolant c;
//--- check
   if(!CAp::Assert(oldwidth>1 && oldheight>1,__FUNCTION__+": width/height less than 1"))
      return;
//--- check
   if(!CAp::Assert(newwidth>1 && newheight>1,__FUNCTION__+": width/height less than 1"))
      return;
//--- Prepare
   mw=MathMax(oldwidth,newwidth);
   mh=MathMax(oldheight,newheight);
//--- allocation
   b.Resize(newheight,newwidth);
   buf.Resize(oldheight,newwidth);
   ArrayResizeAL(x,MathMax(mw,mh));
   ArrayResizeAL(y,MathMax(mw,mh));
//--- Horizontal interpolation
   for(i=0;i<=oldheight-1;i++)
     {
      //--- Fill X,Y
      for(j=0;j<=oldwidth-1;j++)
        {
         x[j]=(double)j/(double)(oldwidth-1);
         y[j]=a[i][j];
        }
      //--- Interpolate and place result into temporary matrix
      CSpline1D::Spline1DBuildCubic(x,y,oldwidth,0,0.0,0,0.0,c);
      for(j=0;j<=newwidth-1;j++)
         buf[i].Set(j,CSpline1D::Spline1DCalc(c,(double)j/(double)(newwidth-1)));
     }
//--- Vertical interpolation
   for(j=0;j<=newwidth-1;j++)
     {
      //--- Fill X,Y
      for(i=0;i<=oldheight-1;i++)
        {
         x[i]=(double)i/(double)(oldheight-1);
         y[i]=buf[i][j];
        }
      //--- Interpolate and place result into B
      CSpline1D::Spline1DBuildCubic(x,y,oldheight,0,0.0,0,0.0,c);
      for(i=0;i<=newheight-1;i++)
         b[i].Set(j,CSpline1D::Spline1DCalc(c,(double)i/(double)(newheight-1)));
     }
  }
//+------------------------------------------------------------------+
//| Bilinear spline resampling                                       |
//| Input parameters:                                                |
//|     A           -   function values at the old grid,             |
//|                     array[0..OldHeight-1, 0..OldWidth-1]         |
//|     OldHeight   -   old grid height, OldHeight>1                 |
//|     OldWidth    -   old grid width, OldWidth>1                   |
//|     NewHeight   -   new grid height, NewHeight>1                 |
//|     NewWidth    -   new grid width, NewWidth>1                   |
//| Output parameters:                                               |
//|     B           -   function values at the new grid,             |
//|                     array[0..NewHeight-1, 0..NewWidth-1]         |
//+------------------------------------------------------------------+
static void CSpline2D::Spline2DResampleBilinear(CMatrixDouble &a,const int oldheight,
                                                const int oldwidth,CMatrixDouble &b,
                                                const int newheight,const int newwidth)
  {
//--- create variables
   int    i=0;
   int    j=0;
   int    l=0;
   int    c=0;
   double t=0;
   double u=0;
//--- allocation
   b.Resize(newheight,newwidth);
   for(i=0;i<=newheight-1;i++)
     {
      for(j=0;j<=newwidth-1;j++)
        {
         //--- calculation
         l=i*(oldheight-1)/(newheight-1);
         //--- check
         if(l==oldheight-1)
            l=oldheight-2;
         //--- calculation
         u=(double)i/(double)(newheight-1)*(oldheight-1)-l;
         c=j*(oldwidth-1)/(newwidth-1);
         //--- check
         if(c==oldwidth-1)
            c=oldwidth-2;
         //--- calculation
         t=(double)(j*(oldwidth-1))/(double)(newwidth-1)-c;
         b[i].Set(j,(1-t)*(1-u)*a[l][c]+t*(1-u)*a[l][c+1]+t*u*a[l+1][c+1]+(1-t)*u*a[l+1][c]);
        }
     }
  }
//+------------------------------------------------------------------+
//| Internal subroutine.                                             |
//| Calculation of the first derivatives and the cross-derivative.   |
//+------------------------------------------------------------------+
static void CSpline2D::BicubicCalcDerivatives(CMatrixDouble &a,double &x[],
                                              double &y[],const int m,
                                              const int n,CMatrixDouble &dx,
                                              CMatrixDouble &dy,CMatrixDouble &dxy)
  {
//--- create variables
   int    i=0;
   int    j=0;
   double s=0;
   double ds=0;
   double d2s=0;
//--- create arrays
   double xt[];
   double ft[];
//--- object of class
   CSpline1DInterpolant c;
//--- allocation
   dx.Resize(m,n);
   dy.Resize(m,n);
   dxy.Resize(m,n);
//--- dF/dX
   ArrayResizeAL(xt,n);
   ArrayResizeAL(ft,n);
   for(i=0;i<=m-1;i++)
     {
      for(j=0;j<=n-1;j++)
        {
         xt[j]=x[j];
         ft[j]=a[i][j];
        }
      //--- function call
      CSpline1D::Spline1DBuildCubic(xt,ft,n,0,0.0,0,0.0,c);
      for(j=0;j<=n-1;j++)
        {
         //--- function call
         CSpline1D::Spline1DDiff(c,x[j],s,ds,d2s);
         dx[i].Set(j,ds);
        }
     }
//--- dF/dY
   ArrayResizeAL(xt,m);
   ArrayResizeAL(ft,m);
   for(j=0;j<=n-1;j++)
     {
      for(i=0;i<=m-1;i++)
        {
         xt[i]=y[i];
         ft[i]=a[i][j];
        }
      //--- function call
      CSpline1D::Spline1DBuildCubic(xt,ft,m,0,0.0,0,0.0,c);
      for(i=0;i<=m-1;i++)
        {
         //--- function call
         CSpline1D::Spline1DDiff(c,y[i],s,ds,d2s);
         dy[i].Set(j,ds);
        }
     }
//--- d2F/dXdY
   ArrayResizeAL(xt,n);
   ArrayResizeAL(ft,n);
   for(i=0;i<=m-1;i++)
     {
      for(j=0;j<=n-1;j++)
        {
         xt[j]=x[j];
         ft[j]=dy[i][j];
        }
      //--- function call
      CSpline1D::Spline1DBuildCubic(xt,ft,n,0,0.0,0,0.0,c);
      for(j=0;j<=n-1;j++)
        {
         //--- function call
         CSpline1D::Spline1DDiff(c,x[j],s,ds,d2s);
         dxy[i].Set(j,ds);
        }
     }
  }
//+------------------------------------------------------------------+
