/* -*- mia-c++  -*-
 *
 * This file is part of MIA - a toolbox for medical image analysis
 * Copyright (c) Leipzig, Madrid 1999-2017 Gert Wollny
 *
 * MIA 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; either version 3 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.
 *
 * You should have received a copy of the GNU General Public License
 * along with MIA; if not, see <http://www.gnu.org/licenses/>.
 *
 */

#if defined(__SSE2__)
#include <emmintrin.h>
#endif


#include <mia/core/splinekernel/bspline.hh>


NS_MIA_BEGIN
NS_BEGIN(bsplinekernel)

using std::invalid_argument;
using std::runtime_error;

template <int sd, int degree>
struct bspline {
       static double apply(double MIA_PARAM_UNUSED(x))
       {
              throw create_exception<invalid_argument>( "Spline ", sd, ":derivative degree "
                            ,  degree, " not supported for spline of degree 2");
       }
};


template <>
struct bspline<0, 0> {
       static double apply(double x)
       {
              return fabs(x) < 0.5 ? 1.0 : 0.0;
       }
};

CBSplineKernel0::CBSplineKernel0():
       CSplineKernel(0, 0.5, ip_bspline0)
{
}

void CBSplineKernel0::get_weights(double /*x*/, VWeight& weight)const
{
       assert(weight.size() == 1);
       weight[0] = 1;
}

void CBSplineKernel0::get_derivative_weights(double /*x*/, VWeight& /*weight*/) const
{
       assert(false && "get_derivative_weights is not defined for the Haar spline");
       throw runtime_error("CBSplineKernel0::get_derivative_weights: not supported for Haar spline");
}

double CBSplineKernel0::get_weight_at(double x, int degree) const
{
       if (degree != 0) {
              throw create_exception<invalid_argument>( "CBSplineKernel0::get_weight_at: degree ",  degree,
                            "not supported for Haar spline");
       }

       return std::abs(x) < 0.5 ? 1.0 : 0.0;
}
void CBSplineKernel0::get_derivative_weights(double /*x*/, VWeight& weight, int degree) const
{
       if (degree == 0)
              weight[0] = 1.0;
       else {
              throw create_exception<invalid_argument>( "CBSplineKernel0::get_derivative_weights: degree ",  degree,
                            "not supported for Haar spline");
       }
}


CBSplineKernel1::CBSplineKernel1():
       CSplineKernel(1, 0.0, ip_bspline1)
{
}

void CBSplineKernel1::get_weights(double x, VWeight& weight)const
{
       assert(weight.size() == 2);
       weight[1] = x;
       weight[0] = 1.0 - x;
}

void CBSplineKernel1::get_derivative_weights(double /*x*/, VWeight& weight) const
{
       assert(weight.size() == 2);
       weight[1] =  1.0;
       weight[0] = -1.0;
}

double CBSplineKernel1::get_weight_at(double x, int degree) const
{
       switch (degree) {
       case 0: {
              if ( fabs(x) < 1)
                     return -x;
              else
                     return 0.0;
       }

       case 1: {
              if ( fabs(x) < 1)
                     return x > 0.0 ? -1 : 1;
              else
                     return 0.0;
       }

       default:
              throw create_exception<invalid_argument>( "CBSplineKernel1::get_weight_at: degree ",  degree,
                            "not supported for linearly interpolating spline");
       }
}

void CBSplineKernel1::get_derivative_weights(double x, VWeight& weight, int degree) const
{
       assert(weight.size() == 2);

       switch (degree) {
       case 0: {
              get_weights(x, weight);
              break;
       }

       case 1: {
              get_derivative_weights(x, weight);
              break;
       }

       default:
              throw create_exception<invalid_argument>( "CBSplineKernel1::get_weight_at: degree ",  degree,
                            "not supported for linearly interpolating spline");
       }
}


CBSplineKernel2::CBSplineKernel2():
       CSplineKernel(2, 0.5, ip_bspline2)
{
       add_pole(sqrt(8.0) - 3.0);
}

