% Preprocessing für den Partitionsschätzer. Hier werden die Daten
% dimensionsweise auf das Intervall [0,1] gelegt. Nur die Extrema an beiden
% Rändern (in 1/100) werden aus dem Intervall gefiltert.

function [result] = nearest_neighbour(X_l, Y_l, X_t, Y_t, X_eval, Y_eval, neighbours_list)

    max_n=size(X_t,1);
    neighbours_list=unique(min(neighbours_list,max_n));
    
    number_neighbours=length(neighbours_list);
    max_neighbour=max(neighbours_list);
    index_matrix=knn(X_t,X_l,max_neighbour);
    errors=zeros(number_neighbours,1);
    
    %best choice
    parfor nb=1:number_neighbours
        
        %calclulate nearest neighbour estimate
        Y_calc=mean(Y_l(index_matrix(:,1:neighbours_list(nb))),2);
        errors(nb)=sum((Y_calc-Y_t).^2);       

    end
    
    [~, best_nb]=min(errors);
    index_matrix=knn(X_eval,X_l,neighbours_list(best_nb));
    Y_calc=mean(Y_l(index_matrix),2);
    
    result.mse=mean((Y_calc-Y_eval).^2);
    result.res=Y_calc-Y_eval;
    result.param=neighbours_list(best_nb);
    
    
end

function [index_matrix]=knn(A,B,K)
    %calculates the k nearest neighbours of each of the lines of A within
    %the lines of the matrix B
    %if A is a N x D matrix and B is a M x D matrix then index_matrix is a
    %N x K matrix where each of the lines represents the K nearest
    %neighbours of the corresponding line in A. Thus the maximal entry of
    %index_matrix is M
    [n,d] = size(A);
    m=size(B,1);
    index_matrix = zeros(n,K);
    %gives us the distances of the lines of matrix A 
    %to all lines in matrix B
    for l=1:n
        
        distance=zeros(m,1);
        for t=1:d
            distance=distance+(B(:,t)-A(l,t)).^2;
        end

        [~,t]=sort(distance);
        %tie breaking by indices (da Daten bereits randomisiert)
        index_matrix(l,:)=t(1:K);
    end
    
end





