function []=mavardisp(beta_median,beta_std,beta_lbound,beta_ubound,psi_median,psi_std,psi_lbound,psi_ubound,sigma_median,X,Z,Y,n,m,p,k1,k3,q1,q2,T,ar,lambda1,lambda2,lambda3,lambda4,lambda5,IRFt,beta_gibbs,endo,exo,psi_gibbs,startdate,enddate,stringdates1,decimaldates1,datapath)

% function []=mavardisp(beta_median,beta_std,beta_lbound,beta_ubound,psi_median,psi_std,psi_lbound,psi_ubound,sigma_median,X,Z,Y,n,m,p,k1,k3,q1,q2,T,ar,lambda1,lambda2,lambda3,lambda4,lambda5,IRFt,beta_gibbs,endo,exo,psi_gibbs,startdate,enddate,stringdates1,decimaldates1,datapath)
% displays estimation results for the MABVAR model on Matlab prompt, creates a copy of these results on the text file results.txt
% inputs:  - vector 'beta_median': median value of the posterior distribution of beta
%          - vector 'beta_std': standard deviation of the posterior distribution of beta
%          - vector 'beta_lbound': lower bound of the credibility interval of beta
%          - vector 'beta_ubound': upper bound of the credibility interval of beta
%          - vector 'psi_median': median value of the posterior distribution of psi
%          - vector 'psi_std': standard deviation of the posterior distribution of psi
%          - vector 'psi_lbound': lower bound of the credibility interval of psi
%          - vector 'psi_ubound': upper bound of the credibility interval of psi
%          - vector 'sigma_median': median value of the posterior distribution of sigma (vectorised)
%          - matrix 'X': matrix of regressors for the VAR model (defined in 3.5.10)
%          - matrix 'Z': matrix of exogenous regressors for the MABVAR model (defined in 3.5.10)
%          - matrix 'Y': matrix of regressands for the VAR model (defined in 3.5.10)
%          - integer 'n': number of endogenous variables in the BVAR model (defined p 7 of technical guide)
%          - integer 'm': number of exogenous variables in the BVAR model (defined p 7 of technical guide)
%          - integer 'p': number of lags included in the model (defined p 7 of technical guide)
%          - integer 'k1': number of endogenous coefficients to estimate for each equation in the MABVAR model (defined p 77 of technical guide)
%          - integer 'k3': number of exogenous coefficients to estimate for each equation in the reformulated MABVAR model (defined p 77 of technical guide)
%          - integer 'q1': total number of endogenous coefficients to estimate in the MABVAR model (defined p 77 of technical guide)
%          - integer 'q2': total number of exogenous coefficients to estimate in the MABVAR model (defined p 77 of technical guide)
%          - integer 'T': number of sample time periods (defined p 7 of technical guide)
%          - scalar 'lambda1': overall tightness hyperparameter (defined p 16 of technical guide)
%          - scalar 'lambda2': cross-variable weighting hyperparameter(defined p 16 of technical guide)
%          - scalar 'lambda3': lag decay hyperparameter (defined p 16 of technical guide)
%          - scalar 'lambda4': exogenous variable tightness hyperparameter (defined p 17 of technical guide)
%          - scalar 'lambda5': block exogeneity shrinkage hyperparameter (defined p 32 of technical guide)
%          - integer 'IRFt': determines which type of structural decomposition to apply (none, Choleski, triangular factorization)
%          - matrix 'beta_gibbs': record of the gibbs sampler draws for the beta vector
%          - cell 'endo': list of endogenous variables of the model
%          - matrix 'psi_gibbs': record of the gibbs sampler draws for the psi vector
%          - cell 'exo': list of exogenous variables of the model
%          - string 'startdate': start date of the sample
%          - string 'enddate': end date of the sample
%          - cell 'stringdates1': date strings for the sample period
%          - vector 'decimaldates1': dates converted into decimal values, for the sample period
%          - string 'datapath': user-supplied path to excel data spreadsheet
% outputs: none

% before displaying and saving the results, start estimating the evaluation measures for the model