void CBSplineKernel2::get_weights(double x, VWeight&  weight)const
{
       weight[1] = 0.75 - x * x;
       weight[2] = 0.5 * (x - weight[1] + 1.0);
       weight[0] = 1.0 - weight[1] - weight[2];
}

template <>
struct bspline<2, 0> {
       static double apply(double x)
       {
              x = fabs(x);

              if (x <= 0.5)
                     return 0.75 - x * x;

              if (x <= 1.5)
                     return (1.5 - x) * (1.5 - x) * 0.5;

              return 0.0;
       }
};

template <>
struct bspline<2, 1> {
       static double apply(double x)
       {
              double xa = fabs(x) ;

              if (xa <= 0.5)
                     return -2.0 * x;

              if (xa <= 1.5)
                     return (x > 0.0) ? xa - 1.5 : 1.5 - xa;

              return 0.0;
       }
};


double CBSplineKernel2::get_weight_at(double x, int degree) const
{
       switch (degree) {
       case 0:
              return bspline<2, 0>::apply(x);

       case 1:
              return bspline<2, 1>::apply(x);

       default:
              throw create_exception<invalid_argument>( "B-Spline 2:derivative degree "
                            ,  degree, " not supported" );
       }
}

void CBSplineKernel2::get_derivative_weights(double x, VWeight& weight) const
{
       weight[1] =  - 2 * x;
       weight[2] = 0.5 * (1 - weight[1]);
       weight[0] = - weight[1] - weight[2];
}

void CBSplineKernel2::get_derivative_weights(double x, VWeight& weight, int degree) const
{
       switch (degree) {
       case 0:
              get_weights(x, weight);
              break;

       case 1:
              get_derivative_weights(x, weight);
              break;

       case 2: {
              weight[1] =  - 2;
              weight[2] = 1;
              weight[0] = 1;
       }
       break;

       default:
              weight[0] = weight[1] = weight[2] = 0.0;
       }
}

CBSplineKernel3::CBSplineKernel3():
       CSplineKernel(3, 0.0, ip_bspline3)
{
       add_pole(sqrt(3.0) - 2.0);
}

#ifdef __SSE2__
typedef double v2df __attribute__ ((vector_size (16)));
// getthis stupid compiler to allocate the memory and set the values
// once and for all.
const double oneby6[2] __attribute__((aligned(16))) = { 1.0 / 6.0,  1.0 / 6.0 };
#endif

void CBSplineKernel3::get_weights(double x, VWeight&  weight)const
{
       const double xm1 = 1 - x;
#ifdef __SSE2__
       v2df X = _mm_set_pd(x, xm1);
       // this needs to be replaced for clang++
       const v2df OB6 =  _mm_load_pd(oneby6);
       const v2df X2  = X * X;
       const v2df XB6 = X * OB6;
       const v2df W03 = X2 * XB6;
       v2df W30 = W03;
       W30 = _mm_shuffle_pd(W30, W30, 0x1);
       // coverity is complaining about variables of type __m128d
       // being pointers where they are indeed to interpreted as
       // arrays of two elements
       // coverity[array_vs_singleton]
       const v2df W12 = X - W03 - W03 + W30;
       _mm_storel_pd(&weight[0], W03);
       _mm_storeh_pd(&weight[3], W03);
       _mm_storeu_pd(&weight[1], W12);
#else
       const double onebysix = 1.0 / 6.0;
       weight[3] = onebysix * x * x * x;
       weight[0] = onebysix  * xm1 * xm1 * xm1;
       weight[2] = x - 2.0 * weight[3]  + weight[0] ;
       weight[1] = xm1 - 2 * weight[0]  + weight[3];
#endif
}

void CBSplineKernel3::get_derivative_weights(double x, VWeight& weight) const
{
       weight[3] = 0.5 * x * x;
       weight[0] = x - 0.5 - weight[3];
       weight[2] = 1 + weight[0] - 2.0 * weight[3];
       weight[1] = - weight[0] - weight[2] - weight[3];
}

