function [classOK, knnClass, votes] = myKnnClass(K, realClassX, distances);
% myKnnClass gives the classification result with K-nearest neighbour
%
% This is optimized for Assignment #2 in T-61.2010 in Autumn 2006,
% and won't work in general cases.
%
%   [classOK, knnClass, votes] = myKnnClass(K, realClassX, distances)
%
% myKnnClass classifies the test vector "x" with a known class
% "realClassX" and with a vector "distances" containing distances
% from test vector "x" to all possible training samples.
% Possible classes are 'Setosa', 'Versicolor', 
% and 'Virginica' according to famous "iris data".
% Classification is based on "K" nearest neighbours.
% If there are several classes with the same amount of neighours,
% class is randomly one of those. 
%
% If the classification is correct, "classOK" gets 1, otherwise 0.
% The class by the classifier is given in "knnClass".
% "votes" contains amount of hits.
%
%        K           number of neighbours, normally odd K = {1, 3, 5}
%        realClassX  (char) real class of the vector "x", one of 
%                    'Setosa', 'Versicolor', or 'Virginica'.
%                    In this Assignment #2 this is 
%                    "sTestD.label{i}", where i refers to index
%                    number of vector "x", i = 1..N.
%        distances   (60x1) vector containing distances from
%                    test sample x to each of data points in 
%                    the training set
%        classOK     1 if classified correctly; otherwise 0.
%                    In other words, if the correct class
%                    is e.g. 'Setosa' and the classified 
%                    class is 'Setosa', 1 is returned.
%        knnClass    (char) class given by k-nn classifier,
%                    one of 'Setosa', 'Versicolor', 'Virginica'
%        votes       (1x3) hits for 'Setosa', 'Versicolor', and
%                    'Virginica', respectively. sum(votes) == K
%
% Example:
%
%   K  = 5;     % we want now classify according to 5 nearest neighbours
%   for m = [1 : <number of testpoints>
%      distances  = <compute this>;   
%      % vector containing Euclidian distances from
%      % the testvector "x" with index m (sTestD.data(m,:),
%      % sTestD.label{m}), to each of vectors in training set
%      % sTrainD.data, size (60 x 1)
%     [c_ok, class_by_kNN, votes]  = ...
%        myKnnClass(K, sTestD.label{m}, distances);
%     <collect results>
%   end;
%
%   % Demo results with e.g. sTestD.label{2} == 'Virginica'
%   %   c_ok == 1   --> classification of sTestD.data(2,:) was correct
%   %   class_by_kNN == 'Virginica'  --> classifier gave Virginica
%   %   votes = [0 0 5] --> all 5 closest neighbours to a test point
%   %                       were Virginica
%
%   % Demo results with e.g. sTestD.label{4} == 'Versicolor'
%   %   c_ok == 0   --> classification of sTestD.data(4,:) was incorrect
%   %   class_by_kNN == 'Virginica'  --> classifier gave Virginica
%   %   votes = [0 2 3] --> 2 closest neighbours to a test point
%   %                       were Versicolor, 3 closest to Virginica
%

% Jukka Parviainen, 9.11.2006 / 16.12.2005 / 28.11.2005

% Vectors of the first class in training set
% are sTrainD.data(classBorders(1):classBorders(2), :),
% the second set sTrainD.data(classBorders(3):classBorders(4), :),
% and last class sTrainD.data(classBorders(5):classBorders(6), :).
classBorders = [1 20 21 40 41 60];

% Vector "distances" is normally euklidian distance from
% the test sample x to each training point,
% distances(1) = ((x_1 - t_1)^2 + (x_2 - t_2)^2 + ...).
% Square root is not needed because only ordering is needed.
if (prod(size(distances)) ~= classBorders(6))
  error('Wrong size of vector "distances"');
end;

[s1, s2]  = sort(distances);
KNInd     = s2(1:K);          % K closest neighbours
votes(1)  = sum((KNInd >= classBorders(1))  & (KNInd <= classBorders(2)));
votes(2)  = sum((KNInd >= classBorders(3)) & (KNInd <= classBorders(4)));
votes(3)  = sum((KNInd >= classBorders(5)) & (KNInd <= classBorders(6)));
[m1, m2]  = sort(votes, 'descend');

% if equal, then flip the coin
if (m1(1) == m1(2))
  if (rand(1,1) >= 0.5)
     tmp   = m1(1);
     m1(1) = m1(2);
     m1(2) = tmp;

     tmp   = m2(1);
     m2(1) = m2(2);
     m2(2) = tmp;
  end;
end;

if (strcmp(realClassX, 'Setosa'))
  rCXInd = 1;
elseif (strcmp(realClassX, 'Versicolor'))
  rCXInd = 2;  
elseif (strcmp(realClassX, 'Virginica'))
  rCXInd = 3;
else
  error('Unknown class type');
end;

if (m2(1) == rCXInd)
  classOK = 1;
else
  classOK = 0;
end;

switch m2(1)
  case 1,
    knnClass = 'Setosa';
  case 2,
    knnClass = 'Versicolor';
  case 3,
    knnClass = 'Virginica';
  otherwise
    error('Unknown class label');
end;
