File indexing completed on 2024-05-12 03:47:50

0001 /*
0002     File                 : nsl_diff.c
0003     Project              : LabPlot
0004     Description          : NSL numerical differentiation functions
0005     --------------------------------------------------------------------
0006     SPDX-FileCopyrightText: 2016 Stefan Gerlach <stefan.gerlach@uni.kn>
0007 
0008     SPDX-License-Identifier: GPL-2.0-or-later
0009 */
0010 
0011 /* TODO:
0012  * add more orders
0013  */
0014 
0015 #include "nsl_diff.h"
0016 #include "nsl_common.h"
0017 #include "nsl_sf_poly.h"
0018 
0019 const char* nsl_diff_deriv_order_name[] = {i18n("First"), i18n("Second"), i18n("Third"), i18n("Fourth"), i18n("Fifth"), i18n("Sixth")};
0020 
0021 double nsl_diff_first_central(double xm, double fm, double xp, double fp) {
0022     return (fp - fm) / (xp - xm);
0023 }
0024 
0025 int nsl_diff_deriv_first_equal(const double* x, double* y, const size_t n) {
0026     if (n < 3)
0027         return -1;
0028 
0029     double dy = 0, oldy = 0, oldy2 = 0;
0030     size_t i;
0031     for (i = 0; i < n; i++) {
0032         if (i == 0) /* forward */
0033             dy = (-y[2] + 4. * y[1] - 3. * y[0]) / (x[2] - x[0]);
0034         else if (i == n - 1) { /* backward */
0035             y[i] = (3. * y[i] - 4. * y[i - 1] + y[i - 2]) / (x[i] - x[i - 2]);
0036             y[i - 1] = oldy;
0037         } else
0038             dy = (y[i + 1] - y[i - 1]) / (x[i + 1] - x[i - 1]);
0039 
0040         if (i > 1)
0041             y[i - 2] = oldy2;
0042         if (i > 0 && i < n - 1)
0043             oldy2 = oldy;
0044 
0045         oldy = dy;
0046     }
0047 
0048     return 0;
0049 }
0050 
0051 int nsl_diff_first_deriv(const double* x, double* y, const size_t n, int order) {
0052     switch (order) {
0053     case 2:
0054         return nsl_diff_first_deriv_second_order(x, y, n);
0055     case 4:
0056         return nsl_diff_first_deriv_fourth_order(x, y, n);
0057     /*TODO: higher order */
0058     default:
0059         printf("nsl_diff_first_deriv() unsupported order %d\n", order);
0060         return -1;
0061     }
0062 }
0063 
0064 int nsl_diff_first_deriv_second_order(const double* x, double* y, const size_t n) {
0065     if (n < 3)
0066         return -1;
0067 
0068     double dy = 0, oldy = 0, oldy2 = 0, xdata[3], ydata[3];
0069     size_t i, j;
0070     for (i = 0; i < n; i++) {
0071         if (i == 0) {
0072             /* 3-point forward */
0073             for (j = 0; j < 3; j++)
0074                 xdata[j] = x[j], ydata[j] = y[j];
0075             dy = nsl_sf_poly_interp_lagrange_2_deriv(x[0], xdata, ydata);
0076         } else if (i < n - 1) {
0077             /* 3-point center */
0078             for (j = 0; j < 3; j++)
0079                 xdata[j] = x[i - 1 + j], ydata[j] = y[i - 1 + j];
0080             dy = nsl_sf_poly_interp_lagrange_2_deriv(x[i], xdata, ydata);
0081         } else if (i == n - 1) {
0082             /* 3-point backward */
0083             y[i] = nsl_sf_poly_interp_lagrange_2_deriv(x[i], xdata, ydata);
0084             y[i - 1] = oldy;
0085         }
0086 
0087         if (i > 1)
0088             y[i - 2] = oldy2;
0089         if (i > 0 && i < n - 1)
0090             oldy2 = oldy;
0091 
0092         oldy = dy;
0093     }
0094 
0095     return 0;
0096 }
0097 
0098 int nsl_diff_first_deriv_fourth_order(const double* x, double* y, const size_t n) {
0099     if (n < 5)
0100         return -1;
0101 
0102     double dy[5] = {0}, xdata[5], ydata[5];
0103     size_t i, j;
0104     for (i = 0; i < n; i++) {
0105         if (i == 0)
0106             for (j = 0; j < 5; j++)
0107                 xdata[j] = x[j], ydata[j] = y[j];
0108         else if (i > 1 && i < n - 2)
0109             for (j = 0; j < 5; j++)
0110                 xdata[j] = x[i - 2 + j], ydata[j] = y[i - 2 + j];
0111 
0112         /* 5-point rule */
0113         dy[0] = nsl_sf_poly_interp_lagrange_4_deriv(x[i], xdata, ydata);
0114 
0115         if (i == n - 1)
0116             for (j = 0; j < 4; j++)
0117                 y[i - j] = dy[j];
0118 
0119         if (i > 3)
0120             y[i - 4] = dy[4];
0121         for (j = 4; j > 0; j--)
0122             if (i >= j - 1)
0123                 dy[j] = dy[j - 1];
0124     }
0125 
0126     return 0;
0127 }
0128 
0129 int nsl_diff_first_deriv_avg(const double* x, double* y, const size_t n) {
0130     if (n < 1)
0131         return -1;
0132 
0133     size_t i;
0134     double dy = 0, oldy = 0;
0135     for (i = 0; i < n; i++) {
0136         if (i == 0)
0137             dy = (y[1] - y[0]) / (x[1] - x[0]);
0138         else if (i == n - 1)
0139             y[i] = (y[i] - y[i - 1]) / (x[i] - x[i - 1]);
0140         else
0141             dy = ((y[i + 1] - y[i]) / (x[i + 1] - x[i]) + (y[i] - y[i - 1]) / (x[i] - x[i - 1])) / 2.;
0142 
0143         if (i > 0)
0144             y[i - 1] = oldy;
0145 
0146         oldy = dy;
0147     }
0148 
0149     return 0;
0150 }
0151 
0152 int nsl_diff_second_deriv(const double* x, double* y, const size_t n, int order) {
0153     switch (order) {
0154     case 1:
0155         return nsl_diff_second_deriv_first_order(x, y, n);
0156     case 2:
0157         return nsl_diff_second_deriv_second_order(x, y, n);
0158     case 3:
0159         return nsl_diff_second_deriv_third_order(x, y, n);
0160     /*TODO: higher order */
0161     default:
0162         printf("nsl_diff_second_deriv() unsupported order %d\n", order);
0163         return -1;
0164     }
0165 }
0166 
0167 int nsl_diff_second_deriv_first_order(const double* x, double* y, const size_t n) {
0168     if (n < 3)
0169         return -1;
0170 
0171     double dy[3] = {0}, xdata[3], ydata[3];
0172     size_t i, j;
0173     for (i = 0; i < n; i++) {
0174         if (i == 0)
0175             for (j = 0; j < 3; j++)
0176                 xdata[j] = x[j], ydata[j] = y[j];
0177         else if (i > 1 && i < n - 1)
0178             for (j = 0; j < 3; j++)
0179                 xdata[j] = x[i - 1 + j], ydata[j] = y[i - 1 + j];
0180 
0181         /* 3-point rule */
0182         dy[0] = nsl_sf_poly_interp_lagrange_2_deriv2(xdata, ydata);
0183 
0184         if (i == n - 1) {
0185             y[i] = dy[0];
0186             y[i - 1] = dy[1];
0187         }
0188 
0189         if (i > 1)
0190             y[i - 2] = dy[2];
0191         if (i > 0)
0192             dy[2] = dy[1];
0193 
0194         dy[1] = dy[0];
0195     }
0196 
0197     return 0;
0198 }
0199 
0200 int nsl_diff_second_deriv_second_order(const double* x, double* y, const size_t n) {
0201     if (n < 4)
0202         return -1;
0203 
0204     double dy[4] = {0}, xdata[4], ydata[4];
0205     size_t i, j;
0206     for (i = 0; i < n; i++) {
0207         if (i == 0) {
0208             /* 4-point forward */
0209             for (j = 0; j < 4; j++)
0210                 xdata[j] = x[j], ydata[j] = y[j];
0211             dy[0] = nsl_sf_poly_interp_lagrange_3_deriv2(x[i], xdata, ydata);
0212         } else if (i == n - 1) {
0213             /* 4-point backward */
0214             for (j = 0; j < 4; j++)
0215                 xdata[j] = x[i - 3 + j], ydata[j] = y[i - 3 + j];
0216             y[i] = nsl_sf_poly_interp_lagrange_3_deriv2(x[i], xdata, ydata);
0217             y[i - 1] = dy[1];
0218             y[i - 2] = dy[2];
0219         } else {
0220             /* 3-point center */
0221             for (j = 0; j < 3; j++)
0222                 xdata[j] = x[i - 1 + j], ydata[j] = y[i - 1 + j];
0223             dy[0] = nsl_sf_poly_interp_lagrange_2_deriv2(xdata, ydata);
0224         }
0225 
0226         if (i > 2)
0227             y[i - 3] = dy[3];
0228         for (j = 3; j > 0; j--)
0229             if (i >= j - 1)
0230                 dy[j] = dy[j - 1];
0231     }
0232 
0233     return 0;
0234 }
0235 
0236 int nsl_diff_second_deriv_third_order(const double* x, double* y, const size_t n) {
0237     if (n < 5)
0238         return -1;
0239 
0240     double dy[5] = {0}, xdata[5], ydata[5];
0241     size_t i, j;
0242     for (i = 0; i < n; i++) {
0243         if (i == 0)
0244             for (j = 0; j < 5; j++)
0245                 xdata[j] = x[j], ydata[j] = y[j];
0246         else if (i > 2 && i < n - 3)
0247             for (j = 0; j < 5; j++)
0248                 xdata[j] = x[i - 2 + j], ydata[j] = y[i - 2 + j];
0249 
0250         /* 5-point rule */
0251         dy[0] = nsl_sf_poly_interp_lagrange_4_deriv2(x[i], xdata, ydata);
0252 
0253         if (i == n - 1)
0254             for (j = 0; j < 4; j++)
0255                 y[i - j] = dy[j];
0256 
0257         if (i > 3)
0258             y[i - 4] = dy[4];
0259         for (j = 4; j > 0; j--)
0260             if (i >= j - 1)
0261                 dy[j] = dy[j - 1];
0262     }
0263 
0264     return 0;
0265 }
0266 
0267 int nsl_diff_third_deriv(const double* x, double* y, const size_t n, int order) {
0268     switch (order) {
0269     case 2:
0270         return nsl_diff_third_deriv_second_order(x, y, n);
0271     /*TODO: higher order */
0272     default:
0273         printf("nsl_diff_third_deriv() unsupported order %d\n", order);
0274         return -1;
0275     }
0276 }
0277 
0278 int nsl_diff_third_deriv_second_order(const double* x, double* y, const size_t n) {
0279     if (n < 5)
0280         return -1;
0281 
0282     double dy[5] = {0}, xdata[5], ydata[5];
0283     size_t i, j;
0284     for (i = 0; i < n; i++) {
0285         if (i == 0)
0286             for (j = 0; j < 5; j++)
0287                 xdata[j] = x[j], ydata[j] = y[j];
0288         else if (i > 2 && i < n - 3)
0289             for (j = 0; j < 5; j++)
0290                 xdata[j] = x[i - 2 + j], ydata[j] = y[i - 2 + j];
0291 
0292         /* 5-point rule */
0293         dy[0] = nsl_sf_poly_interp_lagrange_4_deriv3(x[i], xdata, ydata);
0294 
0295         if (i == n - 1)
0296             for (j = 0; j < 4; j++)
0297                 y[i - j] = dy[j];
0298 
0299         if (i > 3)
0300             y[i - 4] = dy[4];
0301         for (j = 4; j > 0; j--)
0302             if (i >= j - 1)
0303                 dy[j] = dy[j - 1];
0304     }
0305 
0306     return 0;
0307 }
0308 
0309 int nsl_diff_fourth_deriv(const double* x, double* y, const size_t n, int order) {
0310     switch (order) {
0311     case 1:
0312         return nsl_diff_fourth_deriv_first_order(x, y, n);
0313     case 3:
0314         return nsl_diff_fourth_deriv_third_order(x, y, n);
0315     /*TODO: higher order */
0316     default:
0317         printf("nsl_diff_fourth_deriv() unsupported order %d\n", order);
0318         return -1;
0319     }
0320 }
0321 
0322 int nsl_diff_fourth_deriv_first_order(const double* x, double* y, const size_t n) {
0323     if (n < 5)
0324         return -1;
0325 
0326     double dy[5] = {0}, xdata[5], ydata[5];
0327     size_t i, j;
0328     for (i = 0; i < n; i++) {
0329         if (i == 0)
0330             for (j = 0; j < 5; j++)
0331                 xdata[j] = x[j], ydata[j] = y[j];
0332         else if (i > 2 && i < n - 3)
0333             for (j = 0; j < 5; j++)
0334                 xdata[j] = x[i - 2 + j], ydata[j] = y[i - 2 + j];
0335 
0336         /* 5-point rule */
0337         dy[0] = nsl_sf_poly_interp_lagrange_4_deriv4(xdata, ydata);
0338 
0339         if (i == n - 1)
0340             for (j = 0; j < 4; j++)
0341                 y[i - j] = dy[j];
0342 
0343         if (i > 3)
0344             y[i - 4] = dy[4];
0345         for (j = 4; j > 0; j--)
0346             if (i >= j - 1)
0347                 dy[j] = dy[j - 1];
0348     }
0349 
0350     return 0;
0351 }
0352 
0353 int nsl_diff_fourth_deriv_third_order(const double* x, double* y, const size_t n) {
0354     if (n < 7)
0355         return -1;
0356 
0357     double dy[7] = {0}, xdata[7], ydata[7];
0358     size_t i, j;
0359     for (i = 0; i < n; i++) {
0360         if (i == 0)
0361             for (j = 0; j < 7; j++)
0362                 xdata[j] = x[j], ydata[j] = y[j];
0363         else if (i > 3 && i < n - 4)
0364             for (j = 0; j < 7; j++)
0365                 xdata[j] = x[i - 3 + j], ydata[j] = y[i - 3 + j];
0366 
0367         /* 7-point rule */
0368         dy[0] = nsl_sf_poly_interp_lagrange_6_deriv4(x[i], xdata, ydata);
0369 
0370         if (i == n - 1)
0371             for (j = 0; j < 6; j++)
0372                 y[i - j] = dy[j];
0373 
0374         if (i > 5)
0375             y[i - 6] = dy[6];
0376         for (j = 6; j > 0; j--)
0377             if (i >= j - 1)
0378                 dy[j] = dy[j - 1];
0379     }
0380 
0381     return 0;
0382 }
0383 
0384 int nsl_diff_fifth_deriv(const double* x, double* y, const size_t n, int order) {
0385     switch (order) {
0386     case 2:
0387         return nsl_diff_fifth_deriv_second_order(x, y, n);
0388     /*TODO: higher order */
0389     default:
0390         printf("nsl_diff_fifth_deriv() unsupported order %d\n", order);
0391         return -1;
0392     }
0393 }
0394 
0395 int nsl_diff_fifth_deriv_second_order(const double* x, double* y, const size_t n) {
0396     if (n < 7)
0397         return -1;
0398 
0399     double dy[7] = {0}, xdata[7], ydata[7];
0400     size_t i, j;
0401     for (i = 0; i < n; i++) {
0402         if (i == 0)
0403             for (j = 0; j < 7; j++)
0404                 xdata[j] = x[j], ydata[j] = y[j];
0405         else if (i > 3 && i < n - 4)
0406             for (j = 0; j < 7; j++)
0407                 xdata[j] = x[i - 3 + j], ydata[j] = y[i - 3 + j];
0408 
0409         /* 7-point rule */
0410         dy[0] = nsl_sf_poly_interp_lagrange_6_deriv5(x[i], xdata, ydata);
0411 
0412         if (i == n - 1)
0413             for (j = 0; j < 6; j++)
0414                 y[i - j] = dy[j];
0415 
0416         if (i > 5)
0417             y[i - 6] = dy[6];
0418         for (j = 6; j > 0; j--)
0419             if (i >= j - 1)
0420                 dy[j] = dy[j - 1];
0421     }
0422 
0423     return 0;
0424 }
0425 
0426 int nsl_diff_sixth_deriv(const double* x, double* y, const size_t n, int order) {
0427     switch (order) {
0428     case 1:
0429         return nsl_diff_sixth_deriv_first_order(x, y, n);
0430     /*TODO: higher order */
0431     default:
0432         printf("nsl_diff_sixth_deriv() unsupported order %d\n", order);
0433         return -1;
0434     }
0435 }
0436 
0437 int nsl_diff_sixth_deriv_first_order(const double* x, double* y, const size_t n) {
0438     if (n < 7)
0439         return -1;
0440 
0441     double dy[7] = {0}, xdata[7], ydata[7];
0442     size_t i, j;
0443     for (i = 0; i < n; i++) {
0444         if (i == 0)
0445             for (j = 0; j < 7; j++)
0446                 xdata[j] = x[j], ydata[j] = y[j];
0447         else if (i > 3 && i < n - 4)
0448             for (j = 0; j < 7; j++)
0449                 xdata[j] = x[i - 3 + j], ydata[j] = y[i - 3 + j];
0450 
0451         /* 7-point rule */
0452         dy[0] = nsl_sf_poly_interp_lagrange_6_deriv6(xdata, ydata);
0453 
0454         if (i == n - 1)
0455             for (j = 0; j < 6; j++)
0456                 y[i - j] = dy[j];
0457 
0458         if (i > 5)
0459             y[i - 6] = dy[6];
0460         for (j = 6; j > 0; j--)
0461             if (i >= j - 1)
0462                 dy[j] = dy[j - 1];
0463     }
0464 
0465     return 0;
0466 }