function [ best_dist ] = GraphEditDistance( g1, g2, num_missing_nodes)
%GraphEditDistance - calculate the distance between two graphs with missing
%nodes
%   Detailed explanation goes here


%simulated annealing parameters
c = 0.9;
start_temp = 1000;
t = start_temp;

round = 0;
n = size(g1,1);
m = size(g2,1);

%get the maximal size of the two graphs
N = max(n,m);

%if the number of missing nodes is not given, perform the search on all the
%nodes
if nargin < 3
    num_missing_nodes = N;
end

max_iterations = N;

init_distance = 0;

%make the two graphs the same size by padding zeros
if n < N
      %g1 is smaller, make it NxN
    g1(N,N) = 0;
    %the penalty is N-n because we added this number of nodes
    init_distance = N - n;
end
if m < N
      %g2 is smaller, make it NxN and add a penalty of N-m
    g2(N,N) = 0;
    init_distance = N - m;
end

%%%distance = sum(sum(abs(g1 - g2))) / 2;

best_dist = Inf;
for restart = 1 : N*N/4     % sigal 24.12.12 - add 1/4 factor as no improvement seen after N*N/4

      %create a matching vector between the two matrices
      %the first K nodes are known nodes and therefore they match (where     
      %K = N-num_missing_nodes
      %the vector will look like this: 
      %[1,2,3,...,K,  ***random permutation of the numbers between K+1 and N***]
    matching= [1:N-num_missing_nodes, randperm(num_missing_nodes) + N - num_missing_nodes];
    try
          %transform g2 according to the permutation, simply by switching
          %rows and columns according to the matching vector
      perm_graph = g2(matching, matching);
    catch ME1
          x = 90;
    end

     num_failures = 0;
     iter = 0;
    while num_failures < N %&& iter < max_iterations
        iter = iter+1;
        i = randi(num_missing_nodes,1) + N - num_missing_nodes;
        j = randi(num_missing_nodes,1) + N - num_missing_nodes;

        %should we swap i and j?

        row_i = perm_graph(i,:);
        row_j = perm_graph(j,:);
        
        %calculate the current distance due to rows i and j
        curr_dist =     sum(abs(g1(i,:) - row_i)) + sum(abs(g1(j,:) - row_j));
        
        temp = row_i(i);
        row_i(i) = row_i(j);
        row_i(j) = temp;
        
        temp = row_j(i);
        row_j(i) = row_j(j);
        row_j(j) = temp;
        
        %calculate the distance due to rows i and j after swapping them
        new_dist    =     sum(abs(g1(i,:) - row_j)) + sum(abs(g1(j,:) - row_i));

        %if swapping causes an improvement, or randomly according to
        %simulated annealing:
        if new_dist < curr_dist || (rand(1) < exp( - (new_dist - curr_dist) / (c*t) ) && new_dist ~= curr_dist)


            %temp = exp( - (new_dist - curr_dist) / (c*t) )
            %swap the "matching" indexes
            temp = matching(i);
            matching(i) = matching(j);
            matching(j) = temp;

            %swap rows i and j
            temp = perm_graph(i,:);
            perm_graph(i,:) = perm_graph(j,:);
            perm_graph(j,:) = temp;

            %swap columns i and j
            temp = perm_graph(:,i);
            perm_graph(:,i) = perm_graph(:,j);
            perm_graph(:,j) = temp;

            num_failures = 0;    

            round = round+1;

            t = t*c;

        else
            num_failures = num_failures + 1;
        end
    end

    distance = sum(sum(abs(g1 - perm_graph)));

    if distance < best_dist
          best_dist = distance;
          fprintf('restart %d, best_dist %d, N %d\n', full(restart), full(best_dist), full(N));
    end
    

end
%divide by 2 due to symmetry
best_dist = best_dist / 2 + init_distance;

end