void CBSplineKernel3::get_derivative_weights(double x, VWeight& weight, int degree) const
{
       switch (degree) {
       case 0:
              get_weights(x, weight);
              break;

       case 1:
              get_derivative_weights(x, weight);
              break;

       case 2: {
              weight[3] = x;
              weight[0] = 1 - x;
              weight[2] = 1 - 3.0 * x;
              weight[1] = 3 * x - 2;
       }
       break;

       case 3: {
              weight[3] =  1.0;
              weight[0] = -1.0;
              weight[2] = -3.0;
              weight[1] =  3.0;
       }
       break;

       default: {
              fill(weight.begin(), weight.end(), 0.0);
       }
       }
}

template <>
struct bspline<3, 0> {
       static double apply (double x) /* cubic */
       {
              const double onebysix = 1.0 / 6.0;
              x = fabs(x);

              if (x <= 1.0)
                     return 2.0 / 3.0 - x * x * ( 1 - 0.5 * x );

              if (x < 2.0) {
                     const double dx =  2.0 - x;
                     return dx * dx * dx * onebysix;
              }

              return 0.0;
       }
};

template <>
struct bspline<3, 1> {
       static double apply(double x) /* cubic, derivative */
       {
              double ax = fabs(x);

              if (ax <= 1.0)
                     return (1.5 * ax - 2) * x;

              if (ax <= 2.0)
                     return (x > 0.0 ? -0.5 : 0.5) * (2 - ax) * (2 - ax) ;

              return 0.0;
       }
};

template <>
struct bspline<3, 2> {
       static double apply(double x) /* cubic, second derivative */
       {
              x = fabs(x) ;

              if (x > 2.0) return 0.0;

              if (x > 1.0) return 2.0 - x ;

              return 3.0 * x - 2.0;
       }
};

double CBSplineKernel3::get_weight_at(double x, int degree) const
{
       switch (degree) {
       case 0:
              return bspline<3, 0>::apply(x);

       case 1:
              return bspline<3, 1>::apply(x);

       case 2:
              return bspline<3, 2>::apply(x);

       default:
              throw create_exception<invalid_argument>( "B-Spline 3:derivative degree "
                            ,  degree, " not supported" );
       }
}


CBSplineKernelOMoms3::CBSplineKernelOMoms3():
       CSplineKernel(3, 0.0, ip_omoms3)
{
       add_pole((sqrt(105.0) - 13.0) / 8.0);
}

void CBSplineKernelOMoms3::get_weights(double x, VWeight&  weight)const
{
       double x2 = x * x;
       double x3 = x2 * x;
       weight[1] =   x3 * 0.5 - x2  + 1.0 / 14.0 * x + 13.0 / 21.0;
       weight[2] = - x3 * 0.5 + 0.5 * x2  + 3.0 / 7.0 * x + 4.0 / 21.0;
       weight[3] =   x3 / 6.0 + x / 42.0;
       weight[0] = 1.0 - weight[3] - weight[1] - weight[2];
}

void CBSplineKernelOMoms3::get_derivative_weights(double x, VWeight& weight) const
{
       double x2 = 2.0 * x;
       double x3 = 3.0 * x * x;
       weight[1] =   x3 * 0.5 - x2  + 1.0 / 14.0;
       weight[2] = - x3 * 0.5 + 0.5 * x2  + 3.0 / 7.0;
       weight[3] =   x3 / 6.0 + 1.0 / 42.0;
       weight[0] =  - weight[3] - weight[1] - weight[2];
}

void CBSplineKernelOMoms3::get_derivative_weights(double x, VWeight& weight, int degree) const
{
       switch (degree) {
       case 0:
              get_weights(x, weight);
              break;

       case 1:
              get_derivative_weights(x, weight);
              break;

       case 2: {
              double x2 = 2.0;
              double x3 = 6.0 * x;
              weight[1] =   x3 * 0.5 - x2;
              weight[2] = - x3 * 0.5 + 0.5 * x2;
              weight[3] =   x3 / 6.0;
              weight[0] =  - weight[3] - weight[1] - weight[2];
       }
       break;

       default:
              fill(weight.begin(), weight.end(), 0.0);
       }
}

