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