% the program takes the initial data y_0 at the initial time x_0 and 
% computes up to time x_final using N timesteps of size dx.  It creates
% two vectors, x and y, which you can then plot against each other
% plot(x,y)
%
% To call the routine, use the command:
%
% [x,y] = adams_moulton_2(y_0,x_0,x_final,N,@f)
%
function [x,y] = adams_moulton_2(y_0,x_0,x_final,N,f)

% step-size:
dx = (x_final-x_0)/N;

% for dy/dx = f(x,y(x))
%
% y(x+dx) ~ y(x) + dx/2*(f(x,y(x)) + f(x+dx,y(x+dx)))
%
% AKA Crank-Nicolson.  This is implicitly determining y(x+dx)
% so we will do an approximation of y(x+dx) which will then
% go into the right-hand side...

% pux initial data into vector
x(1) = x_0;
y(:,1) = y_0;
% I will take one Euler step.  This has local truncation error O(h^2).
F_now = feval(f,x(1),y(:,1));
x(2) = x_0 + dx;
y(:,2) = y(:,1) + dx*F_now;
for ii=2:N
  F_old = F_now;
  F_now = feval(f,x(ii),y(:,ii));
  % I make a first approximation of y(x+dx) using the second-order
  % Adams-Bashforth
  y_approx = y(:,ii) + dx/2*(3*F_now - F_old);
  % I make a second approximation of y(x+dx) as if finding the root
  % of z = y(x) + dx/2*(f(x,y) + f(x+dx,z)) using an iterative method
  F_new = feval(f,x(ii)+dx,y_approx);
  y_approx = y(:,ii) + dx/2*(F_now + F_new);
  % I stop after one iteration and accept y_approx as y(x+dx).
  y(:,ii+1) = y_approx;
  x(ii+1) = x_0 + ii*dx;
end




