Params.poolSize = 0
MY_DATA_FOLDER = "../"

% Init Matlab parallel pool.
if (Params.poolSize > 0)  % Note: parfor doesn't work on Octave. 
    pool = parpool('local',Params.poolSize);
end;

[Y,X_star_base]=load_data(MY_DATA_FOLDER,"trid",2000,true);

l_set          = (0.05:.1:.95)*25

f1_set         = []
nnz_set        = []
time_total_set = []
time_ll_set    = []

for i = 1:length(l_set)
    [elapsedTotal,elapsedLL,X,permute] = run_SNETCH(Y,l_set(i),Params.poolSize );

    X_star                = X_star_base(permute,permute);
    nnz_set(end+1)        = nnz(X)/size(X,1);
    f1_set(end+1)         = acc(tril(X,-1)',tril(X_star_base,-1)');
    time_total_set(end+1) = elapsedTotal;
    time_ll_set(end+1)    = elapsedLL;

end

disp("---trid---")
nnz_set
f1_set
time_total_set
time_ll_set

[Y,X_star_base]=load_data(MY_DATA_FOLDER,"clus",2000,true);

l_set          = (0.05:.1:.95)*25
f1_set         = []
nnz_set        = []
time_total_set = []
time_ll_set    = []

for i = 1:length(l_set)
    [elapsedTotal,elapsedLL,X,permute]=run_SNETCH(Y,l_set(i),Params.poolSize );

    X_star                = X_star_base(permute,permute);
    nnz_set(end+1)        = nnz(X)/size(X,1);
    f1_set(end+1)         = acc(tril(X,-1)',tril(X_star_base,-1)');
    time_total_set(end+1) = elapsedTotal;
    time_ll_set(end+1)    = elapsedLL;

end

disp("---clus---")
nnz_set
f1_set
time_total_set
time_ll_set

% Cleanup (clean path, parpool, etc.).
if (Params.poolSize > 0)
    delete(pool);
end;

function [Y,X_star] = load_data(MY_DATA_FOLDER,type, p, normalize)

    fprintf("# Loading Data: type=%s p=%d", type, p);

    filename_base = MY_DATA_FOLDER+"/squic_test_db/n_500/";

    if(type=="clus")
        file = filename_base + "Clusters"+ p+".mat";
    else
        file = filename_base + "Trid"+ p+".mat";
    end

    data   = load(file);

    if (normalize)
        sd_inv = 1./sqrt(var(data.Y',1));
        Y      = data.Y .* sd_inv';
        X_star = data.Theta;
    else
        Y      = data.Y;
        X_star = data.Theta;
    end

end

function f1_sel = acc(X,X_star)

    verbatim=1;

    exact    = full(X)~=0;
    estimate = full(X_star)~=0;

    confusion = confusionmat(exact(:),estimate(:),'Order',[1,0]);

    % confusion: 3x3 confusion matrix
    tp = [];
    fp = [];
    fn = [];
    tn = [];
    len = size(confusion, 1);
    for k = 1:len                  %  predict
        % True positives           % | x o o |
        tp_value = confusion(k,k); % | o o o | true
        tp = [tp, tp_value];       % | o o o |
                                                %  predict
        % False positives                          % | o o o |
        fp_value = sum(confusion(:,k)) - tp_value; % | x o o | true
        fp = [fp, fp_value];                       % | x o o |
                                                 %  predict
        % False negatives                          % | o x x |
        fn_value = sum(confusion(k,:)) - tp_value; % | o o o | true
        fn = [fn, fn_value];                       % | o o o |
                                                                        %  predict
        % True negatives (all the rest)                                    % | o o o |
        tn_value = sum(sum(confusion)) - (tp_value + fp_value + fn_value); % | o x x | true
        tn = [tn, tn_value];                                               % | o x x |
    end
    % Statistics of interest for confusion matrix
    prec = tp ./ (tp + fp); % precision
    sens = tp ./ (tp + fn); % sensitivity, recall
    spec = tn ./ (tn + fp); % specificity
    acc = sum(tp) ./ sum(sum(confusion));
    f1 = (2 .* prec .* sens) ./ (prec + sens);
    % For micro-average
    microprec = sum(tp) ./ (sum(tp) + sum(fp)); % precision
    microsens = sum(tp) ./ (sum(tp) + sum(fn)); % sensitivity, recall
    microspec = sum(tn) ./ (sum(tn) + sum(fp)); % specificity
    microacc = acc;
    microf1 = (2 .* microprec .* microsens) ./ (microprec + microsens);
    % Names of the rows
    name = ["true_positive"; "false_positive"; "false_negative"; "true_negative"; ...
        "precision"; "sensitivity"; "specificity"; "accuracy"; "F-measure"];
    % Names of the columns
    varNames = ["name"; "classes"; "macroAVG"; "microAVG"];
    % Values of the columns for each class
    values = [tp; fp; fn; tn; prec; sens; spec; repmat(acc, 1, len); f1];
    % Macro-average
    macroAVG = mean(values, 2);
    % Micro-average
    microAVG = [macroAVG(1:4); microprec; microsens; microspec; microacc; microf1];
    % OUTPUT: final table
    stats = table(name, values, macroAVG, microAVG, 'VariableNames',varNames);
    if verbatim
        stats
    end

    %disp(f1)

    f1_sel=f1(1);

end

function [elapsedTotal,elapsedLL,Ueval,permute]=run_SNETCH(Y,lambda,poolSize)

    N = size(Y,1)
    Y=Y';

    % Threshold for convergence (calculated as sum(dL.^2)). 
    dLthreshold = 0.1.^6; 

    % Max number of iterations.
    numIterations = 100;

    % Set true to normalize covariance matrix (to correlation matrix).
    Params.normalizeCovariance = true;

    % Set true to enable Minimal degree reordering preprocessing step. 
    Params.columnReordering = true;

    %evalc('feature(''numcores'')')

    %% ----- INIT ----

    % Cholesky matrix (parameter to be optimized).
    L = speye(N); % initially identity

    disp('Calculating active set...');
    tic;

    % Precalculated (multilevel) active set.
    activeSet = covLambda(Y, lambda);

    % Starting active set.
    activeSet0 = activeSet{1};

    % Time needed to find starting active set.
    elapsedTimePreproc = toc;

    disp('Starting optimization...');
    tic

    permute = 1:N;

    % Column reordering.
    if(Params.columnReordering)
        
        permute = symamd(activeSet0); 

        %  U = U(reorderedCols, reorderedCols);
        Y = Y(:,permute);
        
        for i=1:length(activeSet)
            activeSet{i} = activeSet{i}(permute, permute);
        end;
    end;

    elapsedTimeReordering = toc;
    tic

    %% ------ OPTIMIZATION ----

    % Main optimization procedure.
    if (poolSize > 0)  % Note: parfor doesn't work on Octave. 
        [L,iterations, dL] = SNETCHpar(Y, L, lambda, numIterations, dLthreshold, activeSet); 
    else
        [L,iterations, dL] = SNETCH(Y, L, lambda, numIterations, dLthreshold, activeSet); 
    end;

    % Time spent doing coordinate descent.
    elapsedTime = toc;

    % Total time.
    elapsedTotal = elapsedTime + elapsedTimePreproc + elapsedTimeReordering;

    % Calculate precision matrix.
    tic;
    Ueval = L*L';
    elapsedLL = toc;

end