public static bool testsplineinterpolation(bool silent) { bool result = new bool(); bool waserrors = new bool(); bool crserrors = new bool(); bool cserrors = new bool(); bool hserrors = new bool(); bool aserrors = new bool(); bool lserrors = new bool(); bool dserrors = new bool(); bool uperrors = new bool(); bool cperrors = new bool(); bool lterrors = new bool(); bool ierrors = new bool(); bool fiterrors = new bool(); double nonstrictthreshold = 0; double threshold = 0; int passcount = 0; double lstep = 0; double h = 0; int maxn = 0; int bltype = 0; int brtype = 0; bool periodiccond = new bool(); int n = 0; int m = 0; int i = 0; int k = 0; int pass = 0; int stype = 0; double[] x = new double[0]; double[] y = new double[0]; double[] yp = new double[0]; double[] w = new double[0]; double[] w2 = new double[0]; double[] y2 = new double[0]; double[] d = new double[0]; double[] xc = new double[0]; double[] yc = new double[0]; int[] dc = new int[0]; spline1d.spline1dinterpolant c = new spline1d.spline1dinterpolant(); spline1d.spline1dinterpolant c2 = new spline1d.spline1dinterpolant(); int info = 0; int info1 = 0; int info2 = 0; double a = 0; double b = 0; double bl = 0; double br = 0; double t = 0; double sa = 0; double sb = 0; double v = 0; double v1 = 0; double v2 = 0; double l10 = 0; double l11 = 0; double l12 = 0; double l20 = 0; double l21 = 0; double l22 = 0; double p0 = 0; double p1 = 0; double p2 = 0; double s = 0; double ds = 0; double d2s = 0; double s2 = 0; double ds2 = 0; double d2s2 = 0; double vl = 0; double vm = 0; double vr = 0; double err = 0; double tension = 0; double intab = 0; spline1d.spline1dfitreport rep = new spline1d.spline1dfitreport(); spline1d.spline1dfitreport rep2 = new spline1d.spline1dfitreport(); double refrms = 0; double refavg = 0; double refavgrel = 0; double refmax = 0; int i_ = 0; waserrors = false; passcount = 20; lstep = 0.005; h = 0.00001; maxn = 10; threshold = 10000*AP.Math.MachineEpsilon; nonstrictthreshold = 0.00001; lserrors = false; cserrors = false; crserrors = false; hserrors = false; aserrors = false; dserrors = false; cperrors = false; uperrors = false; lterrors = false; ierrors = false; fiterrors = false; // // General test: linear, cubic, Hermite, Akima // for(n=2; n<=maxn; n++) { x = new double[n-1+1]; y = new double[n-1+1]; yp = new double[n-1+1]; d = new double[n-1+1]; for(pass=1; pass<=passcount; pass++) { // // Prepare task: // * X contains abscissas from [A,B] // * Y contains function values // * YP contains periodic function values // a = -1-AP.Math.RandomReal(); b = +1+AP.Math.RandomReal(); bl = 2*AP.Math.RandomReal()-1; br = 2*AP.Math.RandomReal()-1; for(i=0; i<=n-1; i++) { x[i] = 0.5*(b+a)+0.5*(b-a)*Math.Cos(Math.PI*(2*i+1)/(2*n)); if( i==0 ) { x[i] = a; } if( i==n-1 ) { x[i] = b; } y[i] = Math.Cos(1.3*Math.PI*x[i]+0.4); yp[i] = y[i]; d[i] = -(1.3*Math.PI*Math.Sin(1.3*Math.PI*x[i]+0.4)); } yp[n-1] = yp[0]; for(i=0; i<=n-1; i++) { k = AP.Math.RandomInteger(n); if( k!=i ) { t = x[i]; x[i] = x[k]; x[k] = t; t = y[i]; y[i] = y[k]; y[k] = t; t = yp[i]; yp[i] = yp[k]; yp[k] = t; t = d[i]; d[i] = d[k]; d[k] = t; } } // // Build linear spline // Test for general interpolation scheme properties: // * values at nodes // * continuous function // Test for specific properties is implemented below. // spline1d.spline1dbuildlinear(x, y, n, ref c); err = 0; for(i=0; i<=n-1; i++) { err = Math.Max(err, Math.Abs(y[i]-spline1d.spline1dcalc(ref c, x[i]))); } lserrors = lserrors | (double)(err)>(double)(threshold); lconst(a, b, ref c, lstep, ref l10, ref l11, ref l12); lconst(a, b, ref c, lstep/3, ref l20, ref l21, ref l22); lserrors = lserrors | (double)(l20/l10)>(double)(1.2); // // Build cubic spline. // Test for interpolation scheme properties: // * values at nodes // * boundary conditions // * continuous function // * continuous first derivative // * continuous second derivative // * periodicity properties // for(bltype=-1; bltype<=2; bltype++) { for(brtype=-1; brtype<=2; brtype++) { // // skip meaningless combination of boundary conditions // (one condition is periodic, another is not) // periodiccond = bltype==-1 | brtype==-1; if( periodiccond & bltype!=brtype ) { continue; } // // build // if( periodiccond ) { spline1d.spline1dbuildcubic(x, yp, n, bltype, bl, brtype, br, ref c); } else { spline1d.spline1dbuildcubic(x, y, n, bltype, bl, brtype, br, ref c); } // // interpolation properties // err = 0; if( periodiccond ) { // // * check values at nodes; spline is periodic so // we add random number of periods to nodes // * we also test for periodicity of derivatives // for(i=0; i<=n-1; i++) { v = x[i]; vm = v+(b-a)*(AP.Math.RandomInteger(5)-2); t = yp[i]-spline1d.spline1dcalc(ref c, vm); err = Math.Max(err, Math.Abs(t)); spline1d.spline1ddiff(ref c, v, ref s, ref ds, ref d2s); spline1d.spline1ddiff(ref c, vm, ref s2, ref ds2, ref d2s2); err = Math.Max(err, Math.Abs(s-s2)); err = Math.Max(err, Math.Abs(ds-ds2)); err = Math.Max(err, Math.Abs(d2s-d2s2)); } // // periodicity between nodes // v = a+(b-a)*AP.Math.RandomReal(); vm = v+(b-a)*(AP.Math.RandomInteger(5)-2); err = Math.Max(err, Math.Abs(spline1d.spline1dcalc(ref c, v)-spline1d.spline1dcalc(ref c, vm))); spline1d.spline1ddiff(ref c, v, ref s, ref ds, ref d2s); spline1d.spline1ddiff(ref c, vm, ref s2, ref ds2, ref d2s2); err = Math.Max(err, Math.Abs(s-s2)); err = Math.Max(err, Math.Abs(ds-ds2)); err = Math.Max(err, Math.Abs(d2s-d2s2)); } else { // // * check values at nodes // for(i=0; i<=n-1; i++) { err = Math.Max(err, Math.Abs(y[i]-spline1d.spline1dcalc(ref c, x[i]))); } } cserrors = cserrors | (double)(err)>(double)(threshold); // // check boundary conditions // err = 0; if( bltype==0 ) { spline1d.spline1ddiff(ref c, a-h, ref s, ref ds, ref d2s); spline1d.spline1ddiff(ref c, a+h, ref s2, ref ds2, ref d2s2); t = (d2s2-d2s)/(2*h); err = Math.Max(err, Math.Abs(t)); } if( bltype==1 ) { t = (spline1d.spline1dcalc(ref c, a+h)-spline1d.spline1dcalc(ref c, a-h))/(2*h); err = Math.Max(err, Math.Abs(bl-t)); } if( bltype==2 ) { t = (spline1d.spline1dcalc(ref c, a+h)-2*spline1d.spline1dcalc(ref c, a)+spline1d.spline1dcalc(ref c, a-h))/AP.Math.Sqr(h); err = Math.Max(err, Math.Abs(bl-t)); } if( brtype==0 ) { spline1d.spline1ddiff(ref c, b-h, ref s, ref ds, ref d2s); spline1d.spline1ddiff(ref c, b+h, ref s2, ref ds2, ref d2s2); t = (d2s2-d2s)/(2*h); err = Math.Max(err, Math.Abs(t)); } if( brtype==1 ) { t = (spline1d.spline1dcalc(ref c, b+h)-spline1d.spline1dcalc(ref c, b-h))/(2*h); err = Math.Max(err, Math.Abs(br-t)); } if( brtype==2 ) { t = (spline1d.spline1dcalc(ref c, b+h)-2*spline1d.spline1dcalc(ref c, b)+spline1d.spline1dcalc(ref c, b-h))/AP.Math.Sqr(h); err = Math.Max(err, Math.Abs(br-t)); } if( bltype==-1 | brtype==-1 ) { spline1d.spline1ddiff(ref c, a+100*AP.Math.MachineEpsilon, ref s, ref ds, ref d2s); spline1d.spline1ddiff(ref c, b-100*AP.Math.MachineEpsilon, ref s2, ref ds2, ref d2s2); err = Math.Max(err, Math.Abs(s-s2)); err = Math.Max(err, Math.Abs(ds-ds2)); err = Math.Max(err, Math.Abs(d2s-d2s2)); } cserrors = cserrors | (double)(err)>(double)(1.0E-3); // // Check Lipschitz continuity // lconst(a, b, ref c, lstep, ref l10, ref l11, ref l12); lconst(a, b, ref c, lstep/3, ref l20, ref l21, ref l22); if( (double)(l10)>(double)(1.0E-6) ) { cserrors = cserrors | (double)(l20/l10)>(double)(1.2); } if( (double)(l11)>(double)(1.0E-6) ) { cserrors = cserrors | (double)(l21/l11)>(double)(1.2); } if( (double)(l12)>(double)(1.0E-6) ) { cserrors = cserrors | (double)(l22/l12)>(double)(1.2); } } } // // Build Catmull-Rom spline. // Test for interpolation scheme properties: // * values at nodes // * boundary conditions // * continuous function // * continuous first derivative // * periodicity properties // for(bltype=-1; bltype<=0; bltype++) { periodiccond = bltype==-1; // // select random tension value, then build // if( (double)(AP.Math.RandomReal())>(double)(0.5) ) { if( (double)(AP.Math.RandomReal())>(double)(0.5) ) { tension = 0; } else { tension = 1; } } else { tension = AP.Math.RandomReal(); } if( periodiccond ) { spline1d.spline1dbuildcatmullrom(x, yp, n, bltype, tension, ref c); } else { spline1d.spline1dbuildcatmullrom(x, y, n, bltype, tension, ref c); } // // interpolation properties // err = 0; if( periodiccond ) { // // * check values at nodes; spline is periodic so // we add random number of periods to nodes // * we also test for periodicity of first derivative // for(i=0; i<=n-1; i++) { v = x[i]; vm = v+(b-a)*(AP.Math.RandomInteger(5)-2); t = yp[i]-spline1d.spline1dcalc(ref c, vm); err = Math.Max(err, Math.Abs(t)); spline1d.spline1ddiff(ref c, v, ref s, ref ds, ref d2s); spline1d.spline1ddiff(ref c, vm, ref s2, ref ds2, ref d2s2); err = Math.Max(err, Math.Abs(s-s2)); err = Math.Max(err, Math.Abs(ds-ds2)); } // // periodicity between nodes // v = a+(b-a)*AP.Math.RandomReal(); vm = v+(b-a)*(AP.Math.RandomInteger(5)-2); err = Math.Max(err, Math.Abs(spline1d.spline1dcalc(ref c, v)-spline1d.spline1dcalc(ref c, vm))); spline1d.spline1ddiff(ref c, v, ref s, ref ds, ref d2s); spline1d.spline1ddiff(ref c, vm, ref s2, ref ds2, ref d2s2); err = Math.Max(err, Math.Abs(s-s2)); err = Math.Max(err, Math.Abs(ds-ds2)); } else { // // * check values at nodes // for(i=0; i<=n-1; i++) { err = Math.Max(err, Math.Abs(y[i]-spline1d.spline1dcalc(ref c, x[i]))); } } crserrors = crserrors | (double)(err)>(double)(threshold); // // check boundary conditions // err = 0; if( bltype==0 ) { spline1d.spline1ddiff(ref c, a-h, ref s, ref ds, ref d2s); spline1d.spline1ddiff(ref c, a+h, ref s2, ref ds2, ref d2s2); t = (d2s2-d2s)/(2*h); err = Math.Max(err, Math.Abs(t)); spline1d.spline1ddiff(ref c, b-h, ref s, ref ds, ref d2s); spline1d.spline1ddiff(ref c, b+h, ref s2, ref ds2, ref d2s2); t = (d2s2-d2s)/(2*h); err = Math.Max(err, Math.Abs(t)); } if( bltype==-1 ) { spline1d.spline1ddiff(ref c, a+100*AP.Math.MachineEpsilon, ref s, ref ds, ref d2s); spline1d.spline1ddiff(ref c, b-100*AP.Math.MachineEpsilon, ref s2, ref ds2, ref d2s2); err = Math.Max(err, Math.Abs(s-s2)); err = Math.Max(err, Math.Abs(ds-ds2)); } crserrors = crserrors | (double)(err)>(double)(1.0E-3); // // Check Lipschitz continuity // lconst(a, b, ref c, lstep, ref l10, ref l11, ref l12); lconst(a, b, ref c, lstep/3, ref l20, ref l21, ref l22); if( (double)(l10)>(double)(1.0E-6) ) { crserrors = crserrors | (double)(l20/l10)>(double)(1.2); } if( (double)(l11)>(double)(1.0E-6) ) { crserrors = crserrors | (double)(l21/l11)>(double)(1.2); } } // // Build Hermite spline. // Test for interpolation scheme properties: // * values and derivatives at nodes // * continuous function // * continuous first derivative // spline1d.spline1dbuildhermite(x, y, d, n, ref c); err = 0; for(i=0; i<=n-1; i++) { err = Math.Max(err, Math.Abs(y[i]-spline1d.spline1dcalc(ref c, x[i]))); } hserrors = hserrors | (double)(err)>(double)(threshold); err = 0; for(i=0; i<=n-1; i++) { t = (spline1d.spline1dcalc(ref c, x[i]+h)-spline1d.spline1dcalc(ref c, x[i]-h))/(2*h); err = Math.Max(err, Math.Abs(d[i]-t)); } hserrors = hserrors | (double)(err)>(double)(1.0E-3); lconst(a, b, ref c, lstep, ref l10, ref l11, ref l12); lconst(a, b, ref c, lstep/3, ref l20, ref l21, ref l22); hserrors = hserrors | (double)(l20/l10)>(double)(1.2); hserrors = hserrors | (double)(l21/l11)>(double)(1.2); // // Build Akima spline // Test for general interpolation scheme properties: // * values at nodes // * continuous function // * continuous first derivative // Test for specific properties is implemented below. // if( n>=5 ) { spline1d.spline1dbuildakima(x, y, n, ref c); err = 0; for(i=0; i<=n-1; i++) { err = Math.Max(err, Math.Abs(y[i]-spline1d.spline1dcalc(ref c, x[i]))); } aserrors = aserrors | (double)(err)>(double)(threshold); lconst(a, b, ref c, lstep, ref l10, ref l11, ref l12); lconst(a, b, ref c, lstep/3, ref l20, ref l21, ref l22); hserrors = hserrors | (double)(l20/l10)>(double)(1.2); hserrors = hserrors | (double)(l21/l11)>(double)(1.2); } } } // // Special linear spline test: // test for linearity between x[i] and x[i+1] // for(n=2; n<=maxn; n++) { x = new double[n-1+1]; y = new double[n-1+1]; // // Prepare task // a = -1; b = +1; for(i=0; i<=n-1; i++) { x[i] = a+(b-a)*i/(n-1); y[i] = 2*AP.Math.RandomReal()-1; } spline1d.spline1dbuildlinear(x, y, n, ref c); // // Test // err = 0; for(k=0; k<=n-2; k++) { a = x[k]; b = x[k+1]; for(pass=1; pass<=passcount; pass++) { t = a+(b-a)*AP.Math.RandomReal(); v = y[k]+(t-a)/(b-a)*(y[k+1]-y[k]); err = Math.Max(err, Math.Abs(spline1d.spline1dcalc(ref c, t)-v)); } } lserrors = lserrors | (double)(err)>(double)(threshold); } // // Special Akima test: test outlier sensitivity // Spline value at (x[i], x[i+1]) should depend from // f[i-2], f[i-1], f[i], f[i+1], f[i+2], f[i+3] only. // for(n=5; n<=maxn; n++) { x = new double[n-1+1]; y = new double[n-1+1]; y2 = new double[n-1+1]; // // Prepare unperturbed Akima spline // a = -1; b = +1; for(i=0; i<=n-1; i++) { x[i] = a+(b-a)*i/(n-1); y[i] = Math.Cos(1.3*Math.PI*x[i]+0.4); } spline1d.spline1dbuildakima(x, y, n, ref c); // // Process perturbed tasks // err = 0; for(k=0; k<=n-1; k++) { for(i_=0; i_<=n-1;i_++) { y2[i_] = y[i_]; } y2[k] = 5; spline1d.spline1dbuildakima(x, y2, n, ref c2); // // Test left part independence // if( k-3>=1 ) { a = -1; b = x[k-3]; for(pass=1; pass<=passcount; pass++) { t = a+(b-a)*AP.Math.RandomReal(); err = Math.Max(err, Math.Abs(spline1d.spline1dcalc(ref c, t)-spline1d.spline1dcalc(ref c2, t))); } } // // Test right part independence // if( k+3<=n-2 ) { a = x[k+3]; b = +1; for(pass=1; pass<=passcount; pass++) { t = a+(b-a)*AP.Math.RandomReal(); err = Math.Max(err, Math.Abs(spline1d.spline1dcalc(ref c, t)-spline1d.spline1dcalc(ref c2, t))); } } } aserrors = aserrors | (double)(err)>(double)(threshold); } // // Differentiation, copy/unpack test // for(n=2; n<=maxn; n++) { x = new double[n-1+1]; y = new double[n-1+1]; // // Prepare cubic spline // a = -1-AP.Math.RandomReal(); b = +1+AP.Math.RandomReal(); for(i=0; i<=n-1; i++) { x[i] = a+(b-a)*i/(n-1); y[i] = Math.Cos(1.3*Math.PI*x[i]+0.4); } spline1d.spline1dbuildcubic(x, y, n, 2, 0.0, 2, 0.0, ref c); // // Test diff // err = 0; for(pass=1; pass<=passcount; pass++) { t = a+(b-a)*AP.Math.RandomReal(); spline1d.spline1ddiff(ref c, t, ref s, ref ds, ref d2s); vl = spline1d.spline1dcalc(ref c, t-h); vm = spline1d.spline1dcalc(ref c, t); vr = spline1d.spline1dcalc(ref c, t+h); err = Math.Max(err, Math.Abs(s-vm)); err = Math.Max(err, Math.Abs(ds-(vr-vl)/(2*h))); err = Math.Max(err, Math.Abs(d2s-(vr-2*vm+vl)/AP.Math.Sqr(h))); } dserrors = dserrors | (double)(err)>(double)(0.001); // // Test copy // unsetspline1d(ref c2); spline1d.spline1dcopy(ref c, ref c2); err = 0; for(pass=1; pass<=passcount; pass++) { t = a+(b-a)*AP.Math.RandomReal(); err = Math.Max(err, Math.Abs(spline1d.spline1dcalc(ref c, t)-spline1d.spline1dcalc(ref c2, t))); } cperrors = cperrors | (double)(err)>(double)(threshold); // // Test unpack // uperrors = uperrors | !testunpack(ref c, ref x); // // Test lin.trans. // err = 0; for(pass=1; pass<=passcount; pass++) { // // LinTransX, general A // sa = 4*AP.Math.RandomReal()-2; sb = 2*AP.Math.RandomReal()-1; t = a+(b-a)*AP.Math.RandomReal(); spline1d.spline1dcopy(ref c, ref c2); spline1d.spline1dlintransx(ref c2, sa, sb); err = Math.Max(err, Math.Abs(spline1d.spline1dcalc(ref c, t)-spline1d.spline1dcalc(ref c2, (t-sb)/sa))); // // LinTransX, special case: A=0 // sb = 2*AP.Math.RandomReal()-1; t = a+(b-a)*AP.Math.RandomReal(); spline1d.spline1dcopy(ref c, ref c2); spline1d.spline1dlintransx(ref c2, 0, sb); err = Math.Max(err, Math.Abs(spline1d.spline1dcalc(ref c, sb)-spline1d.spline1dcalc(ref c2, t))); // // LinTransY // sa = 2*AP.Math.RandomReal()-1; sb = 2*AP.Math.RandomReal()-1; t = a+(b-a)*AP.Math.RandomReal(); spline1d.spline1dcopy(ref c, ref c2); spline1d.spline1dlintransy(ref c2, sa, sb); err = Math.Max(err, Math.Abs(sa*spline1d.spline1dcalc(ref c, t)+sb-spline1d.spline1dcalc(ref c2, t))); } lterrors = lterrors | (double)(err)>(double)(threshold); } // // Testing integration. // Three tests are performed: // // * approximate test (well behaved smooth function, many points, // integration inside [a,b]), non-periodic spline // // * exact test (integration of parabola, outside of [a,b], non-periodic spline // // * approximate test for periodic splines. F(x)=cos(2*pi*x)+1. // Period length is equals to 1.0, so all operations with // multiples of period are done exactly. For each value of PERIOD // we calculate and test integral at four points: // - 0 < t0 < PERIOD // - t1 = PERIOD-eps // - t2 = PERIOD // - t3 = PERIOD+eps // err = 0; for(n=20; n<=35; n++) { x = new double[n-1+1]; y = new double[n-1+1]; for(pass=1; pass<=passcount; pass++) { // // Prepare cubic spline // a = -1-0.2*AP.Math.RandomReal(); b = +1+0.2*AP.Math.RandomReal(); for(i=0; i<=n-1; i++) { x[i] = a+(b-a)*i/(n-1); y[i] = Math.Sin(Math.PI*x[i]+0.4)+Math.Exp(x[i]); } bl = Math.PI*Math.Cos(Math.PI*a+0.4)+Math.Exp(a); br = Math.PI*Math.Cos(Math.PI*b+0.4)+Math.Exp(b); spline1d.spline1dbuildcubic(x, y, n, 1, bl, 1, br, ref c); // // Test // t = a+(b-a)*AP.Math.RandomReal(); v = -(Math.Cos(Math.PI*a+0.4)/Math.PI)+Math.Exp(a); v = -(Math.Cos(Math.PI*t+0.4)/Math.PI)+Math.Exp(t)-v; v = v-spline1d.spline1dintegrate(ref c, t); err = Math.Max(err, Math.Abs(v)); } } ierrors = ierrors | (double)(err)>(double)(0.001); p0 = 2*AP.Math.RandomReal()-1; p1 = 2*AP.Math.RandomReal()-1; p2 = 2*AP.Math.RandomReal()-1; a = -AP.Math.RandomReal()-0.5; b = +AP.Math.RandomReal()+0.5; n = 2; x = new double[n]; y = new double[n]; d = new double[n]; x[0] = a; y[0] = p0+p1*a+p2*AP.Math.Sqr(a); d[0] = p1+2*p2*a; x[1] = b; y[1] = p0+p1*b+p2*AP.Math.Sqr(b); d[1] = p1+2*p2*b; spline1d.spline1dbuildhermite(x, y, d, n, ref c); bl = Math.Min(a, b)-Math.Abs(b-a); br = Math.Min(a, b)+Math.Abs(b-a); err = 0; for(pass=1; pass<=100; pass++) { t = bl+(br-bl)*AP.Math.RandomReal(); v = p0*t+p1*AP.Math.Sqr(t)/2+p2*AP.Math.Sqr(t)*t/3-(p0*a+p1*AP.Math.Sqr(a)/2+p2*AP.Math.Sqr(a)*a/3); v = v-spline1d.spline1dintegrate(ref c, t); err = Math.Max(err, Math.Abs(v)); } ierrors = ierrors | (double)(err)>(double)(threshold); n = 100; x = new double[n]; y = new double[n]; for(i=0; i<=n-1; i++) { x[i] = (double)(i)/((double)(n-1)); y[i] = Math.Cos(2*Math.PI*x[i])+1; } y[0] = 2; y[n-1] = 2; spline1d.spline1dbuildcubic(x, y, n, -1, 0.0, -1, 0.0, ref c); intab = spline1d.spline1dintegrate(ref c, 1.0); v = AP.Math.RandomReal(); vr = spline1d.spline1dintegrate(ref c, v); ierrors = ierrors | (double)(Math.Abs(intab-1))>(double)(0.001); for(i=-10; i<=10; i++) { ierrors = ierrors | (double)(Math.Abs(spline1d.spline1dintegrate(ref c, i+v)-(i*intab+vr)))>(double)(0.001); ierrors = ierrors | (double)(Math.Abs(spline1d.spline1dintegrate(ref c, i-1000*AP.Math.MachineEpsilon)-i*intab))>(double)(0.001); ierrors = ierrors | (double)(Math.Abs(spline1d.spline1dintegrate(ref c, i)-i*intab))>(double)(0.001); ierrors = ierrors | (double)(Math.Abs(spline1d.spline1dintegrate(ref c, i+1000*AP.Math.MachineEpsilon)-i*intab))>(double)(0.001); } // // Test fitting. // for(pass=1; pass<=passcount; pass++) { // // Cubic splines // Ability to handle boundary constraints (1-4 constraints on F, dF/dx). // for(m=4; m<=8; m++) { for(k=1; k<=4; k++) { if( k>=m ) { continue; } n = 100; x = new double[n]; y = new double[n]; w = new double[n]; xc = new double[4]; yc = new double[4]; dc = new int[4]; sa = 1+AP.Math.RandomReal(); sb = 2*AP.Math.RandomReal()-1; for(i=0; i<=n-1; i++) { x[i] = sa*AP.Math.RandomReal()+sb; y[i] = 2*AP.Math.RandomReal()-1; w[i] = 1+AP.Math.RandomReal(); } xc[0] = sb; yc[0] = 2*AP.Math.RandomReal()-1; dc[0] = 0; xc[1] = sb; yc[1] = 2*AP.Math.RandomReal()-1; dc[1] = 1; xc[2] = sa+sb; yc[2] = 2*AP.Math.RandomReal()-1; dc[2] = 0; xc[3] = sa+sb; yc[3] = 2*AP.Math.RandomReal()-1; dc[3] = 1; spline1d.spline1dfitcubicwc(ref x, ref y, ref w, n, ref xc, ref yc, ref dc, k, m, ref info, ref c, ref rep); if( info<=0 ) { fiterrors = true; } else { // // Check that constraints are satisfied // for(i=0; i<=k-1; i++) { spline1d.spline1ddiff(ref c, xc[i], ref s, ref ds, ref d2s); if( dc[i]==0 ) { fiterrors = fiterrors | (double)(Math.Abs(s-yc[i]))>(double)(threshold); } if( dc[i]==1 ) { fiterrors = fiterrors | (double)(Math.Abs(ds-yc[i]))>(double)(threshold); } if( dc[i]==2 ) { fiterrors = fiterrors | (double)(Math.Abs(d2s-yc[i]))>(double)(threshold); } } } } } // // Cubic splines // Ability to handle one internal constraint // for(m=4; m<=8; m++) { n = 100; x = new double[n]; y = new double[n]; w = new double[n]; xc = new double[1]; yc = new double[1]; dc = new int[1]; sa = 1+AP.Math.RandomReal(); sb = 2*AP.Math.RandomReal()-1; for(i=0; i<=n-1; i++) { x[i] = sa*AP.Math.RandomReal()+sb; y[i] = 2*AP.Math.RandomReal()-1; w[i] = 1+AP.Math.RandomReal(); } xc[0] = sa*AP.Math.RandomReal()+sb; yc[0] = 2*AP.Math.RandomReal()-1; dc[0] = AP.Math.RandomInteger(2); spline1d.spline1dfitcubicwc(ref x, ref y, ref w, n, ref xc, ref yc, ref dc, 1, m, ref info, ref c, ref rep); if( info<=0 ) { fiterrors = true; } else { // // Check that constraints are satisfied // spline1d.spline1ddiff(ref c, xc[0], ref s, ref ds, ref d2s); if( dc[0]==0 ) { fiterrors = fiterrors | (double)(Math.Abs(s-yc[0]))>(double)(threshold); } if( dc[0]==1 ) { fiterrors = fiterrors | (double)(Math.Abs(ds-yc[0]))>(double)(threshold); } if( dc[0]==2 ) { fiterrors = fiterrors | (double)(Math.Abs(d2s-yc[0]))>(double)(threshold); } } } // // Hermite splines // Ability to handle boundary constraints (1-4 constraints on F, dF/dx). // for(m=4; m<=8; m++) { for(k=1; k<=4; k++) { if( k>=m ) { continue; } if( m%2!=0 ) { continue; } n = 100; x = new double[n]; y = new double[n]; w = new double[n]; xc = new double[4]; yc = new double[4]; dc = new int[4]; sa = 1+AP.Math.RandomReal(); sb = 2*AP.Math.RandomReal()-1; for(i=0; i<=n-1; i++) { x[i] = sa*AP.Math.RandomReal()+sb; y[i] = 2*AP.Math.RandomReal()-1; w[i] = 1+AP.Math.RandomReal(); } xc[0] = sb; yc[0] = 2*AP.Math.RandomReal()-1; dc[0] = 0; xc[1] = sb; yc[1] = 2*AP.Math.RandomReal()-1; dc[1] = 1; xc[2] = sa+sb; yc[2] = 2*AP.Math.RandomReal()-1; dc[2] = 0; xc[3] = sa+sb; yc[3] = 2*AP.Math.RandomReal()-1; dc[3] = 1; spline1d.spline1dfithermitewc(ref x, ref y, ref w, n, ref xc, ref yc, ref dc, k, m, ref info, ref c, ref rep); if( info<=0 ) { fiterrors = true; } else { // // Check that constraints are satisfied // for(i=0; i<=k-1; i++) { spline1d.spline1ddiff(ref c, xc[i], ref s, ref ds, ref d2s); if( dc[i]==0 ) { fiterrors = fiterrors | (double)(Math.Abs(s-yc[i]))>(double)(threshold); } if( dc[i]==1 ) { fiterrors = fiterrors | (double)(Math.Abs(ds-yc[i]))>(double)(threshold); } if( dc[i]==2 ) { fiterrors = fiterrors | (double)(Math.Abs(d2s-yc[i]))>(double)(threshold); } } } } } // // Hermite splines // Ability to handle one internal constraint // for(m=4; m<=8; m++) { if( m%2!=0 ) { continue; } n = 100; x = new double[n]; y = new double[n]; w = new double[n]; xc = new double[1]; yc = new double[1]; dc = new int[1]; sa = 1+AP.Math.RandomReal(); sb = 2*AP.Math.RandomReal()-1; for(i=0; i<=n-1; i++) { x[i] = sa*AP.Math.RandomReal()+sb; y[i] = 2*AP.Math.RandomReal()-1; w[i] = 1+AP.Math.RandomReal(); } xc[0] = sa*AP.Math.RandomReal()+sb; yc[0] = 2*AP.Math.RandomReal()-1; dc[0] = AP.Math.RandomInteger(2); spline1d.spline1dfithermitewc(ref x, ref y, ref w, n, ref xc, ref yc, ref dc, 1, m, ref info, ref c, ref rep); if( info<=0 ) { fiterrors = true; } else { // // Check that constraints are satisfied // spline1d.spline1ddiff(ref c, xc[0], ref s, ref ds, ref d2s); if( dc[0]==0 ) { fiterrors = fiterrors | (double)(Math.Abs(s-yc[0]))>(double)(threshold); } if( dc[0]==1 ) { fiterrors = fiterrors | (double)(Math.Abs(ds-yc[0]))>(double)(threshold); } if( dc[0]==2 ) { fiterrors = fiterrors | (double)(Math.Abs(d2s-yc[0]))>(double)(threshold); } } } } for(m=4; m<=8; m++) { for(stype=0; stype<=1; stype++) { for(pass=1; pass<=passcount; pass++) { if( stype==1 & m%2!=0 ) { continue; } // // cubic/Hermite spline fitting: // * generate "template spline" C2 // * generate 2*N points from C2, such that result of // ideal fit should be equal to C2 // * fit, store in C // * compare C and C2 // sa = 1+AP.Math.RandomReal(); sb = 2*AP.Math.RandomReal()-1; if( stype==0 ) { x = new double[m-2]; y = new double[m-2]; for(i=0; i<=m-2-1; i++) { x[i] = sa*i/(m-2-1)+sb; y[i] = 2*AP.Math.RandomReal()-1; } spline1d.spline1dbuildcubic(x, y, m-2, 1, 2*AP.Math.RandomReal()-1, 1, 2*AP.Math.RandomReal()-1, ref c2); } if( stype==1 ) { x = new double[m/2]; y = new double[m/2]; d = new double[m/2]; for(i=0; i<=m/2-1; i++) { x[i] = sa*i/(m/2-1)+sb; y[i] = 2*AP.Math.RandomReal()-1; d[i] = 2*AP.Math.RandomReal()-1; } spline1d.spline1dbuildhermite(x, y, d, m/2, ref c2); } n = 50; x = new double[2*n]; y = new double[2*n]; w = new double[2*n]; for(i=0; i<=n-1; i++) { // // "if i=0" and "if i=1" are needed to // synchronize interval size for C2 and // spline being fitted (i.e. C). // t = AP.Math.RandomReal(); x[i] = sa*AP.Math.RandomReal()+sb; if( i==0 ) { x[i] = sb; } if( i==1 ) { x[i] = sa+sb; } v = spline1d.spline1dcalc(ref c2, x[i]); y[i] = v+t; w[i] = 1+AP.Math.RandomReal(); x[n+i] = x[i]; y[n+i] = v-t; w[n+i] = w[i]; } if( stype==0 ) { spline1d.spline1dfitcubicwc(ref x, ref y, ref w, 2*n, ref xc, ref yc, ref dc, 0, m, ref info, ref c, ref rep); } if( stype==1 ) { spline1d.spline1dfithermitewc(ref x, ref y, ref w, 2*n, ref xc, ref yc, ref dc, 0, m, ref info, ref c, ref rep); } if( info<=0 ) { fiterrors = true; } else { for(i=0; i<=n-1; i++) { v = sa*AP.Math.RandomReal()+sb; fiterrors = fiterrors | (double)(Math.Abs(spline1d.spline1dcalc(ref c, v)-spline1d.spline1dcalc(ref c2, v)))>(double)(threshold); } } } } } for(m=4; m<=8; m++) { for(pass=1; pass<=passcount; pass++) { // // prepare points/weights // sa = 1+AP.Math.RandomReal(); sb = 2*AP.Math.RandomReal()-1; n = 10+AP.Math.RandomInteger(10); x = new double[n]; y = new double[n]; w = new double[n]; for(i=0; i<=n-1; i++) { x[i] = sa*AP.Math.RandomReal()+sb; y[i] = 2*AP.Math.RandomReal()-1; w[i] = 1; } // // Fit cubic with unity weights, without weights, then compare // if( m>=4 ) { spline1d.spline1dfitcubicwc(ref x, ref y, ref w, n, ref xc, ref yc, ref dc, 0, m, ref info1, ref c, ref rep); spline1d.spline1dfitcubic(ref x, ref y, n, m, ref info2, ref c2, ref rep2); if( info1<=0 | info2<=0 ) { fiterrors = true; } else { for(i=0; i<=n-1; i++) { v = sa*AP.Math.RandomReal()+sb; fiterrors = fiterrors | (double)(spline1d.spline1dcalc(ref c, v))!=(double)(spline1d.spline1dcalc(ref c2, v)); fiterrors = fiterrors | (double)(rep.taskrcond)!=(double)(rep2.taskrcond); fiterrors = fiterrors | (double)(rep.rmserror)!=(double)(rep2.rmserror); fiterrors = fiterrors | (double)(rep.avgerror)!=(double)(rep2.avgerror); fiterrors = fiterrors | (double)(rep.avgrelerror)!=(double)(rep2.avgrelerror); fiterrors = fiterrors | (double)(rep.maxerror)!=(double)(rep2.maxerror); } } } // // Fit Hermite with unity weights, without weights, then compare // if( m>=4 & m%2==0 ) { spline1d.spline1dfithermitewc(ref x, ref y, ref w, n, ref xc, ref yc, ref dc, 0, m, ref info1, ref c, ref rep); spline1d.spline1dfithermite(ref x, ref y, n, m, ref info2, ref c2, ref rep2); if( info1<=0 | info2<=0 ) { fiterrors = true; } else { for(i=0; i<=n-1; i++) { v = sa*AP.Math.RandomReal()+sb; fiterrors = fiterrors | (double)(spline1d.spline1dcalc(ref c, v))!=(double)(spline1d.spline1dcalc(ref c2, v)); fiterrors = fiterrors | (double)(rep.taskrcond)!=(double)(rep2.taskrcond); fiterrors = fiterrors | (double)(rep.rmserror)!=(double)(rep2.rmserror); fiterrors = fiterrors | (double)(rep.avgerror)!=(double)(rep2.avgerror); fiterrors = fiterrors | (double)(rep.avgrelerror)!=(double)(rep2.avgrelerror); fiterrors = fiterrors | (double)(rep.maxerror)!=(double)(rep2.maxerror); } } } } } for(pass=1; pass<=passcount; pass++) { System.Diagnostics.Debug.Assert(passcount>=2, "PassCount should be 2 or greater!"); // // solve simple task (all X[] are the same, Y[] are specially // calculated to ensure simple form of all types of errors) // and check correctness of the errors calculated by subroutines // // First pass is done with zero Y[], other passes - with random Y[]. // It should test both ability to correctly calculate errors and // ability to not fail while working with zeros :) // n = 4; if( pass==1 ) { v1 = 0; v2 = 0; v = 0; } else { v1 = AP.Math.RandomReal(); v2 = AP.Math.RandomReal(); v = 1+AP.Math.RandomReal(); } x = new double[4]; y = new double[4]; w = new double[4]; x[0] = 0; y[0] = v-v2; w[0] = 1; x[1] = 0; y[1] = v-v1; w[1] = 1; x[2] = 0; y[2] = v+v1; w[2] = 1; x[3] = 0; y[3] = v+v2; w[3] = 1; refrms = Math.Sqrt((AP.Math.Sqr(v1)+AP.Math.Sqr(v2))/2); refavg = (Math.Abs(v1)+Math.Abs(v2))/2; if( pass==1 ) { refavgrel = 0; } else { refavgrel = 0.25*(Math.Abs(v2)/Math.Abs(v-v2)+Math.Abs(v1)/Math.Abs(v-v1)+Math.Abs(v1)/Math.Abs(v+v1)+Math.Abs(v2)/Math.Abs(v+v2)); } refmax = Math.Max(v1, v2); // // Test cubic fitting // spline1d.spline1dfitcubic(ref x, ref y, 4, 4, ref info, ref c, ref rep); if( info<=0 ) { fiterrors = true; } else { s = spline1d.spline1dcalc(ref c, 0); fiterrors = fiterrors | (double)(Math.Abs(s-v))>(double)(threshold); fiterrors = fiterrors | (double)(Math.Abs(rep.rmserror-refrms))>(double)(threshold); fiterrors = fiterrors | (double)(Math.Abs(rep.avgerror-refavg))>(double)(threshold); fiterrors = fiterrors | (double)(Math.Abs(rep.avgrelerror-refavgrel))>(double)(threshold); fiterrors = fiterrors | (double)(Math.Abs(rep.maxerror-refmax))>(double)(threshold); } // // Test cubic fitting // spline1d.spline1dfithermite(ref x, ref y, 4, 4, ref info, ref c, ref rep); if( info<=0 ) { fiterrors = true; } else { s = spline1d.spline1dcalc(ref c, 0); fiterrors = fiterrors | (double)(Math.Abs(s-v))>(double)(threshold); fiterrors = fiterrors | (double)(Math.Abs(rep.rmserror-refrms))>(double)(threshold); fiterrors = fiterrors | (double)(Math.Abs(rep.avgerror-refavg))>(double)(threshold); fiterrors = fiterrors | (double)(Math.Abs(rep.avgrelerror-refavgrel))>(double)(threshold); fiterrors = fiterrors | (double)(Math.Abs(rep.maxerror-refmax))>(double)(threshold); } } // // report // waserrors = lserrors | cserrors | crserrors | hserrors | aserrors | dserrors | cperrors | uperrors | lterrors | ierrors | fiterrors; if( !silent ) { System.Console.Write("TESTING SPLINE INTERPOLATION"); System.Console.WriteLine(); // // Normal tests // System.Console.Write("LINEAR SPLINE TEST: "); if( lserrors ) { System.Console.Write("FAILED"); System.Console.WriteLine(); } else { System.Console.Write("OK"); System.Console.WriteLine(); } System.Console.Write("CUBIC SPLINE TEST: "); if( cserrors ) { System.Console.Write("FAILED"); System.Console.WriteLine(); } else { System.Console.Write("OK"); System.Console.WriteLine(); } System.Console.Write("CATMULL-ROM SPLINE TEST: "); if( crserrors ) { System.Console.Write("FAILED"); System.Console.WriteLine(); } else { System.Console.Write("OK"); System.Console.WriteLine(); } System.Console.Write("HERMITE SPLINE TEST: "); if( hserrors ) { System.Console.Write("FAILED"); System.Console.WriteLine(); } else { System.Console.Write("OK"); System.Console.WriteLine(); } System.Console.Write("AKIMA SPLINE TEST: "); if( aserrors ) { System.Console.Write("FAILED"); System.Console.WriteLine(); } else { System.Console.Write("OK"); System.Console.WriteLine(); } System.Console.Write("DIFFERENTIATION TEST: "); if( dserrors ) { System.Console.Write("FAILED"); System.Console.WriteLine(); } else { System.Console.Write("OK"); System.Console.WriteLine(); } System.Console.Write("COPY/SERIALIZATION TEST: "); if( cperrors ) { System.Console.Write("FAILED"); System.Console.WriteLine(); } else { System.Console.Write("OK"); System.Console.WriteLine(); } System.Console.Write("UNPACK TEST: "); if( uperrors ) { System.Console.Write("FAILED"); System.Console.WriteLine(); } else { System.Console.Write("OK"); System.Console.WriteLine(); } System.Console.Write("LIN.TRANS. TEST: "); if( lterrors ) { System.Console.Write("FAILED"); System.Console.WriteLine(); } else { System.Console.Write("OK"); System.Console.WriteLine(); } System.Console.Write("INTEGRATION TEST: "); if( ierrors ) { System.Console.Write("FAILED"); System.Console.WriteLine(); } else { System.Console.Write("OK"); System.Console.WriteLine(); } System.Console.Write("FITTING TEST: "); if( fiterrors ) { System.Console.Write("FAILED"); System.Console.WriteLine(); } else { System.Console.Write("OK"); System.Console.WriteLine(); } if( waserrors ) { System.Console.Write("TEST FAILED"); System.Console.WriteLine(); } else { System.Console.Write("TEST PASSED"); System.Console.WriteLine(); } System.Console.WriteLine(); System.Console.WriteLine(); } // // end // result = !waserrors; return result; }
/************************************************************************* Bicubic spline resampling Input parameters: A - function values at the old grid, array[0..OldHeight-1, 0..OldWidth-1] OldHeight - old grid height, OldHeight>1 OldWidth - old grid width, OldWidth>1 NewHeight - new grid height, NewHeight>1 NewWidth - new grid width, NewWidth>1 Output parameters: B - function values at the new grid, array[0..NewHeight-1, 0..NewWidth-1] -- ALGLIB routine -- 15 May, 2007 Copyright by Bochkanov Sergey *************************************************************************/ public static void spline2dresamplebicubic(ref double[,] a, int oldheight, int oldwidth, ref double[,] b, int newheight, int newwidth) { double[,] buf = new double[0,0]; double[] x = new double[0]; double[] y = new double[0]; spline1d.spline1dinterpolant c = new spline1d.spline1dinterpolant(); int i = 0; int j = 0; int mw = 0; int mh = 0; System.Diagnostics.Debug.Assert(oldwidth>1 & oldheight>1, "Spline2DResampleBicubic: width/height less than 1"); System.Diagnostics.Debug.Assert(newwidth>1 & newheight>1, "Spline2DResampleBicubic: width/height less than 1"); // // Prepare // mw = Math.Max(oldwidth, newwidth); mh = Math.Max(oldheight, newheight); b = new double[newheight-1+1, newwidth-1+1]; buf = new double[oldheight-1+1, newwidth-1+1]; x = new double[Math.Max(mw, mh)-1+1]; y = new double[Math.Max(mw, mh)-1+1]; // // Horizontal interpolation // for(i=0; i<=oldheight-1; i++) { // // Fill X, Y // for(j=0; j<=oldwidth-1; j++) { x[j] = (double)(j)/((double)(oldwidth-1)); y[j] = a[i,j]; } // // Interpolate and place result into temporary matrix // spline1d.spline1dbuildcubic(x, y, oldwidth, 0, 0.0, 0, 0.0, ref c); for(j=0; j<=newwidth-1; j++) { buf[i,j] = spline1d.spline1dcalc(ref c, (double)(j)/((double)(newwidth-1))); } } // // Vertical interpolation // for(j=0; j<=newwidth-1; j++) { // // Fill X, Y // for(i=0; i<=oldheight-1; i++) { x[i] = (double)(i)/((double)(oldheight-1)); y[i] = buf[i,j]; } // // Interpolate and place result into B // spline1d.spline1dbuildcubic(x, y, oldheight, 0, 0.0, 0, 0.0, ref c); for(i=0; i<=newheight-1; i++) { b[i,j] = spline1d.spline1dcalc(ref c, (double)(i)/((double)(newheight-1))); } } }