File indexing completed on 2024-05-12 15:27:09

0001 /***************************************************************************
0002     File                 : nsl_sf_poly.c
0003     Project              : LabPlot
0004     Description          : NSL special polynomial functions
0005     --------------------------------------------------------------------
0006     Copyright            : (C) 2016 by Stefan Gerlach (stefan.gerlach@uni.kn)
0007 
0008  ***************************************************************************/
0009 
0010 /***************************************************************************
0011  *                                                                         *
0012  *  This program is free software; you can redistribute it and/or modify   *
0013  *  it under the terms of the GNU General Public License as published by   *
0014  *  the Free Software Foundation; either version 2 of the License, or      *
0015  *  (at your option) any later version.                                    *
0016  *                                                                         *
0017  *  This program is distributed in the hope that it will be useful,        *
0018  *  but WITHOUT ANY WARRANTY; without even the implied warranty of         *
0019  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          *
0020  *  GNU General Public License for more details.                           *
0021  *                                                                         *
0022  *   You should have received a copy of the GNU General Public License     *
0023  *   along with this program; if not, write to the Free Software           *
0024  *   Foundation, Inc., 51 Franklin Street, Fifth Floor,                    *
0025  *   Boston, MA  02110-1301  USA                                           *
0026  *                                                                         *
0027  ***************************************************************************/
0028 
0029 #include "nsl_sf_poly.h"
0030 #include <stdio.h>  // debugging
0031 #include <math.h>
0032 #include <gsl/gsl_math.h>
0033 #include <gsl/gsl_sf_pow_int.h>
0034 
0035 /* see https://en.wikipedia.org/wiki/Chebyshev_polynomials */
0036 double nsl_sf_poly_chebyshev_T(int n, double x) {
0037     if (fabs(x) <= 1)
0038         return cos(n * acos(x));
0039     else if (x > 1)
0040         return cosh(n * gsl_acosh(x));
0041     else 
0042         return pow(-1., n)*cosh(n * gsl_acosh(-x));
0043 }
0044 
0045 double nsl_sf_poly_chebyshev_U(int n, double x) {
0046     double sq = sqrt(x*x - 1);
0047     return (pow(x + sq, n + 1) - pow(x - sq, n + 1))/2./sq;
0048 }
0049 
0050 /* from http://www.crbond.com/papers/lopt.pdf */
0051 double nsl_sf_poly_optimal_legendre_L(int n, double x) {
0052     if (n < 1 || n > 10)
0053         return 0.0;
0054 
0055     switch (n) {
0056     case 1:
0057         return x;
0058     case 2:
0059         return x*x;
0060     case 3:
0061         return (1. + (-3. + 3.*x)*x)*x;
0062     case 4:
0063         return (3. + (-8. + 6*x)*x)*x*x;
0064     case 5:
0065         return (1. + (-8. + (28. + (-40. + 20*x)*x)*x)*x)*x;
0066     case 6:
0067         return (6. + (-40. + (105. + (-120. + 50.*x)*x)*x)*x)*x*x;
0068     case 7:
0069         return (1. + (-15. + (105. + (-355. + (615. + (-525. + 175.*x)*x)*x)*x)*x)*x)*x;
0070     case 8:
0071         return (10. + (-120. + (615. + (-1624. + (2310. + (-1680. + 490.*x)*x)*x)*x)*x)*x)*x*x;
0072     case 9:
0073         return (1. + (-24. + (276. + (-1624. + (5376. + (-10416. + (11704. + (-7056. + 1764*x)*x)*x)*x)*x)*x)*x)*x)*x;
0074     case 10:
0075         return (15. + (-280. + (2310. + (-10416. + (27860. + (-45360. + (44100. + (23520. + 5292.*x)*x)*x)*x)*x)*x)*x)*x)*x*x;
0076     }
0077 
0078     return 0.0;
0079 }
0080 
0081 /*
0082  * https://en.wikipedia.org/wiki/Bessel_polynomials
0083  * using recursion
0084 */
0085 gsl_complex nsl_sf_poly_bessel_y(int n, gsl_complex x) {
0086     if (n == 0)
0087         return gsl_complex_rect(1.0, 0.0);
0088     if (n == 1)
0089         return gsl_complex_add_real(x, 1.0);
0090 
0091     gsl_complex z1 = gsl_complex_mul(x, gsl_complex_mul_real(nsl_sf_poly_bessel_y(n - 1, x), 2.*n-1.));
0092     gsl_complex z2 = nsl_sf_poly_bessel_y(n - 2, x);
0093     return gsl_complex_add(z1, z2);
0094 }
0095 
0096 gsl_complex nsl_sf_poly_reversed_bessel_theta(int n, gsl_complex x) {
0097     if (n == 0)
0098         return gsl_complex_rect(1.0, 0.0);
0099     if (n == 1)
0100         return gsl_complex_add_real(x, 1.0);
0101 
0102     gsl_complex z1 = gsl_complex_mul_real(nsl_sf_poly_reversed_bessel_theta(n - 1, x), 2.*n-1.);
0103     gsl_complex z2 = gsl_complex_mul(gsl_complex_mul(x, x), nsl_sf_poly_reversed_bessel_theta(n - 2, x));
0104     return gsl_complex_add(z1, z2);
0105 }
0106 
0107 /***************** interpolating polynomials *************/
0108 
0109 double nsl_sf_poly_interp_lagrange_0_int(double *x, double y) {
0110     /* rectangle rule */
0111     return (x[1]-x[0])*y;
0112 }
0113 
0114 double nsl_sf_poly_interp_lagrange_1(double v, double *x, double *y) {
0115     return (y[0]*(x[1]-v) + y[1]*(v-x[0]))/(x[1]-x[0]);
0116 }
0117 double nsl_sf_poly_interp_lagrange_1_deriv(double *x, double *y) {
0118     return (y[0]-y[1])/(x[1]-x[0]);
0119 }
0120 double nsl_sf_poly_interp_lagrange_1_int(double *x, double *y) {
0121     /* trapezoid rule (2-point) */
0122     return (x[1]-x[0])*(y[0]+y[1])/2.;
0123 }
0124 double nsl_sf_poly_interp_lagrange_1_absint(double *x, double *y) {
0125     double dx = x[1]-x[0];
0126     if (y[0]*y[1] < 0)  /* sign change */
0127         return dx*( (fabs(y[0])-fabs(y[1]))/(fabs(y[1]/y[0])+1) + fabs(y[1]) )/2.;
0128     if (y[0] < 0 && y[1] < 0)
0129         return dx*(fabs(y[0])+fabs(y[1]))/2.;
0130     else
0131         return dx*(y[0]+y[1])/2.;
0132 }
0133 
0134 double nsl_sf_poly_interp_lagrange_2(double v, double *x, double *y) {
0135     double h1 = x[1]-x[0], h2 = x[2]-x[1];  
0136     double h12 = h1+h2;
0137 
0138     return y[0]*(v-x[1])*(v-x[2])/(h1*h12) + y[1]*(x[0]-v)*(v-x[2])/(h1*h2) + y[2]*(x[0]-v)*(x[1]-v)/(h12*h2);
0139 }
0140 double nsl_sf_poly_interp_lagrange_2_deriv(double v, double *x, double *y) {
0141     double h1 = x[1]-x[0], h2 = x[2]-x[1];  
0142     double h12 = h1+h2;
0143 
0144     return y[0]*(2.*v-x[1]-x[2])/(h1*h12) + y[1]*(x[0]-2.*v+x[2])/(h1*h2) + y[2]*(2.*v-x[0]-x[1])/(h12*h2);
0145 }
0146 double nsl_sf_poly_interp_lagrange_2_deriv2(double *x, double *y) {
0147     double h1 = x[1]-x[0], h2 = x[2]-x[1];  
0148     double h12 = h1+h2;
0149 
0150     return 2.*( y[0]/(h1*h12) - y[1]/(h1*h2) + y[2]/(h12*h2) );
0151 }
0152 double nsl_sf_poly_interp_lagrange_2_int(double *x, double *y) {
0153     /* Simpson's 1/3 rule for non-uniform grid (3-point) */
0154     double h1 = x[1]-x[0], h2 = x[2]-x[1];
0155     double h12 = h1+h2, h1_2 = h1/h2;
0156 
0157     return h12/6.*( (2.-1./h1_2)*y[0] + (2.+h1_2+1./h1_2)*y[1] + (2.-h1_2)*y[2] );
0158 }
0159 
0160 double nsl_sf_poly_interp_lagrange_3(double v, double *x, double *y) {
0161     double h1 = x[1]-x[0], h2 = x[2]-x[1], h3 = x[3]-x[2];  
0162     double h12 = h1+h2, h23 = h2+h3, h13=h12+h3;
0163 
0164     return y[0]*(v-x[1])*(v-x[2])*(v-x[3])/(h1*h12*h13) + y[1]*(v-x[0])*(v-x[2])*(v-x[3])/(h1*h2*h23) 
0165         - y[2]*(v-x[0])*(v-x[1])*(v-x[3])/(h12*h2*h3) + y[3]*(v-x[0])*(v-x[1])*(v-x[2])/(h13*h23*h3);
0166 }
0167 double nsl_sf_poly_interp_lagrange_3_deriv(double v, double *x, double *y) {
0168     double h1 = x[1]-x[0], h2 = x[2]-x[1], h3 = x[3]-x[2], S = x[0]+x[1]+x[2]+x[3];
0169     double h12 = h1+h2, h23 = h2+h3, h13=h12+h3;
0170 
0171     return -y[0]*(3*v*v-2.*v*(S-x[0])+x[1]*x[2]+(S-x[0]-x[3])*x[3])/(h1*h12*h13)
0172         + y[1]*(3*v*v-2.*v*(S-x[1])+(x[0]+x[2])*x[3])/(h1*h2*h23)
0173         + y[2]*(3*v*v+x[0]*x[1]-2.*v*(S-x[2])+(x[0]+x[1])*x[3])/(h12*h2*h3)
0174         + y[3]*(3*v*v+x[0]*x[1]+(x[0]+x[1])*x[2]-2.*v*(S-x[3]))/(h13*h23*h3);
0175 }
0176 double nsl_sf_poly_interp_lagrange_3_deriv2(double v, double *x, double *y) {
0177     double h1 = x[1]-x[0], h2 = x[2]-x[1], h3 = x[3]-x[2], S = x[0]+x[1]+x[2]+x[3];
0178     double h12 = h1+h2, h23 = h2+h3, h13=h12+h3;
0179 
0180     return 2.*( y[0]*(S-3.*v-x[0])/(h1*h12*h13) + y[1]*(3.*v-S+x[1])/(h1*h2*h23) 
0181             + y[2]*(S-3.*v-x[2])/(h12*h2*h3) + y[3]*(3.*v-S+x[3])/(h13*h23*h3) );
0182 }
0183 double nsl_sf_poly_interp_lagrange_3_deriv3(double *x, double *y) {
0184     double h1 = x[1]-x[0], h2 = x[2]-x[1], h3 = x[3]-x[2];
0185     double h12 = h1+h2, h23 = h2+h3, h13=h12+h3;
0186 
0187     return 6.*( -y[0]/(h1*h12*h13) + y[1]/(h1*h2*h23) - y[2]/(h12*h2*h3) +y[3]/(h13*h23*h3) );
0188 }
0189 double nsl_sf_poly_interp_lagrange_3_int(double *x, double *y) {
0190     /* Simpson's 3/8 rule for non-uniform grid (4-point) */
0191     double h1 = x[1]-x[0], h2 = x[2]-x[1], h3 = x[3]-x[2];
0192     double h12 = h1+h2, h23 = h2+h3, h13 = h12+h3;
0193 
0194     return h13/12.*( (3.*x[0]*x[0]-4.*x[0]*(x[1]+x[2])+6.*x[1]*x[2]+2.*x[0]*x[3]-2.*(x[1]+x[2])*x[3]+x[3]*x[3])/(h1*h12)*y[0] 
0195             + h13*h13*(h12-h3)/(h1*h2*h23)*y[1] + h13*h13*(h23-h1)/(h12*h2*h3)*y[2] 
0196             + (x[0]*x[0]-2.*x[0]*(x[1]+x[2])+6.*x[1]*x[2]+2.*x[0]*x[3]-4.*(x[1]+x[2])*x[3]+3.*x[3]*x[3])/(h23*h3)*y[3] );
0197 }
0198 
0199 /* 1/2 sum_{i != j}^n (x_i x_j) for n=4 */
0200 double sum_of_2product_combinations_4_2(double a, double b, double c, double d) {
0201     return a*(b+c+d) + b*(c+d) + c*d;
0202 }
0203 /* 1/6 sum_{i != j, j != k, i != k}^n (x_i x_j x_k) for n=4 */
0204 double sum_of_3product_combinations_4_6(double a, double b, double c, double d) {
0205     return a*(b*(c+d) + c*d) + b*c*d;
0206 }
0207 
0208 double nsl_sf_poly_interp_lagrange_4(double v, double *x, double *y) {
0209     double h1 = x[1]-x[0], h2 = x[2]-x[1], h3 = x[3]-x[2], h4 = x[4]-x[3];
0210     double h12 = h1+h2, h23 = h2+h3, h34 = h3+h4, h13=h12+h3, h24 = h23+h4, h14 = h12+h34;
0211 
0212     return y[0]*(v-x[1])*(v-x[2])*(v-x[3])*(v-x[4])/(h1*h12*h13*h14)
0213         - y[1]*(v-x[0])*(v-x[2])*(v-x[3])*(v-x[4])/(h1*h2*h23*h24)
0214         + y[2]*(v-x[0])*(v-x[1])*(v-x[3])*(v-x[4])/(h12*h2*h3*h34)
0215         - y[3]*(v-x[0])*(v-x[1])*(v-x[2])*(v-x[4])/(h13*h23*h3*h4)
0216         + y[4]*(v-x[0])*(v-x[1])*(v-x[2])*(v-x[3])/(h14*h24*h34*h4);
0217 }
0218 double nsl_sf_poly_interp_lagrange_4_deriv(double v, double *x, double *y) {
0219     double h1 = x[1]-x[0], h2 = x[2]-x[1], h3 = x[3]-x[2], h4 = x[4]-x[3], S = x[0]+x[1]+x[2]+x[3]+x[4];
0220     double h12 = h1+h2, h23 = h2+h3, h34 = h3+h4, h13=h12+h3, h24 = h23+h4, h14 = h12+h34;
0221 
0222     return    y[0]*(4.*v*v*v - 3.*v*v*(S-x[0]) - sum_of_3product_combinations_4_6(x[1],x[2],x[3],x[4])
0223             + 2.*v*sum_of_2product_combinations_4_2(x[1],x[2],x[3],x[4]) )/(h1*h12*h13*h14)
0224         - y[1]*(4.*v*v*v - 3.*v*v*(S-x[1]) - sum_of_3product_combinations_4_6(x[0],x[2],x[3],x[4])
0225             + 2.*v*sum_of_2product_combinations_4_2(x[0],x[2],x[3],x[4]) )/(h1*h2*h23*h24)
0226         + y[2]*(4.*v*v*v - 3.*v*v*(S-x[2]) - sum_of_3product_combinations_4_6(x[0],x[1],x[3],x[4])
0227             + 2.*v*sum_of_2product_combinations_4_2(x[0],x[1],x[3],x[4]) )/(h12*h2*h3*h34)
0228         - y[3]*(4.*v*v*v - 3.*v*v*(S-x[3]) - sum_of_3product_combinations_4_6(x[0],x[1],x[2],x[4])
0229             + 2.*v*sum_of_2product_combinations_4_2(x[0],x[1],x[2],x[4]) )/(h13*h23*h3*h4)
0230         + y[4]*(4.*v*v*v - 3.*v*v*(S-x[4]) - sum_of_3product_combinations_4_6(x[0],x[1],x[2],x[3])
0231             + 2.*v*sum_of_2product_combinations_4_2(x[0],x[1],x[2],x[3]) )/(h14*h24*h34*h4);
0232 }
0233 double nsl_sf_poly_interp_lagrange_4_deriv2(double v, double *x, double *y) {
0234     double h1 = x[1]-x[0], h2 = x[2]-x[1], h3 = x[3]-x[2], h4 = x[4]-x[3], S = x[0]+x[1]+x[2]+x[3]+x[4];
0235     double h12 = h1+h2, h23 = h2+h3, h34 = h3+h4, h13=h12+h3, h24 = h23+h4, h14 = h12+h34;
0236 
0237     return 2.*(
0238           y[0]*(6.*v*v-3.*v*(S-x[0]) + sum_of_2product_combinations_4_2(x[1],x[2],x[3],x[4]))/(h1*h12*h13*h14)
0239         - y[1]*(6.*v*v-3.*v*(S-x[1]) + sum_of_2product_combinations_4_2(x[0],x[2],x[3],x[4]))/(h1*h2*h23*h24)
0240         + y[2]*(6.*v*v-3.*v*(S-x[2]) + sum_of_2product_combinations_4_2(x[0],x[1],x[3],x[4]))/(h12*h2*h3*h34)
0241         - y[3]*(6.*v*v-3.*v*(S-x[3]) + sum_of_2product_combinations_4_2(x[0],x[1],x[2],x[4]))/(h13*h23*h3*h4)
0242         + y[4]*(6.*v*v-3.*v*(S-x[4]) + sum_of_2product_combinations_4_2(x[0],x[1],x[2],x[3]))/(h14*h24*h34*h4) );
0243 }
0244 double nsl_sf_poly_interp_lagrange_4_deriv3(double v, double *x, double *y) {
0245     double h1 = x[1]-x[0], h2 = x[2]-x[1], h3 = x[3]-x[2], h4 = x[4]-x[3], S = x[0]+x[1]+x[2]+x[3]+x[4];
0246     double h12 = h1+h2, h23 = h2+h3, h34 = h3+h4, h13=h12+h3, h24 = h23+h4, h14 = h12+h34;
0247 
0248     return 6.*( y[0]*(4.*v-S+x[0])/(h1*h12*h13*h14) + y[1]*(S-4.*v-x[1])/(h1*h2*h23*h24) + y[2]*(4.*v-S+x[2])/(h12*h2*h3*h34)
0249             + y[3]*(S-4.*v-x[3])/(h13*h23*h3*h4) + y[4]*(4.*v-S+x[4])/(h14*h24*h34*h4) );
0250 }
0251 double nsl_sf_poly_interp_lagrange_4_deriv4(double *x, double *y) {
0252     double h1 = x[1]-x[0], h2 = x[2]-x[1], h3 = x[3]-x[2], h4 = x[4]-x[3];
0253     double h12 = h1+h2, h23 = h2+h3, h34 = h3+h4, h13=h12+h3, h24 = h23+h4, h14 = h12+h34;
0254 
0255     return 24.*( y[0]/(h1*h12*h13*h14) - y[1]/(h1*h2*h23*h24) + y[2]/(h12*h2*h3*h34)- y[3]/(h13*h23*h3*h4) + y[4]/(h14*h24*h34*h4) );
0256 }
0257 
0258 /* 1/2 sum_{i != j}^n (x_i x_j) for n=6 */
0259 double sum_of_product_combinations_6_2(double a, double b, double c, double d, double e, double f) {
0260     return a*(b+c+d+e+f) + b*(c+d+e+f) + c*(d+e+f) + d*(e+f) + e*f;
0261 }
0262 
0263 double nsl_sf_poly_interp_lagrange_6_deriv4(double v, double *x, double *y) {
0264     double h1 = x[1]-x[0], h2 = x[2]-x[1], h3 = x[3]-x[2], h4 = x[4]-x[3], h5 = x[5]-x[4], h6 = x[6]-x[5];
0265     double h12 = h1+h2, h23 = h2+h3, h34 = h3+h4, h45 = h4+h5, h56 = h5+h6;
0266     double h13 = h12+h3, h24 = h23+h4, h35 = h34+h5, h46 = h45+h6, h14 = h13+h4, h25 = h24+h5, h36 = h35+h6;
0267     double h15 = h14+h5, h26 = h25 + h6, h16 = h15+h6, S = x[0]+x[1]+x[2]+x[3]+x[4]+x[5]+x[6];
0268 
0269     return 24.*(
0270           y[0]*(15.*v*v-5.*v*(S-x[0]) + sum_of_product_combinations_6_2(x[1],x[2],x[3],x[4],x[5],x[6]))/(h1*h12*h13*h14*h15*h16)
0271         - y[1]*(15.*v*v-5.*v*(S-x[1]) + sum_of_product_combinations_6_2(x[0],x[2],x[3],x[4],x[5],x[6]))/(h1*h2*h23*h24*h25*h26)
0272         + y[2]*(15.*v*v-5.*v*(S-x[2]) + sum_of_product_combinations_6_2(x[0],x[1],x[3],x[4],x[5],x[6]))/(h12*h2*h3*h34*h35*h36)
0273         - y[3]*(15.*v*v-5.*v*(S-x[3]) + sum_of_product_combinations_6_2(x[0],x[1],x[2],x[4],x[5],x[6]))/(h13*h23*h3*h4*h45*h46)
0274         + y[4]*(15.*v*v-5.*v*(S-x[4]) + sum_of_product_combinations_6_2(x[0],x[1],x[2],x[3],x[5],x[6]))/(h14*h24*h34*h4*h5*h56)
0275         - y[5]*(15.*v*v-5.*v*(S-x[5]) + sum_of_product_combinations_6_2(x[0],x[1],x[2],x[3],x[4],x[6]))/(h15*h25*h35*h45*h5*h6)
0276         + y[6]*(15.*v*v-5.*v*(S-x[6]) + sum_of_product_combinations_6_2(x[0],x[1],x[2],x[3],x[4],x[5]))/(h16*h26*h36*h46*h56*h6) );
0277 }
0278 double nsl_sf_poly_interp_lagrange_6_deriv5(double v, double *x, double *y) {
0279     double h1 = x[1]-x[0], h2 = x[2]-x[1], h3 = x[3]-x[2], h4 = x[4]-x[3], h5 = x[5]-x[4], h6 = x[6]-x[5];
0280     double h12 = h1+h2, h23 = h2+h3, h34 = h3+h4, h45 = h4+h5, h56 = h5+h6;
0281     double h13 = h12+h3, h24 = h23+h4, h35 = h34+h5, h46 = h45+h6, h14 = h13+h4, h25 = h24+h5, h36 = h35+h6;
0282     double h15 = h14+h5, h26 = h25 + h6, h16 = h15+h6, S = x[0]+x[1]+x[2]+x[3]+x[4]+x[5]+x[6];
0283 
0284     return 120.*( y[0]*(6.*v-S+x[0])/(h1*h12*h13*h14*h15*h16) - y[1]*(6.*v-S+x[1])/(h1*h2*h23*h24*h25*h26)
0285             + y[2]*(6.*v-S+x[2])/(h12*h2*h3*h34*h35*h36) - y[3]*(6.*v-S+x[3])/(h13*h23*h3*h4*h45*h46)
0286             + y[4]*(6.*v-S+x[4])/(h14*h24*h34*h4*h5*h56) - y[5]*(6.*v-S+x[5])/(h15*h25*h35*h45*h5*h6)
0287             + y[6]*(6.*v-S+x[6])/(h16*h26*h36*h46*h56*h6) );
0288 }
0289 double nsl_sf_poly_interp_lagrange_6_deriv6(double *x, double *y) {
0290     double h1 = x[1]-x[0], h2 = x[2]-x[1], h3 = x[3]-x[2], h4 = x[4]-x[3], h5 = x[5]-x[4], h6 = x[6]-x[5];
0291     double h12 = h1+h2, h23 = h2+h3, h34 = h3+h4, h45 = h4+h5, h56 = h5+h6;
0292     double h13 = h12+h3, h24 = h23+h4, h35 = h34+h5, h46 = h45+h6, h14 = h13+h4, h25 = h24+h5, h36 = h35+h6;
0293     double h15 = h14+h5, h26 = h25 + h6, h16 = h15+h6;
0294 
0295     return 720.*( y[0]/(h1*h12*h13*h14*h15*h16) - y[1]/(h1*h2*h23*h24*h25*h26)
0296             + y[2]/(h12*h2*h3*h34*h35*h36) - y[3]/(h13*h23*h3*h4*h45*h46)
0297             + y[4]/(h14*h24*h34*h4*h5*h56) - y[5]/(h15*h25*h35*h45*h5*h6)
0298             + y[6]/(h16*h26*h36*h46*h56*h6) );
0299 }