function [root,froot,r,iterTotal] = BiSecant(x0,x1,E,tolx,tolf,iterMax)
% function that computes the roots of a given function f(x) via the
% hybrid Bisection-Secant method
% Input Data:
%               x0: inital approximation of the independent variable x
%               Tolx: Prescribed tolerance applicable to x
%               Tolf: Prescribed tolerance applicable to f(x)
%               E: Bound of the absolute error (smaller than Tolx)
%               iterMax: maximum number of iterations
%
% Output Data:
%               iterTotal: actual number of iterations undertaken
%               root: approximated root
%               froot: approximated solution of f(root) 
%               r: relative error
%

% Initialize extremes of the interval using the proposed independent...
% variables, as well as their associated functions
x_k = x0; x_k1 = x1;
f_k = f(x0); f_k1 = f(x1);

% First check aims to confirm if our interval meets the requirements to...
% contain roots and if so, evaluates if any of the extremes is actually a
% zero
if f_k*f_k1>0
    error([' The value of the function at the proposed extremes of the '...
        'interval must have opposed signs, please review and provide '...
        'a valid interval']);
elseif abs(f_k)==0
    root=x_k; froot=0; r=0; iterTotal=0; return
elseif abs(f_k1)==0
    root=x_k1; froot=0; r=0; iterTotal=0; return
end

% Initializes counter
%k=0;

% Ensures the Bisection is the first method
method='Bisection_method';

% Evaluates the function zeros. The loop conditions are consistent with...
% the maximum number of iterations "iterMax" and the prescribed ...
% tolerances: "E", "tolx", "tolf"
for k=1:iterMax
    z(k) = k;
    if strcmp(method,'Bisection_method')  % Bisection Method
        b = (x_k1+x_k)/2;
        f_b = f(b);
        if sign(f_b)==sign(f_k1)
            x_k1 = b;
            f_k1 = f_b;
        else
            x_k = x_k1;
            f_k = f_k1;
            x_k1 = b;
            f_k1 = f_b;
        end
    
    elseif strcmp(method,'Secant_method')  % Secant Method
        if abs(f_k)<abs(f_k1)  % re-orders k where needed
            x_kk=x_k1; x_k1=x_k; x_k=x_kk;
            f_kk=f_k1; f_k1=f_k; f_k=f_kk;
        end
        s_k = (f_k1-f_k)/(x_k1-x_k);
        b = x_k1 - f_k1/s_k;
        x_k = x_k1; f_k = f_k1;
        x_k1 = b; f_k1 = f(b);
    end
    
    % defines the convergence's criteria
      y(k) = (abs((x_k-x_k1)/x_k1)); 

    % Prescribes conditions to ensure convergence to the root
    if abs(f_k1)<=tolf & ((abs(x_k-x_k1))<=(tolx*abs(x_k1)+E))
        iterTotal = k;  
        root = x_k1;
        froot = f_k1; 
        r = abs((x_k1-x_k)/x_k);
        disp(['A total of ',num2str(k),' iterations are required to achieve a satisfactory solution'])
        disp(['x_k=',num2str(root),' for which the function f(x) and relative error yields:'])
        disp(['f(x_k)=',num2str(froot),' and r_k=',num2str(r),', respectively.'])
        
        % Plots convergence against number of iterations
        plot(z,y,'r-+')
        xlabel('Number of Iterations - k')
        ylabel('Convergence Ratio')
        title('Convergence Ratio vs Number of Iterations')
        hold on; grid on;
        
        return
    end
    
    % When the increment with the secant method is larger it swaps to...
    % bisection and vice-versa. 
    if (f_k*f_k1<0) & (abs((x_k1-x_k)/2) < abs((f_k1*(x_k1-x_k))/(f_k1-f_k)))
        method = 'Bisection_method';
    else
        method = 'Secant_method';
    end
end

% display error if the conditions are not met for the prescribed number 
% of iterations
error('No roots have been obtained for the prescribed number of iterations')

            