% obtain first a point estimate betatilde of the VAR coefficients related to endogenous variables
% this is simply beta_median, which is both the mean and the median of the normal distribution
betatilde=beta_median;
Btilde=reshape(betatilde,k1,n);
% generate U, using Btilde, from (3.5.15)
% U=eye(n*m);
%    for jj=1:p
%    U=[U;kron(eye(m),Btilde((jj-1)*n+1:jj*n,:)')];
%    end

% obtain then a point estimate psitilde of the VAR coefficients related to exogenous variables
% this is simply psi_median, which is both the mean and the median of the normal distribution
psitilde=psi_median;

% % combine the two to obtain vec(DELTA'), using (3.5.14)
% vecdeltap=U*psitilde;
% % recover delta
% deltap=reshape(vecdeltap,n,k3);
% DELTA=deltap';

% use this estimate to produce predicted values for the model, following (3.5.9)
Ytilde=X*Btilde+Z*DELTA;
% then produce the corresponding residuals, using (3.5.9)
EPStilde=Y-Ytilde;

% check first whether the model is stationary, using (1.9.1)
[stationary eigmodulus]=bear.macheckstable(Btilde,n,p);

% Compute then the sum of squared residuals
% compute first the RSS matrix, defined in (1.9.5)
RSS=EPStilde'*EPStilde;
% retain only the diagonal elements to get the vector of RSSi values
rss=diag(RSS);

% Go on calculating R2
% generate Mbar
Mbar=eye(T)-ones(T,T)/T;
% then compute the TSS matrix, defined in (1.9.8)
TSS=Y'*Mbar*Y;
% generate the R2 matrix in (1.9.9)
R2=eye(n)-RSS./TSS;
% retain only the diagonal elements to get the vector of R2 values
r2=diag(R2);

% then calculate the adjusted R2, using (1.9.11)
R2bar=eye(n)-((T-1)/(T-(k1+m)))*(eye(n)-R2);
% retain only the diagonal elements to get the vector of R2bar values
r2bar=diag(R2bar);

% now start displaying and saving the results

% preliminary task: create and open the txt file used to save the results

filelocation=datapath;
filelocation=[filelocation '\results.txt'];
fid=fopen(filelocation,'wt');

% print toolbox header

fprintf('%s\n','');
fprintf(fid,'%s\n','');

fprintf('%s\n','%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%');
fprintf(fid,'%s\n','%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%');
fprintf('%s\n','%                                                                                %%');
fprintf(fid,'%s\n','%                                                                                %');
fprintf('%s\n','%    BAYESIAN ESTIMATION, ANALYSIS AND REGRESSION (BEAR) TOOLBOX                                         %');
fprintf(fid,'%s\n','%    BAYESIAN ESTIMATION, ANALYSIS AND REGRESSION (BEAR) TOOLBOX                                     %');
fprintf('%s\n','%                                                                                %');
fprintf(fid,'%s\n','%                                                                                %');
fprintf('%s\n','%    This statistical package has been developed by the external developments division of the ECB.       %');
fprintf(fid,'%s\n','%    This statistical package has been developed by the external developments division of the ECB.   %');
fprintf('%s\n','%                                                                                %');
fprintf(fid,'%s\n','%                                                                                %');
fprintf('%s\n','%    Authors:                                                                                %');
fprintf(fid,'%s\n','%    Authors:                                                                                %');
fprintf('%s\n','%    Romain Legrand  (Romain Legrand <b00148883@essec.edu>)                                              %');
fprintf(fid,'%s\n','%    Romain Legrand  (Romain Legrand <b00148883@essec.edu>)                                          %');
fprintf('%s\n','%    Alistair Dieppe (adieppe@worldbank.org)                                                             %');
fprintf(fid,'%s\n','%    Alistair Dieppe (adieppe@worldbank.org)                                                         %');
fprintf('%s\n','%    Björn van Roye  (Bjorn.van_Roye@ecb.europa.eu)                                                      %');
fprintf(fid,'%s\n','%    Björn van Roye  (Bjorn.van_Roye@ecb.europa.eu)                                                  %');
fprintf('%s\n','%                                                                                %');
fprintf(fid,'%s\n','%                                                                                %');
fprintf('%s\n','%    Version 4.4                                                                                %');
fprintf(fid,'%s\n','%    Version 4.4                                                                                %');
fprintf('%s\n','%                                                                                %');
fprintf(fid,'%s\n','%                                                                                %');
fprintf('%s\n','%    The authors are grateful to Paolo Bonomolo, Marta Banbura, Martin Bruns, Fabio Canova,              %');
fprintf(fid,'%s\n','%    The authors are grateful to Paolo Bonomolo, Marta Banbura, Martin Bruns, Fabio Canova,          %');
fprintf('%s\n','%    Matteo Ciccarelli, Marek Jarocinski, Niccolo Battistini, Gabriel Bobeica                            %');
fprintf(fid,'%s\n','%    Matteo Ciccarelli, Marek Jarocinski, Niccolo Battistini, Gabriel Bobeica                        %');
fprintf('%s\n','%    Michele Lenza, Chiara Osbat, Mirela Miescu, Gary Koop, Giorgio Primiceri                            %');
fprintf(fid,'%s\n','%    Michele Lenza, Chiara Osbat, Mirela Miescu, Gary Koop, Giorgio Primiceri,                       %');
fprintf('%s\n','%    Michal Rubaszek, Barbara Rossi, Ben Schumann, Peter Welz, Hugo Vega de la Cruz and Francesca Loria. %');
fprintf(fid,'%s\n','%  Michal Rubaszek, Barbara Rossi, Ben Schumann, Peter Welz, Hugo Vega de la Cruz and Francesca Loria%');
fprintf('%s\n','%    valuable input and advice which contributed to improve the quality of this work.                    %');
fprintf(fid,'%s\n','%  valuable input and advice which contributed to improve the quality of this work.                  %');
fprintf('%s\n','%                                                                                %');
fprintf(fid,'%s\n','%                                                                                %');
fprintf('%s\n','%   These programmes are the responsibilities of the authors and not of the ECB and the Worldbank.       %');
fprintf(fid,'%s\n','%   These programmes are the responsibilities of the authors and not of the ECB and the Worldbank.   %');
fprintf('%s\n','%   Errors and ommissions remain those of the authors.                                                   %');
fprintf(fid,'%s\n','%   Errors and ommissions remain those of the authors.                                               %');
fprintf('%s\n','%                                                                                %');
fprintf(fid,'%s\n','%                                                                                %');
fprintf('%s\n','%    Please do not use or quote this work without permission.                                            %');
fprintf(fid,'%s\n','%    Please do not use or quote this work without permission.                                        %');
fprintf('%s\n','%                                                                                %');
fprintf(fid,'%s\n','%                                                                                %');
fprintf('%s\n','%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%');
fprintf(fid,'%s\n','%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%');

% print then estimation results

fprintf('%s\n','');
fprintf(fid,'%s\n','');
fprintf('%s\n','');
fprintf(fid,'%s\n','');

toolboxinfo='BEAR toolbox estimates';
fprintf('%s\n',toolboxinfo);
fprintf(fid,'%s\n',toolboxinfo);

time=clock;
datestring=datestr(time);
dateinfo=['Date: ' datestring(1,1:11) '   Time: ' datestring(1,13:17)];
fprintf('%s\n',dateinfo);
fprintf(fid,'%s\n',dateinfo);

fprintf('%s\n','');
fprintf(fid,'%s\n','');
fprintf('%s\n','');
fprintf(fid,'%s\n','');

VARtypeinfo='Mean-adjusted BVAR';
fprintf('%s\n',VARtypeinfo);
fprintf(fid,'%s\n',VARtypeinfo);

if IRFt==1
SVARinfo='structural decomposition: none';
elseif IRFt==2
SVARinfo='structural decomposition: choleski factorisation';
elseif IRFt==3
SVARinfo='structural decomposition: triangular factorisation';
elseif IRFt==4
SVARinfo='structural decomposition: sign restrictions';
end
fprintf('%s\n',SVARinfo);
fprintf(fid,'%s\n',SVARinfo);

temp='endogenous variables: ';
for ii=1:n
temp=[temp ' ' endo{ii,1} ' '];
end
endoinfo=temp;
fprintf('%s\n',endoinfo);
fprintf(fid,'%s\n',endoinfo);

temp='exogenous variables: constant';
% if m>1
%    for ii=1:m-1
%    temp=[temp ' ' exo{ii,1} ' '];
%    end
% end
exoinfo=temp;
fprintf('%s\n',exoinfo);
fprintf(fid,'%s\n',exoinfo);

sampledateinfo=['estimation sample: ' startdate '-' enddate];
fprintf('%s\n',sampledateinfo);
fprintf(fid,'%s\n',sampledateinfo);

samplelengthinfo=['sample size (omitting initial conditions): ' num2str(T)];
fprintf('%s\n',samplelengthinfo);
fprintf(fid,'%s\n',samplelengthinfo);

laginfo=['number of lags included in regression: ' num2str(p)];
fprintf('%s\n',laginfo);
fprintf(fid,'%s\n',laginfo);

hyperparam1='hyperparameters:';
fprintf('%s\n',hyperparam1);
fprintf(fid,'%s\n',hyperparam1);

hyperparam2=['autoregressive coefficient (ar):          ' num2str(ar)];
fprintf('%s\n',hyperparam2);
fprintf(fid,'%s\n',hyperparam2);

hyperparam3=['overall tightness (lambda1):              ' num2str(lambda1)];
fprintf('%s\n',hyperparam3);
fprintf(fid,'%s\n',hyperparam3);

hyperparam4=['cross-variable weighting (lambda2):       ' num2str(lambda2)];
fprintf('%s\n',hyperparam4);
fprintf(fid,'%s\n',hyperparam4);

hyperparam5=['lag decay (lambda3):                      ' num2str(lambda3)];
fprintf('%s\n',hyperparam5);
fprintf(fid,'%s\n',hyperparam5);

hyperparam6=['exogenous variable tightness (lambda4):   ' num2str(lambda4)];
fprintf('%s\n',hyperparam6);
fprintf(fid,'%s\n',hyperparam6);

hyperparam7=['block exogeneity shrinkage (lambda5):     ' num2str(lambda5)];
fprintf('%s\n',hyperparam7);
fprintf(fid,'%s\n',hyperparam7);

% display coefficient estimates

fprintf('%s\n','');
fprintf(fid,'%s\n','');
fprintf('%s\n','');
fprintf(fid,'%s\n','');
fprintf('%s\n','');
fprintf(fid,'%s\n','');

coeffinfo=['VAR coefficients (beta): posterior estimates'];
fprintf('%s\n',coeffinfo);
fprintf(fid,'%s\n',coeffinfo);

for ii=1:n

fprintf('%s\n','');
fprintf(fid,'%s\n','');
if ii~=1
fprintf('%s\n','');
fprintf(fid,'%s\n','');
end

endoinfo=['Endogenous: ' endo{ii,1}];
fprintf('%s\n',endoinfo);
fprintf(fid,'%s\n',endoinfo);

coeffheader=fprintf('%25s %15s %15s %15s %15s\n','','Median','St.dev','Low.bound','Upp.bound');
coeffheader=fprintf(fid,'%25s %15s %15s %15s %15s\n','','Median','St.dev','Low.bound','Upp.bound');

% handle the endogenous
   for jj=1:n
      for kk=1:p
      values=[beta_median((ii-1)*k1+n*(kk-1)+jj,1) beta_std((ii-1)*k1+n*(kk-1)+jj,1) beta_lbound((ii-1)*k1+n*(kk-1)+jj,1) beta_ubound((ii-1)*k1+n*(kk-1)+jj,1)];
      fprintf('%25s %15.3f %15.3f %15.3f %15.3f\n',strcat(endo{jj,1},'(-',int2str(kk),')'),values);
      fprintf(fid,'%25s %15.3f %15.3f %15.3f %15.3f\n',strcat(endo{jj,1},'(-',int2str(kk),')'),values);
      end
   end

% handle the exogenous
% display the results related to the constant
values=[psi_median(ii,1) psi_std(ii,1) psi_lbound(ii,1) psi_ubound(ii,1)];
fprintf('%25s %15.3f %15.3f %15.3f %15.3f\n','Constant',values);
fprintf(fid,'%25s %15.3f %15.3f %15.3f %15.3f\n','Constant',values);
   % if there is no other exogenous, stop here
   if m==1
   % if there are other exogenous, display their results
   elseif m>1
      for jj=1:m-1
      values=[psi_median(n*jj+ii,1) psi_std(n*jj+ii,1) psi_lbound(n*jj+ii,1) psi_ubound(n*jj+ii,1)];
      %fprintf('%25s %15.3f %15.3f %15.3f %15.3f\n',exo{jj,1},values);
     %fprintf(fid,'%25s %15.3f %15.3f %15.3f %15.3f\n',exo{jj,1},values);
     end
   end

fprintf('%s\n','');
fprintf(fid,'%s\n','');

% display evaluation measures
rssinfo=['Sum of squared residuals: ' num2str(rss(ii,1),'%.2f')];
fprintf('%s\n',rssinfo);
fprintf(fid,'%s\n',rssinfo);

r2info=['R-squared: ' num2str(r2(ii,1),'%.3f')];
fprintf('%s\n',r2info);
fprintf(fid,'%s\n',r2info);

adjr2info=['adj. R-squared: ' num2str(r2bar(ii,1),'%.3f')];
fprintf('%s\n',adjr2info);
fprintf(fid,'%s\n',adjr2info);

end

fprintf('%s\n','');
fprintf(fid,'%s\n','');
fprintf('%s\n','');
fprintf(fid,'%s\n','');

% display VAR stability results
eigmodulus=reshape(eigmodulus,p,n);
stabilityinfo1=['Roots of the characteristic polynomial (modulus):'];
fprintf('%s\n',stabilityinfo1);
fprintf(fid,'%s\n',stabilityinfo1);
for ii=1:p
temp=num2str(eigmodulus(ii,1),'%.3f');
   for jj=2:n
   temp=[temp,'  ',num2str(eigmodulus(ii,jj),'%.3f')];
   end
fprintf('%s\n',temp);
fprintf(fid,'%s\n',temp);
end
if stationary==1;
stabilityinfo2=['No root lies outside the unit circle.'];
stabilityinfo3=['The estimated VAR model satisfies the stability condition'];
fprintf('%s\n',stabilityinfo2);
fprintf(fid,'%s\n',stabilityinfo2);
fprintf('%s\n',stabilityinfo3);
fprintf(fid,'%s\n',stabilityinfo3);
else
stabilityinfo2=['Warning: at leat one root lies on or outside the unit circle.'];
stabilityinfo3=['The estimated VAR model will not be stable'];
fprintf('%s\n',stabilityinfo2);
fprintf(fid,'%s\n',stabilityinfo2);
fprintf('%s\n',stabilityinfo3);
fprintf(fid,'%s\n',stabilityinfo3);
end

fprintf('%s\n','');
fprintf(fid,'%s\n','');
fprintf('%s\n','');
fprintf(fid,'%s\n','');

% display posterior for sigma
sigmainfo=['sigma (residual covariance matrix): posterior estimates'];
fprintf('%s\n',sigmainfo);
fprintf(fid,'%s\n',sigmainfo);
% calculate the (integer) length of the largest number in sigma, for formatting purpose
width=length(sprintf('%d',floor(max(abs(bear.vec(sigma_median))))));
% add a separator, a potential minus sign, and three digits (total=5) to obtain the total space for each entry in the matrix
width=width+5;
for ii=1:n
temp=[];
   for jj=1:n
   % convert matrix entry into string
   number=num2str(sigma_median(ii,jj),'% .3f');
      % pad potential missing blanks
      while numel(number)<width
      number=[' ' number];
      end
   number=[number '  '];
   temp=[temp number];
   end
fprintf('%s\n',temp);
fprintf(fid,'%s\n',temp);
end

fclose(fid);

% Finally, display the results in terms of graph

% graph the empirical posterior distribution of the VAR parameters
% first reshape the gibbs sampler output to be compatible with the way Matlab uses subplot
% first concatenate psi_gibbs on beta_gibbs to obtain a vector similar to beta in the classical BVAR model
temp=[];
for ii=1:n
temp=[temp;beta_gibbs((ii-1)*k1+1:ii*k1,:)];
   for jj=1:m
   temp=[temp;psi_gibbs((jj-1)*m+ii,:)];
   end
end
beta_reshape=temp;
[beta_swap]=bear.betaswap(beta_reshape,n,m,p,k1+m);
plotvar=1;
plotlag=1;
plotexo=1;
plotconst=1;
% then plot the figure
%{
postdis=figure;
set(postdis,'Color',[0.9 0.9 0.9]);
set(postdis,'name','posterior ditribution of VAR coefficients');
for ii=1:(q1+q2)
subplot(n,k1+m,ii)
hist(beta_swap(ii,:),20);
histproperties=findobj(gca,'Type','patch');
set(histproperties,'FaceColor',[0.7 0.78 1],'EdgeColor',[0 0 0],'LineWidth',0.1);
set(gca,'FontName','Times New Roman');
   % top labels for endogenous
   if ii<=n*p
   temp=[endo{plotvar,1} '(-' num2str(plotlag) ')'];
   title(temp,'FontWeight','normal');
      if plotlag<p
      plotlag=plotlag+1;
      elseif plotlag==p
      plotlag=1;
      plotvar=plotvar+1;
      end
   end
   % top labels for exogenous
   %if ii==k1+1
   %title('Constant','FontWeight','normal');
   %end
   %if ii>k1+1 && ii<=k1+m
   %title(exo{plotexo,1},'FontWeight','normal');
   %plotexo=plotexo+1;
   %end
   % side labels
   %if rem((ii-1)/(k1+m),1)==0
   ylabel(endo{(ii-1)/(k1+m)+1,1},'FontWeight','normal');
   %end
end
%}

% plot actual vs. fitted
actualfitted=figure;
set(actualfitted,'Color',[0.9 0.9 0.9]);
set(actualfitted,'name','model estimation: actual vs fitted')
ncolumns=ceil(n^0.5);
nrows=ceil(n/ncolumns);
for ii=1:n
subplot(nrows,ncolumns,ii)
hold on
plot(decimaldates1,Y(:,ii),'Color',[0 0 0],'LineWidth',2);
plot(decimaldates1,Ytilde(:,ii),'Color',[1 0 0],'LineWidth',2);
hold off
set(gca,'XLim',[decimaldates1(1,1) decimaldates1(end,1)],'FontName','Times New Roman');
title(endo{ii,1},'FontName','Times New Roman','FontSize',10);
   if ii==1
   plotlegend=legend('actual','fitted');
   set(plotlegend,'FontName','Times New Roman');
   end
end

% plot the residuals
residuals=figure;
set(residuals,'Color',[0.9 0.9 0.9]);
set(residuals,'name','model estimation: residuals')
for ii=1:n
subplot(nrows,ncolumns,ii)
plot(decimaldates1,EPStilde(:,ii),'Color',[0 0 0],'LineWidth',2)
set(gca,'XLim',[decimaldates1(1,1) decimaldates1(end,1)],'FontName','Times New Roman');
title(endo{ii,1},'FontName','Times New Roman','FontSize',10);
end

% finally, save the results on excel
bear.data.excelrecord2fcn(T, n, endo, stringdates1, Y, Ytilde, pref, EPStilde)


function [imBWGrid, imColorGridMasked, imBWBoardMask] = segColorGrid(whiteLight, colorGrid, camCorners, verbose)
%% Extracts the color grid on white board.
% See also: ImgProc.getMatchedNodes

%% License
% ACADEMIC OR NON-PROFIT ORGANIZATION NONCOMMERCIAL RESEARCH USE ONLY
% Copyright (c) 2018 Bingyao Huang
% All rights reserved.

% Redistribution and use in source and binary forms, with or without
% modification, are permitted provided that the following conditions are met:

% The above copyright notice and this permission notice shall be included in all
% copies or substantial portions of the Software.

% If you publish results obtained using this software, please cite our paper.

% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
% SOFTWARE.

%%

imLight = imread(whiteLight);
imColorGrid = imread(colorGrid);

% find seg mask
backSlashidx = strfind(whiteLight, '\');
whiteLightFileName = whiteLight(backSlashidx(end)+1:end);
setNumIdx = regexp(whiteLightFileName,'\d');

if(~isempty(camCorners))
    bwBoardName = fullfile(whiteLight(1:backSlashidx(end)), ['bwBoard', whiteLightFileName(setNumIdx),'.png']);
    if(verbose)
        figure('Name', 'segColorGrid', 'units','normalized','outerposition',[0 0 1 1]);
    end
else
    bwBoardName = fullfile(whiteLight(1:backSlashidx(end)), ['objectROI', whiteLightFileName(setNumIdx),'.png']);
end

%% Flood fill using the 1st checkerboard corner
% Convert RGB image into HSV color space.
% imHsv = rgb2hsv(imLight);
% imV = imHsv(:,:,3); % intensity channelim
% imLightEnhance = ImgProc.imadjust3(imLight);
% imGray = mat2gray(rgb2gray(imLightEnhance)); % normalized gray

% find color grid pixels
im1 = rgb2hsv(imColorGrid);
im2 = rgb2hsv(imLight);
s = mean(im1(:,:,3)) / mean(im2(:,:,3));
s = min(s, 1);
imGrid = imColorGrid-s*imLight;

%%
if(exist(bwBoardName,'file'))
    imBWBoard = imread(bwBoardName);
elseif(~isempty(camCorners))
    imGrayGrid = rgb2gray(imColorGrid-s*imLight);
    imGridROI = bwconvhull(bwareafilt(imGrayGrid > mean(imGrayGrid(:)), 1));
    imLightMasked = ImgProc.maskImage(imLight, imGridROI);
    imGrayMasked = rgb2gray(imLightMasked); % normalized gray

    imBWBoard = false(size(imGrayMasked));

    % get bounding box corners of the checkerboard
    maxXY = max(camCorners);
    minXY = min(camCorners);

    permsX = perms([minXY(:,1), maxXY(:,1)]);
    permsY = perms([minXY(:,2), maxXY(:,2)]);

    % check the best flood fill results when using the 4 corners
    winSize = 5;

    for i = 1:length(permsX)
        for j = 1:length(permsY)
            % init seed set to corner
            x = round(permsX(i));
            y = round(permsY(j));

            % find the brightest pixel index in a 5x5 local roi
            imRoi = imgaussfilt(imGrayMasked(y-winSize:y+winSize,x-winSize:x+winSize));
            [maxVal, maxIdx] = max(imRoi(:));

            % convert back to global row and col
            [row, col] = ind2sub(size(imRoi), maxIdx);
            col = col + x - winSize - 1;
            row = row + y - winSize - 1;

            % Flood fill using geodesic distance
            imCurSeg = ImgProc.segFloodFill(imLightMasked, row, col);
            imBWBoard = imBWBoard | imCurSeg;

            if(verbose)
                %             fs(imCurSeg);hold on; plot(col,row,'ro');
            end
        end
    end
else
    imBWBoard = true(size(imLight,1),size(imLight,2));
    % imBWBoard = bwconvhull(imBWBoard);

    % imWeight = graydiffweight(imLabNorm, col, row, 'GrayDifferenceCutoff', tol);
    % imBWBoard = imsegfmm(imWeight, col, row, 0.01);
end

if(verbose && ~isempty(camCorners))
    subplot(2,2,1);
    imshow(imBWBoard);
    title('Flood fill white board');
    drawnow
end

%% Crop checkerboard area
if(~isempty(camCorners))
    % find checkerboard area
    imBWCb = ~imBWBoard;
    winSize = 5;

    seeds = [];
    for i = 1:length(camCorners)
        x = round(camCorners(i,1));
        y = round(camCorners(i,2));
        imBWCb(y-winSize:y+winSize,x-winSize:x+winSize) = 1;

        % seeds for bwselect
        seeds = [seeds; [x,y]];
    end

    % select the checkerboard area using all corners as seeds
    imBWCb = bwselect(imBWCb, seeds(:,1), seeds(:,2), 8);

    imBWCb = bwconvhull(imBWCb);
    % imBWCb( round(minXY(:,2)):round(maxXY(:,2)), round(minXY(:,1)):round(maxXY(:,1))) = 1;
    imBWBoardMask = imfill(imBWBoard, 'holes');
    imBWBoardMask(imBWCb(:)) = 0;
else
    imBWCb = ~imBWBoard;
    imBWBoardMask = imBWBoard;
end

if(verbose && ~isempty(camCorners))
    subplot(2,2,2);
    imshow(imBWBoardMask);
    title('White board without checkerboard area');
    drawnow
end

%% Segment color grid
% im1 = rgb2hsv(imColorGrid);
% im2 = rgb2hsv(imLight);
% s = mean(im1(:,:,3)) / mean(im2(:,:,3));
% s = min(s, 1);
imColorGridMasked = ImgProc.maskImage(imGrid, imBWBoardMask);

% enhance color
imColorGridMasked = ImgProc.imadjust3(imColorGridMasked);

% to hsv
imHsv = rgb2hsv(imColorGridMasked);

% adaptive thresholding using Gaussian filter
sigma = 3;
imBWGrid = ImgProc.adaptiveThresh(imHsv(:,:,3), sigma);

% only keep the largest area
imBWGrid = bwareafilt(imBWGrid, 1);
% imBWGrid = bwmorph(imBWGrid,'clean', inf);

% close and open
se1 = strel('line',2,0);
se2 = strel('line',2,90);
imBWGrid = imclose(imBWGrid, se1);
imBWGrid = imclose(imBWGrid, se2);

% clean edges
imBWCb = imdilate(imBWCb, ones(8, 8));
imBWGrid(imBWCb(:)) = 0;

if(~isempty(camCorners))
    if(verbose)
        subplot(2,2,3);
        imshow(imColorGridMasked);
        title('Color grid masked');
        drawnow

        subplot(2,2,4);
        imshow(imBWGrid);
        title('Color grid binary mask');
        drawnow
    end
end

% imColorGridMasked = ImgProc.maskImage(imColorGridMasked, imBWGrid);
end


% Copyright 2017 Lime Microsystems Ltd.
%
% Licensed under the Apache License, Version 2.0 (the "License");
% you may not use this file except in compliance with the License.
% You may obtain a copy of the License at
%
%    http://www.apache.org/licenses/LICENSE-2.0
%
% Unless required by applicable law or agreed to in writing, software
% distributed under the License is distributed on an "AS IS" BASIS,
% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
% See the License for the specific language governing permissions and
% limitations under the License.
%
% 3GPP TS 25.211 7.2.0 r7 p12 Table 3&4
% slots 0-14
% bits 3,4,5,6,7,8
function y=WCDMAULPilots( slot, bits )
	if bits==3 % col 1&2 can be used as FSW
		pilots=[1,1,1;0,0,1;0,1,1;0,0,1; \
		1,0,1;1,1,1;1,1,1;1,0,1; \
		0,1,1;1,1,1;0,1,1;1,0,1; \
		1,0,1;0,0,1;0,0,1];
	elseif bits==4 % col 2&3 can be used as FSW
		pilots=[1,1,1,1;1,0,0,1;1,0,1,1;1,0,0,1; \
		1,1,0,1;1,1,1,1;1,1,1,1;1,1,0,1; \
		1,0,1,1;1,1,1,1;1,0,1,1;1,1,0,1; \
		1,1,0,1;1,0,0,1;1,0,0,1];
	elseif bits==5 % col 1,2 and 4,5 can be used as FSW
		pilots=[1,1,1,1,0;0,0,1,1,0;0,1,1,0,1;0,0,1,0,0; \
		1,0,1,0,1;1,1,1,1,0;1,1,1,0,0;1,0,1,0,0; \
		0,1,1,1,0;1,1,1,1,1;0,1,1,0,1;1,0,1,1,1; \
		1,0,1,0,0;0,0,1,1,1;0,0,1,1,1];
	elseif bits==6 % col 2,3 and 5,6
		pilots=[1,1,1,1,1,0;1,0,0,1,1,0;1,0,1,1,0,1;1,0,0,1,0,0; \
		1,1,0,1,0,1;1,1,1,1,1,0;1,1,1,1,0,0;1,1,0,1,0,0; \
		1,0,1,1,1,0;1,1,1,1,1,1;1,0,1,1,0,1;1,1,0,1,1,1; \
		1,1,0,1,0,0;1,0,0,1,1,1;1,0,0,1,1,1];
	elseif bits==7 % col 2,3 and 5,6
		pilots=[1,1,1,1,1,0,1;1,0,0,1,1,0,1;1,0,1,1,0,1,1;1,0,0,1,0,0,1; \
		1,1,0,1,0,1,1;1,1,1,1,1,0,1;1,1,1,1,0,0,1;1,1,0,1,0,0,1; \
		1,0,1,1,1,0,1;1,1,1,1,1,1,1;1,0,1,1,1,0,1;1,1,0,1,1,1,1; \
		1,1,0,1,0,0,1;1,0,0,1,1,1,1;1,0,0,1,1,1,1];
	else % bits=8 % col 1,3 and 5,7
		pilots=[1,1,1,1,1,1,1,0;1,0,1,0,1,1,1,0;1,0,1,1,1,0,1,1;1,0,1,0,1,0,1,0;
		1,1,1,0,1,0,1,1;1,1,1,1,1,1,1,0;1,1,1,1,1,0,1,0;1,1,1,0,1,0,1,0;
		1,0,1,1,1,1,1,0;1,1,1,1,1,1,1,1;1,0,1,1,1,0,1,1;1,1,1,0,1,1,1,1;
		1,1,1,0,1,0,1,0;1,0,1,0,1,1,1,1;1,0,1,0,1,1,1,1];
	end
	size(pilots);
	y=pilots(slot+1,:);
end


function [data] = ft_denoise_hfc(cfg,data)

% FT_DENOISE_HFC implements harmonic field correction, which models
% interference as a harmonic magnetic field. It is particulaly useful
% for MEG data with low channel numbers (e.g. OPM data). Homogenous field based
% on Tierney et al. (2021) NIMG, 118484. Harmonic expansion based on Tierney
% et al. (2022) NIMG, 119338.
%
% Use as:
%   data = ft_denoise_hfc(cfg,data)
%
% Where cfg is a configuration structure that contains:
%   cfg.channel         = channels for HFC (default = 'all')
% 	cfg.order           = spherical harmonic order:
%                           order = 1 is a homogenous field; order = 2
%                           includes gradients; order = 3 includes
%                           quadratic terms etc. (default = 1)
%   cfg.trials          = which trials do you want to denoise?
%                           (default = 'all')
%   cfg.updatesens      = do you want to update sensor info with projector?
%                           (default = 'yes')
%   cfg.feedback        = do you want feedback (default = 'yes')
%   cfg.residualcheck   = do you want to check channel residuals
%                          (default = 'yes')
%   cfg.residualthresh  = (in pT) what level of residual signal is fine for
%                           quality assurance (default = 50)
% See also FT_DENOISE_SYNTHETIC, FT_DENOISE_PCA, FT_DENOISE_DSSP, FT_DENOISE_TSP

% Copyright (C) 2021-22, Tim Tierney, George O'Neill, Robert Seymour
% Wellcome Centre for Human Neuroimaging, UCL
%
% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org
% for the documentation and details.
%
%    FieldTrip is free software: you can redistribute it and/or modify
%    it under the terms of the GNU General Public License as published by
%    the Free Software Foundation, either version 3 of the License, or
%    (at your option) any later version.
%
%    FieldTrip is distributed in the hope that it will be useful,
%    but WITHOUT ANY WARRANTY; without even the implied warranty of
%    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
%    GNU General Public License for more details.
%
%    You should have received a copy of the GNU General Public License
%    along with FieldTrip. If not, see <http://www.gnu.org/licenses/>.
%
% $Id$

% these are used by the ft_preamble/ft_postamble function and scripts
ft_revision = '$Id$';
ft_nargin   = nargin;
ft_nargout  = nargout;

% do the general setup of the function
ft_defaults
ft_preamble init
ft_preamble debug
ft_preamble loadvar    data
ft_preamble provenance data

% the ft_abort variable is set to true or false in ft_preamble_init
if ft_abort
  % do not continue function execution in case the outputfile is present and the user indicated to keep it
  return
end

% check if the input cfg is valid for this function
cfg = ft_checkconfig(cfg, 'forbidden',  {'trial'}); % prevent accidental typos, see issue 1729

% check the input data
data = ft_checkdata(data, 'datatype', {'raw'});

% check if the input cfg is valid for this function
cfg = ft_checkconfig(cfg, 'forbidden',  {'trial'}); % prevent accidental typos, see issue 1729
cfg = ft_checkconfig(cfg, 'forbidden',  {'channels'}); % prevent accidental typos, see issue 1729

% Check if the data has a grad structure
if ~isfield(data,'grad')
  error(['Data needs a grad structure']);
end

% set the defaults
cfg.order           = ft_getopt(cfg, 'order', 1);
cfg.channel         = ft_getopt(cfg, 'channel', 'all', 1);
cfg.trials          = ft_getopt(cfg, 'trials', 'all', 1);
cfg.updatesens      = ft_getopt(cfg, 'updatesens', 'yes');
cfg.feedback        = ft_getopt(cfg, 'feedback', 'yes');
cfg.residualcheck   = ft_getopt(cfg, 'residualcheck', 'yes');
cfg.residualthresh  = ft_getopt(cfg, 'residualthresh', 50);

% select data based on cfg.channel and cfg.trials
tmpcfg = keepfields(cfg, {'trials', 'showcallinfo'});

% Select the correct channels using ft_channelselection
tmpcfg.channel = cfg.channel;
data   = ft_selectdata(tmpcfg, data);

% restore the provenance information
[cfg, data] = rollback_provenance(cfg, data);

% Check match between input data chans and grad chans
[x, dummy] = ismember(data.grad.label,data.label);

% Warn the user if there are chans in grad not in data
num_mismatch = sum(x(:) == 0);

if num_mismatch > 0 && istrue(cfg.feedback)
  ft_warning(['Found ' num2str(num_mismatch) ' channels in grad structure'...
    ' not present in cfg.channel. These channels will NOT be used for '...
    'Harmonic Field Correction']);
end

% Check for Dr. Tim Tierney's OPM toolbox on the path, and add if needed
% See: https://github.com/tierneytim/OPM
ft_hastoolbox('opm', 1);

% generate harmonic basis set
opt = [];
opt.li = cfg.order;
opt.v = data.grad.coilpos(x,:);
opt.o = data.grad.coilori(x,:);
N = spm_opm_vslm(opt);

% Make montage for the next step
montage     = [];
montage.tra = eye(length(N)) - N*pinv(N);
montage.labelold = data.grad.label(x);
montage.labelnew = data.grad.label(x);
montage.chantypeold = data.grad.chantype(x);
montage.chantypenew = data.grad.chantype(x);
montage.chanunitold = data.grad.chanunit(x);
montage.chanunitnew = data.grad.chanunit(x);

labelold = data.label;

% apply montage;
% Tell the user
data = ft_apply_montage(data,montage,'keepunused','yes');
if istrue(cfg.feedback)
  disp('Applied HFC to the data');
end

% Update the tra to account for the g. Essential to correct the lead
% fields going forward.
if istrue(cfg.updatesens)
  data.grad = ft_apply_montage(data.grad, montage, 'keepunused',...
    'yes', 'balancename', 'hfc','warning',false);
  if istrue(cfg.feedback)
    disp('Converted the sensor description to HFC');
  end
end

% reorder the channels to stay close to the original ordering
[dummy, selnew] = match_str(montage.labelold, data.label);
if numel(selnew)==numel(labelold)
  for i=1:numel(data.trial)
    data.trial{i} = data.trial{i}(selnew,:);
  end
  data.label = data.label(selnew);
else
  ft_warning('channel ordering might have changed');
end

% Perform running variance check to identify odd channels
if strcmp(cfg.residualcheck,'yes')
  residual_check(cfg.residualthresh,data,montage.labelold)
end

% do the general cleanup and bookkeeping at the end of the function
ft_postamble debug
ft_postamble previous   data
ft_postamble provenance data
ft_postamble history    data
ft_postamble savevar    data

function residual_check(residualthresh,data,oldlabels)
% Script to determine residual variance post HFC

% find corrected channels in the output data
[dummy, selnew2] = match_str(oldlabels, data.label);

trvar = [];
for ii = 1:numel(data.trial)
  tmp = data.trial{ii}(selnew2,:);
  Mk =  tmp(:,1);
  Sk = zeros(size(Mk));
  count = 1;
  for jj = 1:size(tmp,2)
    Xk = tmp(:,jj);
    Mkprev = Mk;
    Mk = Mkprev +(Xk-Mkprev)/count;
    Sk=Sk+(Xk-Mkprev).*(Xk-Mk) ;
    count=count+1;
  end
  trvar(:,ii)=Sk/(count-1);
end

% Identify the most common chanunit
chanunit = ft_chanunit(data);
[s,dummy,j]=unique(chanunit);
chanunit = s{mode(j)};

switch chanunit % some of this are silly, but safety first!
  case 'fT'
    scale = 1e-3;
  case 'pT'
    scale = 1;
  case 'nT'
    scale = 1e3;
  case 'uT'
    scale = 1e6;
  case 'mT'
    scale = 1e9;
  case {'T','T/m'}
    scale = 1e12;
  otherwise
    ft_error('Cannot check residuals due to unknown sensor units!')
end

SD = mean(sqrt(trvar),2)*scale;

fprintf('Checking for unsual channels post-corrections\n')
count = 0;
for ii = 1:length(SD)
  index = selnew2(ii);
  if SD(ii) > residualthresh
    count = count + 1;
    if strcmp(chanunit,'fT')
      fprintf(['Residual on channel ' num2str(index) ', '...
        data.label{index} ': %3.2f pT\n'], SD(ii));
    else
      fprintf(['Residual on channel ' num2str(index) ', '...
        data.label{index} ': %3.2f' chanunit '\n'], SD(ii));
    end
  end
end
if ~count
  fprintf('No unusual channel residuals found!\n')
end


function [x,info] = opti_zmumps(A,b)
%OPTI_ZMUMPS Solve a system of complex sparse linear equations using MUMPS
%
%   x = opti_zmumps(A,b) solves the system of complex sparse linear equations Ax = b.
%
%   THIS IS A WRAPPER FOR ZMUMPS USING THE MEX INTERFACE
%   See supplied License

%   Copyright (C) 2013 Jonathan Currie (IPL)

t = tic;

%Initialize MUMPS
id = struct('SYM',0,'JOB',-1,'ICNTL',zeros(1,40)-9998,'CNTL',zeros(1,15)-9998,...
            'PERM_IN',-9999,'COLSCA',-9999,'ROWSCA',-9999,'RHS',-9999,'INFOG',zeros(1,40)-9998,...
            'RINFOG',zeros(1,40)-9998,'VAR_SCHUR',-9999,'SCHUR',-9999,'INST',-9999,'SOL',-9999,...
            'REDRHS',-9999,'PIVNUL_LIST',-9999,'MAPPING',-9999,'SYM_PERM',-9999,'UNS_PERM',-9999,'TYPE',0);

id = zmumps(id);

id.Type = 2; %arithmetic type
id.JOB = 6; %analyze + factorize + solve

%Setup and Solve
id.RHS = b;
if(~issparse(A))
    optiwarn('opti:sparse','The A matrix should be sparse, correcting: [sparse(A)]');
    A = sparse(A);
end
id = zmumps(id,A);
x = id.SOL;

%Destroy mumps instance
id.JOB = -2;
id = zmumps(id); %#ok<NASGU>

info.Iterations = [];
info.Time = toc(t);
info.Algorithm = 'MUMPS: Complex Sequential Build';
info.Status = [];


function comp_nmf_online_algorithms_test()
% Test file for stochastic/online NMF.
%
% Created by H.Kasai on Apr. 17, 2018

    clc;
    clear;
    close all;

    %% set options
    max_epoch                       = 100;
    verbose                         = 2;
    batch_size                      = 50;
    permute_on                      = true;
    svrmu_inneriter                 = 5;
    max_h_repeat                    = 5;
    calc_sol                        = 1;
    lambda                          = 1;

    dataset                         = 'CBCL';
    %dataset                         = 'COIL';
    %dataset                         = 'Synthetic';

    F                               = 100;
    K                               = 10;
    N                               = 500;
    noise_level                     = 0.0;
    outlier_rho                     = 0.0; % 0.9

    dic_display_flag                = 1;
    plot_flag                       = 1;
    clustering_flag                 = 1;
    denoise_display_flag            = 1;

    %% algorithms
    if outlier_rho == 0
        % batch
        nmf_hals_flag                   = 0;
        nmf_acc_hals_flag               = 0;
        rnmf_flag                       = 0;

        % spg
        spg_nmf_flag                    = 0;
        spg_acc_nmf_flag                = 0;
        apg_nmf_flag                    = 0;
        spg_ls_nmf_flag                 = 0;
        spg_precon_ls_nmf_flag          = 0;

        % online mu
        ronmf_flag                      = 0;
        onmf_flag                       = 0;
        onmf_acc_flag                   = 0;
        inmf_flag                       = 0;
        inmf_online_flag                = 0;
        asag_mu_nmf_flag                = 0;

        % smu
        smu_nmf_flag                    = 0;
        smu_acc_nmf_flag                = 0;
        smu_ls_nmf_flag                 = 0;

        % svrmu
        svrmu_nmf_flag                  = 0;
        svrmu_acc_nmf_flag              = 1;
        svrmu_ls_nmf_flag               = 0;
        svrmu_ls_acc_nmf_flag           = 0;
        svrmu_precon_ls_nmf_flag        = 0;
        svrmu_precon_ls_acc_nmf_flag    = 0;
        rsvrmu_acc_nmf_flag             = 0;
        svrmu_acc_adaptive_nmf_flag     = 0;
    else
        % batch
        nmf_hals_flag                   = 0;
        nmf_acc_hals_flag               = 0;
        rnmf_flag                       = 1;

        % spg
        spg_nmf_flag                    = 0;
        spg_acc_nmf_flag                = 0;
        apg_nmf_flag                    = 0;
        spg_ls_nmf_flag                 = 0;
        spg_precon_ls_nmf_flag          = 0;

        % online mu
        ronmf_flag                      = 1;
        onmf_flag                       = 0;
        onmf_acc_flag                   = 0;
        inmf_flag                       = 0;
        inmf_online_flag                = 0;
        asag_mu_nmf_flag                = 0;

        % smu
        smu_nmf_flag                    = 0;
        smu_acc_nmf_flag                = 0;
        smu_ls_nmf_flag                 = 0;

        % svrmu
        svrmu_nmf_flag                  = 0;
        svrmu_acc_nmf_flag              = 0;
        svrmu_ls_nmf_flag               = 0;
        svrmu_ls_acc_nmf_flag           = 0;
        svrmu_precon_ls_nmf_flag        = 0;
        svrmu_precon_ls_acc_nmf_flag    = 0;
        rsvrmu_acc_nmf_flag             = 1;
        svrmu_acc_adaptive_nmf_flag     = 0;
    end

    %% generate/load data
    fprintf('Loading data [%s] ...', dataset);
    if strcmp(dataset, 'PIE') || strcmp(dataset, 'AR') || strcmp(dataset, 'COIL') || strcmp(dataset, 'CBCL')

        if strcmp(dataset, 'PIE')
            img_in_dim  = [32, 32];
            img_out_dim = img_in_dim;
            batch_size  = 100; % up to N
            dic_display_dim = [4, 5];

        elseif strcmp(dataset, 'AR')
            img_in_dim  = [28, 20];
            img_out_dim = [28, 28];
            batch_size  = 100; % up to N
            dic_display_dim = [10, 12];

        elseif strcmp(dataset, 'COIL')
            img_in_dim  = [32, 32];
            img_out_dim = img_in_dim;
            batch_size  = 100; % up to N
            dic_display_dim = [4, 5];

        elseif strcmp(dataset, 'CBCL')
            img_in_dim  = [19, 19];
            img_out_dim = img_in_dim;
            batch_size  = 100; % up to N
            dic_display_dim = [7, 7];

        end

        [N, F, K, Vo, V, ~, classes] = load_dataset(dataset, '../../../data', outlier_rho);

        oriimg_display_flag = 1;

        if isempty(classes)
            clustering_flag = 0;
        end

    elseif strcmp(dataset, 'Synthetic')
        [Vo, Ro, No, V] = generate_syntheticdata(F, N, K, noise_level, outlier_rho);

        oriimg_display_flag     = 0;
        clustering_flag         = 0;
        denoise_display_flag    = 0;
        dic_display_flag        = 0;
    end

    dim = F * N;
    fprintf(' done\n');

    %% set initial data
    if 0
        x_init.W = rand(F, K);
        x_init.H = rand(K, N);
    elseif 1
        [W_init, H_init] = Qiao_SVD_Init(V, K);
        x_init.W = W_init;
        x_init.H = H_init;
    else
        [W_init, H_init] = NNDSVD(V, K, 0);
        x_init.W = W_init;
        x_init.H = H_init;
    end

    if outlier_rho == 0
        x_init.R = zeros(F, N);
    else
        x_init.R = rand(F, N);
    end

    %% calc solution
    if calc_sol

        clear options;
        options.max_epoch = max_epoch;
        options.x_init = x_init;
        options.verbose = 0;
        options.verbose = verbose;

        if ~outlier_rho
            fprintf('Calculating f_opt by HALS ...\n');
            options.alg = 'hals';
            [w_sol, infos_sol] = als_nmf(V, K, options);
        else
            fprintf('Calculating f_opt by R-NMF ...\n');
            options.lambda = lambda;
            [w_sol, infos_sol] = robust_mu_nmf(V, K, options);
        end

        f_opt = infos_sol.cost(end);
        fprintf('Done.. f_opt: %.16e\n', f_opt);
    else
        f_opt = -Inf;
    end

    %% execute algorithms
    names = cell(1);
    sols = cell(1);
    infos = cell(1);
    costs = cell(1);
    alg_idx = 0;

    %% Batch
    if nmf_hals_flag
        alg_idx = alg_idx + 1;
        clear options;
        options.max_epoch = max_epoch;
        options.x_init = x_init;
        options.verbose = verbose;
        options.f_opt = f_opt;
        options.alg = 'hals';

        [w_nmf_hals, infos_nmf_hals] = als_nmf(V, K, options);

        names{alg_idx} = 'NMF HALS';
        sols{alg_idx} = w_nmf_hals;
        infos{alg_idx} = infos_nmf_hals;
        costs{alg_idx} = nmf_cost(Vo, w_nmf_hals.W, w_nmf_hals.H, zeros(F, N)) * 2 / dim;
    end

    if nmf_acc_hals_flag
        alg_idx = alg_idx + 1;
        clear options;
        options.max_epoch = max_epoch;
        options.x_init = x_init;
        options.verbose = verbose;
        options.f_opt = f_opt;
        options.alg = 'acc_hals';

        [w_nmf_acc_hals, infos_nmf_acc_hals] = als_nmf(V, K, options);

        names{alg_idx} = 'NMF ACC HALS';
        sols{alg_idx} = w_nmf_acc_hals;
        infos{alg_idx} = infos_nmf_acc_hals;
        costs{alg_idx} = nmf_cost(Vo, w_nmf_acc_hals.W, w_nmf_acc_hals.H, zeros(F, N)) * 2 / dim;
    end

    if rnmf_flag
        alg_idx = alg_idx + 1;
        clear options;
        options.max_epoch = max_epoch;
        options.x_init = x_init;
        options.verbose = verbose;
        options.f_opt = f_opt;

        [w_rnmf, infos_rnmf] = robust_mu_nmf(V, K, options);

        names{alg_idx} = 'R-NMF(Batch)';
        sols{alg_idx} = w_rnmf;
        infos{alg_idx} = infos_rnmf;
        costs{alg_idx} = nmf_cost(Vo, w_rnmf.W, w_rnmf.H, zeros(F, N)) * 2 / dim;
    end

    %% Online MU
    if ronmf_flag
        alg_idx = alg_idx + 1;
        clear options;
        options.lambda = lambda;
        options.max_epoch = max_epoch;
        options.batch_size = batch_size;
        options.x_init = x_init;
        options.verbose = verbose;
        options.f_opt = f_opt;

        [w_ronmf, infos_ronmf] = robust_online_mu_nmf(V, K, options);

        names{alg_idx} = 'R-ONMF';
        sols{alg_idx} = w_ronmf;
        infos{alg_idx} = infos_ronmf;
        costs{alg_idx} = nmf_cost(Vo, w_ronmf.W, w_ronmf.H, zeros(F, N)) * 2 / dim;
    end

    if onmf_flag
        alg_idx = alg_idx + 1;
        clear options;
        %options.lambda = lambda;
        options.max_epoch = max_epoch;
        options.batch_size = batch_size;
        options.x_init = x_init;
        options.verbose = verbose;
        options.f_opt = f_opt;

        [w_onmf, infos_onmf] = online_mu_nmf(V, K, options);

        names{alg_idx} = 'ONMF';
        sols{alg_idx} = w_onmf;
        infos{alg_idx} = infos_onmf;
        costs{alg_idx} = nmf_cost(Vo, w_onmf.W, w_onmf.H, zeros(F, N)) * 2 / dim;
    end

    if onmf_acc_flag
        alg_idx = alg_idx + 1;
        clear options;
        options.lambda = lambda;
        options.max_epoch = max_epoch;
        options.batch_size = batch_size;
        options.x_init = x_init;
        options.verbose = verbose;
        options.f_opt = f_opt;
        options.rep_mode = 'fix';
        %options.rep_mode = 'adaptive';
        options.w_repeat = 1;
        options.h_repeat = max_h_repeat;

        [w_onmf_acc, infos_onmf_acc] = acc_online_mu_nmf(V, K, options);

        names{alg_idx} = 'ONMF-ACC';
        sols{alg_idx} = w_onmf_acc;
        infos{alg_idx} = infos_onmf_acc;
        costs{alg_idx} = nmf_cost(Vo, w_onmf_acc.W, w_onmf_acc.H, zeros(F, N)) * 2 / dim;
    end

    if inmf_flag
        alg_idx = alg_idx + 1;
        clear options;
        options.max_epoch = max_epoch;
        options.online = 0; % online mode
        options.x_init = x_init;
        options.verbose = 1;
        options.f_opt = f_opt;
        options.batch_size = batch_size;

        [w_inmf, infos_inmf] = incremental_mu_nmf(V, K, options);

        names{alg_idx} = 'INMF';
        sols{alg_idx} = w_inmf;
        infos{alg_idx} = infos_inmf;
        costs{alg_idx} = nmf_cost(Vo, w_inmf.W, w_inmf.H, zeros(F, N)) * 2 / dim;
    end

    if inmf_online_flag
        alg_idx = alg_idx + 1;
        clear options;
        options.max_epoch = 1;
        options.online = 1; % online mode
        options.max_inneriter = 100;
        options.x_init = x_init;
        options.verbose = 2;
        options.f_opt = f_opt;
        options.batch_size = batch_size;

        [w_online_inmf, infos_online_inmf] = incremental_mu_nmf(V, K, options);

        names{alg_idx} = 'INMF (Online)';
        sols{alg_idx} = w_online_inmf;
        infos{alg_idx} = infos_online_inmf;
        costs{alg_idx} = nmf_cost(Vo, w_online_inmf.W, w_online_inmf.H, zeros(F, N)) * 2 / dim;
    end

    %%
    if asag_mu_nmf_flag
        alg_idx = alg_idx + 1;
        clear options;
        options.lambda = 1;
        options.max_epoch = max_epoch;
        options.batch_size = batch_size;
        options.x_init = x_init;
        options.verbose = verbose;
        options.permute_on = permute_on;
        options.lambda = 0.5;
        options.f_opt = f_opt;

        [w_asag_mu_nmf, infos_asag_mu_nmf] = asag_mu_nmf(V, K, options);

        names{alg_idx} = 'ASAG-MU';
        sols{alg_idx} = w_asag_mu_nmf;
        infos{alg_idx} = infos_asag_mu_nmf;
        costs{alg_idx} = nmf_cost(Vo, w_asag_mu_nmf.W, w_asag_mu_nmf.H, zeros(F, N)) * 2 / dim;
    end

    %% SMU
    if smu_nmf_flag
        alg_idx = alg_idx + 1;
        clear options;
        options.lambda = lambda;
        options.max_epoch = max_epoch;
        options.batch_size = batch_size;
        options.x_init = x_init;
        options.verbose = verbose;
        options.f_opt = f_opt;
        options.accel = 0;

        [w_smu_nmf, infos_smu_nmf] = smu_nmf(V, K, options);

        names{alg_idx} = 'SMU';
        sols{alg_idx} = w_smu_nmf;
        infos{alg_idx} = infos_smu_nmf;
        costs{alg_idx} = nmf_cost(Vo, w_smu_nmf.W, w_smu_nmf.H, zeros(F, N)) * 2 / dim;
    end

    if smu_acc_nmf_flag
        alg_idx = alg_idx + 1;
        clear options;
        options.lambda = lambda;
        options.max_epoch = max_epoch;
        options.batch_size = batch_size;
        options.x_init = x_init;
        options.verbose = verbose;
        options.f_opt = f_opt;
        options.accel = 1;
        options.rep_mode = 'fix';
        %options.rep_mode = 'adaptive';
        options.w_repeat = 1;
        options.h_repeat = max_h_repeat;

        [w_smu_nmf_acc, infos_smu_nmf_acc] = smu_nmf(V, K, options);

        names{alg_idx} = 'SMU-ACC';
        sols{alg_idx} = w_smu_nmf_acc;
        infos{alg_idx} = infos_smu_nmf_acc;
        costs{alg_idx} = nmf_cost(Vo, w_smu_nmf_acc.W, w_smu_nmf_acc.H, zeros(F, N)) * 2 / dim;
    end

    if smu_ls_nmf_flag
        alg_idx = alg_idx + 1;
        clear options;
        options.lambda = lambda;
        options.max_epoch = max_epoch;
        options.batch_size = batch_size;
        options.x_init = x_init;
        options.verbose = verbose;
        options.f_opt = f_opt;
        options.ls = 1;
        options.rep_mode = 'fix';
        %options.rep_mode = 'adaptive';
        options.w_repeat = 1;
        options.h_repeat = max_h_repeat;

        [w_smu_nmf_acc, infos_smu_nmf_acc] = smu_nmf(V, K, options);

        names{alg_idx} = 'SMU-LS';
        sols{alg_idx} = w_smu_nmf_acc;
        infos{alg_idx} = infos_smu_nmf_acc;
        costs{alg_idx} = nmf_cost(Vo, w_smu_nmf_acc.W, w_smu_nmf_acc.H, zeros(F, N)) * 2 / dim;
    end

    if smu_ls_nmf_flag
        alg_idx = alg_idx + 1;
        clear options;
        options.lambda = lambda;
        options.max_epoch = max_epoch;
        options.batch_size = batch_size;
        options.x_init = x_init;
        options.verbose = verbose;
        options.f_opt = f_opt;
        options.ls = 1;
        options.precon = 1;
        options.rep_mode = 'fix';
        %options.rep_mode = 'adaptive';
        options.w_repeat = 1;
        options.h_repeat = max_h_repeat;

        [w_smu_nmf_acc, infos_smu_nmf_acc] = smu_nmf(V, K, options);

        names{alg_idx} = 'SMU-Precon-LS';
        sols{alg_idx} = w_smu_nmf_acc;
        infos{alg_idx} = infos_smu_nmf_acc;
        costs{alg_idx} = nmf_cost(Vo, w_smu_nmf_acc.W, w_smu_nmf_acc.H, zeros(F, N)) * 2 / dim;
    end

    %% SPG
    if spg_nmf_flag
        alg_idx = alg_idx + 1;
        clear options;
        options.lambda = lambda;
        options.max_epoch = max_epoch;
        options.batch_size = batch_size;
        options.x_init = x_init;
        options.verbose = verbose;
        options.f_opt = f_opt;
        options.accel = 0;
        options.ls = 0;

        [w_spg_nmf, infos_spg_nmf] = spg_nmf(V, K, options);

        names{alg_idx} = 'SPG';
        sols{alg_idx} = w_spg_nmf;
        infos{alg_idx} = infos_spg_nmf;
        costs{alg_idx} = nmf_cost(Vo, w_spg_nmf.W, w_spg_nmf.H, zeros(F, N)) * 2 / dim;
    end

    if spg_acc_nmf_flag
        alg_idx = alg_idx + 1;
        clear options;
        options.lambda = lambda;
        options.max_epoch = max_epoch;
        options.batch_size = batch_size;
        options.x_init = x_init;
        options.verbose = verbose;
        options.f_opt = f_opt;
        options.accel = 1;
        options.ls = 0;
        options.h_repeat = max_h_repeat;

        [w_spg_nmf, infos_spg_nmf] = spg_nmf(V, K, options);

        names{alg_idx} = 'SPG-ACC';
        sols{alg_idx} = w_spg_nmf;
        infos{alg_idx} = infos_spg_nmf;
        costs{alg_idx} = nmf_cost(Vo, w_spg_nmf.W, w_spg_nmf.H, zeros(F, N)) * 2 / dim;
    end

    if apg_nmf_flag
        alg_idx = alg_idx + 1;
        clear options;
        options.lambda = lambda;
        options.max_epoch = max_epoch;
        options.batch_size = batch_size;
        options.x_init = x_init;
        options.verbose = verbose;
        options.f_opt = f_opt;
        options.accel = 0;
        options.ls = 0;
        %options.h_repeat = max_h_repeat;
        options.W_sub_mode = 'Nesterov';

        [w_spg_nmf, infos_spg_nmf] = spg_nmf(V, K, options);

        names{alg_idx} = 'APG';
        sols{alg_idx} = w_spg_nmf;
        infos{alg_idx} = infos_spg_nmf;
        costs{alg_idx} = nmf_cost(Vo, w_spg_nmf.W, w_spg_nmf.H, zeros(F, N)) * 2 / dim;
    end

    if spg_ls_nmf_flag
        alg_idx = alg_idx + 1;
        clear options;
        options.lambda = lambda;
        options.max_epoch = max_epoch;
        options.batch_size = batch_size;
        options.x_init = x_init;
        options.verbose = verbose;
        options.f_opt = f_opt;
        options.accel = 0;
        options.ls = 1;

        [w_spg_nmf, infos_spg_nmf] = spg_nmf(V, K, options);

        names{alg_idx} = 'SPG-LS';
        sols{alg_idx} = w_spg_nmf;
        infos{alg_idx} = infos_spg_nmf;
        costs{alg_idx} = nmf_cost(Vo, w_spg_nmf.W, w_spg_nmf.H, zeros(F, N)) * 2 / dim;
    end

    if spg_precon_ls_nmf_flag
        alg_idx = alg_idx + 1;
        clear options;
        options.lambda = lambda;
        options.max_epoch = max_epoch;
        options.batch_size = batch_size;
        options.x_init = x_init;
        options.verbose = verbose;
        options.f_opt = f_opt;
        options.accel = 0;
        options.ls = 1;
        options.precon = 1;

        [w_spg_nmf, infos_spg_nmf] = spg_nmf(V, K, options);

        names{alg_idx} = 'SPG-Precon-LS';
        sols{alg_idx} = w_spg_nmf;
        infos{alg_idx} = infos_spg_nmf;
        costs{alg_idx} = nmf_cost(Vo, w_spg_nmf.W, w_spg_nmf.H, zeros(F, N)) * 2 / dim;
    end

    %% SVRMU
    if svrmu_nmf_flag
        alg_idx = alg_idx + 1;
        clear options;
        options.lambda = lambda;

        options.batch_size = batch_size;
        options.x_init = x_init;
        options.x_init.R = zeros(F, N); % enfore non-robust mode
        options.verbose = verbose;
        options.permute_on = permute_on;
        options.fast_calc = 0;
        options.f_opt = f_opt;
        options.repeat_inneriter = svrmu_inneriter;
        options.accel = 0;
        options.ratio = 1; % 1: original 1<: adaptive
        options.permute_on = permute_on;
        options.robust = false;
        %options.stepsize_ratio = 1;

        options.max_epoch = floor(max_epoch / (options.repeat_inneriter + 1));

        %[w_svrmu_nmf, infos_svrmu_nmf] = svrmu_nmf_old(V, K, options);
        [w_svrmu_nmf, infos_svrmu_nmf] = svrmu_nmf(V, K, options);

        names{alg_idx} = 'SVRMU';
        sols{alg_idx} = w_svrmu_nmf;
        infos{alg_idx} = infos_svrmu_nmf;
        costs{alg_idx} = nmf_cost(Vo, w_svrmu_nmf.W, w_svrmu_nmf.H, zeros(F, N)) * 2 / dim;
    end

    if svrmu_acc_nmf_flag
        alg_idx = alg_idx + 1;
        clear options;
        options.lambda = lambda;

        options.batch_size = batch_size;
        options.x_init = x_init;
        options.verbose = verbose;
        options.fast_calc = 0;
        options.f_opt = f_opt;
        options.repeat_inneriter = svrmu_inneriter;
        options.accel = 1;
        options.rep_mode = 'fix';
        %options.rep_mode = 'adaptive';
        options.w_repeat = 1;
        options.h_repeat = max_h_repeat;
        options.ratio = 1;
        options.permute_on = permute_on;
        options.robust = false;

        options.max_epoch = floor(max_epoch / (options.repeat_inneriter + 1));

        [w_svrmu_acc_nmf, infos_svrmu_acc_nmf] = svrmu_nmf(V, K, options);

        names{alg_idx} = 'SVRMU-ACC';
        sols{alg_idx} = w_svrmu_acc_nmf;
        infos{alg_idx} = infos_svrmu_acc_nmf;
        costs{alg_idx} = nmf_cost(Vo, w_svrmu_acc_nmf.W, w_svrmu_acc_nmf.H, zeros(F, N)) * 2 / dim;
    end

    if svrmu_ls_nmf_flag
        alg_idx = alg_idx + 1;
        clear options;
        options.lambda = lambda;

        options.batch_size = batch_size;
        options.x_init = x_init;
        options.verbose = verbose;
        options.fast_calc = 0;
        options.f_opt = f_opt;
        options.repeat_inneriter = svrmu_inneriter;
        options.accel = 0;
        options.ls = 1;
        options.rep_mode = 'fix';
        %options.rep_mode = 'adaptive';
        options.w_repeat = 1;
        options.h_repeat = max_h_repeat;
        options.ratio = 1;
        options.permute_on = permute_on;
        options.robust = false;

        options.max_epoch = floor(max_epoch / (options.repeat_inneriter + 1));

        [w_svrmu_acc_nmf, infos_svrmu_acc_nmf] = svrmu_nmf(V, K, options);

        names{alg_idx} = 'SVRMU-LS';
        sols{alg_idx} = w_svrmu_acc_nmf;
        infos{alg_idx} = infos_svrmu_acc_nmf;
        costs{alg_idx} = nmf_cost(Vo, w_svrmu_acc_nmf.W, w_svrmu_acc_nmf.H, zeros(F, N)) * 2 / dim;
    end

    if svrmu_ls_acc_nmf_flag
        alg_idx = alg_idx + 1;
        clear options;
        options.lambda = lambda;

        options.batch_size = batch_size;
        options.x_init = x_init;
        options.verbose = verbose;
        options.fast_calc = 0;
        options.f_opt = f_opt;
        options.repeat_inneriter = svrmu_inneriter;
        options.accel = 1;
        options.ls = 1;
        options.rep_mode = 'fix';
        options.w_repeat = 1;
        options.h_repeat = max_h_repeat;
        options.ratio = 1;
        options.permute_on = permute_on;
        options.robust = false;

        options.max_epoch = floor(max_epoch / (options.repeat_inneriter + 1));

        [w_svrmu_acc_nmf, infos_svrmu_acc_nmf] = svrmu_nmf(V, K, options);

        names{alg_idx} = 'SVRMU-LS-ACC';
        sols{alg_idx} = w_svrmu_acc_nmf;
        infos{alg_idx} = infos_svrmu_acc_nmf;
        costs{alg_idx} = nmf_cost(Vo, w_svrmu_acc_nmf.W, w_svrmu_acc_nmf.H, zeros(F, N)) * 2 / dim;
    end

    if svrmu_precon_ls_acc_nmf_flag
        alg_idx = alg_idx + 1;
        clear options;
        options.lambda = lambda;

        options.batch_size = batch_size;
        options.x_init = x_init;
        options.verbose = verbose;
        options.fast_calc = 0;
        options.f_opt = f_opt;
        options.repeat_inneriter = svrmu_inneriter;
        options.accel = 0;
        options.ls = 1;
        options.precon = 1;
        options.rep_mode = 'fix';
        %options.rep_mode = 'adaptive';
        options.w_repeat = 1;
        options.h_repeat = max_h_repeat;
        options.ratio = 1;
        options.permute_on = permute_on;
        options.robust = false;

        options.max_epoch = floor(max_epoch / (options.repeat_inneriter + 1));

        [w_svrmu_acc_nmf, infos_svrmu_acc_nmf] = svrmu_nmf(V, K, options);

        names{alg_idx} = 'SVRMU-Precon-LS';
        sols{alg_idx} = w_svrmu_acc_nmf;
        infos{alg_idx} = infos_svrmu_acc_nmf;
        costs{alg_idx} = nmf_cost(Vo, w_svrmu_acc_nmf.W, w_svrmu_acc_nmf.H, zeros(F, N)) * 2 / dim;
    end

    if svrmu_precon_ls_nmf_flag
        alg_idx = alg_idx + 1;
        clear options;
        options.lambda = lambda;

        options.batch_size = batch_size;
        options.x_init = x_init;
        options.verbose = verbose;
        options.fast_calc = 0;
        options.f_opt = f_opt;
        options.repeat_inneriter = svrmu_inneriter;
        options.accel = 1;
        options.ls = 1;
        options.precon = 1;
        options.rep_mode = 'fix';
        %options.rep_mode = 'adaptive';
        options.w_repeat = 1;
        options.h_repeat = max_h_repeat;
        options.ratio = 1;
        options.permute_on = permute_on;
        options.robust = false;

        options.max_epoch = floor(max_epoch / (options.repeat_inneriter + 1));

        [w_svrmu_acc_nmf, infos_svrmu_acc_nmf] = svrmu_nmf(V, K, options);

        names{alg_idx} = 'SVRMU-Precon-LS-ACC';
        sols{alg_idx} = w_svrmu_acc_nmf;
        infos{alg_idx} = infos_svrmu_acc_nmf;
        costs{alg_idx} = nmf_cost(Vo, w_svrmu_acc_nmf.W, w_svrmu_acc_nmf.H, zeros(F, N)) * 2 / dim;
    end

    if svrmu_acc_adaptive_nmf_flag
        alg_idx = alg_idx + 1;
        clear options;
        options.lambda = lambda;

        options.batch_size = batch_size;
        options.x_init = x_init;
        options.verbose = verbose;
        options.permute_on = permute_on;
        options.fast_calc = 0;
        options.f_opt = f_opt;
        options.repeat_inneriter = svrmu_inneriter;
        options.accel = 1;
        %options.rep_mode = 'fix';
        options.rep_mode = 'adaptive';
        options.w_repeat = 1;
        options.h_repeat = max_h_repeat;
        options.ratio = 1;
        options.ratio = 0.3; % 1: original 1<: adaptive
        options.permute_on = permute_on;
        options.robust = false;

        options.max_epoch = floor(max_epoch / (options.repeat_inneriter + 1));

        [w_svrmu_acc_adaptive_nmf, infos_svrmu_acc_adaptive_nmf] = svrmu_nmf(V, K, options);

        names{alg_idx} = 'SVRMU-ACC-Adaptive';
        sols{alg_idx} = w_svrmu_acc_adaptive_nmf;
        infos{alg_idx} = infos_svrmu_acc_adaptive_nmf;
        costs{alg_idx} = nmf_cost(Vo, w_svrmu_acc_adaptive_nmf.W, w_svrmu_acc_adaptive_nmf.H, zeros(F, N)) * 2 / dim;
    end

    if rsvrmu_acc_nmf_flag
        alg_idx = alg_idx + 1;
        clear options;
        options.lambda = lambda;

        options.batch_size = batch_size;
        options.x_init = x_init;
        options.verbose = verbose;
        options.permute_on = permute_on;
        options.fast_calc = 0;
        options.f_opt = f_opt;
        options.repeat_inneriter = svrmu_inneriter;
        options.accel = 0;
        options.ls = 1;
        options.rep_mode = 'fix';
        %options.rep_mode = 'adaptive';
        options.w_repeat = 1;
        options.h_repeat = max_h_repeat;
        options.ratio = 1;
        options.permute_on = permute_on;
        options.robust = true;

        options.max_epoch = floor(max_epoch / (options.repeat_inneriter + 1));

        [w_rsvrmu_acc_nmf, infos_rsvrmu_acc_nmf] = svrmu_nmf(V, K, options);

        names{alg_idx} = 'R-SVRMU-LS';
        sols{alg_idx} = w_rsvrmu_acc_nmf;
        infos{alg_idx} = infos_rsvrmu_acc_nmf;
        costs{alg_idx} = nmf_cost(Vo, w_rsvrmu_acc_nmf.W, w_rsvrmu_acc_nmf.H, zeros(F, N)) * 2 / dim;
    end

    alg_total = alg_idx;

    %% plot
    if plot_flag
        %display_graph_icassp('epoch','cost', names, sols, infos);
        %display_graph_icassp('grad_calc_count','cost', names, sols, infos);
        %display_graph_icassp('time','cost', names, sols, infos);

        display_graph('numofgrad','cost', names, sols, infos);
        display_graph('time','cost', names, sols, infos);
        if f_opt ~= -Inf
            display_graph('numofgrad','optimality_gap', names, sols, infos);
            display_graph('time','optimality_gap', names, sols, infos);
        end
    end

    % display original dic
    if oriimg_display_flag && ~isempty(classes)
        for ii = 1 : K
            k_index = find(classes==ii);
            col_img = Vo(:,k_index(1));
            col_img_reshape = reshape(col_img, img_in_dim);
            col_img_resize = imresize(col_img_reshape, img_out_dim);
            img(:,ii) = col_img_resize(:);
        end
        plot_dictionnary(img, [], dic_display_dim);
    end

    clear results;

    for alg_idx = 1 : alg_total

        mse = calc_mse(Vo, sols{alg_idx}.W, sols{alg_idx}.H);
        psnr = calc_psnr(Vo, sols{alg_idx}.W, sols{alg_idx}.H);
        fprintf('# %s:\t MSE:%e,\t PSNR:%e,\t time:%e [sec]\n', names{alg_idx}, mse, psnr, infos{alg_idx}.time(end));

        if clustering_flag
            cluster_num = length(unique(classes));
            Coeff = sols{alg_idx}.H;
            M = Coeff';
            num_samples = size(M,1);
            perm_idx = randperm(num_samples);
            center = zeros(cluster_num, K);
            for i = 1 : cluster_num
                center(i,:) = M(perm_idx(i),:);
            end

            % do k-menas
            k_means_result = k_means(M, center, cluster_num);

            purity = calc_purity(classes, k_means_result);
            nmi = calc_nmi(classes, k_means_result);

            fprintf('# %s:\t Purity:%e,\t NMI: %e\n', names{alg_idx}, purity, nmi);
        end

    end

    if dic_display_flag
        for alg_idx = 1 : alg_total
            clear img;
            for ii = 1 : K
                col_img = sols{alg_idx}.W(:,ii);
                col_img_reshape = reshape(col_img, img_in_dim);
                col_img_resize = imresize(col_img_reshape, img_out_dim);
                img(:,ii) = col_img_resize(:);
            end

            plot_dictionnary(img, [], dic_display_dim);

        end
    end

    if denoise_display_flag && ~isempty(classes)
        for alg_idx = 1 : alg_total

            Rec = sols{alg_idx}.W * sols{alg_idx}.H;

            for ii = 1 : K
                k_index = find(classes==ii);
                col_img = Rec(:,k_index(1));
                col_img_reshape = reshape(col_img, img_in_dim);
                col_img_resize = imresize(col_img_reshape, img_out_dim);
                img(:,ii) = col_img_resize(:);
            end

            plot_dictionnary(img, [], dic_display_dim);

        end
    end

end


function[varargout]=vswap(varargin)
%VSWAP  VSWAP(X,A,B) replaces A with B in numeric array X.
%
%   VSWAP(X,A,B) replaces A with B in numeric array X.  A and B may be
%   numbers, NAN, +/- INF, NAN+SQRT(-1)*NAN, or INF+SQRT(-1)*INF.
%
%   Note that X may also be a cell array of numeric arrays.
%
%   [Y1,Y2,...YN]=VSWAP(X1,X2,...XN,A,B) also works.
%
%   VSWAP(X1,X2,...XN,A,B); with no output arguments overwrites the
%   original input variables.
%   __________________________________________________________________
%   This is part of JLAB --- type 'help jlab' for more information
%   (C) 2001--2015 J.M. Lilly --- type 'help jlab_license' for details

if strcmpi(varargin{1}, '--t')
  vswap_test,return
end

a=varargin{end-1};
b=varargin{end};

for i=1:length(varargin)-2
  x=varargin{i};
  varargout{i}=swapnum_loop(x,a,b);
end

eval(to_overwrite(nargin-2))

function[x]=swapnum_loop(x,a,b)
if iscell(x)
    for i=1:length(x)
        x{i}=swapnum1(x{i},a,b);
    end
else
    x=swapnum1(x,a,b);
end

function[x]=swapnum1(x,a,b)

if isfinite(a)
    bool=(x==a);
else
  if isnan(a)
       if isnan(imag(a))
            bool=isnan(real(x))&isnan(imag(x));
       else
            bool=isnan(x);
       end
  elseif isinf(a)
       if isinf(imag(a))
          bool=isinf(real(x))&isinf(imag(x));
       else
          if a>0
              bool=(isinf(x)&x>0);
          else
              bool=(isinf(x)&x<0);
          end
       end
   end
end

%Convert booleans to double
x=double(x);
x(bool)=b;

function[]=vswap_test
x=(1:10);
ans1=[2 (2:10)];
reporttest('VSWAP num case', aresame(vswap(x,1,2),ans1))

x=[nan (1:10)];
ans1=(0:10);
reporttest('VSWAP nan case', aresame(vswap(x,nan,0),ans1))

x=[nan*(1+sqrt(-1)) nan inf 1:10];
ans1=[0 nan inf 1:10];
reporttest('VSWAP complex nan case', aresame(vswap(x,nan+sqrt(-1)*nan,0),ans1))

x=[inf*(1+sqrt(-1)) nan inf 1:10];
ans1=[0 nan inf 1:10];
reporttest('VSWAP complex inf case', aresame(vswap(x,inf+sqrt(-1)*inf,0),ans1))

x=[nan*(1+sqrt(-1)) nan -inf 1:10];
ans1=[nan*(1+sqrt(-1)) nan 0 1:10];
reporttest('VSWAP negative inf case', aresame(vswap(x,-inf,0),ans1))


function cmatrix = get_condition_assignments(obj)
% Condition assignments
%   - Indicator matrix coding for which columns in X belong to the same
%     modeled condition, and are part of the same HRF fit
%   - There is one set of columns for each condition modeled, and one set of
%     columns for each parametric modulator of each condition
%   - Because parametric modulators may not exist for all conditions, we need
%     to build this dynamically for modulators.
%
% Design matrix build (which calls method get_session_X) builds columns in
% this order:
%
% All within Session:
% Regressors of interest, basis functions within conditions
% Parametric modulators, basis functions within conditions
% Covariates of no interest
%
% Then:
% Baselines (session/run intercepts)
%
% This method is called automatically in the build method.

switch obj.build_method
    case 'Separate sessions'
        nsess = length(obj.Sess);

    case 'Concatenated sessions'
        nsess = 1;

    otherwise
        error('Unknown build method for fmri_model object. See help for valid strings.');

end

for s = 1:nsess
    nconds(s) = length(obj.Sess(s).U);
end
if any(diff(nconds)), error('Variable numbers of conditions; this should have been caught in build.'); end

% cell of indicator matrices for each session separately
all_indic = cell(1, nsess);

for s = 1:nsess

    % regs of interest
    % -----------------------------------------------------------
    sessindic = {};

    for i = 1:nconds(s)

        % Allow for variable number of bfs.
        nbf = size(obj.xBF(i).bf, 2);

        % indicate a set of regressors based on basis set
        sessindic{i} = true(nbf, 1);

    end

    % concatenate into block diagonal
    sessindic = blkdiag(sessindic{:});

    % Modulators
    % -----------------------------------------------------------
    pmindic = {};

    for i = 1:nconds(s)
        is_pm = isfield(obj.Sess(s).U(i), 'P') && ~isempty(obj.Sess(s).U(i).P) && isfield(obj.Sess(s).U(i).P, 'P') && ~isempty(obj.Sess(s).U(i).P.P);

        if is_pm
            % Allow for variable number of bfs.
            nbf = size(obj.xBF(i).bf, 2);

            pmindic{end + 1} = true(nbf, 1);
        end
    end
    if ~isempty(pmindic)
        pmindic = blkdiag(pmindic{:});
    else
        pmindic = [];
    end

    % add them together in block diag form
    all_indic{s} = blkdiag(sessindic, pmindic);

end % session

cmatrix = logical(blkdiag(all_indic{:}));

ncols = size(obj.xX.X, 2);

if size(cmatrix, 1) > ncols
    error('More conditions of interest than rows in obj.xX.X!! Must build xX before running this.');
end

% add rows of zeros to match total number of columns
z = false(ncols - size(cmatrix, 1), size(cmatrix, 2));

cmatrix = [cmatrix; z];

end % function


% TEST_WAVELET_FACTORY_2D Test case for WAVELET_FACTORY_2D
%
% See also
%   WAVELET_FACTORY_2D
classdef test_wavelet_factory_2d < matlab.unittest.TestCase
    methods(Test)

        function testWithNoOptions(testcase)
            %% define
            x = rand(64, 64);
            [Wop, filters] = wavelet_factory_2d(size(x));

            %% check number of high pass filter
            expected = 3;
            actual = numel(Wop);

            %% assert
            testcase.assertEqual(expected, actual);

        end

          function testWithRandomOptions(testcase)
            for i = 1:32
                %%
                sz = 1 + floor(128*rand(1,2));
                %% define
                white_list_filt = {'Q', 'J', 'L'};
                type={'d','d','d'};
                values={{1,2,4,8,16},{0,1},{1,10}};
                options_f = generate_random_options(white_list_filt,type,values);

                white_list_scat = { 'oversampling','M'};
                type={'d','i'};
                values={{0,1},{1,3}};

                options_s = generate_random_options(white_list_scat,type,values);

                x = rand(64, 64);
                [Wop, filters] = wavelet_factory_2d(size(x),options_f,options_s);

                actual = length(Wop);
                expected = options_s.M;
                %% assert
                testcase.assertEqual(expected, actual);
            end

        end

        function testWithRandomSize(testcase)
            for i = 1:32
                %%
                sz = 1 + floor(128*rand(1,2));
                %% define
                x = rand(sz);
                [Wop, filters] = wavelet_factory_2d(size(x));

                % Test
                expected = 3;
                actual = numel(Wop);

                %% assert
                testcase.assertEqual(expected, actual);

            end
            for i = 1:32
                %%
                sz = 1 + floor(2*rand(1,2));
                %% define
                x = rand(sz);
                [Wop, filters] = wavelet_factory_2d(size(x));

                % Test
                expected = 3;
                actual = numel(Wop);

                %% assert
                testcase.assertEqual(expected, actual);

            end
    end
    end
end


% Assumes depth == 0
function point3D = proj2Dto3D(point2D, cameraParams, rotationMatrix, translationVector)
    N = size(point2D, 1);
    if N ~= 2
        point2D = point2D';
    end
    if size(point2D, 1) == 2
        point2D(3, :) = 1;
    end
    numPoints = size(point2D, 2);

    % if length(point2D) == 2
    %     point2D(3) = 1;
    % end
    % if size(point2D, 2) > 1
    %     point2D = point2D';
    % end
    K = cameraParams.IntrinsicMatrix';
    z_small = inv(rotationMatrix) * inv(K) * point2D;
    lamda = repmat(-translationVector(3), 1, numPoints) ./ z_small(3, :);

    translationVector = translationVector';
    % multiply lamda*z_small, i.e. lamda(i)*z_small(:, i)
    B = cellfun(@(x, y)(translationVector + x * y), num2cell(lamda), num2cell(z_small, 1), 'uniformoutput', false);
    point3D = cell2mat(B);
end