function [processed, preproc] = findpreproc(X, y, conf, preproc)
%
% Finds best preprocessing method or run the specified preprocessing method.
%
% Output:
%    processed - the input data set after preprocessing
%    preproc - a structure describing the preprocessing method:
%       pctrans - a matrix of the meanigful principal components
%       eigval  - the eigenvalues of the same principal components
%       clusterings - an array of structures consisting of cen and rad meaning the centers 
%                     and radiuses of the clusters.
%                     if clusterings is performed then the processed output is not a matrix but an array 
%                     of matrixes each matching one of the clusterings
%
% Input:
%    X - training set
%    y - output
%    conf - a configuration structure with the following parameters:
%       nclusters - a vector of number of clusters to be find by kmeans
%    preproc - same as the output format, used to do actual processing on the data rather then finding
%              the preprocessing
%
% Remarks:
%    For now the only preprocessing methodology is finding the principal components and desregarding
%    the meanigless components, the preprocessing itself is simply reducing the input dimesion by projecting 
%    the data set on the principal components, then running kmeans clustering on the reduced dataset.
%    The kmeans part didn't give good results at all, so it is only calculated and not performed for now
  
disp('Learning and neural computation project by - Igor Bogodlov and Ron Sidi');
disp('Function: findpreproc(X, y, conf, preproc)');

disp('Finding preprocessig methodology...');

if nargin < 4
   preproc = [];
end

[p,d] = size(X);
Y = X;

if ~isfield(preproc, 'pctrans')
   % Reduce dimensions via principal components
   mu = mean(Y);
   Y = X - mu(ones(1,p),:);
   CovMat = Y' * Y;
   [eigvec, eigval] = eig(CovMat);
   
   eigval = diag(eigval);
   [sorted, places] = sort(eigval);
   
   alpha = 0.001;
   % ignore meanigless principal components
   minval = alpha * max(sorted) + (1 - alpha) * min(sorted);
   V = eigvec(:,places(sorted >= minval));
   E = eigval(places(sorted >= minval));
   preproc.pctrans = V;
   preproc.eigval = E;
else
   disp('   Reducing dimension via given principal components');
end

% project the trainig set on the principal components to get the processed data
Y = X * preproc.pctrans;

% This was commented out because of some weird
% results sometimes. More experimenting is needed!
% Y = Y ./ sqrt(preproc.eigval(:,ones(1,p)))';
%% Y = scale(Y);

processed = Y;

d = size(Y,2);

if ~isfield(preproc, 'clusterings')
   
   % Perform k-means clustering on the reduced dataset
   if ~isfield(conf, 'nclusters')
      conf.nclusters = [d];
   end;
   
   clusterings = [];
   for k = conf.nclusters
      
      [clustering.cen, clustering.rad] = mykmeans(k, Y);
      %cen2 = dist(clustering.cen, clustering.cen');
      %clustering.cen2 = cen2 ./ clustering.rad(:,ones(1,size(cen2,1)));
      %clustering.rad2 = 1;
      if isempty(clusterings)
         clusterings = [clustering];
      else
         clusterings = [clusterings, clustering];
      end
   end
   preproc.clusterings = clusterings;
end

% don't perform clustering on the data set, it introduces problems
return;

nreps = size(preproc.clusterings,2);
processed = cell(1,nreps);

for i = 1:nreps
   % Get distances space, normalized according to radiuses
   Z = dist(Y, preproc.clusterings(i).cen');
   Z = Z ./ preproc.clusterings(i).rad(:,ones(1,p))';
   processed{i} = Z;
end

function Y=scale(X)
[p,d] = size(X);
minX = min(X,1);
Y = X - minX(ones(1,p),:);
maxY = max(Y,1);
maxY(maxY==0) = 1;
Y = Y ./ maxY(ones(1,p),:);