template <>
struct bspline<4, 0> {
       static double apply (double x)
       {
              x = fabs(x);

              if (x <= 0.5) {
                     const double f = 1.0 / 192.0;
                     const double x2 = x * x;
                     return   f * (115.0 + (48.0 * x2 - 120) * x2);
              }

              if (x <= 1.5) {
                     const double f = 1.0 / 96.0;
                     double ax = x - 1.0;
                     return -f * ((((16 * ax - 16) * ax - 24) * ax + 44) * ax - 19);
              }

              if (x <= 2.5) {
                     double h = 2.5 - x;
                     h *= h;
                     return h * h / 24.0;
              }

              return 0.0;
       }
};

template <>
struct bspline<4, 1> {
       static double apply(double x)
       {
              double ax = fabs(x);

              if (ax <= 0.5) {
                     const double x2 = x * x;
                     return   x * (x2  - 1.25);
              }

              if (ax <= 1.5) {
                     const double f = x > 0 ? - 1.0 / 96.0 : 1.0 / 96.0;
                     double bx =  ax - 1.0;
                     return f * (64 * bx * bx * bx - 48 * bx * bx - 48 * bx + 44);
              }

              if (ax <= 2.5) {
                     double h = x < 0 ? 2.5 - ax : ax - 2.5;
                     return h * h * h / 6.0;
              }

              return 0.0;
       }
};

template <>
struct bspline<4, 2> {
       static double apply(double x)
       {
              double ax = fabs(x);

              if (ax <= 0.5)
                     return   3 * x * x  - 1.25;

              if (ax <= 1.5) {
                     return -(2 * x * x + 240.0 / 96.0 - 480.0 / 96 * ax);
              }

              if (ax <= 2.5) {
                     double h = 2.5 - ax;
                     return 0.5 * h * h;
              }

              return 0.0;
       }
};


template <>
struct bspline<4, 3> {
       static double apply(double x)
       {
              double ax = fabs(x);

              if (ax <= 0.5)
                     return   6 * x;

              if (ax <= 1.5) {
                     const double f = x > 0 ? -0.5 : 0.5;
                     double bx =  ax - 1.0;
                     return f * (8 * bx - 2);
              }

              if (ax <= 2.5)
                     return  x < 0 ? 2.5 - ax : ax - 2.5;

              return 0.0;
       }
};

template <>
struct bspline<4, 4> {
       static double apply(double x)
       {
              double ax = fabs(x);

              if (ax <= 0.5)
                     return  6;

              if (ax <= 1.5)
                     return  -4;

              if (ax <= 2.5)
                     return  1;

              return 0.0;
       }
};


double CBSplineKernel4::get_weight_at(double x, int degree) const
{
       switch (degree) {
       case 0:
              return bspline<4, 0>::apply(x);

       case 1:
              return bspline<4, 1>::apply(x);

       case 2:
              return bspline<4, 2>::apply(x);

       case 3:
              return bspline<4, 3>::apply(x);

       case 4:
              return bspline<4, 4>::apply(x);

       default:
              throw create_exception<invalid_argument>( "B-Spline 3:derivative degree "
                            ,  degree, " not supported" );
       }
}

CBSplineKernel4::CBSplineKernel4():
       CSplineKernel(4, 0.5, ip_bspline4)
{
       add_pole(sqrt(664.0 + sqrt(438976.0)) - sqrt(304.0) - 19.0);
       add_pole(sqrt(664.0 - sqrt(438976.0)) + sqrt(304.0) - 19.0);
}

void CBSplineKernel4::get_weights(double x, VWeight&  weight)const
{
       double x2 = x * x;
       double t = (1.0 / 6.0) * x2;
       weight[0] = 1.0 / 2.0 - x;
       weight[0] *= weight[0];
       weight[0] *= (1.0 / 24.0) * weight[0];
       double t0 = x * (t - 11.0 / 24.0);
       double t1 = 19.0 / 96.0 + x2 * (1.0 / 4.0 - t);
       weight[1] = t1 + t0;
       weight[3] = t1 - t0;
       weight[4] = weight[0] + t0 + (1.0 / 2.0) * x;
       weight[2] = 1.0 - weight[0] - weight[1] - weight[3] - weight[4];
}

