% weight_vector = lms_adaline(InputSamples,TrainingRate,SatisfactoryMSE)
% the LMS (adaline) training algorithm pg 59 Elements of ANN
% Given Input, a set of training input vectors Ij of the form 
%   [X0,X1,...Xn-1,Class] 
% where X0 is a constant 1 and Class is -1 for Class0 or +1 for Class1
% and a TrainingRate (0,1]
% returns weight a vector of the form 
%   [W0,W1,...Wm] : m = n-1
% In constrast to a perceptron, the adaline modifies weights in a way that 
% minimizes the mean squared error for each iteration
function w = lms_adaline(Input,n,smse)
    Ip = Input(:,1:end-1);   % Ip: the sample x's
    c  = Input(:,end);       % c: vector of corresponding classes
    [rows,cols] = size(Ip);  % cols: number of columns in Ip
    w{1} = randn(1,cols)';   % create random weight vector of size cols
    k = 2;                   % using 1-based indexing set k to 2
    
    % while the mean squared error is unsatisfactory and the computational
    % bounds are not exceeded
    % NOTE: for mean squared error, find the mean of the vector of the 
    % of the squared errors where c = desired output, net = Ip * w{k-1}  
    % => use .^ 2 to compute squared errors of (c - Ip * w{k-1}) and take 
    % the mean of this vector
    while k < 100000 && mean((c-Ip*w{k-1}).^2) > smse
        % we'll selct Ij in a sequence Ij1 - Ij-n
        Ij = Ip(mod(k,rows)+1,1:end);
        dj = c(mod(k,rows)+1);
        
        % update the weight vector to
        % n * (dj - netj)*ij
        w{k} = w{k-1} + (n * (dj - Ij*w{k-1}) * Ij)';
        k = k + 1;
    end
    w = w{k-1}';
    
    
        