function iteration = prob_5c( learningRate )
% Problem 5c
%
% This script implements the Perceptron learning rule to classify a 
% linearly-separable set of training data.  The training data is generated 
% by the script gen2classes.m.  The classifier will respond with +1 for data 
% in the set represented by X1 and will respond with -1 for data in the set 
% represented by X2. 
%
% The parameter 'learningRate' controls how quickly the algorithm converges on a 
% solution.
%
% This script will return the number of iterations that were needed to train the 
% network.
%
% An applied bias will be represented by an additional input with a fixed value 
% of +1.  Modifying the associated weight will result in a variable bias 
% being applied to the neuron.
%
% Training data will be presented one at a time, and in random order

% Get our two sets of training data X1 and X2 and make sure we haven't been 
% given crap data
gen2classes;
tic
if size(X1,2) ~= size(X2, 2);
    disp 'X1 and X2 must represent training data of the same dimension.  Make' 
    disp 'the columns the same size'
    return;
end;

% Set up our input matrix
X = [ X1; X2 ];

% Insert a column for the bias into the training data
X = [ ones(size(X,1),1) X ];

% Set up a vector containing the desired response
d = + ones(size(X1, 1), 1);
d = [ d ; - ones(size(X2, 1), 1) ];

% Set up our weight vector (initially zero)
w = zeros(size(X, 2), 1);

% Make a maximum iteration count in case the computation doesn't converge
maxIterations = 400;

% Store the input data and the desired response together so that I can permute 
% the order of both
concatenation = [d X];

% Do the iteration
iteration = 0;
while ( iteration < maxIterations );
    
    % Randomize the order of our input data and our desired response.  This works 
    % by randomly reordering rows of the 'concatenation' matrix.  The vector 
    % 'order' contains the new row order.  The 'transform' matrix is an identity 
    % matrix in which each diagonal is shifted to the new position of the 
    % corresponding row.  Premultiplying this reorders 'concatenation.'  The 
    % X and d matrices can then be pulled back out, in a re-ordered form.
    order = randperm(size(concatenation, 1));
    transform = zeros(size(concatenation, 1));
    for i=1:size(concatenation,1);
        transform(i, order(i)) = 1;
    end;
    concatenation = transform * concatenation;
    
    % Pull our input data and desired response out of the concatenation
    d = concatenation(:,1);
    X = concatenation(:,2:1+size(X,2));
    
    % Present every element in the test set and then look at the mean correction
    misClassifications = 0;
    for i=1:size(X,1);
    
        % Find the error for a random element in the training data set
        e = d(i) - sign(  X(i, :)  *  w  );
    
        % Find the update to the weight vector
        if e ~= 0;
            misClassifications = misClassifications + 1;
            if d(i) == 1;
                w = w + learningRate * X(i, :).';
            else
                w = w - learningRate * X(i, :).';
            end;
        end;
    end;
    
    % If there were no mis-classifications, we're done
    if misClassifications == 0;
        break;
    end;
        
    % Update the iteration count
    iteration = iteration + 1;
end;
toc
if iteration >= maxIterations;
    disp 'The iteration did not converge.  Try changing the learning rate.';
end;
disp(sprintf('Iterations for a learning rate of %1.4f: %d',                ...
                learningRate, iteration + 1))
drawline(w, 'r');