void CBSplineKernel4::get_derivative_weights(double x, VWeight& weight) const
{
       const double x2 = x * x;
       weight[0] = 1.0 / 2.0 - x;
       weight[0] *= - weight[0] * weight[0] / 6.0;
       const double t = x2 / 6.0;
       const double t0 = (t - 11.0 / 24.0) + x2 / 3.0;
       const double t1 = 2.0 * x * (0.25 - t) - x2 * x / 3.0;
       weight[1] = t1 + t0;
       weight[3] = t1 - t0;
       weight[4] = weight[0] + t0 + 0.5;
       weight[2] = - weight[0] - weight[1] - weight[3] - weight[4];
}

void CBSplineKernel4::get_derivative_weights(double x, VWeight& weight, int degree) const
{
       switch (degree) {
       case 0:
              get_weights(x, weight);
              break;

       case 1:
              get_derivative_weights(x, weight);
              break;

       case 2: {
              weight[0] = 1.0 / 2.0 - x;
              weight[0] *= weight[0] / 2.0;
              const double t0 = x;
              const double t1 = 0.5 - 2.0 * x * x ;
              weight[1] = t1 + t0;
              weight[3] = t1 - t0;
              weight[4] = weight[0] + t0;
              weight[2] = - weight[0] - weight[1] - weight[3] - weight[4];
              cvdebug() << "weight[2] = " << weight[2] << "\n";
       }
       break;

       case 3: {
              weight[0] =  x - 1.0 / 2.0;
              const double t0 = 1;
              const double t1 = - 4.0 * x ;
              weight[1] = t1 + t0;
              weight[3] = t1 - t0;
              weight[4] = weight[0] + t0;
              weight[2] = - weight[0] - weight[1] - weight[3] - weight[4];
       }
       break;

       default:
              fill(weight.begin(), weight.end(), 0.0);
       }
}



CBSplineKernel5::CBSplineKernel5():
       CSplineKernel(5, 0.0, ip_bspline5)
{
       add_pole((sqrt(270.0 - sqrt(70980.0)) + sqrt(105.0) - 13.0) / 2.0);
       add_pole((sqrt(270.0 + sqrt(70980.0)) - sqrt(105.0) - 13.0) / 2.0);
}


void CBSplineKernel5::get_weights(double x, VWeight&  weight)const
{
       double w2 = x * x;
       weight[5] = (1.0 / 120.0) * x * w2 * w2;
       w2 -= x;
       double w4 = w2 * w2;
       x -= 1.0 / 2.0;
       double t = w2 * (w2 - 3.0);
       weight[0] = (1.0 / 24.0) * (1.0 / 5.0 + w2 + w4) - weight[5];
       double t0 = (1.0 / 24.0) * (w2 * (w2 - 5.0) + 46.0 / 5.0);
       double t1 = (-1.0 / 12.0) * x * (t + 4.0);
       weight[2] = t0 + t1;
       weight[3] = t0 - t1;
       t0 = (1.0 / 16.0) * (9.0 / 5.0 - t);
       t1 = (1.0 / 24.0) * x * (w4 - w2 - 5.0);
       weight[1] = t0 + t1;
       weight[4] = t0 - t1;
}

void CBSplineKernel5::get_derivative_weights(double x, VWeight& weight) const
{
       double w2 = x * x;
       weight[5] = (1.0 / 24.0) * w2 * w2;
       double h2 = 2.0 * x - 1.0;
       w2 -= x;
       double t = h2 * (2.0 * w2 - 3.0);
       weight[0] = (h2 + 2.0 * w2 * h2) / 24.0  - weight[5];
       x -= 0.5;
       double t0 =   h2 * (2.0 * w2 - 5.0) / 24.0;
       double t1 = - 1.0 / 3.0 - (w2 * (w2 - 3.0)  + x * t) / 12.0;
       weight[2] = t0 + t1;
       weight[3] = t0 - t1;
       t0 =  - t / 16.0;
       t1 = ((w2 * w2 - w2 - 5.0) + x * (2 * w2 * h2 - h2)) / 24.0;
       weight[1] = t0 + t1;
       weight[4] = t0 - t1;
}


