/* lmath.c This file is part of a program that implements a Software-Defined Radio. Copyright (C) 2015, 2016, 2023 Warren Pratt, NR0V This program 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 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. The author can be reached by email at warren@wpratt.com */ #include "comm.h" void dR (int n, double* r, double* y, double* z) { int i, j, k; double alpha, beta, gamma; memset (z, 0, (n - 1) * sizeof (double)); // work space y[0] = -r[1]; alpha = -r[1]; beta = 1.0; for (k = 0; k < n - 1; k++) { beta *= 1.0 - alpha * alpha; gamma = 0.0; for (i = k + 1, j = 0; i > 0; i--, j++) gamma += r[i] * y[j]; alpha = - (r[k + 2] + gamma) / beta; for (i = 0, j = k; i <= k; i++, j--) z[i] = y[i] + alpha * y[j]; memcpy (y, z, (k + 1) * sizeof (double)); y[k + 1] = alpha; } } void trI ( int n, double* r, double* B, double* y, double* v, double* dR_z ) { int i, j, ni, nj; double gamma, t, scale, b; memset (y, 0, (n - 1) * sizeof (double)); // work space memset (v, 0, (n - 1) * sizeof (double)); // work space scale = 1.0 / r[0]; for (i = 0; i < n; i++) r[i] *= scale; dR(n - 1, r, y, dR_z); t = 0.0; for (i = 0; i < n - 1; i++) t += r[i + 1] * y[i]; gamma = 1.0 / (1.0 + t); for (i = 0, j = n - 2; i < n - 1; i++, j--) v[i] = gamma * y[j]; B[0] = gamma; for (i = 1, j = n - 2; i < n; i++, j--) B[i] = v[j]; for (i = 1; i <= (n - 1) / 2; i++) for (j = i; j < n - i; j++) B[i * n + j] = B[(i - 1) * n + (j - 1)] + (v[n - j - 1] * v[n - i - 1] - v[i - 1] * v[j - 1]) / gamma; for (i = 0; i <= (n - 1)/2; i++) for (j = i; j < n - i; j++) { b = B[i * n + j] *= scale; B[j * n + i] = b; ni = n - i - 1; nj = n - j - 1; B[ni * n + nj] = b; B[nj * n + ni] = b; } } void asolve(int xsize, int asize, double* x, double* a, double* r, double* z) { int i, j, k; double beta, alpha, t; memset(r, 0, (asize + 1) * sizeof(double)); // work space memset(z, 0, (asize + 1) * sizeof(double)); // work space for (i = 0; i <= asize; i++) { for (j = 0; j < xsize; j++) r[i] += x[j] * x[j - i]; } z[0] = 1.0; beta = r[0]; for (k = 0; k < asize; k++) { alpha = 0.0; for (j = 0; j <= k; j++) alpha -= z[j] * r[k + 1 - j]; alpha /= beta; for (i = 0; i <= (k + 1) / 2; i++) { t = z[k + 1 - i] + alpha * z[i]; z[i] = z[i] + alpha * z[k + 1 - i]; z[k + 1 - i] = t; } beta *= 1.0 - alpha * alpha; } for (i = 0; i < asize; i++) { a[i] = - z[i + 1]; if (a[i] != a[i]) a[i] = 0.0; } } void median (int n, double* a, double* med) { int S0, S1, i, j, m, k; double x, t; S0 = 0; S1 = n - 1; k = n / 2; while (S1 > S0 + 1) { m = (S0 + S1) / 2; t = a[m]; a[m] = a[S0 + 1]; a[S0 + 1] = t; if (a[S0] > a[S1]) { t = a[S0]; a[S0] = a[S1]; a[S1] = t; } if (a[S0 + 1] > a[S1]) { t = a[S0 + 1]; a[S0 + 1] = a[S1]; a[S1] = t; } if (a[S0] > a[S0 + 1]) { t = a[S0]; a[S0] = a[S0 + 1]; a[S0 + 1] = t; } i = S0 + 1; j = S1; x = a[S0 + 1]; do i++; while (a[i] < x); do j--; while (a[j] > x); while (j >= i) { t = a[i]; a[i] = a[j]; a[j] = t; do i++; while (a[i] < x); do j--; while (a[j] > x); } a[S0 + 1] = a[j]; a[j] = x; if (j >= k) S1 = j - 1; if (j <= k) S0 = i; } if (S1 == S0 + 1 && a[S1] < a[S0]) { t = a[S0]; a[S0] = a[S1]; a[S1] = t; } *med = a[k]; } BLDR create_builder(int points, int ints) { // for the create function, 'points' and 'ints' are the MAXIMUM values that will be encountered BLDR a = (BLDR)malloc0 (sizeof(bldr)); a->catxy = (double*)malloc0(2 * points * sizeof(double)); a->sx = (double*)malloc0( points * sizeof(double)); a->sy = (double*)malloc0( points * sizeof(double)); a->h = (double*)malloc0( ints * sizeof(double)); a->p = (int*) malloc0( ints * sizeof(int)); a->np = (int*) malloc0( ints * sizeof(int)); a->taa = (double*)malloc0( ints * sizeof(double)); a->tab = (double*)malloc0( ints * sizeof(double)); a->tag = (double*)malloc0( ints * sizeof(double)); a->tad = (double*)malloc0( ints * sizeof(double)); a->tbb = (double*)malloc0( ints * sizeof(double)); a->tbg = (double*)malloc0( ints * sizeof(double)); a->tbd = (double*)malloc0( ints * sizeof(double)); a->tgg = (double*)malloc0( ints * sizeof(double)); a->tgd = (double*)malloc0( ints * sizeof(double)); a->tdd = (double*)malloc0( ints * sizeof(double)); int nsize = 3 * ints + 1; int intp1 = ints + 1; int intm1 = ints - 1; a->A = (double*)malloc0(intp1 * intp1 * sizeof(double)); a->B = (double*)malloc0(intp1 * intp1 * sizeof(double)); a->C = (double*)malloc0(intm1 * intp1 * sizeof(double)); a->D = (double*)malloc0(intp1 * sizeof(double)); a->E = (double*)malloc0(intp1 * intp1 * sizeof(double)); a->F = (double*)malloc0(intm1 * intp1 * sizeof(double)); a->G = (double*)malloc0(intp1 * sizeof(double)); a->MAT = (double*)malloc0(nsize * nsize * sizeof(double)); a->RHS = (double*)malloc0(nsize * sizeof(double)); a->SLN = (double*)malloc0(nsize * sizeof(double)); a->z = (double*)malloc0(intp1 * sizeof(double)); a->zp = (double*)malloc0(intp1 * sizeof(double)); a->wrk = (double*)malloc0(nsize * sizeof(double)); a->ipiv = (int*) malloc0(nsize * sizeof(int)); return a; } void destroy_builder(BLDR a) { _aligned_free(a->ipiv); _aligned_free(a->wrk); _aligned_free(a->catxy); _aligned_free(a->sx); _aligned_free(a->sy); _aligned_free(a->h); _aligned_free(a->p); _aligned_free(a->np); _aligned_free(a->taa); _aligned_free(a->tab); _aligned_free(a->tag); _aligned_free(a->tad); _aligned_free(a->tbb); _aligned_free(a->tbg); _aligned_free(a->tbd); _aligned_free(a->tgg); _aligned_free(a->tgd); _aligned_free(a->tdd); _aligned_free(a->A); _aligned_free(a->B); _aligned_free(a->C); _aligned_free(a->D); _aligned_free(a->E); _aligned_free(a->F); _aligned_free(a->G); _aligned_free(a->MAT); _aligned_free(a->RHS); _aligned_free(a->SLN); _aligned_free(a->z); _aligned_free(a->zp); _aligned_free(a); } void flush_builder(BLDR a, int points, int ints) { memset(a->catxy, 0, 2 * points * sizeof(double)); memset(a->sx, 0, points * sizeof(double)); memset(a->sy, 0, points * sizeof(double)); memset(a->h, 0, ints * sizeof(double)); memset(a->p, 0, ints * sizeof(int)); memset(a->np, 0, ints * sizeof(int)); memset(a->taa, 0, ints * sizeof(double)); memset(a->tab, 0, ints * sizeof(double)); memset(a->tag, 0, ints * sizeof(double)); memset(a->tad, 0, ints * sizeof(double)); memset(a->tbb, 0, ints * sizeof(double)); memset(a->tbg, 0, ints * sizeof(double)); memset(a->tbd, 0, ints * sizeof(double)); memset(a->tgg, 0, ints * sizeof(double)); memset(a->tgd, 0, ints * sizeof(double)); memset(a->tdd, 0, ints * sizeof(double)); int nsize = 3 * ints + 1; int intp1 = ints + 1; int intm1 = ints - 1; memset(a->A, 0, intp1 * intp1 * sizeof(double)); memset(a->B, 0, intp1 * intp1 * sizeof(double)); memset(a->C, 0, intm1 * intp1 * sizeof(double)); memset(a->D, 0, intp1 * sizeof(double)); memset(a->E, 0, intp1 * intp1 * sizeof(double)); memset(a->F, 0, intm1 * intp1 * sizeof(double)); memset(a->G, 0, intp1 * sizeof(double)); memset(a->MAT, 0, nsize * nsize * sizeof(double)); memset(a->RHS, 0, nsize * sizeof(double)); memset(a->SLN, 0, nsize * sizeof(double)); memset(a->z, 0, intp1 * sizeof(double)); memset(a->zp, 0, intp1 * sizeof(double)); memset(a->wrk, 0, nsize * sizeof(double)); memset(a->ipiv, 0, nsize * sizeof(int)); } int fcompare(const void* a, const void* b) { if (*(double*)a < *(double*)b) return -1; else if (*(double*)a == *(double*)b) return 0; else return 1; } void decomp(int n, double* a, int* piv, int* info, double* wrk) { int i, j, k; int t_piv; double m_row, mt_row, m_col, mt_col; *info = 0; for (i = 0; i < n; i++) { piv[i] = i; m_row = 0.0; for (j = 0; j < n; j++) { mt_row = a[n * i + j]; if (mt_row < 0.0) mt_row = -mt_row; if (mt_row > m_row) m_row = mt_row; } if (m_row == 0.0) { *info = i; goto cleanup; } wrk[i] = m_row; } for (k = 0; k < n - 1; k++) { j = k; m_col = a[n * piv[k] + k] / wrk[piv[k]]; if (m_col < 0) m_col = -m_col; for (i = k + 1; i < n; i++) { mt_col = a[n * piv[i] + k] / wrk[piv[k]]; if (mt_col < 0.0) mt_col = -mt_col; if (mt_col > m_col) { m_col = mt_col; j = i; } } if (m_col == 0) { *info = -k; goto cleanup; } t_piv = piv[k]; piv[k] = piv[j]; piv[j] = t_piv; for (i = k + 1; i < n; i++) { a[n * piv[i] + k] /= a[n * piv[k] + k]; for (j = k + 1; j < n; j++) a[n * piv[i] + j] -= a[n * piv[i] + k] * a[n * piv[k] + j]; } } if (a[n * n - 1] == 0.0) *info = -n; cleanup: return; } void dsolve(int n, double* a, int* piv, double* b, double* x) { int j, k; double sum; for (k = 0; k < n; k++) { sum = 0.0; for (j = 0; j < k; j++) sum += a[n * piv[k] + j] * x[j]; x[k] = b[piv[k]] - sum; } for (k = n - 1; k >= 0; k--) { sum = 0.0; for (j = k + 1; j < n; j++) sum += a[n * piv[k] + j] * x[j]; x[k] = (x[k] - sum) / a[n * piv[k] + k]; } } void cull(int* n, int ints, double* x, double* t, double ptol) { int k = 0; int i = *n; int ntopint; int npx; while (x[i - 1] > t[ints - 1]) i--; ntopint = *n - i; npx = (int)(ntopint * (1.0 - ptol)); i = *n; while ((k < npx) && (x[--i] > t[ints])) k++; *n -= k; } void xbuilder(BLDR a, int points, double* x, double* y, int ints, double* t, int* info, double* c, double ptol) { double u, v, alpha, beta, gamma, delta; int nsize = 3 * ints + 1; int intp1 = ints + 1; int intm1 = ints - 1; int i, j, k, m; int dinfo; flush_builder(a, points, ints); for (i = 0; i < points; i++) { a->catxy[2 * i + 0] = x[i]; a->catxy[2 * i + 1] = y[i]; } qsort(a->catxy, points, 2 * sizeof(double), fcompare); for (i = 0; i < points; i++) { a->sx[i] = a->catxy[2 * i + 0]; a->sy[i] = a->catxy[2 * i + 1]; } cull(&points, ints, a->sx, t, ptol); if (points <= 0 || a->sx[points - 1] > t[ints]) { *info = -1000; goto cleanup; } else *info = 0; for (j = 0; j < ints; j++) a->h[j] = t[j + 1] - t[j]; a->p[0] = 0; j = 0; for (i = 0; i < points; i++) { if (a->sx[i] <= t[j + 1]) a->np[j]++; else { a->p[++j] = i; while (a->sx[i] > t[j + 1]) a->p[++j] = i; a->np[j] = 1; } } for (i = 0; i < ints; i++) for (j = a->p[i]; j < a->p[i] + a->np[i]; j++) { u = (a->sx[j] - t[i]) / a->h[i]; v = u - 1.0; alpha = (2.0 * u + 1.0) * v * v; beta = u * u * (1.0 - 2.0 * v); gamma = a->h[i] * u * v * v; delta = a->h[i] * u * u * v; a->taa[i] += alpha * alpha; a->tab[i] += alpha * beta; a->tag[i] += alpha * gamma; a->tad[i] += alpha * delta; a->tbb[i] += beta * beta; a->tbg[i] += beta * gamma; a->tbd[i] += beta * delta; a->tgg[i] += gamma * gamma; a->tgd[i] += gamma * delta; a->tdd[i] += delta * delta; a->D[i + 0] += 2.0 * a->sy[j] * alpha; a->D[i + 1] += 2.0 * a->sy[j] * beta; a->G[i + 0] += 2.0 * a->sy[j] * gamma; a->G[i + 1] += 2.0 * a->sy[j] * delta; } for (i = 0; i < ints; i++) { a->A[(i + 0) * intp1 + (i + 0)] += 2.0 * a->taa[i]; a->A[(i + 1) * intp1 + (i + 1)] = 2.0 * a->tbb[i]; a->A[(i + 0) * intp1 + (i + 1)] = 2.0 * a->tab[i]; a->A[(i + 1) * intp1 + (i + 0)] = 2.0 * a->tab[i]; a->B[(i + 0) * intp1 + (i + 0)] += 2.0 * a->tag[i]; a->B[(i + 1) * intp1 + (i + 1)] = 2.0 * a->tbd[i]; a->B[(i + 0) * intp1 + (i + 1)] = 2.0 * a->tbg[i]; a->B[(i + 1) * intp1 + (i + 0)] = 2.0 * a->tad[i]; a->E[(i + 0) * intp1 + (i + 0)] += 2.0 * a->tgg[i]; a->E[(i + 1) * intp1 + (i + 1)] = 2.0 * a->tdd[i]; a->E[(i + 0) * intp1 + (i + 1)] = 2.0 * a->tgd[i]; a->E[(i + 1) * intp1 + (i + 0)] = 2.0 * a->tgd[i]; } for (i = 0; i < intm1; i++) { a->C[i * intp1 + (i + 0)] = +3.0 * a->h[i + 1] / a->h[i]; a->C[i * intp1 + (i + 2)] = -3.0 * a->h[i] / a->h[i + 1]; a->C[i * intp1 + (i + 1)] = -a->C[i * intp1 + (i + 0)] - a->C[i * intp1 + (i + 2)]; a->F[i * intp1 + (i + 0)] = a->h[i + 1]; a->F[i * intp1 + (i + 1)] = 2.0 * (a->h[i] + a->h[i + 1]); a->F[i * intp1 + (i + 2)] = a->h[i]; } for (i = 0, k = 0; i < intp1; i++, k++) { for (j = 0, m = 0; j < intp1; j++, m++) a->MAT[k * nsize + m] = a->A[i * intp1 + j]; for (j = 0, m = intp1; j < intp1; j++, m++) a->MAT[k * nsize + m] = a->B[j * intp1 + i]; for (j = 0, m = 2 * intp1; j < intm1; j++, m++) a->MAT[k * nsize + m] = a->C[j * intp1 + i]; a->RHS[k] = a->D[i]; } for (i = 0, k = intp1; i < intp1; i++, k++) { for (j = 0, m = 0; j < intp1; j++, m++) a->MAT[k * nsize + m] = a->B[i * intp1 + j]; for (j = 0, m = intp1; j < intp1; j++, m++) a->MAT[k * nsize + m] = a->E[i * intp1 + j]; for (j = 0, m = 2 * intp1; j < intm1; j++, m++) a->MAT[k * nsize + m] = a->F[j * intp1 + i]; a->RHS[k] = a->G[i]; } for (i = 0, k = 2 * intp1; i < intm1; i++, k++) { for (j = 0, m = 0; j < intp1; j++, m++) a->MAT[k * nsize + m] = a->C[i * intp1 + j]; for (j = 0, m = intp1; j < intp1; j++, m++) a->MAT[k * nsize + m] = a->F[i * intp1 + j]; for (j = 0, m = 2 * intp1; j < intm1; j++, m++) a->MAT[k * nsize + m] = 0.0; a->RHS[k] = 0.0; } decomp(nsize, a->MAT, a->ipiv, &dinfo, a->wrk); dsolve(nsize, a->MAT, a->ipiv, a->RHS, a->SLN); if (dinfo != 0) { *info = dinfo; goto cleanup; } for (i = 0; i <= ints; i++) { a->z[i] = a->SLN[i]; a->zp[i] = a->SLN[i + ints + 1]; } for (i = 0; i < ints; i++) { c[4 * i + 0] = a->z[i]; c[4 * i + 1] = a->zp[i]; c[4 * i + 2] = -3.0 / (a->h[i] * a->h[i]) * (a->z[i] - a->z[i + 1]) - 1.0 / a->h[i] * (2.0 * a->zp[i] + a->zp[i + 1]); c[4 * i + 3] = 2.0 / (a->h[i] * a->h[i] * a->h[i]) * (a->z[i] - a->z[i + 1]) + 1.0 / (a->h[i] * a->h[i]) * (a->zp[i] + a->zp[i + 1]); } cleanup: return; }