CoastalME (Coastal Modelling Environment)
Simulates the long-term behaviour of complex coastlines
Loading...
Searching...
No Matches
smooth_line.cpp
Go to the documentation of this file.
1
12
13/* ==============================================================================================================================
14
15 This file is part of CoastalME, the Coastal Modelling Environment.
16
17 CoastalME 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.
18
19 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.
20
21 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., 675 Mass Ave, Cambridge, MA 02139, USA.
22
23==============================================================================================================================*/
24#include <assert.h>
25
26#include <cmath>
27using std::abs;
28
29#include <iostream>
30using std::cerr;
31using std::cout;
32using std::endl;
33using std::ios;
34
35#include "cme.h"
36#include "simulation.h"
37
38// Visible throughout this file. Note that arrays here are used from index 1
40
41void LUDecomp(Matrix, int const, int const, int[], int*, int*);
42void LULinearSolve(Matrix const, int const, int const[], double[]);
43
44//===============================================================================================================================
46//===============================================================================================================================
48{
50
51 // Note that m_nCoastSmoothWindow must be odd (have already checked this)
52 int nHalfWindow = m_nCoastSmoothWindow / 2;
53
54 // Calculate the shift index for this value of nHalfWindow
55 int j = 3;
56
57 for (int i = 2; i <= nHalfWindow + 1; i++)
58 {
59 m_VnSavGolIndexCoast[i] = i - j;
60 j += 2;
61 }
62
63 j = 2;
64
65 for (int i = nHalfWindow + 2; i <= m_nCoastSmoothWindow; i++)
66 {
67 m_VnSavGolIndexCoast[i] = i - j;
68 j += 2;
69 }
70
71 // Now calculate the Savitzky-Golay filter coefficients
73
75}
76
77//===============================================================================================================================
79//===============================================================================================================================
80CGeomLine CSimulation::LSmoothCoastSavitzkyGolay(CGeomLine* pLineIn, int const nStartEdge, int const nEndEdge) const
81{
82 // Note that m_nCoastSmoothWindow must be odd (have already checked this)
83 int nHalfWindow = m_nCoastSmoothWindow / 2;
84
85 // Make a copy of the unsmoothed CGeomLine (must be blank)
86 int nSize = pLineIn->nGetSize();
87 CGeomLine LTemp;
88 LTemp.Resize(nSize);
89
90 // Apply the Savitzky-Golay smoothing filter
91 for (int i = 0; i < nSize; i++)
92 {
93 CGeom2DPoint PtThis(pLineIn->dGetXAt(i), pLineIn->dGetYAt(i));
94 CGeom2DIPoint PtiThis = PtiExtCRSToGridRound(&PtThis);
95 int nXThis = PtiThis.nGetX();
96 int nYThis = PtiThis.nGetY();
97
98 // Safety check
99 if (! bIsWithinValidGrid(nXThis, nYThis))
100 KeepWithinValidGrid(nXThis, nYThis);
101
102 // Don't smooth intervention cells
103 if (bIsInterventionCell(nXThis, nYThis))
104 {
105 LTemp[i] = pLineIn->pPtGetAt(i);
106 continue;
107 }
108
109 if (i < nHalfWindow)
110 {
111 // For the first few values of LTemp, just apply a running mean with a variable-sized window
112 int nTmpWindow = 0;
113 double dWindowTotX = 0, dWindowTotY = 0;
114
115 for (int j = -nHalfWindow; j < m_nCoastSmoothWindow - nHalfWindow; j++)
116 {
117 int k = i + j;
118
119 if ((k > 0) && (k < nSize))
120 {
121 dWindowTotX += pLineIn->dGetXAt(k);
122 dWindowTotY += pLineIn->dGetYAt(k);
123 nTmpWindow++;
124 }
125 }
126
127 switch (nStartEdge)
128 {
129 case NORTH:
130 case SOUTH:
131 // Don't apply the filter in the Y direction
132 LTemp[i] = CGeom2DPoint(dWindowTotX / nTmpWindow, pLineIn->dGetYAt(i));
133 // LTemp.SetXAt(i, dWindowTotX / static_cast<double>(nTmpWindow));
134 // LTemp.SetYAt(i, pLineIn->dGetYAt(i));
135 break;
136
137 case EAST:
138 case WEST:
139 // Don't apply the filter in the X direction
140 LTemp[i] = CGeom2DPoint(pLineIn->dGetXAt(i), dWindowTotY / nTmpWindow);
141 // LTemp.SetXAt(i, pLineIn->dGetXAt(i));
142 // LTemp.SetYAt(i, dWindowTotY / static_cast<double>(nTmpWindow));
143 break;
144 }
145 }
146
147 else if (i >= (nSize - nHalfWindow))
148 {
149 // For the last few values of PtVTemp, just apply a running mean with a variable-sized window
150 int nTmpWindow = 0;
151 double dWindowTotX = 0, dWindowTotY = 0;
152
153 for (int j = -nHalfWindow; j < m_nCoastSmoothWindow - nHalfWindow; j++)
154 {
155 int k = i + j;
156
157 if ((k > 0) && (k < nSize))
158 {
159 dWindowTotX += pLineIn->dGetXAt(k);
160 dWindowTotY += pLineIn->dGetYAt(k);
161 nTmpWindow++;
162 }
163 }
164
165 switch (nEndEdge)
166 {
167 case NORTH:
168 case SOUTH:
169 // Don't apply the filter in the Y direction
170 LTemp[i] = CGeom2DPoint(dWindowTotX / nTmpWindow, pLineIn->dGetYAt(i));
171 // LTemp.SetXAt(i, dWindowTotX / static_cast<double>(nTmpWindow));
172 // LTemp.SetYAt(i, pLineIn->dGetYAt(i));
173 break;
174
175 case EAST:
176 case WEST:
177 // Don't apply the filter in the X direction
178 LTemp[i] = CGeom2DPoint(pLineIn->dGetXAt(i), dWindowTotY / nTmpWindow);
179 // LTemp.SetXAt(i, pLineIn->dGetXAt(i));
180 // LTemp.SetYAt(i, dWindowTotY / static_cast<double>(nTmpWindow));
181 break;
182 }
183 }
184
185 else
186 {
187 // For all other PtVTemp values, calc Savitzky-Golay weighted values for both X and Y
188 for (int j = 0; j < m_nCoastSmoothWindow; j++)
189 {
190 int k = i + m_VnSavGolIndexCoast[j + 1];
191
192 if ((k >= 0) && (k < nSize)) // Skip points that do not exist, note starts from 1
193 {
194 double dX = LTemp.dGetXAt(i);
195 dX += m_VdSavGolFCRWCoast[j + 1] * pLineIn->dGetXAt(k);
196 // LTemp.SetXAt(i, dX);
197
198 double dY = LTemp.dGetYAt(i);
199 dY += m_VdSavGolFCRWCoast[j + 1] * pLineIn->dGetYAt(k);
200
201 LTemp[i] = CGeom2DPoint(dX, dY);
202 // LTemp.SetYAt(i, dY);
203 }
204 }
205 }
206 }
207
208 // Return the smoothed CGeomLine
209 return LTemp;
210}
211
212//===============================================================================================================================
214//===============================================================================================================================
216{
217 // Note that m_nCoastSmoothWindow must be odd (have already checked this)
218 int nHalfWindow = m_nCoastSmoothWindow / 2;
219 double dHalfWindow = nHalfWindow;
220
221 // Make a copy of the unsmoothed CGeomLine
222 int nSize = pLineIn->nGetSize();
223 CGeomLine LTemp;
224 LTemp = * pLineIn;
225
226 // Apply the running mean smoothing filter, with a variable window size at both ends of the line
227 for (int i = 0; i < nSize; i++)
228 {
229 // Don't smooth intervention cells
230 CGeom2DPoint PtThis(pLineIn->dGetXAt(i), pLineIn->dGetYAt(i));
231 CGeom2DIPoint PtiThis = PtiExtCRSToGridRound(&PtThis);
232 int nXThis = tMin(PtiThis.nGetX(), m_nXGridSize - 1);
233 int nYThis = tMin(PtiThis.nGetY(), m_nYGridSize - 1);
234
235 // Safety checks
236 nXThis = tMax(nXThis, 0);
237 nYThis = tMax(nYThis, 0);
238
239 if (bIsInterventionCell(nXThis, nYThis))
240 {
241 LTemp[i] = pLineIn->pPtGetAt(i);
242 continue;
243 }
244
245 // bool bNearStartEdge = false, bNearEndEdge = false;
246 // int consTant = 0;
247 double nTmpWindow = 0;
248 double dWindowTotX = 0, dWindowTotY = 0;
249
250 if (i < nHalfWindow)
251 {
252 for (int j = 0; j <= i; j++)
253 {
254 // // For points at both ends of the coastline, use a smaller window
255 double weight = (dHalfWindow - tAbs(i - j)) / dHalfWindow;
256 dWindowTotX += pLineIn->dGetXAt(j) * weight;
257 dWindowTotY += pLineIn->dGetYAt(j) * weight;
258 nTmpWindow += weight;
259 }
260 }
261
262 else if (i >= nSize - nHalfWindow)
263 {
264 for (int j = nSize - 1; j >= i; j--)
265 {
266 double weight = (dHalfWindow - tAbs(i - j)) / dHalfWindow;
267 dWindowTotX += pLineIn->dGetXAt(j) * weight;
268 dWindowTotY += pLineIn->dGetYAt(j) * weight;
269 nTmpWindow += weight;
270 }
271 } // namespace name
272
273 else
274 {
275 for (int j = i - nHalfWindow; j < i + nHalfWindow; j++)
276 {
277 double weight = (dHalfWindow - tAbs(i - j)) / dHalfWindow;
278 dWindowTotX += pLineIn->dGetXAt(j) * weight;
279 dWindowTotY += pLineIn->dGetYAt(j) * weight;
280 nTmpWindow += weight;
281 }
282 }
283
284 // if (bNearStartEdge)
285 // {
286 // // We are near the start edge
287 // switch (nStartEdge)
288 // {
289 // case NORTH:
290 // // Don't apply the filter in the y direction
291 // LTemp.SetXAt(i, dWindowTotX / static_cast<double>(nTmpWindow));
292 // break;
293 // case SOUTH:
294 // // Don't apply the filter in the y direction
295 // LTemp.SetXAt(i, dWindowTotX / static_cast<double>(nTmpWindow));
296 // break;
297
298 // case EAST:
299 // // Don't apply the filter in the x direction
300 // LTemp.SetYAt(i, dWindowTotY / static_cast<double>(nTmpWindow));
301 // break;
302 // case WEST:
303 // // Don't apply the filter in the x direction
304 // LTemp.SetYAt(i, dWindowTotY / static_cast<double>(nTmpWindow));
305 // break;
306 // }
307 // }
308 // else if (bNearEndEdge)
309 // {
310 // // We are near the end edge
311 // switch (nEndEdge)
312 // {
313 // case NORTH:
314 // // Don't apply the filter in the y direction
315 // LTemp.SetXAt(i, dWindowTotX / static_cast<double>(nTmpWindow));
316 // break;
317 // case SOUTH:
318 // // Don't apply the filter in the y direction
319 // LTemp.SetXAt(i, dWindowTotX / static_cast<double>(nTmpWindow));
320 // break;
321
322 // case EAST:
323 // // Don't apply the filter in the x direction
324 // LTemp.SetYAt(i, dWindowTotY / static_cast<double>(nTmpWindow));
325 // break;
326 // case WEST:
327 // // Don't apply the filter in the x direction
328 // LTemp.SetYAt(i, dWindowTotY / static_cast<double>(nTmpWindow));
329 // break;
330 // }
331 // }
332 // else
333 // {
334 // // Not near any edge, apply both x and y filters
335 LTemp[i] = CGeom2DPoint(dWindowTotX / nTmpWindow, dWindowTotY / nTmpWindow);
336 // LTemp.SetXAt(i, dWindowTotX / static_cast<double>(nTmpWindow));
337 // LTemp.SetYAt(i, dWindowTotY / static_cast<double>(nTmpWindow));
338 // }
339 }
340
341 // Return the smoothed CGeomLine
342 return LTemp;
343}
344
345//===============================================================================================================================
347//===============================================================================================================================
348vector<double> CSimulation::dVSmoothProfileSlope(vector<double> *pdVSlope) const
349{
350 // Make a copy of the unsmoothed profile slope vector
351 int const nSize = static_cast<int>(pdVSlope->size());
352 vector<double> dVSmoothed = * pdVSlope;
353
354 // Note that m_nProfileSmoothWindow must be odd (have already checked this)
355 int const nHalfWindow = m_nProfileSmoothWindow / 2;
356
357 // Apply the running mean smoothing filter, with a variable window size at both ends of the line
358 for (int i = 0; i < nSize; i++)
359 {
360 int nTmpWindow = 0;
361 double dWindowTot = 0;
362
363 for (int j = -nHalfWindow; j < m_nProfileSmoothWindow - nHalfWindow; j++)
364 {
365 // For points at both ends of the profile, use a smaller window
366 int const k = i + j;
367
368 if ((k < 0) || (k >= nSize))
369 continue;
370
371 dWindowTot += pdVSlope->at(k);
372 nTmpWindow++;
373 }
374
375 dVSmoothed[i] = dWindowTot / static_cast<double>(nTmpWindow);
376
377 // If necessary, constrain the slope as in SCAPE
378 if (dVSmoothed[i] >= 0)
379 dVSmoothed[i] = tMin(dVSmoothed[i], m_dProfileMaxSlope);
380
381 else
382 dVSmoothed[i] = tMax(dVSmoothed[i], -m_dProfileMaxSlope);
383 }
384
385 // Return the smoothed vector
386 return dVSmoothed;
387}
388
389//===============================================================================================================================
391//===============================================================================================================================
392void CSimulation::CalcSavitzkyGolay(double dFilterCoeffsArray[], int const nWindowSize, int const nLeft, int const nRight, int const nDerivOrder, int const nSmoothPolyOrder)
393{
394 // Some sanity checks
395 if ((nWindowSize < nLeft + nRight + 1) || (nLeft < 0) || (nRight < 0) || (nDerivOrder > nSmoothPolyOrder) || (nSmoothPolyOrder > static_cast<int>(SAVGOL_POLYNOMIAL_MAX_ORDER)) || (nLeft + nRight < nSmoothPolyOrder))
396 {
397 cerr << ERR << "Error in arguments to CalcSavitzkyGolay" << endl;
398 return;
399 }
400
401 // Initialise arrays (including index 0 for tidiness)
402 int nIndexArray[SAVGOL_POLYNOMIAL_MAX_ORDER + 2];
403 Matrix dMatrix;
404 double dSolutionArray[SAVGOL_POLYNOMIAL_MAX_ORDER + 2];
405
406 for (int i = 0; i <= SAVGOL_POLYNOMIAL_MAX_ORDER + 1; i++)
407 {
408 for (int j = 0; j <= SAVGOL_POLYNOMIAL_MAX_ORDER + 1; j++)
409 dMatrix[i][j] = 0;
410
411 dSolutionArray[i] = 0;
412 nIndexArray[i] = 0;
413 }
414
415 for (int ipj = 0; ipj <= 2 * nSmoothPolyOrder; ipj++)
416 {
417 // Set up the equations for the desired least squares fit
418 double dSum = 0;
419
420 if (ipj == 0)
421 dSum = 1;
422
423 for (int k = 1; k <= nRight; k++)
424 dSum += pow(k, ipj);
425
426 for (int k = 1; k <= nLeft; k++)
427 dSum += pow(-k, ipj);
428
429 int mm = tMin(ipj, 2 * nSmoothPolyOrder - ipj);
430 int imj = -mm;
431
432 do
433 {
434 dMatrix[1 + (ipj + imj) / 2][1 + (ipj - imj) / 2] = dSum;
435 imj += 2;
436 } while (imj <= mm);
437 }
438
439 // Solve them using LU decomposition
440 int nDCode = 0, nICode = 0;
441 LUDecomp(dMatrix, nSmoothPolyOrder + 1, SAVGOL_POLYNOMIAL_MAX_ORDER + 1, nIndexArray, &nDCode, &nICode);
442
443 // Right-hand side vector is unit vector, depending on which derivative we want
444 dSolutionArray[nDerivOrder + 1] = 1;
445
446 // Backsubstitute, giving one row of the inverse matrix
447 LULinearSolve(dMatrix, nSmoothPolyOrder + 1, nIndexArray, dSolutionArray);
448
449 // Zero the output array (it may be bigger than the number of coefficients). Again include index 0 for tidiness
450 // for (int n = 0; n < SAVGOL_POLYNOMIAL_MAX_ORDER+1; n++)
451 // for (int n = 0; n <= nWindowSize; n++)
452 // dFilterCoeffsArray[n] = 0;
453
454 for (int n = -nLeft; n <= nRight; n++)
455 {
456 // Each Savitzky-Golay coefficient is the dot product of powers of an integer with the inverse matrix row
457 double dSum = dSolutionArray[1];
458 double dFac = 1;
459
460 for (int m = 1; m <= nSmoothPolyOrder; m++)
461 {
462 dFac *= n;
463 dSum += dSolutionArray[m + 1] * dFac;
464 }
465
466 // Store in wrap-around order
467 int nInd = ((nWindowSize - n) % nWindowSize) + 1;
468 dFilterCoeffsArray[nInd] = dSum;
469 }
470}
471
472//===============================================================================================================================
474//===============================================================================================================================
475void LUDecomp(Matrix A, int const N, int const np, int nIndexArray[], int* nDCode, int* nICode)
476{
477 if (N >= np)
478 {
479 // cerr << ERR << "in LUDecomp" << endl;
480 return;
481 }
482
483 double TINY = 1e-12;
484 double AMAX, DUM, SUM;
485 double* VV = new double[np];
486 int I, IMAX = 0, J, K;
487
488 * nDCode = 1;
489 * nICode = 0;
490
491 for (I = 1; I <= N; I++)
492 {
493 AMAX = 0.0;
494
495 for (J = 1; J <= N; J++)
496 if (tAbs(A[I][J]) > AMAX)
497 AMAX = tAbs(A[I][J]);
498
499 if (AMAX < TINY)
500 {
501 * nICode = 1;
502 delete[] VV;
503 return;
504 }
505
506 VV[I] = 1.0 / AMAX;
507 }
508
509 for (J = 1; J <= N; J++)
510 {
511 for (I = 1; I < J; I++)
512 {
513 SUM = A[I][J];
514
515 for (K = 1; K < I; K++)
516 SUM -= A[I][K] * A[K][J];
517
518 A[I][J] = SUM;
519 }
520
521 AMAX = 0.0;
522
523 for (I = J; I <= N; I++)
524 {
525 SUM = A[I][J];
526
527 for (K = 1; K < J; K++)
528 SUM -= A[I][K] * A[K][J];
529
530 A[I][J] = SUM;
531 DUM = VV[I] * tAbs(SUM);
532
533 if (DUM >= AMAX)
534 {
535 IMAX = I;
536 AMAX = DUM;
537 }
538 }
539
540 if (J != IMAX)
541 {
542 for (K = 1; K <= N; K++)
543 {
544 DUM = A[IMAX][K];
545 A[IMAX][K] = A[J][K];
546 A[J][K] = DUM;
547 }
548
549 * nDCode = -( * nDCode);
550 VV[IMAX] = VV[J];
551 }
552
553 nIndexArray[J] = IMAX;
554
555 if (tAbs(A[J][J]) < TINY)
556 A[J][J] = TINY;
557
558 if (J != N)
559 {
560 DUM = 1.0 / A[J][J];
561
562 for (I = J + 1; I <= N; I++)
563 A[I][J] *= DUM;
564 }
565 }
566
567 delete[] VV;
568}
569
570//===============================================================================================================================
572//===============================================================================================================================
573void LULinearSolve(Matrix const A, int const N, int const nIndexArray[], double B[])
574{
575 int II = 0;
576 double SUM;
577
578 for (int I = 1; I <= N; I++)
579 {
580 int LL = nIndexArray[I];
581 SUM = B[LL];
582 B[LL] = B[I];
583
584 if (II != 0)
585 for (int J = II; J < I; J++)
586 SUM -= A[I][J] * B[J];
587
588 else if (! bFPIsEqual(SUM, 0.0, TOLERANCE))
589 II = I;
590
591 B[I] = SUM;
592 }
593
594 for (int I = N; I > 0; I--)
595 {
596 SUM = B[I];
597
598 if (I < N)
599 for (int J = I + 1; J <= N; J++)
600 SUM -= A[I][J] * B[J];
601
602 B[I] = SUM / A[I][I];
603 }
604}
int nGetSize(void) const
Definition 2d_shape.cpp:56
void Resize(int const)
Resizes the vector which represents this 2D shape.
Definition 2d_shape.cpp:50
Geometry class used to represent 2D point objects with integer coordinates.
Definition 2di_point.h:29
int nGetY(void) const
Returns the CGeom2DIPoint object's integer Y coordinate.
Definition 2di_point.cpp:48
int nGetX(void) const
Returns the CGeom2DIPoint object's integer X coordinate.
Definition 2di_point.cpp:42
Geometry class used to represent 2D point objects with floating-point coordinates.
Definition 2d_point.h:27
Geometry class used to represent 2D vector line objects.
Definition line.h:31
CGeom2DPoint * pPtGetAt(int const)
Returns the point at a given place in the line.
Definition line.cpp:73
double dGetYAt(int const)
Returns the Y value at a given place in the line.
Definition line.cpp:67
double dGetXAt(int const)
Returns the X value at a given place in the line.
Definition line.cpp:61
void CalcSavitzkyGolayCoeffs(void)
Calculates the Savitzky-Golay smoothing coefficients for a given size of smoothing window....
static void CalcSavitzkyGolay(double[], int const, int const, int const, int const, int const)
CalcSavitzkyGolay uses LULinearSolve and LUDecomp. It returns dFilterCoeffsArray[nWindowSize],...
CGeomLine LSmoothCoastRunningMean(CGeomLine *) const
Does running-mean smoothing of a CGeomLine coastline vector (is in external CRS coordinates)
int m_nXGridSize
The size of the grid in the x direction.
Definition simulation.h:462
CGeom2DIPoint PtiExtCRSToGridRound(CGeom2DPoint const *) const
Transforms a pointer to a CGeom2DPoint in the external CRS to the equivalent CGeom2DIPoint in the ras...
int m_nYGridSize
The size of the grid in the y direction.
Definition simulation.h:465
void KeepWithinValidGrid(int &, int &) const
Constrains the supplied point (in the grid CRS) to be a valid cell within the raster grid.
int m_nSavGolCoastPoly
Definition simulation.h:478
vector< double > dVSmoothProfileSlope(vector< double > *) const
Does running-mean smoothing of the slope of a coastline-normal profile.
vector< double > m_VdSavGolFCRWCoast
Savitzky-Golay filter coefficients for the coastline vector(s)
CGeomLine LSmoothCoastSavitzkyGolay(CGeomLine *, int const, int const) const
Does smoothing of a CGeomLine coastline vector (is in external CRS coordinates) using a Savitzky-Gola...
bool bIsWithinValidGrid(int const, int const) const
Checks whether the supplied point (an x-y pair, in the grid CRS) is within the raster grid,...
bool bIsInterventionCell(int const, int const) const
Returns true if the cell is an intervention.
Definition utils.cpp:2918
vector< int > m_VnSavGolIndexCoast
Savitzky-Golay shift index for the coastline vector(s)
double m_dProfileMaxSlope
Maximum slope on coastline-normal profiles.
Definition simulation.h:963
int m_nProfileSmoothWindow
Definition simulation.h:495
int m_nCoastSmoothWindow
The size of the window used for coast smoothing. Must be an odd number.
Definition simulation.h:474
This file contains global definitions for CoastalME.
T tMin(T a, T b)
Definition cme.h:1251
double const TOLERANCE
Definition cme.h:811
int const SAVGOL_POLYNOMIAL_MAX_ORDER
Definition cme.h:484
string const ERR
Definition cme.h:890
bool bFPIsEqual(const T d1, const T d2, const T dEpsilon)
Definition cme.h:1284
T tMax(T a, T b)
Definition cme.h:1240
int const SOUTH
Definition cme.h:499
int const EAST
Definition cme.h:497
T tAbs(T a)
Definition cme.h:1261
int const NORTH
Definition cme.h:495
int const WEST
Definition cme.h:501
Contains CSimulation definitions.
void LULinearSolve(Matrix const, int const, int const[], double[])
Solves the set of N linear equations A . X = B. Here A is input, not as the matrix A but rather as it...
void LUDecomp(Matrix, int const, int const, int[], int *, int *)
Given an N x N matrix A, this routine replaces it by the LU decomposition of a rowwise permutation of...
double Matrix[SAVGOL_POLYNOMIAL_MAX_ORDER+2][SAVGOL_POLYNOMIAL_MAX_ORDER+2]