template <>
struct bspline<5, 0> {
       static double apply (double x)
       {
              const double ax = fabs(x);

              if (ax < 1.0) {
                     const double x2 = ax * ax;
                     const double x4 = x2 * x2;
                     const double x5 = x4 * ax;
                     return -(5 * x5 - 15 * x4  + 30 * x2 - 33) / 60;
              }

              if (ax < 2.0) {
                     double h = 2.0 - ax;
                     double h2 = h * h;
                     return -(5 * h2 * h * h2 - 5 * h2 * h2 - 10 * h2 * h  - 10 * h2 - 5 * h - 1) / 120.0;
              }

              if (ax < 3.0) {
                     const double h = 3.0 - ax;
                     const double h2 =  h * h;
                     return h2 * h2 * h / 120.0;
              }

              return 0.0;
       }
};

template <>
struct bspline<5, 1> {
       static double apply(double x)
       {
              double ax = fabs(x);

              if (ax < 1.0) {
                     const double x2 = x * x;
                     const double x4 = x2 * x2;
                     return ( x > 0 ? -1 : 1) * ( 5.0 / 12.0 * x4 - ax * (x2 - 1.0));
              }

              if (ax < 2.0) {
                     double h = 2.0 - ax;
                     double h2 = h * h;
                     return ( x > 0 ? 1 : -1) * (5 * h2  * h2 - 4 * h * h2 - 6 * h2  - 4 * h - 1 ) / 24.0;
              }

              if (ax < 3.0) {
                     const double h = 3.0 - ax;
                     const double h2 =  h * h;
                     return ( x > 0 ? -1 : 1) *  h2 * h2 / 24.0;
              }

              return 0.0;
       }
};


template <>
struct bspline<5, 2> {
       static double apply(double x)
       {
              double ax = fabs(x);

              if (ax < 1.0) {
                     const double x2 = x * x;
                     return -( 5.0 / 3.0 * x2 * ax - 3 * x2 + 1);
              }

              if (ax < 2.0) {
                     double h = 2.0 - ax;
                     double h2 = h * h;
                     return -(5 * h2  * h - 3  * h2 - 3 * h  - 1 ) / 6.0;
              }

              if (ax < 3.0) {
                     const double h = 3.0 - ax;
                     const double h2 =  h * h;
                     return   h2 * h / 6.0;
              }

              return 0.0;
       }
};


template <>
struct bspline<5, 3> {
       static double apply(double x)
       {
              const double f = x > 0  ? -1 : 1;
              const double ax = fabs(x);

              if (ax < 1.0) {
                     const double x2 = x * x;
                     return f * ( 5.0 * x2 - 6 * ax);
              }

              if (ax < 2.0) {
                     double h = 2.0 - ax;
                     double h2 = h * h;
                     return -f * ( 5 * h2 - 2.0  * h - 1) / 2.0;
              }

              if (ax < 3.0) {
                     const double h = 3.0 - ax;
                     const double h2 =  h * h;
                     return f * h2 / 2.0;
              }

              return 0.0;
       }
};


template <>
struct bspline<5, 4> {
       static double apply(double x)
       {
              const double f = x > 0  ? -1 : 1;
              const double ax = fabs(x);

              if (ax < 1.0) {
                     const double x2 = x * x;
                     return f * ( 5.0 * x2 - 6 * ax);
              }

              if (ax < 2.0) {
                     double h = 2.0 - ax;
                     double h2 = h * h;
                     return -f * ( 5 * h2 - 2.0  * h - 1) / 2.0;
              }

              if (ax < 3.0) {
                     const double h = 3.0 - ax;
                     const double h2 =  h * h;
                     return f * h2 / 2.0;
              }

              return 0.0;
       }
};

#if 0

template <>
struct bspline<5, 5> {
       static double apply(double x)
       {
              double ax = fabs(x);

              if (ax <= 0.5)
                     return  6;

              if (ax <= 1.5)
                     return  -4;

              if (ax <= 2.5)
                     return  1;

              return 0.0;
       }
};
#endif

