%{ Numerical integration of f(x) on [a,b] using quanc8.cpp AG: February 2022 %} clc fun=@f; abserr=0.0; relerr=1.0e-8; a = 0.0; % left endpoint b = 1.0; % right endpoint % === Step 0: plot the function === Nintervals = 1000; h = (b-a)/Nintervals; for k = 1:Nintervals+1 xf(k) = a+ (k-1)*h; yf(k) = f(xf(k)); end figure(1) plot(xf,yf,'r','LineWidth',1.5) xlabel('x') ylabel('f(x)') grid; % === end plot === % *** call quanc8 *** [result,errest,flag,nofun] = quanc8(fun,a,b,abserr,relerr); % *** end of call *** fprintf(' Quanc8 = %12.8f \n',result) fprintf(' tolearnce req. = %12.1e \n',relerr) fprintf(' tolerance est. = %12.1e \n',errest) fprintf(' flag = %12.5f \n',flag) fprintf(' function calls = %12i \n',nofun) function f = f(x) % f = sin(x); f = 1.0 ./ (1.0- 0.998 * x.^2); % f = x.*sin(30*x).*cos(50*x); % f = exp(-2.0*x) + cos(x); % f = (exp(-x.^2))/(1+x.^2); end % ================================================================== function [result,errest,flag,nofun] = quanc8(fun,a,b,abserr,relerr) %{ estimate the integral of fun(x) from a to b to a user provided tolerance. an automatic adaptive routine based on the 8-panel Newton-Cotes rule. input: fun the name of the integrand function subprogram fun(x). a the lower limit of integration. b the upper limit of integration.(b may be less than a.) relerr a relative error tolerance. (should be non-negative) abserr an absolute error tolerance. (should be non-negative) output: result an approximation to the integral hopefully satisfying the least stringent of the two error tolerances. errest an estimate of the magnitude of the actual error. nofun the number of function values used in calculation of result. flag a reliability indicator. if flag is zero, then result probably satisfies the error tolerance. if flag is xxx.yyy , then xxx = the number of intervals which have not converged and 0.yyy = the fraction of the interval left to do when the limit on nofun was approached. comments: Alex Godunov (February 2022) the program is based on a fortran version of program quanc8.f %} % check for limits if (a == b) return end % *** stage 0 *** reserve arryas (MatLab specific) qright = zeros(32,1); f = zeros(17,1); x = zeros(17,1); fsave = zeros(31,31); xsave = zeros(31,31); % *** stage 1 *** general initialization levmin = 1; levmax = 30; levout = 6; nomax = 5000; nofin = nomax - 8*(levmax - levout + 128); % trouble when nofun reaches nofin % NC coefficients w0 = 3956.0 / 14175.0; w1 = 23552.0 / 14175.0; w2 = -3712.0 / 14175.0; w3 = 41984.0 / 14175.0; w4 = -18160.0 / 14175.0; % initialize running sums to zero. flag = 0.0; result = 0.0; cor11 = 0.0; errest = 0.0; area = 0.0; nofun = 0; % *** stage 2 *** initialization for first interval lev = 0; nim = 1; x0 = a; x(16) = b; qprev = 0.0; f0 = fun(x0); stone = (b - a) / 16.0; x(8) = (x0 + x(16)) / 2.0; x(4) = (x0 + x(8)) / 2.0; x(12) = (x(8) + x(16)) / 2.0; x(2) = (x0 + x(4)) / 2.0; x(6) = (x(4) + x(8)) / 2.0; x(10) = (x(8) + x(12)) / 2.0; x(14) = (x(12) + x(16)) / 2.0; for j=2:2:16 f(j) = fun(x(j)); end nofun = 9; % *** stage 3 *** central calculation while nofun <= nomax x(1) = (x0 + x(2)) / 2.0; f(1) = fun(x(1)); for j=3:2:15 x(j) = (x(j-1) + x(j+1)) / 2.0; f(j) = fun(x(j)); end nofun = nofun + 8; step = (x(16) - x0) / 16.0; qleft = (w0*(f0 + f(8)) + w1*(f(1)+f(7)) + w2*(f(2)+f(6)) ... + w3*(f(3)+f(5)) + w4*f(4)) * step; qright(lev+1) = (w0*(f(8)+f(16))+w1*(f(9)+f(15))+w2*(f(10)+f(14)) ... + w3*(f(11)+f(13)) + w4*f(12)) * step; qnow = qleft + qright(lev+1); qdiff = qnow - qprev; area = area + qdiff; % *** stage 4 *** interval convergence test esterr = abs(qdiff) / 1023.0; if (abserr >=relerr*abs(area)) tolerr = abserr; else tolerr = relerr*abs(area); end tolerr = tolerr*(step/stone); % multiple logic conditions for the convergence test %key = 1; if (lev < levmin) key = 1; elseif (lev >= levmax) key = 2; elseif (nofun > nofin) key = 3; elseif (esterr <= tolerr) key = 4; else key = 1; end switch key case 1 % *** stage 5 *** no convergence % locate next interval. nim = 2*nim; lev = lev+1; % store right hand elements for future use. for i=1:1:8 fsave(i,lev) = f(i+8); xsave(i,lev) = x(i+8); end % assemble left hand elements for immediate use. qprev = qleft; for i=1:1:8 j = 0-i; f(2*j+18) = f(j+9); x(2*j+18) = x(j+9); end continue % go to start of stage 3 "central calculation" case 2 flag = flag + 1.0; case 3 % *** stage 6 *** trouble section % number of function values is about to exceed limit. nofin = 2*nofin; levmax = levout; flag = flag + (b - x0) / (b - a); case 4 %abc = 0.0; otherwise %abc = 1.0; end %switch % *** stage 7 *** interval converged % add contributions into running sums. result = result + qnow; errest = errest + esterr; cor11 = cor11 + qdiff / 1023.0; % locate next interval while rem(nim,2) ~= 0 %while floor(nim/2) ~= nim/2 nim = floor(nim/2); lev = lev-1; end nim = nim + 1; if (lev <= 0) break; % may exit futher calculation end % assemble elements required for the next interval. qprev = qright(lev); x0 = x(16); f0 = f(16); for i = 1:1:8 f(2*i) = fsave(i,lev); x(2*i) = xsave(i,lev); end end % end WHILE (main loop) % *** end stage 3 *** central calculation % *** stage 8 *** finalize and return result = result + cor11; if (errest == 0.0) return end temp = abs(result) + errest; while temp == abs(result) temp = abs(result) + errest; errest = 2.0*errest; end end % END of the function quanc8