void CBSplineKernel5::get_derivative_weights(double x, VWeight& weight, int degree) const
{
       switch (degree) {
       case 0:
              get_weights(x, weight);
              break;

       case 1:
              get_derivative_weights(x, weight);
              break;

       case 2: {
              weight[5] = (1.0 / 6.0) * x * x * x;
              const double h2 = 2.0 * x - 1.0;
              const double w2 = x * x - x;
              const double h2w2 = 2 * h2 * w2;
              const double h2h22 = 2 * h2 * h2;
              const double t = 4 * w2  + h2h22 - 6;
              weight[0] = (t + 8.0) / 24.0  - weight[5];
              const double t0 = (t - 4.0 ) / 24.0;
              const double xm = x - 0.5;
              const double t1 = ( - 2.0 * h2w2 +  6 * h2 - xm * t) / 12;
              weight[2] = t0 + t1;
              weight[3] = t0 - t1;
              const double tt0 =  - t / 16.0;
              const double tt1 = (2 * (h2w2 - h2) + xm * (t + 4)) / 24.0;
              weight[1] = tt0 + tt1;
              weight[4] = tt0 - tt1;
       }
       break;

       case 3: {
              weight[5] = (1.0 / 2.0) * x * x;
              const double h2 = 2.0 * x - 1.0;
              const double w2 = x * x - x;
              //const double h2w2 = 2 * h2 * w2;
              //const double h2h22 = 2 * h2 * h2;
              const double t = 12 * h2;
              weight[0] = h2 / 2.0  - weight[5];
              const double t0 = h2 / 2.0;
              //const double xm = x - 0.5;
              const double t1 = -( 10 * w2 - 1) / 2.0;
              weight[2] = t0 + t1;
              weight[3] = t0 - t1;
              const double tt0 =  - t / 16.0;
              const double tt1 = (10 * w2 + 1) / 4.0;
              weight[1] = tt0 + tt1;
              weight[4] = tt0 - tt1;
       }
       break;

       default:
              fill(weight.begin(), weight.end(), 0.0);
       }
}

double CBSplineKernel5::get_weight_at(double x, int degree) const
{
       switch (degree) {
       case 0:
              return bspline<5, 0>::apply(x);

       case 1:
              return bspline<5, 1>::apply(x);

       case 2:
              return bspline<5, 2>::apply(x);

       case 3:
              return bspline<5, 3>::apply(x);

       case 4:
              return bspline<5, 4>::apply(x);

       default:
              throw create_exception<invalid_argument>( "B-Spline5:derivative degree ",  degree, " not supported" );
       }
}


CBSplineKernelPlugin::CBSplineKernelPlugin():
       CSplineKernelPlugin("bspline"),
       m_degree(3)
{
       add_parameter("d", make_ci_param(m_degree, 0, 5, false, "Spline degree"));
}

CSplineKernel *CBSplineKernelPlugin::do_create() const
{
       switch (m_degree) {
       case 0:
              return new CBSplineKernel0;

       case 1:
              return new CBSplineKernel1;

       case 2:
              return new CBSplineKernel2;

       case 3:
              return new CBSplineKernel3;

       case 4:
              return new CBSplineKernel4;

       case 5:
              return new CBSplineKernel5;

       default:
              assert(0 && "add parameter didn't catch the proper range");
       }

       return NULL;
}

const std::string CBSplineKernelPlugin::do_get_descr()const
{
       return "B-spline kernel creation ";
}


COMomsSplineKernelPlugin::COMomsSplineKernelPlugin():
       CSplineKernelPlugin("omoms"),
       m_degree(3)
{
       add_parameter("d", make_ci_param(m_degree, 3, 3, false, "Spline degree"));
}

CSplineKernel *COMomsSplineKernelPlugin::do_create() const
{
       return new CBSplineKernelOMoms3;
}

const std::string COMomsSplineKernelPlugin::do_get_descr()const
{
       return "OMoms-spline kernel creation";
}

extern "C" EXPORT CPluginBase  *get_plugin_interface()
{
       CPluginBase  *result = new COMomsSplineKernelPlugin();
       result->append_interface(new CBSplineKernelPlugin());
       return result;
}

NS_END
NS_MIA_END
