%%% Loading - Step 1.
% Summary:
% load data from 'cell_resp.stackf' etc, and saved into
% 'FishX_direct_load.mat' (X = number) in the same directory.
% Stimulus parsing is done with function 'StimulusKeyPreprocessing', but
% one is advised to also inspect the results manually.

% Validation:
% There is an option 'isDiscard50' to discard the noisier 50%, disabled now.
% There is also a section to manually select (draw polygons) regions of
% cells to discard that are anatomical outliers.

% Format:
% Cell-responses are converted into z-scores and kept that way through out.
% CInfo (cell info for the valid cells) stores the anatomical positions.
% For the rest of parameters saved, see the end of this file.

% Next:
% For the following Loading steps, only this 'FishX_direct_load.mat' is
% needed, the rest will be cleared from working memory.

%%
clear all;close all;clc

%% Set Manaully!

i_fish = 1;

M_dir = GetFishDirectories();

fpsec = 1.97; % Hz

%% load data
disp(['load data: fish ' num2str(i_fish)]);
datadir = M_dir{i_fish};
if exist(fullfile(datadir,'cell_resp_dim_lowcut.mat'), 'file'),
  load(fullfile(datadir,'cell_resp_dim_lowcut.mat'));
elseif exist(fullfile(datadir,'cell_resp_dim.mat'), 'file'),
    load(fullfile(datadir,'cell_resp_dim.mat'));
else
    errordlg('find data to load!');
end

load(fullfile(datadir,'cell_info.mat'));
if exist(fullfile(datadir,'cell_resp_lowcut.stackf'), 'file'),
  cell_resp_full = read_LSstack_fast_float(fullfile(datadir,'cell_resp_lowcut.stackf'),cell_resp_dim);
elseif exist(fullfile(datadir,'cell_resp.stackf'), 'file'),
    cell_resp_full = read_LSstack_fast_float(fullfile(datadir,'cell_resp.stackf'),cell_resp_dim);
else
    errordlg('find data to load!');
end

load(fullfile(datadir,'frame_turn.mat'));

% parse stimulus - should check manually to be sure!
[stimset,stim] = StimulusKeyPreprocessing(frame_turn,i_fish);

%% load anatomy
tiffname = fullfile(datadir,'ave.tif');
info = imfinfo(tiffname,'tiff');
nPlanes = length(info);
s1 = info(1).Height;
s2 = info(1).Width;
ave_stack = zeros(s1,s2,nPlanes);
for i=1:nPlanes,
    ave_stack(:,:,i) = imread(fullfile(datadir,'ave.tif'),i);
end

% x-y view
im = max(ave_stack,[],3);
out=imNormalize99(im);
anat_yx = repmat(out,[1 1 3]);

% y-z view
im = squeeze(max(ave_stack,[],2));
out=imNormalize99(im);
anat_yz = repmat(out,[1 1 3]);

% x-z view
im = squeeze(max(ave_stack,[],1));
out=imNormalize99(im);
out = flipud(out'); %%%% empirically necessary...
anat_zx = repmat(out,[1 1 3]);

dimv_yx = size(anat_yx);
dimv_yz = size(anat_yz);
dimv_zx = size(anat_zx);

%% NEW: fix the left-right flip in the anatomy stack and subsequent cell_info
ave_stack = fliplr(ave_stack);
anat_yx = fliplr(anat_yx);
anat_zx = fliplr(anat_zx);

[s1,s2,~] = size(ave_stack);
tic
for i_cell = 1:length(cell_info),
    % fix '.center'
    cell_info(i_cell).center(2) = s2-cell_info(i_cell).center(2)+1;
    % fix '.inds'
    IX = cell_info(i_cell).inds;
    [I,J] = ind2sub([s1,s2],IX);
    J = s2-J+1;
    cell_info(i_cell).inds = sub2ind([s1,s2],I,J);
    % fix '.x_minmax'
    cell_info(i_cell).x_minmax(1) = s2-cell_info(i_cell).x_minmax(1)+1;
    cell_info(i_cell).x_minmax(2) = s2-cell_info(i_cell).x_minmax(2)+1;
end
toc

%% compute z-score
disp('compute z-score')
cell_resp_z_full = zscore(cell_resp_full')';
nCells = cell_resp_dim(1);

%% Validating all cells

isDiscard50 = false; % option to pre-screen
if isDiscard50,
    %% Round 1: discard 50% noisy cells based on std of zscore of baseline

    % Compute std of dimest 10% of frames for each cell.
    prc = prctile(cell_resp_z_full,10,2);
    STD_full = zeros(nCells,1);
    for i = 1:nCells,
        ix = find(cell_resp_z_full(i,:)<prc(i));
        STD_full(i) = std(cell_resp_z_full(i,ix));
    end
    % Set threshold at 50%, i.e. discard 50% of all cells
    thr = prctile(STD_full,50);

    temp = find(STD_full>thr);
    [~,I] = sort(STD_full(temp));
    cIX = temp(I);

    % visualize cells to discard
    % gIX = (round((1:length(cIX))/1000)+1)'; % option: view sorted index
    gIX = round(cIX/1000)+1; % option: view anatomical z index
    M = cell_resp_z_full(cIX,1:1000);
    BasicPlotMaps(cIX,gIX,M,cell_info,stim,anat_yx,anat_yz,anat_zx);

    % I_v: index of valid cells
    I_v_Holder = ones(1,nCells);
    I_v_Holder(cIX) = 0;
    I_v = find(I_v_Holder);

    cell_resp_z = cell_resp_z_full(I_v,:);
    STD = STD_full(I_v);
    nCells = length(I_v);
    CInfo_0 = cell_info(I_v);

    %% visualize valid cells, sorted by noise
    [~,I] = sort(STD);
    cIX = I;
    gIX = (round((1:length(cIX))/1000)+1)'; % sorted index
    M = cell_resp_z(I,1:1000);
    BasicPlotMaps(cIX,gIX,M,CInfo_0,stim,anat_yx,anat_yz,anat_zx);

else
    %% Round 1 alternative: full set
    cell_resp_z = cell_resp_z_full;
    I_v_Holder = ones(1,nCells);
    I_v = (1:nCells)'; % valid Index. here not actually screened, I_v is fullset

    cIX = I_v; % 'cell-Index'
    gIX = (round((1:length(cIX))/1000)+1)'; % sorted 'group-Index'
    M = cell_resp_z_full(cIX,1:1000);
    CInfo_0 = cell_info;
    BasicPlotMaps(cIX,gIX,M,CInfo_0,stim,anat_yx,anat_yz,anat_zx);
end

%% %%%% Round 2: delete anatomically out-of-bound cells by hand
% only execute once (manual choice 1):
cHolder_Anat = []; % collect cIX of all out-of-bound cells

% draw cells first
% (choose start and stop index to draw; low numbers ~ ventral)

%% (optional) step 1: set limit to only plot ventral layer, helps to find outliers within that layer
I_start = 1;
I_stop = round(length(I_v)/10);

% plot those cells
cIX = I_start:I_stop;
gIX = round(cIX/1000)+1;
figure('Position',[100 0 1300 900]);
numK = round(cIX(end)/1000)+1;
[tot_image, dim_totimage] = DrawClustersOnMap_LSh(CInfo_0,cIX,gIX,numK,anat_yx,anat_yz,anat_zx,'hsv','full');

% Here manually draw polygons around cells to discard.
% Double click to connect last vertex to first vertex, then double click again within polygon to fix.
% then click again to start drawing the next one
% and when finished, break loop with Ctrl+C...
MaskArray = zeros(dim_totimage(1), dim_totimage(2));
k_zres = 20;
% draw polygon around outliers on Y-X view
for i = 1:100, % NOMINAL LOOP, break manually (with Ctrl+C, somehow the design didn't work)
    h_poly_yx = impoly;
    wait(h_poly_yx); % double click to finalize position!
    % update finalized polygon in bright color
    setColor(h_poly_yx,[0 1 1]);

    IJs = reshape([CInfo_0(I_start:I_stop).center],2,[])';
    A = sub2ind(dimv_yx(1:2),IJs(:,1),IJs(:,2));
    MaskArray = createMask(h_poly_yx);
    MaskArray(1:dimv_zx*k_zres+10,:) = [];
    B = find(MaskArray); % find indices of pixels within ROI
    cIX2 = find(ismember(A,B));
    cHolder_Anat = union(cHolder_Anat,cIX2);
    w = waitforbuttonpress;
    if w == 1,
        break; % this doesn't work ?!
    end
end

%% step 2: plot all cells (including the ones in the ventral layer, don't
I_start = 1;
I_stop = length(I_v);

% ...here until the end of the cell is the exact dupliate of the last cell...
cIX = I_start:I_stop;
gIX = round(cIX/1000)+1;
figure('Position',[100 0 1300 900]);
numK = round(cIX(end)/1000)+1;
[tot_image, dim_totimage] = DrawClustersOnMap_LSh(CInfo_0,cIX,gIX,numK,anat_yx,anat_yz,anat_zx,'hsv','full');

% Here manually draw polygons around cells to discard.
% Double click to connect last vertex to first vertex, then double click again within polygon to fix.
% then click again to start drawing the next one
% and when finished, break loop with Ctrl+C...
MaskArray = zeros(dim_totimage(1), dim_totimage(2));
k_zres = 20;
% draw polygon around outliers on Y-X view
for i = 1:100, % NOMINAL LOOP, break manually (with Ctrl+C, somehow the design didn't work)
    h_poly_yx = impoly;
    wait(h_poly_yx); % double click to finalize position!
    % update finalized polygon in bright color
    setColor(h_poly_yx,[0 1 1]);

    IJs = reshape([CInfo_0(I_start:I_stop).center],2,[])';
    A = sub2ind(dimv_yx(1:2),IJs(:,1),IJs(:,2));
    MaskArray = createMask(h_poly_yx);
    MaskArray(1:dimv_zx*k_zres+10,:) = [];
    B = find(MaskArray); % find indices of pixels within ROI
    cIX2 = find(ismember(A,B));
    cHolder_Anat = union(cHolder_Anat,cIX2);
    w = waitforbuttonpress;
    if w == 1,
        break; % this doesn't work ?!
    end
end

%% find extra outliers on top/bottom border of image (can't always get with polygon)
temp = [CInfo_0(:).center];
XY = reshape(temp',[],nCells)';
IX = find(XY(:,1)<15 | XY(:,1)> s1-15 ...
        | XY(:,2)<15 | XY(:,2)> s2-15);
cHolder_Anat = union(cHolder_Anat,IX);

%% test Plot: all antomy outliers
cIX = cHolder_Anat;
gIX = (1:length(cIX))';
figure;
DrawClustersOnMap_LSh(CInfo_0,cIX,gIX,numK,anat_yx,anat_yz,anat_zx,'hsv','full');

%% Clean up anatomy outliers
cIX_Invalid_Anat = I_v(cHolder_Anat); % convert back to index for original full set
I_v_Holder(cIX_Invalid_Anat) = 0;
I_v2 = find(I_v_Holder);

CR_raw = cell_resp_z_full(I_v2,:);
nCells = length(I_v2);
CInfo = cell_info(I_v2);

%% Plot all valid cells to save
cIX = (1:nCells)';
gIX = round(cIX/1000)+1;
M = CR_raw(cIX,1:1000);
figure;
DrawClustersOnMap_LSh(CInfo,cIX,gIX,numK,anat_yx,anat_yz,anat_zx,'hsv','full');

%% Save mat files
CInfo_full = cell_info;
tic
temp = fullfile(datadir,['Fish' num2str(i_fish) '_full_extrainfo.mat']);
save(temp,'cHolder_Anat','cIX_Invalid_Anat','I_v','I_v2','CInfo_full','-v7.3'); % CInfo_full not saved in mats before 4/24/15!! %% 'STD_full' saved before

temp = fullfile(datadir,['Fish' num2str(i_fish) '_direct_load_nodiscard.mat']);
varList = {'CR_raw','nCells','CInfo','anat_yx','anat_yz','anat_zx','ave_stack','fpsec','frame_turn'};
save(temp,varList{:},'-v7.3');
toc

%% (if needed)
% save(temp,'somethingelse','-append');

%% next run step 2: detrend


clear all; close all

%% case 1
fprintf('\n\n\nCase 1\n')

surf(peaks)
axis vis3d
h = camlight('left');
a = get(gca);
set(gca,'view',[30,60])
b = get(gca);
b.junk = 'test';
[match, er1, er2] = comp_struct(a,b,1);
match
er1
list_struct(er1)
er2
list_struct(er2)

fprintf('\n\n\n\n\n');

if menu('Run next test?','Yes','No') - 1; error('Stop run'); end

%% case 2

clear all; close all
fprintf('\n\n\nCase 2\n')

% first struct
a(1).a = 1;
a(2).a = 2;
a(1).b.a = 'test';
a(2).b.a = 'test';
a(1).b.b = cellstr('a');
a(2).b.b = cellstr('a');
a(1).b.c = 1==1;
a(2).b.c = 1==1;
a(1).c = single([1 2 3]);
a(2).c = single([1 2 3]);
a(1).d(1).a = int8(2);
a(1).d(2).a = uint8(2);
a(1).d(3).a = int16(2);
a(1).d(4).a = uint16(2);
a(1).d(5).a = int32(2);
a(1).d(6).a = uint32(2);
a(1).d(7).a = int64(2);
a(1).d(8).a = uint64(2);
a(2).d(1).a = int8(2);
a(2).d(2).a = uint8(2);
a(2).d(3).a = int16(2);
a(2).d(4).a = uint16(2);
a(2).d(5).a = int32(2);
a(2).d(6).a = uint32(2);
a(2).d(7).a = int64(2);
a(2).d(8).a = uint64(2);
a(1).e = @(x)sin(x);
a(2).e = @(x)sin(x);
a(1).f = 1;
a(1).j = [1.1 2.2 3.3];
a(1).k = @(x)sin(x);

% second struct
b = a;
b(2).a = 1;
b(2).b.a = 'x';
b(2).b.b = cellstr('b');
b(2).b.c = 1==2;
b(2).c = single([1 2 5]);
b(2).d(1).a = int8(4);
b(2).d(2).a = uint8(54);
b(2).d(3).a = int16(5);
b(2).d(4).a = uint16(65);
b(2).d(5).a = int32(7);
b(2).d(6).a = uint32(7);
b(2).d(7).a = int64(7);
b(2).d(8).a = uint64(7);
b(2).e = @(x)tan(x);
b(1).f = 1+1e-8;
b(1).j = single(a(1).j);

% non-match
a(2).h = 1;
b(1).g = 1;
b(2).i.a = 'r';
b(3) = b(2);
b(4) = b(2);
x = .5; a(2).k = @(x)sin(x);
x = .25; b(2).k = @(x)sin(x);
a(1).l.a = 1;
b(1).l.c = 2;

a(1).o = 't';
b(1).o = 5;

% wrong order fields
a(1).m.a = 1; a(1).m.b = 1;
a(1).n.a = 1; a(1).n.b = 1;
b(1).n.b = 1; b(1).n.a = 1;
b(1).m.b = 1; b(1).m.a = 1;

[match, er1, er2] = comp_struct(a,b,1,0,1e-3);

match
er1
er2

if menu('Run next test?','Yes','No') - 1; error('Stop run'); end

%% case 3
clear all; close all
fprintf('\n\n\nCase 3\n')

a.a = 1; b.a = 1;
a.b = 2; b.b = 1;
a.c.a = 1; a.c.b = 2; b.c = 1;
a.d = cellstr('test')'; b.d = cellstr('test')';
a.e = cellstr('test')'; b.e = cellstr('a')';
a.f = 'tt'; b.f = 'tt';
a.g = 'tt'; b.g = 't';
a.h.a = 1; b.h.b = 1;
a.i.a = 1;
a.j.a = @sin; b.j.a = @sin;
a.j.b = @sin; b.j.b = @cos;
x = .1; a.j.c = @(x) sin(x); x = .5; b.j.c = @(x) sin(x);
a(2) = a(1); b(2) = b(1); a(3) = a(1);

[match, er1, er2] = comp_struct(a,b);
match
list_struct(match)
er1
list_struct(er1)
er2
list_struct(er2)

if menu('Run next test?','Yes','No') - 1; error('Stop run'); end

%% case 4
clear all; close all
fprintf('\n\n\nCase 4\n')

str = char(97:122);

% build structure
for ii = 1:26
	stu1.(str(ii)) = rand(ceil(5*rand(1)),ceil(5*rand(1)));
end
stu2 = stu1;
for ii = 1:26
	if round(rand(1))
		if rand(1) > 0.3
			if round(rand)
				stu2.(str(ii)) = rand(size(stu2.(str(ii))));
			else
				stu2.(str(ii)) = stu2.(str(ii)) + 1e-8;
			end
		else
			stu2.(str(ii)) = rand(ceil(5*rand(1)),ceil(5*rand(1)));
		end
	end
end

[match, er1, er2] = comp_struct(stu1,stu2);
match
er1
list_struct(er1)
er2
list_struct(er2)


function [out1,out2,out3] = ginput(arg1)
    %GINPUT Graphical input from mouse.
    %   [X,Y] = GINPUT(N) gets N points from the current axes and returns
    %   the X- and Y-coordinates in length N vectors X and Y.  The cursor
    %   can be positioned using a mouse (or by using the Arrow Keys on some
    %   systems).  Data points are entered by pressing a mouse button
    %   or any key on the keyboard except carriage return, which terminates
    %   the input before N points are entered.
    %
    %   [X,Y] = GINPUT gathers an unlimited number of points until the
    %   return key is pressed.
    %
    %   [X,Y,BUTTON] = GINPUT(N) returns a third result, BUTTON, that
    %   contains a vector of integers specifying which mouse button was
    %   used (1,2,3 from left) or ASCII numbers if a key on the keyboard
    %   was used.

    %   Copyright 1984-2001 The MathWorks, Inc.
    %   $Revision: 1399 $  $Date: 2006-08-11 11:19:27 +0200 (Fr, 11 Aug 2006) $

    out1 = []; out2 = []; out3 = []; y = [];
    c = computer;
    if ~strcmp(c(1:2),'PC')  &&  ~strcmp(c(1:2),'MA')
        tp = get(groot,'TerminalProtocol');
    else
        tp = 'micro';
    end

    if ~strcmp(tp,'none') & ~strcmp(tp,'x')  &&  ~strcmp(tp,'micro')
        if nargout == 1
            if nargin == 1
                out1 = trmginput(arg1);
            else
                out1 = trmginput;
            end
        elseif nargout == 2  ||  nargout == 0
            if nargin == 1
                [out1,out2] = trmginput(arg1);
            else
                [out1,out2] = trmginput;
            end
            if  nargout == 0
                out1 = [out1 out2 ];
            end
        elseif nargout == 3
            if nargin == 1
                [out1,out2,out3] = trmginput(arg1);
            else
                [out1,out2,out3] = trmginput;
            end
        end
    else

        fig = gcf;
        figure_w_normalized_uicontrolunits(gcf);

        if nargin == 0
            how_many = -1;
            b = [];
        else
            how_many = arg1;
            b = [];
            if  ischar(how_many) ...
                    | size(how_many,1) ~= 1 | size(how_many,2) ~= 1 ...
                    | ~(fix(how_many) == how_many) ...
                    | how_many < 0
                error('Requires a positive integer.')
            end
            if how_many == 0
                ptr_fig = 0;
                while(ptr_fig ~= fig)
                    ptr_fig = get(groot,'PointerWindow');
                end
                scrn_pt = get(groot,'PointerLocation');
                loc = get(fig,'Position');
                pt = [scrn_pt(1) - loc(1), scrn_pt(2) - loc(2)];
                out1 = pt(1); y = pt(2);
            elseif how_many < 0
                error('Argument must be a positive integer.')
            end
        end

        % Remove figure button functions
        state = uisuspend(fig);
        pointer = get(gcf,'pointer');
        set(gcf,'pointer','crosshair');
        fig_units = get(fig,'units');
        char = 0;

        while how_many ~= 0
            % Use no-side effect WAITFORBUTTONPRESS
            waserr = 0;
            try
                keydown = wfbp;
            catch
                waserr = 1;
            end
            if(waserr == 1)
                if(ishandle(fig))
                    set(fig,'units',fig_units);
                    uirestore(state);
                    error('Interrupted');
                else
                    error('Interrupted by figure deletion');
                end
            end

            ptr_fig = get(groot,'CurrentFigure');
            if(ptr_fig == fig)
                if keydown
                    char = get(fig, 'CurrentCharacter');
                    button = abs(get(fig, 'CurrentCharacter'));
                    scrn_pt = get(groot, 'PointerLocation');
                    set(fig,'units','pixels')
                    loc = get(fig, 'Position');
                    pt = [scrn_pt(1) - loc(1), scrn_pt(2) - loc(2)];
                    set(fig,'CurrentPoint',pt);
                else
                    button = get(fig, 'SelectionType');
                    if strcmp(button,'open')
                        button = b(length(b));
                    elseif strcmp(button,'normal')
                        button = 1;
                    elseif strcmp(button,'extend')
                        button = 2;
                    elseif strcmp(button,'alt')
                        button = 3;
                    else
                        error('Invalid mouse selection.')
                    end
                end
                pt = get(gca, 'CurrentPoint');

                how_many = how_many - 1;

                if(char == 13) % & how_many ~= 0)
                    % if the return key was pressed, char will == 13
                    % and that's our signal to break out of here whether
                    % or not we have collected all the requested data
                    % points.
                    % If this was an early breakout, don't include
                    % the <Return> key info in the return arrays.
                    % We will no longer count it if it's the last input.
                    break;
                end

                out1 = [out1;pt(1,1)];
                y = [y;pt(1,2)];
                b = [b;button];
            end
        end

        uirestore(state);
        set(fig,'units',fig_units);

        if nargout > 1
            out2 = y;
            if nargout > 2
                out3 = b;
            end
        else
            out1 = [out1 y];
        end

    end

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function key = wfbp
    %WFBP   Replacement for WAITFORBUTTONPRESS that has no side effects.

    fig = gcf;
    current_char = [];

    % Now wait for that buttonpress, and check for error conditions
    waserr = 0;
    try
        h=findall(fig,'type','uimenu','accel','C');   % Disabling ^C for edit menu so the only ^C is for
        set(h,'accel','');   % interrupting the function.
        pause(0.1)
        keydown = waitforbuttonpress;
        current_char = double(get(fig,'CurrentCharacter')); % Capturing the character.
        if~isempty(current_char) & (keydown == 1)           % If the character was generated by the
            if(current_char == 3)                       % current keypress AND is ^C, set 'waserr'to 1
                waserr = 1;                             % so that it errors out.
            end
        end

        set(h,'accel','C');                                 % Set back the accelerator for edit menu.
    catch
        waserr = 1;
    end
    drawnow;
    if(waserr == 1)
        set(h,'accel','C');                                % Set back the accelerator if it errored out.
        error('Interrupted');
    end

    if nargout>0, key = keydown; end
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


classdef scaleBar < handle
% Inserts a scale bar on the current ebsd or grain map.
%
% Syntax
%   hg = scaleBar(ebsd, scanunits)
%
% Input
%  ebsd      - an mtex ebsd or grain object
%  scanunits - units of the xy coordinates of the ebsd scan (e.g., 'um')
%
% Output
%  oval  - output value
%  ounit - output unit
%
% Options
%  BACKGROUNDCOLOR - background color (ColorSpec)
%  BACKGROUNDALPHA - background transparency (scalar 0<=a<=1)

%
% Example
%  Use a scale bar on the aachen mtexdata.
%
%   mtexdata aachen
%   plot(ebsd)
%   scaleBar(ebsd,'um','BackgroundColor','k','LineColor','w', ...
%                 'Border','off','BackgroundAlpha',0.6,'Location','nw')
%
% Bugs/Issues
%  [1] Using the hardware OpenGL renderer can sometimes cause the text not
%  to appear on the scale bar. This can usually be fixed by adding the line
%
%   opengl software
%
%  _Before_ the call to scaleBar().
%
%  [2] The font and the bounding box do not scale with the figure window.
%  Therefore, the desired figure window size needs to be set before the
%  call to scaleBar().
%
% Authors
%  Eric Payton, eric.payton@bam.de
%  Philippe Pinard, pinard@gfe.rwth-aachen.de
%
% Revision History
%  2012.07.17 EJP - First version submitted for mtex commit.
%  2012.07.27 EJP - Added option for specifying scale bar lengths.

properties (Hidden = true)
  hgt %
end

properties
  txt                      % handle of the text
  shadow                   % handle of the shadow
  ruler                    % handle of the ruler
end

properties (SetObservable)
  backgroundColor = 'k'    % background color (ColorSpec)
  backgroundAlpha = 0.6    % background transparency (scalar 0<=a<=1)
  scanUnit        = 'um'   % units of the xy coordinates of the ebsd scan (e.g., 'um')
  lineColor       = 'w'    % border color and text color (ColorSpec)
  length          = NaN    % desired scale bar length
  % border          = 'off ' % controls whether the box has a border ('on', 'off')
  % borderWidth     = 2      % width of border (scalar)
  % location        = 'sw'   % location of the scale bar ('nw,'ne','sw','se', or [x,y] coordinate vector (len(vector)==2)
end

properties (Dependent = true)
  visible
end

methods

  function sB = scaleBar(mP,scanUnit,varargin)

    sB.scanUnit = scanUnit;
    sB.hgt = hgtransform('parent',mP.ax);
    sB.shadow = patch('parent',sB.hgt,'Faces',1,'Vertices',[NaN NaN NaN]);
    sB.txt = text('parent',sB.hgt,'string','1mm','position',[NaN,NaN],...
      'Interpreter',getMTEXpref('textInterpreter'),'FontSize',getMTEXpref('FontSize'));
    sB.ruler = patch('parent',sB.hgt,'Faces',1,'Vertices',[NaN NaN NaN]);

    % set resize function
    hax = handle(mP.ax);
    try
      hListener(1) = handle.listener(hax, findprop(hax, 'Position'), ...
        'PropertyPostSet',@(a,b) sB.update);
      % save listener, otherwise  callback may die
      setappdata(hax, 'updatePos', hListener);
    catch
      if ~isappdata(hax, 'updatePos')
        hListener = addlistener(hax,'Position','PostSet',...
          @(obj,events) sB.update);
        setappdata(hax, 'updatePos', hListener);
      end
    end

    try
      addlistener(sB,'length','PostSet', @(x,y) sB.update);
      addlistener(sB,'scanUnit','PostSet', @(x,y) sB.update);
      addlistener(sB,'lineColor','PostSet', @(x,y) sB.update);
      addlistener(sB,'backgroundColor','PostSet', @(x,y) sB.update);
      addlistener(sB,'backgroundAlpha','PostSet', @(x,y) sB.update);
      %addlistener(sB,'location','PostSet', @(x,y) sB.update);
      %addlistener(sB,'borderWidth','PostSet', @(x,y) sB.update);
      %addlistener(sB,'border','PostSet', @(x,y) sB.update);
    end

  end

  function setOnTop(sB)
    uistack(sB.hgt,'top')
  end

  function set.visible(sB,value)
    set(sB.hgt,'visible',value);
  end

  function value = get.visible(sB)
    value = get(sB.hgt,'visible');
  end

  function update(sB)

    % get axes orientation
    ax = get(sB.hgt,'parent');
    [el,az] = view(ax);
    xDir = mod((-1)^(az<0) * round(el / 90),4); % E-S-W-N is 0 1 2 3
    yDir = mod(xDir - round(az / 90),4); % E-S-W-N is 0 1 2 3

    % get extent
    dx = xlim(ax); dy = ylim(ax);
    if any(xDir == [1,2]), dx= fliplr(dx); end
    if any(yDir == [1,2]), dy= fliplr(dy); end
    if mod(xDir,2), [dx,dy] = deal(dy,dx); end

    % Find the range in meters for later determination of magnitude
    % We do this so that we never display 10000 nm and always something like
    % 10 microns. Also, the correct choice of units will avoid decimals.
    [sBLength, sBUnit, factor] = switchUnit(0.1*abs(diff(dx)), sB.scanUnit);
    if strcmpi(sBUnit,'um'), sBUnit = '$\mu$m';end

    % we would like to have SBlength beeing a nice number
    if isnan(sB.length)
      goodValues = [1 2 5 10 15 20 25 50 75 100 125 150 200 500 750]; % Possible values for scale bar length
      [~,ind] = min(abs(sBLength-goodValues));
      sB.length = goodValues(ind);
    end
    rulerLength = sB.length * factor * sign(diff(dx));

    % A gap around the bar of 1% of bar length looks nice
    set(sB.txt,'position',[dx(1),dy(1)])
    textHeight = get(sB.txt, 'Extent');
    textHeight = min(textHeight(3:4)) * sign(diff(dy));
    gapY = textHeight/3;
    gapX = abs(gapY) * sign(diff(dx));

    % Box position
    boxWidth = rulerLength + 2.0 * gapX;
    boxx = dx(1) + gapX;
    boxy = dy(1) + gapY;

    % Make bounding box. The z-coordinate is used to put the box under the
    % line.
    verts = [boxx, boxy;
      boxx, boxy +  3*gapY + textHeight;
      boxx + boxWidth, boxy + 3*gapY + textHeight;
      boxx + boxWidth, boxy];
    set(sB.shadow,'Vertices', cP(verts), ...
      'Faces', [1 2 3 4], ...
      'FaceColor', sB.backgroundColor , 'EdgeColor', 'none', ...
      'LineWidth', 1, 'FaceAlpha', sB.backgroundAlpha);

    % update text
    set(sB.txt,'string',['\rm{\textbf{' num2str(sB.length) ' ' sBUnit '}}'],...
      'HorizontalAlignment', 'Center',...
      'VerticalAlignment', 'baseline','color',sB.lineColor,...
      'Position', cP([boxx+boxWidth/2,boxy+3*gapY]));

    % Create line as a patch. The z-coordinate is used to layer the patch over
    % top of the bounding box.
    set(sB.ruler,'Vertices',cP([boxx+gapX, boxy+gapY; ...
      boxx + gapX, boxy+2*gapY; ...
      boxx + gapX + rulerLength, boxy + 2*gapY; ...
      boxx + gapX + rulerLength, boxy + gapY]), ...
      'Faces',[1 2 3 4], 'FaceColor',sB.lineColor, 'FaceAlpha',1);

    sB.setOnTop;

    function pos = cP(pos)
      % interchange x and y if needed
      if mod(xDir,2), pos(:,[1,2]) = pos(:,[2,1]); end
    end

  end

end

end


function parts = strsplit_cerr(splitstr, str, option)
%STRSPLIT_CERR Split string into pieces.
%
%   STRSPLIT_CERR(SPLITSTR, STR, OPTION) splits the string STR at every occurrence
%   of SPLITSTR and returns the result as a cell array of strings.  By default,
%   SPLITSTR is not included in the output.
%
%   STRSPLIT_CERR(SPLITSTR, STR, OPTION) can be used to control how SPLITSTR is
%   included in the output.  If OPTION is 'include', SPLITSTR will be included
%   as a separate string.  If OPTION is 'append', SPLITSTR will be appended to
%   each output string, as if the input string was split at the position right
%   after the occurrence SPLITSTR.  If OPTION is 'omit', SPLITSTR will not be
%   included in the output.

%   Author:      Peter J. Acklam
%   Time-stamp:  2004-09-22 08:48:01 +0200
%   E-mail:      pjacklam@online.no
%   URL:         http://home.online.no/~pjacklam
%
% Copyright 2010, Joseph O. Deasy, on behalf of the CERR development team.
%
% This file is part of The Computational Environment for Radiotherapy Research (CERR).
%
% CERR development has been led by:  Aditya Apte, Divya Khullar, James Alaly, and Joseph O. Deasy.
%
% CERR has been financially supported by the US National Institutes of Health under multiple grants.
%
% CERR is distributed under the terms of the Lesser GNU Public License.
%
%     This version of CERR 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.
%
% CERR 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 CERR.  If not, see <http://www.gnu.org/licenses/>.

nargsin = nargin;
error(nargchk(2, 3, nargsin));
if nargsin < 3
	option = 'omit';
else
	option = lower(option);
end

splitlen = length(splitstr);
parts = {};

while 1

	k = strfind(str, splitstr);
	if isempty(k)
		parts{end+1} = str;
		break
	end

	switch option
		case 'include'
			parts(end+1:end+2) = {str(1:k(1)-1), splitstr};
		case 'append'
			parts{end+1} = str(1 : k(1)+splitlen-1);
		case 'omit'
			parts{end+1} = str(1 : k(1)-1);
		otherwise
			error(['Invalid option string -- ', option]);
	end

	str = str(k(1)+splitlen : end);

end


classdef SyntheticSequenceGenerator < handle
    %SYNTHETICSEQUENCEGENERATOR  Synthetic frame sequence generator for testing background subtraction algorithms
    %
    % It will generate the moving object on top of the background.
    % It will apply some distortion to the background to make the test more
    % complex.
    %
    % See also: cv.BackgroundSubtractorLSBP, cv.BackgroundSubtractorGSOC
    %

    properties (SetAccess = private)
        % Object ID
        id
    end

    %% SyntheticSequenceGenerator
    methods
        function this = SyntheticSequenceGenerator(background, object, varargin)
            %SYNTHETICSEQUENCEGENERATOR  Creates an instance of SyntheticSequenceGenerator
            %
            %     obj = cv.SyntheticSequenceGenerator(background, object)
            %     obj = cv.SyntheticSequenceGenerator(..., 'OptionName',optionValue, ...)
            %
            % ## Input
            % * __background__ Background image for object.
            % * __object__ Object image which will move slowly over the
            %   background.
            %
            % ## Options
            % * __Amplitude__ Amplitude of wave distortion applied to
            %   background. default 2.0
            % * __Wavelength__ Length of waves in distortion applied to
            %   background. default 20.0
            % * __Wavespeed__ How fast waves will move. default 0.2
            % * __Objspeed__ How fast object will fly over background.
            %   default 6.0
            %
            % See also: cv.SyntheticSequenceGenerator.getNextFrame
            %
            this.id = SyntheticSequenceGenerator_(0, 'new', background, object, varargin{:});
        end

        function delete(this)
            %DELETE  Destructor
            %
            %     obj.delete()
            %
            % See also: cv.SyntheticSequenceGenerator
            %
            if isempty(this.id), return; end
            SyntheticSequenceGenerator_(this.id, 'delete');
        end

        function [frame, gtMask] = getNextFrame(this)
            %GETNEXTFRAME  Obtain the next frame in the sequence
            %
            %     [frame, gtMask] = obj.getNextFrame()
            %
            % ## Output
            % * __frame__ Output frame.
            % * __gtMask__ Output ground-truth (reference) segmentation mask
            %   object/background.
            %
            [frame, gtMask] = SyntheticSequenceGenerator_(this.id, 'getNextFrame');
        end
    end

    %% Algorithm
    methods (Hidden)
        function clear(this)
            %CLEAR  Clears the algorithm state
            %
            %     obj.clear()
            %
            % See also: cv.SyntheticSequenceGenerator.empty,
            %  cv.SyntheticSequenceGenerator.load
            %
            SyntheticSequenceGenerator_(this.id, 'clear');
        end

        function b = empty(this)
            %EMPTY  Returns true if the algorithm is empty
            %
            %     b = obj.empty()
            %
            % ## Output
            % * __b__ Returns true if the object is empty (e.g in the
            %   very beginning or after unsuccessful read).
            %
            % See also: cv.SyntheticSequenceGenerator.clear,
            %  cv.SyntheticSequenceGenerator.load
            %
            b = SyntheticSequenceGenerator_(this.id, 'empty');
        end

        function name = getDefaultName(this)
            %GETDEFAULTNAME  Returns the algorithm string identifier
            %
            %     name = obj.getDefaultName()
            %
            % ## Output
            % * __name__ This string is used as top level XML/YML node tag
            %   when the object is saved to a file or string.
            %
            % See also: cv.SyntheticSequenceGenerator.save,
            %  cv.SyntheticSequenceGenerator.load
            %
            name = SyntheticSequenceGenerator_(this.id, 'getDefaultName');
        end

        function save(this, filename)
            %SAVE  Saves the algorithm parameters to a file
            %
            %     obj.save(filename)
            %
            % ## Input
            % * __filename__ Name of the file to save to.
            %
            % This method stores the algorithm parameters in the specified
            % XML or YAML file.
            %
            % See also: cv.SyntheticSequenceGenerator.load
            %
            SyntheticSequenceGenerator_(this.id, 'save', filename);
        end

        function load(this, fname_or_str, varargin)
            %LOAD  Loads algorithm from a file or a string
            %
            %     obj.load(fname)
            %     obj.load(str, 'FromString',true)
            %     obj.load(..., 'OptionName',optionValue, ...)
            %
            % ## Input
            % * __fname__ Name of the file to read.
            % * __str__ String containing the serialized model you want to
            %   load.
            %
            % ## Options
            % * __ObjName__ The optional name of the node to read (if empty,
            %   the first top-level node will be used). default empty
            % * __FromString__ Logical flag to indicate whether the input is a
            %   filename or a string containing the serialized model.
            %   default false
            %
            % This method reads algorithm parameters from the specified XML or
            % YAML file (either from disk or serialized string). The previous
            % algorithm state is discarded.
            %
            % See also: cv.SyntheticSequenceGenerator.save
            %
            SyntheticSequenceGenerator_(this.id, 'load', fname_or_str, varargin{:});
        end
    end

end


function data_num = p07_data_num ( )

%*****************************************************************************80
%
%% P07_DATA_NUM returns the number of data points for problem p07.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    29 July 2012
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Output, integer DATA_NUM, the number of data points.
%
  data_num = 4;

  return
end


function hn=arrow3(p1,p2,s,w,h,ip,alpha,beta)
% ARROW3
%   ARROW3(P1,P2) will draw vector lines (2D/3D) from P1 to P2 with
%   arrowheads, where P1 and P2 can be either nx2 matrices (for 2D),
%   or nx3 matrices (for 3D).
%
%   ARROW3(P1,P2,S,W,H,IP,ALPHA,BETA) can be used to specify properties
%   of the line and arrowhead.  S is a character string made with one
%   element from any or all of the following 3 columns:
%
%      Color Switches       LineStyle             LineWidth
%      ------------------   -------------------   --------------------
%      k  black (default)   -   solid (default)   0.5 points (default)
%      y  yellow            :   dotted            0   no lines
%      m  magenta           -.  dashdot           /   LineWidthOrder
%      c  cyan              --  dashed
%      r  red               *   LineStyleOrder
%      g  green
%      b  blue
%      w  white                                    __________ __
%      a  apple green                            ^           |
%      d  dark gray                             / \          |
%      e  evergreen                            /   \         |
%      f  fuchsia                             /     \        |
%      h  honey                              /       \    Height
%      i  indigo                            /         \      |
%      j  jade                             /           \     |
%      l  lilac                           /             \    |
%      n  nutbrown                       /______   ______\ __|__
%      p  pink                           |      | |      |
%      q  kumquat                        |---- Width ----|
%      s  sky blue                       |      | |      |
%      t  tan                                   | |
%      u  umber                                 | |
%      v  violet                                | |
%      z  zinc                               -->| |<--LineWidth
%      x  random named color                    | |
%      o  ColorOrder
%      |  magnitude
%
%   The components of S may be specified in any order.  Invalid
%   characters in S will be ignored and replaced by default settings.
%
%   Prefixing the color code with '_' produces a darker shade, e.g.
%   '_t' is dark tan; prefixing the color code with '^' produces a
%   lighter shade, e.g. '^q' is light kumquat.  The relative
%   brightness of light and dark color shades is controled by the
%   scalar parameter BETA.  Color code prefixes do not affect the
%   basic colors (kymcrgbw) or the special color switches (xo|).
%
%   ColorOrder may be achieved in two fashions:  The user may either
%   set the ColorOrder property (using RGB triples) or define the
%   global variable ColorOrder (using a string of valid color codes).
%   If the color switch is specified with 'o', and the global variable
%   ColorOrder is a string of color codes (color switches less 'xo|',
%   optionally prefixed with '_' or '^'), then the ColorOrder property
%   will be set to the sequence of colors indicated by the ColorOrder
%   variable.  If the color switch is specified with 'o', and the
%   global variable ColorOrder is empty or invalid, then the current
%   ColorOrder property will be used.  Note that the ColorOrder
%   variable takes precedence over the ColorOrder property.
%
%   The magnitude color switch is used to visualize vector magnitudes
%   in conjunction with a colorbar.  If the color switch is specified
%   with '|', colors are linearly interpolated from the current ColorMap
%   according to the length of the associated line.  This option sets
%   CLim to [MinM,MaxM], where MinM and MaxM are the minimum and maximum
%   magnitudes, respectively.
%
%   The current LineStyleOrder property will be used if LineStyle is
%   specified with '*'.  MATLAB cycles through the line styles defined
%   by the LineStyleOrder property only after using all colors defined
%   by the ColorOrder property.  If however, the global variable
%   LineWidthOrder is defined, and LineWidth is specified with '/',
%   then each line will be drawn with sequential color, linestyle, and
%   linewidth.
%
%   W is a vector of arrowhead widths.  For linear plots with equal
%   axes, the units of W (default = 1/72 of the PlotBox diagonal) are
%   the same as those of the coordinate data (P1,P2).  For linear plots
%   with unequal axes, the units of W are scaled to fit as if the axes
%   were equal.  For log plots, the units of W (default = 1) are 1/72
%   of the PositionRectangle diagonal.
%
%   H (default = 3W) is a vector of arrowhead heights.  If vector IP is
%   neither empty nor negative, initial point markers will be plotted
%   with diameter IP; for default diameter W, use IP = 0.  The units of
%   W, H, and IP are absolute for linear plots and relative to the
%   PositionRectangle for log plots.
%
%   ALPHA (default = 1) is a vector of FaceAlpha (MATLAB 6+) values
%   ranging between 0 (clear) and 1 (opaque).  FaceAlpha is a surface
%   (arrowhead and initial point marker) property and does not affect
%   lines.  FaceAlpha is not supported for 2D rendering.
%
%   BETA (default = 0.4) is a scalar that controls the relative
%   brightness of light and dark color shades, ranging between 0 (no
%   contrast) and 1 (maximum contrast).
%
%   Plotting lines with a single color, linestyle, and linewidth is
%   faster than plotting lines with multiple colors and/or linestyles.
%   Plotting lines with multiple linewidths is slower still.  ARROW3
%   chooses renderers that produce the best screen images; exported
%   or printed plots may benifit from different choices.
%
%   HN = ARROW3(P1,P2,...) returns a vector of handles to line and
%   surface objects created by ARROW3.
%
%   ARROW3 COLORS will plot a table of named colors with default
%   brightness.  ARROW3('colors',BETA) will plot a table of named
%   colors with brightness BETA.
%
%   If a particular aspect ratio is required, use DASPECT, PBASPECT,
%   AXIS, or XYZLIM commands before calling ARROW3.  Changing limits
%   or aspect ratios after calling ARROW3 may alter the appearance
%   of arrowheads and initial point markers.  ARROW3 sets XYZCLimMode
%   to manual for all plots, sets DataAspectRatioMode to manual for
%   linear plots, and sets PlotBoxAspectRatioMode to manual for log
%   plots and 3D plots.
%
%   ARROW3 UPDATE will restore the the appearance of arrowheads and
%   initial point markers that have become corrupted by changes to
%   limits or aspect ratios.  ARROW3('update',SF) will redraw initial
%   point markers and arrowheads with scale factor SF.  If SF has one
%   element, SF scales W, H and IP.  If SF has two elements, SF(1)
%   scales W and IP, and SF(2) scales H.  If SF has three elements,
%   SF(1) scales W, SF(2) scales H, and SF(3) scales IP.
%
%   ARROW3 UPDATE COLORS will update the magnitude coloring of
%   arrowheads, initial point markers, and lines to conform to the
%   current colormap.
%
%   HN = ARROW3('update',...) returns a vector of handles to updated
%   objects.
%
%   Usage Examples:
%
%      % 2D vectors
%      Arrow3([0 0],[1 3])
%      Arrow3([0 0],[1 2],'-.e')
%      Arrow3([0 0],[10 10],'--x2',1)
%      Arrow3(zeros(10,2),50*rand(10,2),'x',1,3)
%      Arrow3(zeros(10,2),[10*rand(10,1),500*rand(10,1)],'u')
%      Arrow3(10*rand(10,2),50*rand(10,2),'x',1,[],1)
%
%      % 3D vectors
%      Arrow3([0 0 0],[1 1 1])
%      Arrow3(zeros(20,3),50*rand(20,3),'--x1.5',2)
%      Arrow3(zeros(100,3),50*rand(100,3),'x',1,3)
%      Arrow3(zeros(10,3),[10*rand(10,1),500*rand(10,1),50*rand(10,1)],'a')
%      Arrow3(10*rand(10,3),50*rand(10,3),'x',[],[],0)
%
%      % Just for fun
%      Arrow3(zeros(100,3),50*rand(100,3),'x',10,3,[],0.95)
%      light('Position',[-10 -10 -10],'Style','local')
%      light('Position',[60,60,60]), lighting gouraud
%
%      % ColorOrder variable, color code prefixes, and Beta
%      global ColorOrder, ColorOrder='^ui^e_hq^v';
%      theta=[0:pi/22:pi/2]';
%      Arrow3(zeros(12,2),[cos(theta),sin(theta)],'1.5o',0.1,[],[],[],0.5)
%
%      % ColorOrder property, LineStyleOrder, and LineWidthOrder
%      global ColorOrder, ColorOrder=[];
%      set(gca,'ColorOrder',[1,0,0;0,0,1;0.25,0.75,0.25;0,0,0])
%      set(gca,'LineStyleOrder',{'-','--','-.',':'})
%      global LineWidthOrder, LineWidthOrder=[1,2,4,8];
%      w=[5,10,15,20]; h=[20,40,30,40];
%      Arrow3(zeros(4,2),[10*rand(4,1),500*rand(4,1)],'o*/',w,h,10)
%
%      % Magnitude coloring
%      colormap spring
%      Arrow3(20*randn(20,3),50*randn(20,3),'|',[],[],0)
%      set(gca,'color',0.7*[1,1,1])
%      set(gcf,'color',0.5*[1,1,1]), grid on, colorbar
%      pause % change the colormap and update colors
%      colormap hot
%      Arrow3('update','colors')
%
%      % LogLog plot
%      set(gca,'xscale','log','yscale','log');
%      axis([1e2,1e8,1e-2,1e-1]); hold on
%      p1=repmat([1e3,2e-2],15,1);
%      q1=[1e7,1e6,1e5,1e4,1e3,1e7,1e7,1e7,1e7,1e7,1e7,1e6,1e5,1e4,1e3];
%      q2=1e-2*[9,9,9,9,9,7,5,4,3,2,1,1,1,1,1]; p2=[q1',q2'];
%      global ColorOrder, ColorOrder=[];
%      set(gca,'ColorOrder',rand(15,3))
%      Arrow3(p1,p2,'o'), grid on, hold off
%
%      % SemiLogX plot
%      set(gca,'xscale','log','yscale','linear');
%      axis([1e2,1e8,1e-2,1e-1]); hold on
%      p1=repmat([1e3,0.05],15,1);
%      q1=[1e7,1e6,1e5,1e4,1e3,1e7,1e7,1e7,1e7,1e7,1e7,1e6,1e5,1e4,1e3];
%      q2=1e-2*[9,9,9,9,9,7,5,4,3,2,1,1,1,1,1]; p2=[q1',q2'];
%      Arrow3(p1,p2,'x'), grid on, hold off
%
%      % SemiLogY plot
%      set(gca,'xscale','linear','yscale','log');
%      axis([2,8,1e-2,1e-1]); hold on
%      p1=repmat([3,2e-2],17,1);
%      q1=[7,6,5,4,3,7,7,7,7,7,7,7,7,6,5,4,3];
%      q2=1e-2*[9,9,9,9,9,8,7,6,5,4,3,2,1,1,1,1,1]; p2=[q1',q2'];
%      set(gca,'LineStyleOrder',{'-','--','-.',':'})
%      Arrow3(p1,p2,'*',1,[],0), grid on, hold off
%
%      % Color tables
%      Arrow3('colors')           % default color table
%      Arrow3('colors',0.3)       % low contrast color table
%      Arrow3('colors',0.5)       % high contrast color table
%
%      % Update initial point markers and arrowheads
%      Arrow3('update')           % redraw same size
%      Arrow3('update',2)         % redraw double size
%      Arrow3('update',0.5)       % redraw half size
%      Arrow3('update',[0.5,2,1]) % redraw W half size,
%                                 %        H double size, and
%                                 %        IP same size

%   Copyright(c)2002,2003 Version 4.58
%      Jeff Chang <cpmame@hotmail.com>
%      Tom Davis  <tdavis@eng.usf.edu>

%   Revision History:
%
%      12/17/03 - Semilog examples, CAXIS support, magnitude
%                 coloring, and color updating; use CData instead
%                 of FaceColor; minor bug fixes (TD)
%      07/17/03 - Changed 2D rendering from OpenGL to ZBuffer;
%                 defined HN for COLORS and UPDATE options (TD)
%      02/27/03 - Replaced calls to RANDPERM, VIEW, REPMAT, SPHERE,
%                 and CYLINDER; added ZBuffer for log plots, RESET
%                 for CLA and CLF, and ABS for W and H (TD)
%      02/01/03 - Added UPDATE scale factor and matlab version
%                 checking, replaced call to CROSS (TD)
%      12/26/02 - Added UserData and UPDATE option (TD)
%      11/16/02 - Added more named colors, color code prefix,
%                 global ColorOrder, ALPHA , and BETA (TD)
%      10/12/02 - Added global LineWidthOrder,
%                 vectorized W, H and IP (TD)
%      10/05/02 - Changed CLF to CLA for subplot support,
%                 added ColorOrder and LineStyleOrder support (TD)
%      04/27/02 - Minor log plot revisions (TD)
%      03/26/02 - Added log plot support (TD)
%      03/24/02 - Adaptive grid spacing control to trade off
%                 appearance vs. speed based on size of matrix (JC)
%      03/16/02 - Added "axis tight" for improved appearance (JC)
%      03/12/02 - Added initial point marker (TD)
%      03/03/02 - Added aspect ratio support (TD)
%      03/02/02 - Enchance program's user friendliness (JC)
%                 (lump Color, LineStyle, and LineWidth together)
%      03/01/02 - Replaced call to ROTATE (TD)
%      02/28/02 - Modified line plotting,
%                 added linewidth and linestyle (TD)
%      02/27/02 - Minor enhancements on 3D appearance (JC)
%      02/26/02 - Minor enhancements for speed (TD&JC)
%      02/26/02 - Optimise PLOT3 and SURF for speed (TD)
%      02/25/02 - Return handler, error handling, color effect,
%                 generalize for 2D/3D vectors (JC)
%      02/24/02 - Optimise PLOT3 and SURF for speed (TD)
%      02/23/02 - First release (JC&TD)

%==========================================================================
% Error Checking
oldver=0; v=version; if v(1)<'6', oldver=1; end % matlab version
if nargin<8 | isempty(beta), beta=0.4; end
beta=abs(beta(1)); if nargout, hn=[]; end
if strcmpi(p1,'colors')                         % plot color table
   if nargin>1, beta=abs(p2(1)); end
   LocalColorTable(1,beta); return
end
fig=gcf; ax=gca;
if strcmpi(p1,'update'), ud=get(ax,'UserData'); % update
   if size(ud,2)<13, error('Invalid UserData'), end
   set(ax,'UserData',[]); sf=[1,1,1]; flag=0;
   if nargin>1
      if strcmp(p2,'colors'), flag=1;           % update colors
      else                                      % update surfaces
         sf=p2(1)*sf; n=length(p2(:));
         if n>1, sf(2)=p2(2); if n>2, sf(3)=p2(3); end, end
      end
   end
   H=LocalUpdate(fig,ax,ud,sf,flag); if nargout, hn=H; end, return
end
InputError=['Invalid input, type HELP ',upper(mfilename),...
   ' for usage examples'];
if nargin<2, error(InputError), end
[r1,c1]=size(p1); [r2,c2]=size(p2); n=r1; Zeros=zeros(n,1);
if c1<2 | c1>3, error(InputError), end
if r1~=r2, error('P1 and P2 must have same number of rows'), end
if c1~=c2, error('P1 and P2 must have same number of columns'), end
if c1==2, p1=[p1,Zeros]; p2=[p2,Zeros];
elseif ~any([p1(:,3);p2(:,3)]), c1=2; end
L=get(ax,'LineStyleOrder'); C=get(ax,'ColorOrder');
ST=get(ax,'DefaultSurfaceTag'); LT=get(ax,'DefaultLineTag');
EC=get(ax,'DefaultSurfaceEdgeColor');
if strcmp(get(ax,'nextplot'),'add') & strcmp(get(fig,'nextplot'),'add')
   Xr=get(ax,'xlim'); Yr=get(ax,'ylim'); Zr=get(ax,'zlim');
   set(ax,'XLimMode','auto','YLimMode','auto','ZLimMode','auto',...
      'CLim',get(ax,'CLim'));
   xs=strcmp(get(ax,'xscale'),'log');
   ys=strcmp(get(ax,'yscale'),'log');
   zs=strcmp(get(ax,'zscale'),'log');
   if zs, error('Z log scale not supported'), end
   xys=xs+ys; restore=1;
   if xys & any([p1(:,3);p2(:,3)])
      error('3D log plot not supported')
   end
else, restore=0; cla reset; xys=0; set(fig,'nextplot','add');
   if c1==2, azel=[0,90]; else, azel=[-37.5,30]; end
   set(ax,'UserData',[],'nextplot','add','View',azel);
end

%==========================================================================
% Style Control
[vc,cn]=LocalColorTable(0); prefix=''; OneColor=0;
if nargin<3, [c,ls,lw]=LocalValidateCLSW; % default Color, LineStyle/Width
else,
   [c,ls,lw]=LocalValidateCLSW(s);
   if length(c)>1, if sum('_^'==c(1)), prefix=c(1); end, c=c(2); end
   if c=='x'                              % random named color (less white)
      [ignore,i]=sort(rand(1,23)); c=cn(i,:);
   elseif c=='o', global ColorOrder       % ColorOrder
      if length(ColorOrder)
         [c,failed]=LocalColorMap(lower(ColorOrder),vc,cn,beta);
         if failed, warning(['Invalid ColorOrder variable, ',...
            'current ColorOrder property will be used'])
         else, C=c; end
      end, c=C;
   elseif c=='|', map=colormap;           % magnitude coloring
      M=(p1-p2); M=sqrt(sum(M.*M,2));
      minM=min(M); maxM=max(M); set(ax,'clim',[minM,maxM]);
      c=interp1(linspace(minM,maxM,size(map,1)),map,M);
      c(c<0)=0; c(c>1)=1;
   elseif ~sum(vc==c), c='k'; warning(['Invalid color switch, ',...
      'default color (black) will be used'])
   end
end
if length(c)==1                                    % single color
   c=LocalColorMap([prefix,c],vc,cn,beta); OneColor=1;
end
set(ax,'ColorOrder',c); c=LocalRepmat(c,[ceil(n/size(c,1)),1]);
if ls~='*', set(ax,'LineStyleOrder',ls); end       % LineStyleOrder
if lw=='/', global LineWidthOrder                  % LineWidthOrder
   if length(LineWidthOrder)
      lw=LocalRepmat(LineWidthOrder(:),[ceil(n/length(LineWidthOrder)),1]);
   else, lw=0.5; warning(['Undefined LineWidthOrder, ',...
      'default width (0.5) will be used'])
   end
end
if nargin<7 | isempty(alpha), alpha=1; end
a=LocalRepmat(alpha(:),[ceil(n/length(alpha)),1]); % FaceAlpha

%==========================================================================
% Log Plot
if xys
   units=get(ax,'units'); set(ax,'units','points');
   pos=get(ax,'position'); set(ax,'units',units);
   if strcmp(get(ax,'PlotBoxAspectRatioMode'),'auto')
      set(ax,'PlotBoxAspectRatio',[pos(3),pos(4),1]);
   end
   par=get(ax,'PlotBoxAspectRatio');
   set(ax,'DataAspectRatio',[par(2),par(1),par(3)]);
   % map coordinates onto unit square
   q=[p1;p2]; xr=Xr; yr=Yr;
   if xs, xr=log10(xr); q(:,1)=log10(q(:,1)); end
   if ys, yr=log10(yr); q(:,2)=log10(q(:,2)); end
   q=q-LocalRepmat([xr(1),yr(1),0],[2*n,1]);
   dx=xr(2)-xr(1); dy=yr(2)-yr(1);
   q=q*diag([1/dx,1/dy,1]);
   q1=q(1:n,:); q2=q(n+1:end,:);
else, xs=0; ys=0; dx=0; dy=0; xr=0; yr=0; end

%==========================================================================
% Line
set(ax,'DefaultLineTag','arrow3');
if length(lw)==1
   if lw>0
      if OneColor & ls~='*'   % single color, linestyle, and linewidth
         P=zeros(3*n,3); i=1:n;
         P(3*i-2,:)=p1(i,:); P(3*i-1,:)=p2(i,:); P(3*i,1)=NaN;
         H1=plot3(P(:,1),P(:,2),P(:,3),'LineWidth',lw);
      else                    % single linewidth
         H1=plot3([p1(:,1),p2(:,1)]',[p1(:,2),p2(:,2)]',...
            [p1(:,3),p2(:,3)]','LineWidth',lw);
      end
   else, H1=[]; end
else                          % use LineWidthOrder
   ls=LocalRepmat(cellstr(L),[ceil(n/size(L,1)),1]);
   H1=Zeros;
   for i=1:n
      H1(i)=plot3([p1(i,1),p2(i,1)],[p1(i,2),p2(i,2)],[p1(i,3),p2(i,3)],...
         ls{i},'Color',c(i,:),'LineWidth',lw(i));
   end
end

%==========================================================================
% Scale
ar=get(ax,'DataAspectRatio'); ar=sqrt(3)*ar/norm(ar);
set(ax,'DataAspectRatioMode','manual');
if nargin<4 | isempty(w)             % width
   if xys, w=1;
   else, xr=get(ax,'xlim'); yr=get(ax,'ylim'); zr=get(ax,'zlim');
      w=norm([xr(2)-xr(1),yr(2)-yr(1),zr(2)-zr(1)])/72;
   end
end
w=LocalRepmat(abs(w(:)),[ceil(n/length(w)),1]);
if nargin<5 | isempty(h), h=3*w; end % height
h=LocalRepmat(abs(h(:)),[ceil(n/length(h)),1]);
if nargin>5 & ~isempty(ip)           % ip
   i=find(ip==0); ip(i)=w(i);
   ip=LocalRepmat(ip(:),[ceil(n/length(ip)),1]);
else, ip=-ones(n,1); end

%==========================================================================
% UserData
set(ax,'UserData',[get(ax,'UserData');...
   p1,p2,c(1:n,:),w(1:n),h(1:n),ip(1:n),a]);

%==========================================================================
% Arrowhead
if xys, whip=[w,h,ip]*sqrt(2)/72;
   w=whip(:,1); h=whip(:,2); ip=whip(:,3); p1=q1; p2=q2;
end
W=(p1-p2)./LocalRepmat(ar,[n,1]);
W=W./LocalRepmat(sqrt(sum(W.*W,2)),[1,3]);  % new z direction
U=[-W(:,2),W(:,1),Zeros];
N=sqrt(sum(U.*U,2)); i=find(N<eps); j=length(i);
U(i,:)=LocalRepmat([1,0,0],[j,1]); N(i)=ones(j,1);
U=U./LocalRepmat(N,[1,3]);                  % new x direction
V=[W(:,2).*U(:,3)-W(:,3).*U(:,2),...        % new y direction
   W(:,3).*U(:,1)-W(:,1).*U(:,3),...
   W(:,1).*U(:,2)-W(:,2).*U(:,1)];

m1=30; m2=10; num=200;               % max, min grid spacing, and threshold
if n<num, m=round((m2-m1)*n/num+m1); % adjust grid spacing automatically
else m=m2; end                       % for speed when matrix size > num

set(ax,'DefaultSurfaceTag','arrow3','DefaultSurfaceEdgeColor','none');
r=[0;1]; theta=(0:m)/m*2*pi; Ones=ones(1,m+1);
x=r*cos(theta); y=r*sin(theta); z=r*Ones;
G=surface(x/2,y/2,z); dar=diag(ar);
X=get(G,'XData'); Y=get(G,'YData'); Z=get(G,'ZData');
H2=Zeros; [j,k]=size(X);
for i=1:n                            % translate, rotate, and scale
   H2(i)=copyobj(G,ax);
   xyz=[w(i)*X(:),w(i)*Y(:),h(i)*Z(:)]*[U(i,:);V(i,:);W(i,:)]*dar;
   x=reshape(xyz(:,1),j,k)+p2(i,1);
   y=reshape(xyz(:,2),j,k)+p2(i,2);
   z=reshape(xyz(:,3),j,k)+p2(i,3);
   LocalSetSurface(xys,xs,ys,dx,dy,xr,yr,...
      x,y,z,a(i),c(i,:),H2(i),2,m+1,oldver);
end, delete(G);

%==========================================================================
% Initial Point Marker
if any(ip>0)
   theta=(-m:2:m)/m*pi; phi=(-m:2:m)'/m*pi/2; cosphi=cos(phi);
   x=cosphi*cos(theta); y=cosphi*sin(theta); z=sin(phi)*Ones;
   G=surface(x*ar(1)/2,y*ar(2)/2,z*ar(3)/2);
   X=get(G,'XData'); Y=get(G,'YData'); Z=get(G,'ZData');
   H3=zeros(n,1);
   for i=1:n                         % translate
      if ip(i)>0
         H3(i)=copyobj(G,ax);
         x=p1(i,1)+X*ip(i); y=p1(i,2)+Y*ip(i); z=p1(i,3)+Z*ip(i);
         LocalSetSurface(xys,xs,ys,dx,dy,xr,yr,...
            x,y,z,a(i),c(i,:),H3(i),m+1,m+1,oldver);
      end
   end, delete(G);
else, H3=[]; end

%==========================================================================
% Finish
if xys, xr=Xr; yr=Yr; zr=Zr;
   set(ax,'DataAspectRatioMode','auto');
else % "nearly" tight limits (much faster than "axis tight")
   w=max(w)*ar/2; ip=max([ip;0])*ar/2;
   xr=[min([p1(:,1)-ip(1);p2(:,1)-w(1)]),max([p1(:,1)+ip(1);p2(:,1)+w(1)])];
   yr=[min([p1(:,2)-ip(2);p2(:,2)-w(2)]),max([p1(:,2)+ip(2);p2(:,2)+w(2)])];
   zr=[min([p1(:,3)-ip(3);p2(:,3)-w(3)]),max([p1(:,3)+ip(3);p2(:,3)+w(3)])];
   if restore
      xr=[min(xr(1),Xr(1)),max(xr(2),Xr(2))];
      yr=[min(yr(1),Yr(1)),max(yr(2),Yr(2))];
      zr=[min(zr(1),Zr(1)),max(zr(2),Zr(2))];
   else, set(ax,'nextplot','replace'); end
end
azel=get(ax,'view');
if azel(2)==90, renderer='ZBuffer'; else, renderer='OpenGL'; end
set(fig,'Renderer',renderer);
set(ax,'LineStyleOrder',L,'ColorOrder',C,'DefaultLineTag',LT,...
   'DefaultSurfaceTag',ST,'DefaultSurfaceEdgeColor',EC,...
   'xlim',xr,'ylim',yr,'zlim',zr,'clim',get(ax,'CLim'));
if c1==3, set(ax,'CameraViewAngle',get(ax,'CameraViewAngle'),...
   'PlotBoxAspectRatio',get(ax,'PlotBoxAspectRatio'));
end
if nargout, hn=[H1(:);H2(:);H3(:)]; end

%==========================================================================
% Update
function H=LocalUpdate(fig,ax,ud,sf,flag)
p1=ud(:,1:3); p2=ud(:,4:6); c=ud(:,7:9); a=ud(:,13);
w=sf(1)*ud(:,10); h=sf(2)*ud(:,11); ip=sf(3)*ud(:,12);
H=get(ax,'children'); tag=get(H,'tag'); type=get(H,'type');
delete(H(strcmp(tag,'arrow3') & strcmp(type,'surface')));
set(fig,'nextplot','add'); set(ax,'nextplot','add');
if flag, map=colormap;               % update colors
   M=(p1-p2); M=sqrt(sum(M.*M,2)); minM=min(M); maxM=max(M);
   H1=H(strcmp(tag,'arrow3') & strcmp(type,'line'));
   for i=1:length(H1)
      x=get(H1(i),'xdata'); y=get(H1(i),'ydata'); z=get(H1(i),'zdata');
      if length(x)>2                 % multiple lines
         warning(sprintf(['Cannot perform magnitude coloring ',...
            'on lines that \nwere drawn with a single color, ',...
            'linestyle, and linewidth'])), break
      end
      M=sqrt((x(1)-x(2))^2+(y(1)-y(2))^2+(z(1)-z(2))^2);
      c=interp1(linspace(minM,maxM,size(map,1)),map,M);
      c(c<0)=0; c(c>1)=1; set(H1(i),'color',c);
   end
   H=arrow3(p1,p2,'|0',w,h,ip,a); H=[H1(:);H(:)];
else                                 % update surfaces
   set(ax,'ColorOrder',c); global ColorOrder, ColorOrder=[];
   H=arrow3(p1,p2,'o0',w,h,ip,a);
end
set(ax,'nextplot','replace');

%==========================================================================
% SetSurface
function LocalSetSurface(xys,xs,ys,dx,dy,xr,yr,x,y,z,a,c,H,n,m,oldver)
if xys
   x=x*dx+xr(1); y=y*dy+yr(1);
   if xs, x=10.^x; end
   if ys, y=10.^y; end
end
cd=zeros(n,m,3); cd(:,:,1)=c(1); cd(:,:,2)=c(2); cd(:,:,3)=c(3);
if oldver
   set(H,'XData',x,'YData',y,'ZData',z,'CData',cd);
else
   set(H,'XData',x,'YData',y,'ZData',z,'CData',cd,'FaceAlpha',a);
end

%==========================================================================
% ColorTable
function [vc,cn]=LocalColorTable(n,beta)
vc='kymcrgbadefhijlnpqstuvzw';       % valid color codes
%   k               y               m               c
cn=[0.00,0.00,0.00; 1.00,1.00,0.00; 1.00,0.00,1.00; 0.00,1.00,1.00;
%   r               g               b               a
    1.00,0.00,0.00; 0.00,1.00,0.00; 0.00,0.00,1.00; 0.00,0.70,0.00;
%   d               e               f               h
    0.40,0.40,0.40; 0.00,0.40,0.00; 0.90,0.00,0.40; 1.00,0.80,0.00;
%   i               j               l               n
    0.00,0.00,0.70; 0.20,0.80,0.50; 0.80,0.40,0.80; 0.50,0.20,0.00;
%   p               q               s               t
    1.00,0.40,0.60; 1.00,0.40,0.00; 0.00,0.80,1.00; 0.80,0.40,0.00;
%   u               v               z               w
    0.70,0.00,0.00; 0.60,0.00,1.00; 0.60,0.60,0.60; 1.00,1.00,1.00;];
if n, clf reset                      % plot color table
   name={'black','yellow','magenta','cyan',...
      'red','green','blue','apple green',...
      'dark gray','evergreen','fuchsia','honey',...
      'indigo','jade','lilac','nutbrown',...
      'pink','kumquat','sky blue','tan',...
      'umber','violet','zinc','white'};
   c=['yhtn';'gjae';'csbi';'plmv';'frqu';'wzdk'];
   set(gcf,'DefaultAxesXTick',[],'DefaultAxesYTick',[],...
      'DefaultAxesXTickLabel',[],'DefaultAxesYTickLabel',[],...
      'DefaultAxesXLim',[0,0.75],'DefaultAxesYLim',[0,0.75],...
      'DefaultRectangleEdgeColor','none');
   for i=1:24
      subplot(4,6,i); j=find(vc==c(i)); title(name{j});
      dark=LocalBrighten(cn(j,:),-beta);
      light=LocalBrighten(cn(j,:),beta);
      rectangle('Position',[0,0.00,0.75,0.25],'FaceColor',dark);
      rectangle('Position',[0,0.25,0.75,0.25],'FaceColor',cn(j,:));
      rectangle('Position',[0,0.50,0.75,0.25],'FaceColor',light);
      rectangle('Position',[0,0.00,0.75,0.75],'EdgeColor','k');
      if rem(i,6)==1
         set(gca,'YTickLabel',{'dark','normal','light'},...
            'YTick',[0.125,0.375,0.625]);
         if i==19
            text(0,-0.25,['{\bf\itARROW3}  Named Color Table  ',...
                  '( \beta = ',num2str(beta),' )']);
         end
      end
   end
end

%==========================================================================
% ColorMap
function [C,failed]=LocalColorMap(c,vc,cn,beta)
n=length(c); failed=0; C=zeros(n,3); i=1; j=1;
while 1
   if isempty(find([vc,'_^']==c(i))), failed=1; break, end
   if sum('_^'==c(i))
      if i+1>n, failed=1; break, end
      if ~sum(vc==c(i+1)), failed=1; break, end
      cc=cn(find(vc==c(i+1)),:); gamma=beta;
      if c(i)=='_', gamma=-beta; end
      C(j,:)=LocalBrighten(cc,gamma); i=i+2;
   else, C(j,:)=cn(find(vc==c(i)),:); i=i+1; end
   if i>n, break, end, j=j+1;
end, if n>j, C(j+1:n,:)=[]; end

%==========================================================================
% Brighten
function C=LocalBrighten(c,beta)
C=c.^((1-min(1-sqrt(eps),abs(beta)))^sign(beta));

%==========================================================================
% Repmat
function B = LocalRepmat(A,siz)
if length(A)==1, B(prod(siz))=A; B(:)=A; B=reshape(B,siz);
else, [m,n]=size(A); mind=(1:m)'; nind=(1:n)';
   mind=mind(:,ones(1,siz(1))); nind=nind(:,ones(1,siz(2)));
   B=A(mind,nind);
end

%==========================================================================
% Generate valid value for color, linestyle and linewidth
function [c,ls,lw]=LocalValidateCLSW(s)
if nargin<1, c='k'; ls='-'; lw=0.5;
else
   % identify linestyle
   if findstr(s,'--'), ls='--'; s=strrep(s,'--','');
   elseif findstr(s,'-.'), ls='-.'; s=strrep(s,'-.','');
   elseif findstr(s,'-'), ls='-'; s=strrep(s,'-','');
   elseif findstr(s,':'), ls=':'; s=strrep(s,':','');
   elseif findstr(s,'*'), ls='*'; s=strrep(s,'*','');
   else, ls='-'; end

   % identify linewidth
   tmp=double(s);
   tmp=find(tmp>45 & tmp<58);
   if length(tmp)
      if any(s(tmp)=='/'), lw='/'; else, lw=str2num(s(tmp)); end
      s(tmp)='';
   else, lw=0.5; end

   % identify color
   if length(s), s=lower(s);
      if length(s)>1, c=s(1:2);
      else, c=s(1); end
   else, c='k'; end
end


function S = sum(disc)
%SUM    Integration functional for TRIGCOLLOC.

% Copyright 2017 by The University of Oxford and The Chebfun Developers.
% See http://www.chebfun.org/ for Chebfun information.

[ignored, S] = functionPoints(disc);

end


function varargout = process_decoding_maxcorr( varargin )
% PROCESS_DECODING_MAXCORR: Decoding of multiple conditions using max-correlation

% @=============================================================================
% This function is part of the Brainstorm software:
% https://neuroimage.usc.edu/brainstorm
%
% Copyright (c) University of Southern California & McGill University
% This software is distributed under the terms of the GNU General Public License
% as published by the Free Software Foundation. Further details on the GPLv3
% license can be found at http://www.gnu.org/copyleft/gpl.html.
%
% FOR RESEARCH PURPOSES ONLY. THE SOFTWARE IS PROVIDED "AS IS," AND THE
% UNIVERSITY OF SOUTHERN CALIFORNIA AND ITS COLLABORATORS DO NOT MAKE ANY
% WARRANTY, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO WARRANTIES OF
% MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, NOR DO THEY ASSUME ANY
% LIABILITY OR RESPONSIBILITY FOR THE USE OF THIS SOFTWARE.
%
% For more information type "brainstorm license" at command prompt.
% =============================================================================@
%
% Authors: Martin Cousineau, 2019; Dimitrios Pantazis, 2019

eval(macro_method);
end

%% ===== GET DESCRIPTION =====
function sProcess = GetDescription() %#ok<DEFNU>
    % Description the process
    sProcess.Comment     = 'Max-correlation decoding';
    sProcess.Category    = 'Custom';
    sProcess.SubGroup    = 'Decoding';
    sProcess.Index       = 702;
    sProcess.Description = 'https://neuroimage.usc.edu/brainstorm/Tutorials/Decoding';
    % Definition of the input accepted by this process
    sProcess.InputTypes  = {'data'};
    sProcess.OutputTypes = {'matrix'};
    sProcess.nInputs     = 1;
    sProcess.nMinFiles   = 2;

    % Definition of the options
    sProcess.options.description.Comment = ['Apply max-correlation classifier on MEG trials.
' ...
                                            'Uses trial subaverages and permutations.

'];
    sProcess.options.description.Type    = 'label';
    % === Sensor types
    sProcess.options.sensortypes.Comment = 'Sensor types or names (empty=all): ';
    sProcess.options.sensortypes.Type    = 'text';
    sProcess.options.sensortypes.Value   = 'MEG';
    % === lowpass filtering
    sProcess.options.lowpass.Comment = 'Low-pass cutoff frequency (0=disabled): ';
    sProcess.options.lowpass.Type    = 'value';
    sProcess.options.lowpass.Value   = {30,'Hz',2};
    % === number of permutations
    sProcess.options.num_permutations.Comment = 'Number of permutations: ';
    sProcess.options.num_permutations.Type    = 'value';
    sProcess.options.num_permutations.Value   = {50,'',0};
    % === trial bin size for sub-averaging
    sProcess.options.kfold.Comment = 'Number of folds: ';
    sProcess.options.kfold.Type    = 'value';
    sProcess.options.kfold.Value   = {5,'',0};
    % === decoding method
    sProcess.options.method.Comment = {'Pairwise', 'Temporal generalization', 'Multiclass', 'Decoding method:'};
    sProcess.options.method.Type    = 'radio_line';
    sProcess.options.method.Value   = 1;
    % === decoding model
    sProcess.options.model.Comment = 'Decoding model: ';
    sProcess.options.model.Type    = 'text';
    sProcess.options.model.Value   = 'maxcorr';
    sProcess.options.model.Hidden  = 1;
end

%% ===== FORMAT COMMENT =====
function Comment = FormatComment(sProcess) %#ok<DEFNU>
    Comment = sProcess.Comment;
end

%% ===== RUN =====
function OutputFiles = Run(sProcess, sInputs) %#ok<DEFNU>
    % Call mother process with actual implementation
    OutputFiles = process_decoding_svm('Run', sProcess, sInputs);
end


function [ReactionAbundance,ReactionPresence] = fastCalculateReactionAbundance(abundancePath, modelPath, rxnsList, numWorkers)
% Part of the Microbiome Modeling Toolbox. This function calculates and
% plots the total abundance of reactions of interest in a given microbiome
% sample based on the strain-level composition.
% Reaction presence or absence in each strain is derived from the reaction content
% of the respective AGORA model.
%
% USAGE
%
%    ReactionAbundance = fastCalculateReactionAbundance(abundancePath, modelPath, rxnsList, numWorkers)
%
% INPUTS:
%    abundancePath:          Path to the .csv file with the abundance data.
%                            Example: 'cobratoolbox/papers/018_microbiomeModelingToolbox/examples/normCoverage.csv'
%    modelPath:              Folder containing the strain-specific AGORA models
% OPTIONAL INPUTS:
%    rxnsList:               List of reactions for which the abundance
%                            should be calculated (if left empty: all
%                            reactions in all models)
%    numWorkers:             Number of workers used for parallel pool. If
%                            left empty, the parallel pool will not be
%                            started. Parallellization is recommended if
%                            all reactions are computed.
%
% OUTPUT:
%    ReactionAbundance       Table with total abundance for each microbiome
%                            and reaction
%    ReactionPresence:       Table with absolute reaction presence for
%                            all reactions in all microbiome samples
%
% .. Author: - Almut Heinken, 04/2021

% read the csv file with the abundance data
abundance = readInputTableForPipeline(abundancePath);
if isnumeric(abundance{2, 1})
    abundance(:, 1) = [];
end

% load the models
for i = 2:size(abundance, 1)
    model = readCbModel([modelPath filesep abundance{i, 1} '.mat']);
    modelsList{i, 1} = model;
end

if ~exist('rxnsList', 'var') || isempty(rxnsList)  % define reaction list if not entered
    fprintf('No reaction list entered. Abundances will be calculated for all reactions in all models. \n')
    % get model list from abundance input file
    for i = 2:size(abundance, 1)
        model = modelsList{i, 1};
        rxnsList = vertcat(model.rxns, rxnsList);
    end
    rxnsList = unique(rxnsList);
end

% load the models found in the individuals and extract which reactions are
% in which model
for i = 2:size(abundance, 1)
    model = modelsList{i, 1};
    ReactionPresenceInModels{i, 1} = abundance{i, 1};
    for j = 1:length(rxnsList)
        ReactionPresenceInModels{1, j + 1} = rxnsList{j};
        if ~isempty(find(ismember(model.rxns, rxnsList{j})))
            ReactionPresenceInModels{i, j + 1} = '1';
        else
            ReactionPresenceInModels{i, j + 1} = '0';
        end
    end
end
ReactionPresenceInModels{1,1}='Strains';

% prepare table for the total abundance
ReactionAbundance = {};
for i = 2:size(abundance, 2)
    ReactionAbundance{i, 1} = abundance{1, i};
end

% use parallel pool if workers specified as input
if exist('numWorkers', 'var') && numWorkers > 0
    poolobj = gcp('nocreate');
    if isempty(poolobj)
        parpool(numWorkers)
    end
end

clear abundance
clear modelsList

totalAbun={};
parfor i = 2:size(ReactionAbundance, 1)
    % reload the file to avoid running out of memory
    abundance = readInputTableForPipeline(abundancePath);
    if isnumeric(abundance{2, 1})
        abundance(:, 1) = [];
    end

    % temporarily store reaction abundances
    totalAbun{i} = zeros(length(rxnsList), 1);

    for j = 2:size(abundance, 1)
        % find all reactions present in the strain
        presentRxns = find(strcmp(ReactionPresenceInModels(j,2:end),'1'));

        for k = 1:length(presentRxns)
            % summarize total abundance
            if contains(version,'(R202') % for Matlab R2020a and newer
                totalAbun{i}(presentRxns(k),1) = totalAbun{i}(presentRxns(k),1) + abundance{j,i};
            else
                totalAbun{i}(presentRxns(k),1) = totalAbun{i}(presentRxns(k),1) + str2double(abundance{j,i});
            end
        end
    end
end

for i = 1:length(rxnsList)
    ReactionAbundance{1, i + 1} = rxnsList{i};
end

% collect the temporarily stored abundances to put together the table
for i = 2:size(ReactionAbundance, 1)
    ReactionAbundance(i,2:end) = num2cell(totalAbun{i});
end

% export the absolute reaction presence
tol=0.0000001;

ReactionPresence = ReactionAbundance;
for i=2:size(ReactionPresence,1)
    for j=2:size(ReactionPresence,2)
        if contains(version,'(R202') % for Matlab R2020a and newer
            val = ReactionPresence{i,j};
        else
            val = str2double(ReactionPresence{i,j});
        end
        if val > tol
            ReactionPresence{i,j} = 1;
        else
            ReactionPresence{i,j} = 0;
        end
    end
end

end


function subNo = nodesSub(nodeNo,wt)

if(any(nodeNo>numel(wt.nodes)))
   error('%s: Invalid node index range. Number of nodes is %d.\n',upper(mfilename),numel(wt.nodes));
end

nodeNoa = cellfun(@(nEl) nEl.a,wt.nodes(nodeNo),'UniformOutput',0);
nodeNoUps = nodesFiltUps(nodeNo,wt);

nodesCount = numel(nodeNo);
subNo = cell(1,nodesCount);
for ii=1:nodesCount
   subNo{ii} = nodeNoUps(ii)*nodeNoa{ii};
end


function [sFile, ChannelMat] = in_fopen_itab(DataFile)
% IN_FOPEN_ITAB: Open a ITAB raw MEG file.
%
% USAGE:  [sFile, ChannelMat] = in_fopen_itab(DataFile)

% @=============================================================================
% This function is part of the Brainstorm software:
% https://neuroimage.usc.edu/brainstorm
%
% Copyright (c) University of Southern California & McGill University
% This software is distributed under the terms of the GNU General Public License
% as published by the Free Software Foundation. Further details on the GPLv3
% license can be found at http://www.gnu.org/copyleft/gpl.html.
%
% FOR RESEARCH PURPOSES ONLY. THE SOFTWARE IS PROVIDED "AS IS," AND THE
% UNIVERSITY OF SOUTHERN CALIFORNIA AND ITS COLLABORATORS DO NOT MAKE ANY
% WARRANTY, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO WARRANTIES OF
% MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, NOR DO THEY ASSUME ANY
% LIABILITY OR RESPONSIBILITY FOR THE USE OF THIS SOFTWARE.
%
% For more information type "brainstorm license" at command prompt.
% =============================================================================@
%
% Authors: Francois Tadel, 2015-2018

%% ===== READ HEADER =====
% Locate header file
HeaderFile = [DataFile '.mhd'];
if ~file_exist(HeaderFile)
    error(['Header file was not found: ' HeaderFile]);
end
% Read header
hdr = read_itab_mhd(HeaderFile);
% Get endianness
switch (hdr.data_type)
    case {0,1,2},  byteorder = 'b';
    case {3,4,5},  byteorder = 'l';
    otherwise,     error('Data type not supported.');
end

%% ===== CREATE CHANNEL FILE =====
ChannelMat = db_template('channelmat');
ChannelMat.Comment = 'ITAB channels';
ChannelMat.Channel = repmat(db_template('channeldesc'), [1, hdr.nchan]);
iChannels = 1:hdr.nchan;
% For each channel
for i = 1:hdr.nchan
    ChannelMat.Channel(i).Name    = hdr.ch(iChannels(i)).label;
    ChannelMat.Channel(i).Comment = '';
    % Position
    for iCoil = 1:hdr.ch(iChannels(i)).ncoils
        ChannelMat.Channel(i).Loc(:,iCoil)    = hdr.ch(iChannels(i)).position(iCoil).r_s' ./ 1000;
        ChannelMat.Channel(i).Orient(:,iCoil) = hdr.ch(iChannels(i)).position(iCoil).u_s' ./ 1000;
        ChannelMat.Channel(i).Weight(1,iCoil) = hdr.ch(iChannels(i)).wgt(iCoil);
    end
    % Type
    switch (hdr.ch(iChannels(i)).type)
        case 1,    ChannelMat.Channel(i).Type = 'EEG';
        case 2,    ChannelMat.Channel(i).Type = 'MEG';
        case 4,    ChannelMat.Channel(i).Type = 'EEG';
        case 8,    ChannelMat.Channel(i).Type = 'MEG REF';
        case 16,   ChannelMat.Channel(i).Type = 'Misc';     %AUX
        case 32,   ChannelMat.Channel(i).Type = 'Misc';     %PARAM
        case 64,   ChannelMat.Channel(i).Type = 'Misc';     %DIGIT
        case 128,  ChannelMat.Channel(i).Type = 'Misc';     %FLAG
        otherwise, ChannelMat.Channel(i).Type = 'Misc';     %Misc
    end

    % Type: everything below the "_"
%     iUnder = find(ChannelMat.Channel(i).Name == '_');
%     if (length(iUnder) == 1) && (iUnder > 1)
%         Type = ChannelMat.Channel(i).Name(1:iUnder-1);
%         % Rename some known types
%         switch (Type)
%             case 'MAG',   ChannelMat.Channel(i).Type = 'MEG';
%             case 'REF',   ChannelMat.Channel(i).Type = 'MEG REF';
%             case 'ELE',   ChannelMat.Channel(i).Type = 'EEG';
%             otherwise,    ChannelMat.Channel(i).Type = Type;
%         end
%     else
%         ChannelMat.Channel(i).Type = 'Misc';
%     end
end

% Channel flag
ChannelFlag = double([hdr.ch(iChannels).flag] == 0);
ChannelFlag(ChannelFlag == 0) = -1;

% %% ===== ADD MEG POSITIONS =====
% iChan = channel_find(ChannelMat.Channel, 'MEG');
% % Position / orientation
% if ~isempty(SensorDat)
%     % Split the matrix in sensor and
%     LocAll    = SensorDat(1:size(SensorDat,1)/2, :);
%     OrientAll = SensorDat((size(SensorDat,1)/2+1):end, :);
%     % Normalize orientation vector
%     OrientAll = bst_bsxfun(@rdivide, OrientAll, sqrt(sum(OrientAll.^2,2)));
%     % Apply to every sensor
%     for i = 1:length(iChan)
%         ind = str2double(ChannelMat.Channel(i).Name);
%         if ~isempty(ind) && ~isnan(ind) && (ind <= size(LocAll,1))
%             ChannelMat.Channel(i).Loc    = LocAll(ind,:)' ./ 1000;
%             ChannelMat.Channel(i).Orient = OrientAll(ind,:)';
%         end
%     end
% end
% % Add definition of sensors
% ChannelMat.Channel = ctf_add_coil_defs(ChannelMat.Channel, 'KRISS');

%% ===== EXTRA HEAD POINTS =====
% Get extra head points
HpLoc = hdr.marker;
iGood = find(~all(HpLoc == 0, 1));
HpLoc = HpLoc(:,iGood) ./ 1000;
nPoints = size(HpLoc,2);
% Save points in channel structure
if ~isempty(HpLoc) && (nPoints > 3)
    % All positions
    ChannelMat.HeadPoints.Loc = HpLoc;
    % All types and labels
    ChannelMat.HeadPoints.Type  = repmat({'EXTRA'}, 1, nPoints);
    ChannelMat.HeadPoints.Label = repmat({'EXTRA'}, 1, nPoints);
    % First three points: NAS/LPA/RPA
    ChannelMat.HeadPoints.Label{1} = 'NAS';
    ChannelMat.HeadPoints.Label{2} = 'RPA';
    ChannelMat.HeadPoints.Label{3} = 'LPA';
    ChannelMat.HeadPoints.Type{1} = 'CARDINAL';
    ChannelMat.HeadPoints.Type{2} = 'CARDINAL';
    ChannelMat.HeadPoints.Type{3} = 'CARDINAL';
    % Force re-alignment on the new set of NAS/LPA/RPA (switch from coil-based to SCS anatomical-based coordinate system)
    ChannelMat = channel_detect_type(ChannelMat, 1, 0);
end

%% ===== CREATE BRAINSTORM SFILE STRUCTURE =====
% Initialize returned file structure
sFile = db_template('sfile');
% Add information read from header
sFile.byteorder  = byteorder;
sFile.filename   = DataFile;
sFile.format     = 'ITAB';
sFile.device     = 'ITAB MEG';
sFile.header     = hdr;
% Comment: short filename
[tmp__, sFile.comment, tmp__] = bst_fileparts(DataFile);
% Consider that the sampling rate of the file is the sampling rate of the first signal
sFile.prop.sfreq   = hdr.smpfq;
sFile.prop.times   = [0, hdr.ntpdata - 1] ./ sFile.prop.sfreq;
sFile.prop.nAvg    = 1;
% No info on bad channels
sFile.channelflag = ChannelFlag;
% Acquisition date
sFile.acq_date = str_date(hdr.date);

%% ===== EVENTS =====
if (hdr.nsmpl >= 1)
    % Get events
    allStart = [hdr.smpl(1:hdr.nsmpl).start];
    allType  = [hdr.smpl(1:hdr.nsmpl).type];
    uniqueType = unique(allType);
    % Initialize list of events
    events = repmat(db_template('event'), 1, length(uniqueType));
    % Format list
    for iEvt = 1:length(uniqueType)
        % Find list of occurences of this event
        iOcc = find(strcmpi(allType, uniqueType(iEvt)));
        % Fill events structure
        events(iEvt).label      = num2str(uniqueType(iEvt));
        events(iEvt).color      = [];
        events(iEvt).reactTimes = [];
        events(iEvt).select     = 1;
        events(iEvt).times      = allStart(iOcc) ./ sFile.prop.sfreq;  % Get time and samples  (considering that samples are zero-based)
        events(iEvt).epochs     = ones(1, length(events(iEvt).times));  % Epoch: set as 1 for all the occurrences
        events(iEvt).channels   = [];
        events(iEvt).notes      = [];
    end
    % Import this list
    sFile = import_events(sFile, [], events);
end


function show_particles(X, Y_k)

figure(1)
image(Y_k)
title('+++ Showing Particles +++')

hold on
plot(X(2,:), X(1,:), '.')
hold off

drawnow


function  [ id, type, rep, field, symm ] = mm_header_read ( input_unit )

%*****************************************************************************80
%
%% MM_HEADER_READ reads the header line from a Matrix Market file.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    28 April 2004
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer INPUT_UNIT, the input unit identifier.
%
%    Output, character ( len = 14 ) ID, the Matrix Market identifier.
%    This value must be '%%MatrixMarket'.
%
%    Output, character ( len = 6 ) TYPE, the Matrix Market type.
%    This value must be 'matrix'.
%
%    Output, character ( len = 10 ) REP, the Matrix Market 'representation'
%    indicator.  Possible values include:
%    'coordinate'   (for sparse data)
%    'array'        (for dense data)
%    'elemental'    (to be added)
%
%    Output, character ( len = 7 ) FIELD, the Matrix Market 'field'.
%    Possible values include:
%    'real'
%    'double'
%    'complex'
%    'integer'
%    'pattern'
%
%    Output, character ( len = 19 ) SYMM, the Matrix Market symmetry.
%    Possible values include:
%    'symmetric'
%    'hermitian'
%    'skew-symmetric'
%    'general'
%
  header = fgets ( input_unit );

  if ( header == -1 )
    fprintf ( 1, '\n' );
    fprintf ( 1, 'MM_HEADER_READ - Fatal error!\n' );
    fprintf ( 1, '  An empty header line was read.\n' );
    error ( 'MM_HEADER_READ - Fatal error!' )
  end

  [ id,    header ] = strtok ( header );
  [ type,  header ] = strtok ( header );
  [ rep,   header ] = strtok ( header );
  [ field, header ] = strtok ( header );
  [ symm,  header ] = strtok ( header );

  return
end


function test_approx_test02 ( )

%*****************************************************************************80
%
%% TEST_APPROX_TEST02 shows how P00_STORY can be called.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    12 August 2011
%
%  Author:
%
%    John Burkardt
%
  fprintf ( 1, '\n' );
  fprintf ( 1, 'TEST_APPROX_TEST02\n' );
  fprintf ( 1, '  P00_STORY prints the problem "story".\n' );

  prob_num = p00_prob_num ( );

  for prob = 1 : prob_num

    fprintf ( 1, '\n' );
    fprintf ( 1, '  Problem %d\n', prob );

    p00_story ( prob );

  end

  return
end


function res = nsamples(this)
% Method for getting the number of samples per trial
% FORMAT res = nsamples(this)
% _______________________________________________________________________
% Copyright (C) 2008-2012 Wellcome Trust Centre for Neuroimaging

% Vladimir Litvak
% $Id: nsamples.m 5025 2012-10-31 14:44:13Z vladimir $

res = this.Nsamples;


function str = print(obj, inputSizes, varargin)
%PRINT Print information about the DagNN object
%   PRINT(OBJ) displays a summary of the functions and parameters in the network.
%   STR = PRINT(OBJ) returns the summary as a string instead of printing it.
%
%   PRINT(OBJ, INPUTSIZES) where INPUTSIZES is a cell array of the type
%   {'input1nam', input1size, 'input2name', input2size, ...} prints
%   information using the specified size for each of the listed inputs.
%
%   PRINT(___, 'OPT', VAL, ...) accepts the following options:
%
%   `All`:: false
%      Display all the information below.
%
%   `Layers`:: '*'
%      Specify which layers to print. This can be either a list of
%      indexes, a cell array of array names, or the string '*', meaning
%      all layers.
%
%   `Parameters`:: '*'
%      Specify which parameters to print, similar to the option above.
%
%   `Variables`:: []
%      Specify which variables to print, similar to the option above.
%
%   `Dependencies`:: false
%      Whether to display the dependency (geometric transformation)
%      of each variables from each input.
%
%   `Format`:: 'ascii'
%      Choose between `ascii`, `latex`, `csv`, 'digraph', and `dot`.
%      The first three format print tables; `digraph` uses the plot function
%      for a `digraph` (supported in MATLAB>=R2015b) and the last one
%      prints a graph  in `dot` format. In case of zero outputs, it
%      attmepts to compile and visualise the dot graph using `dot` command
%      and `start` (Windows), `display` (Linux) or `open` (Mac OSX) on your system.
%      In the latter case, all variables and layers are included in the
%      graph, regardless of the other parameters.
%
%   `FigurePath`:: 'tempname.pdf'
%      Sets the path where any generated `dot` figure will be saved. Currently,
%      this is useful only in combination with the format `dot`.
%      By default, a unique temporary filename is used (`tempname`
%      is replaced with a `tempname()` call). The extension specifies the
%      output format (passed to dot as a `-Text` parameter).
%      If not extension provided, PDF used by default.
%      Additionally, stores the .dot file used to generate the figure to
%      the same location.
%
%    `dotArgs`:: ''
%       Additional dot arguments. E.g. '-Gsize="7"' to generate a smaller
%       output (for a review of the network structure etc.).
%
%   `MaxNumColumns`:: 18
%      Maximum number of columns in each table.
%
%   See also: DAGNN, DAGNN.GETVARSIZES().

if nargin > 1 && ischar(inputSizes)
  % called directly with options, skipping second argument
  varargin = {inputSizes, varargin{:}} ;
  inputSizes = {} ;
end

opts.all = false ;
opts.format = 'ascii' ;
opts.figurePath = 'tempname.pdf' ;
opts.dotArgs = '';
[opts, varargin] = vl_argparse(opts, varargin) ;

opts.layers = '*' ;
opts.parameters = [] ;
opts.variables = [] ;
if opts.all || nargin > 1
  opts.variables = '*' ;
end
if opts.all
  opts.parameters = '*' ;
end
opts.memory = true ;
opts.dependencies = opts.all ;
opts.maxNumColumns = 18 ;
opts = vl_argparse(opts, varargin) ;

if nargin == 1, inputSizes = {} ; end
varSizes = obj.getVarSizes(inputSizes) ;
paramSizes = cellfun(@size, {obj.params.value}, 'UniformOutput', false) ;
str = {''} ;

if strcmpi(opts.format, 'dot')
  str = printDot(obj, varSizes, paramSizes, opts) ;
  if nargout == 0
    displayDot(str, opts) ;
  end
  return ;
end

if strcmpi(opts.format,'digraph')
  str = printdigraph(obj, varSizes) ;
  return ;
end

if ~isempty(opts.layers)
  table = {'func', '-', 'type', 'inputs', 'outputs', 'params', 'pad', 'stride'} ;
  for l = select(obj, 'layers', opts.layers)
    layer = obj.layers(l) ;
    table{l+1,1} = layer.name ;
    table{l+1,2} = '-' ;
    table{l+1,3} = player(class(layer.block)) ;
    table{l+1,4} = strtrim(sprintf('%s ', layer.inputs{:})) ;
    table{l+1,5} = strtrim(sprintf('%s ', layer.outputs{:})) ;
    table{l+1,6} = strtrim(sprintf('%s ', layer.params{:})) ;
    if isprop(layer.block, 'pad')
      table{l+1,7} = pdims(layer.block.pad) ;
    else
      table{l+1,7} = 'n/a' ;
    end
    if isprop(layer.block, 'stride')
      table{l+1,8} = pdims(layer.block.stride) ;
    else
      table{l+1,8} = 'n/a' ;
    end
  end
  str{end+1} = printtable(opts, table') ;
  str{end+1} = sprintf('\n') ;
end

if ~isempty(opts.parameters)
  table = {'param', '-', 'dims', 'mem', 'fanout'} ;
  for v = select(obj, 'params', opts.parameters)
    table{v+1,1} = obj.params(v).name ;
    table{v+1,2} = '-' ;
    table{v+1,3} = pdims(paramSizes{v}) ;
    table{v+1,4} = pmem(prod(paramSizes{v}) * 4) ;
    table{v+1,5} = sprintf('%d',obj.params(v).fanout) ;
  end
  str{end+1} = printtable(opts, table') ;
  str{end+1} = sprintf('\n') ;
end

if ~isempty(opts.variables)
  table = {'var', '-', 'dims', 'mem', 'fanin', 'fanout'} ;
  for v = select(obj, 'vars', opts.variables)
    table{v+1,1} = obj.vars(v).name ;
    table{v+1,2} = '-' ;
    table{v+1,3} = pdims(varSizes{v}) ;
    table{v+1,4} = pmem(prod(varSizes{v}) * 4) ;
    table{v+1,5} = sprintf('%d',obj.vars(v).fanin) ;
    table{v+1,6} = sprintf('%d',obj.vars(v).fanout) ;
  end
  str{end+1} = printtable(opts, table') ;
  str{end+1} = sprintf('\n') ;
end

if opts.memory
  paramMem = sum(cellfun(@getMem, paramSizes)) ;
  varMem = sum(cellfun(@getMem, varSizes)) ;
  table = {'params', 'vars', 'total'} ;
  table{2,1} = pmem(paramMem) ;
  table{2,2} = pmem(varMem) ;
  table{2,3} = pmem(paramMem + varMem) ;
  str{end+1} = printtable(opts, table') ;
  str{end+1} = sprintf('\n') ;
end

if opts.dependencies
  % print variable to input dependencies
  inputs = obj.getInputs() ;
  rfs = obj.getVarReceptiveFields(inputs) ;
  for i = 1:size(rfs,1)
    table = {sprintf('rf in ''%s''', inputs{i}), '-', 'size', 'stride', 'offset'} ;
    for v = 1:size(rfs,2)
      table{v+1,1} = obj.vars(v).name ;
      table{v+1,2} = '-' ;
      table{v+1,3} = pdims(rfs(i,v).size) ;
      table{v+1,4} = pdims(rfs(i,v).stride) ;
      table{v+1,5} = pdims(rfs(i,v).offset) ;
    end
    str{end+1} = printtable(opts, table') ;
    str{end+1} = sprintf('\n') ;
  end
end

% finish
str = horzcat(str{:}) ;
if nargout == 0,
  fprintf('%s',str) ;
  clear str ;
end

end

% -------------------------------------------------------------------------
function str = printtable(opts, table)
% -------------------------------------------------------------------------
str = {''} ;
for i=2:opts.maxNumColumns:size(table,2)
  sel = i:min(i+opts.maxNumColumns-1,size(table,2)) ;
  str{end+1} = printtablechunk(opts, table(:, [1 sel])) ;
  str{end+1} = sprintf('\n') ;
end
str = horzcat(str{:}) ;
end

% -------------------------------------------------------------------------
function str = printtablechunk(opts, table)
% -------------------------------------------------------------------------
str = {''} ;
switch opts.format
  case 'ascii'
    sizes = max(cellfun(@(x) numel(x), table),[],1) ;
    for i=1:size(table,1)
      for j=1:size(table,2)
        s = table{i,j} ;
        fmt = sprintf('%%%ds|', sizes(j)) ;
        if isequal(s,'-'), s=repmat('-', 1, sizes(j)) ; end
        str{end+1} = sprintf(fmt, s) ;
      end
      str{end+1} = sprintf('\n') ;
    end

  case 'latex'
    sizes = max(cellfun(@(x) numel(x), table),[],1) ;
    str{end+1} = sprintf('\\begin{tabular}{%s}\n', repmat('c', 1, numel(sizes))) ;
    for i=1:size(table,1)
      if isequal(table{i,1},'-'), str{end+1} = sprintf('\\hline\n') ; continue ; end
      for j=1:size(table,2)
        s = table{i,j} ;
        fmt = sprintf('%%%ds', sizes(j)) ;
        str{end+1} = sprintf(fmt, latexesc(s)) ;
        if j<size(table,2), str{end+1} = sprintf('&') ; end
      end
      str{end+1} = sprintf('\\\\\n') ;
    end
    str{end+1}= sprintf('\\end{tabular}\n') ;

  case 'csv'
    sizes = max(cellfun(@(x) numel(x), table),[],1) + 2 ;
    for i=1:size(table,1)
      if isequal(table{i,1},'-'), continue ; end
      for j=1:size(table,2)
        s = table{i,j} ;
        fmt = sprintf('%%%ds,', sizes(j)) ;
        str{end+1} = sprintf(fmt, ['"' s '"']) ;
      end
      str{end+1} = sprintf('\n') ;
    end

  otherwise
    error('Uknown format %s', opts.format) ;
end
str = horzcat(str{:}) ;
end

% -------------------------------------------------------------------------
function s = latexesc(s)
% -------------------------------------------------------------------------
s = strrep(s,'\','\\') ;
s = strrep(s,'_','\char`_') ;
end

% -------------------------------------------------------------------------
function s = pmem(x)
% -------------------------------------------------------------------------
if isnan(x),       s = 'NaN' ;
elseif x < 1024^1, s = sprintf('%.0fB', x) ;
elseif x < 1024^2, s = sprintf('%.0fKB', x / 1024) ;
elseif x < 1024^3, s = sprintf('%.0fMB', x / 1024^2) ;
else               s = sprintf('%.0fGB', x / 1024^3) ;
end
end

% -------------------------------------------------------------------------
function s = pdims(x)
% -------------------------------------------------------------------------
if all(isnan(x))
  s = 'n/a' ;
  return ;
end
if all(x==x(1))
  s = sprintf('%.4g', x(1)) ;
else
  s = sprintf('%.4gx', x(:)) ;
  s(end) = [] ;
end
end

% -------------------------------------------------------------------------
function x = player(x)
% -------------------------------------------------------------------------
if numel(x) < 7, return ; end
if x(1:6) == 'dagnn.', x = x(7:end) ; end
end

% -------------------------------------------------------------------------
function m = getMem(sz)
% -------------------------------------------------------------------------
m = prod(sz) * 4 ;
if isnan(m), m = 0 ; end
end

% -------------------------------------------------------------------------
function sel = select(obj, type, pattern)
% -------------------------------------------------------------------------
if isnumeric(pattern)
  sel = pattern ;
else
  if isstr(pattern)
    if strcmp(pattern, '*')
      sel = 1:numel(obj.(type)) ;
      return ;
    else
      pattern = {pattern} ;
    end
  end
  sel = find(cellfun(@(x) any(strcmp(x, pattern)), {obj.(type).name})) ;
end
end

% -------------------------------------------------------------------------
function h = printdigraph(net, varSizes)
% -------------------------------------------------------------------------
if exist('digraph') ~= 2
  error('MATLAB graph support not present.');
end
s = []; t = []; w = [];
varsNames = {net.vars.name};
layerNames = {net.layers.name};
numVars = numel(varsNames);
spatSize = cellfun(@(vs) vs(1), varSizes);
spatSize(isnan(spatSize)) = 1;
varChannels = cellfun(@(vs) vs(3), varSizes);
varChannels(isnan(varChannels)) = 0;

for li = 1:numel(layerNames)
  l = net.layers(li); lidx = numVars + li;
  s = [s l.inputIndexes];
  t = [t lidx*ones(1, numel(l.inputIndexes))];
  w = [w spatSize(l.inputIndexes)];
  s = [s lidx*ones(1, numel(l.outputIndexes))];
  t = [t l.outputIndexes];
  w = [w spatSize(l.outputIndexes)];
end
nodeNames = [varsNames, layerNames];
g = digraph(s, t, w);
lw = 5*g.Edges.Weight/max([g.Edges.Weight; 5]);
h = plot(g, 'NodeLabel', nodeNames, 'LineWidth', lw);
highlight(h, numVars+1:numVars+numel(layerNames), 'MarkerSize', 8, 'Marker', 's');
highlight(h, 1:numVars, 'MarkerSize', 5, 'Marker', 's');
cmap = copper;
varNvalRel = varChannels./max(varChannels);
for vi = 1:numel(varChannels)
  highlight(h, vi, 'NodeColor', cmap(max(round(varNvalRel(vi)*64), 1),:));
end
axis off;
layout(h, 'force');
end

% -------------------------------------------------------------------------
function str = printDot(net, varSizes, paramSizes, otps)
% -------------------------------------------------------------------------
str = {} ;
str{end+1} = sprintf('digraph DagNN {\n\tfontsize=12\n') ;
font_style = 'fontsize=12 fontname="helvetica"';

for v = 1:numel(net.vars)
  label=sprintf('{{%s} | {%s | %s }}', net.vars(v).name, pdims(varSizes{v}), pmem(4*prod(varSizes{v}))) ;
  str{end+1} = sprintf('\tvar_%s [label="%s" shape=record style="solid,rounded,filled" color=cornsilk4 fillcolor=beige %s ]\n', ...
    net.vars(v).name, label, font_style) ;
end

for p = 1:numel(net.params)
  label=sprintf('{{%s} | {%s | %s }}', net.params(p).name, pdims(paramSizes{p}), pmem(4*prod(paramSizes{p}))) ;
  str{end+1} = sprintf('\tpar_%s [label="%s" shape=record style="solid,rounded,filled" color=lightsteelblue4 fillcolor=lightsteelblue %s ]\n', ...
    net.params(p).name, label, font_style) ;
end

for l = 1:numel(net.layers)
  label = sprintf('{ %s | %s }', net.layers(l).name, class(net.layers(l).block)) ;
  str{end+1} = sprintf('\t%s [label="%s" shape=record style="bold,filled" color="tomato4" fillcolor="tomato" %s ]\n', ...
    net.layers(l).name, label, font_style) ;
  for i = 1:numel(net.layers(l).inputs)
    str{end+1} = sprintf('\tvar_%s->%s [weight=10]\n', ...
      net.layers(l).inputs{i}, ...
      net.layers(l).name) ;
  end
  for o = 1:numel(net.layers(l).outputs)
    str{end+1} = sprintf('\t%s->var_%s [weight=10]\n', ...
      net.layers(l).name, ...
      net.layers(l).outputs{o}) ;
  end
  for p = 1:numel(net.layers(l).params)
    str{end+1} = sprintf('\tpar_%s->%s [weight=1]\n', ...
      net.layers(l).params{p}, ...
      net.layers(l).name) ;
  end
end

str{end+1} = sprintf('}\n') ;
str = cat(2,str{:}) ;
end

% -------------------------------------------------------------------------
function displayDot(str, opts)
% -------------------------------------------------------------------------
%mwdot = fullfile(matlabroot, 'bin', computer('arch'), 'mwdot') ;
dotPaths = {'/opt/local/bin/dot', 'dot'} ;
if ismember(computer, {'PCWIN64', 'PCWIN'})
  winPath = 'c:\Program Files (x86)';
  dpath = dir(fullfile(winPath, 'Graphviz*'));
  if ~isempty(dpath)
    dotPaths = [{fullfile(winPath, dpath.name, 'bin', 'dot.exe')}, dotPaths];
  end
end
dotExe = '' ;
for i = 1:numel(dotPaths)
  [~,~,ext] = fileparts(dotPaths{i});
  if exist(dotPaths{i},'file') && ~strcmp(ext, '.m')
    dotExe = dotPaths{i} ;
    break;
  end
end
if isempty(dotExe)
  warning('Could not genereate a figure because the `dot` utility could not be found.') ;
  return ;
end

[path, figName, ext] = fileparts(opts.figurePath) ;

if isempty(ext), ext = '.pdf' ; end
if strcmp(figName, 'tempname')
  figName = tempname();
end
in = fullfile(path, [ figName, '.dot' ]) ;
out = fullfile(path, [ figName, ext ]) ;

f = fopen(in, 'w') ; fwrite(f, str) ; fclose(f) ;

cmd = sprintf('"%s" -T%s %s -o "%s" "%s"', dotExe, ext(2:end), ...
  opts.dotArgs, out, in) ;
[status, result] = system(cmd) ;
if status ~= 0
  error('Unable to run %s\n%s', cmd, result) ;
end
if ~isempty(strtrim(result))
  fprintf('Dot output:\n%s\n', result) ;
end

%f = fopen(out,'r') ; file=fread(f, 'char=>char')' ; fclose(f) ;
switch computer
  case {'PCWIN64', 'PCWIN'}
    system(sprintf('start "" "%s"', out)) ;
  case 'MACI64'
    system(sprintf('open "%s"', out)) ;
  case 'GLNXA64'
    system(sprintf('display "%s"', out)) ;
  otherwise
    fprintf('The figure saved at "%s"\n', out) ;
end
end


T_10 = readtable('/Users/lbasek/image-denoising-benchmark/results/final_results_sigma10.csv');
T_10(:,'WNNM') = [];
T_10(:,'NCSR') = [];

T_15 = readtable('/Users/lbasek/image-denoising-benchmark/results/final_results_sigma15.csv');
T_15(:,'WNNM') = [];
T_15(:,'NCSR') = [];

T_20 = readtable('/Users/lbasek/image-denoising-benchmark/results/final_results_sigma20.csv');
T_20(:,'WNNM') = [];
T_20(:,'NCSR') = [];

T_25 = readtable('/Users/lbasek/image-denoising-benchmark/results/final_results_sigma25.csv');
T_25(:,'WNNM') = [];
T_25(:,'NCSR') = [];

T_50 = readtable('/Users/lbasek/image-denoising-benchmark/results/final_results_sigma50.csv');
T_50(:,'WNNM') = [];
T_50(:,'NCSR') = [];

mse_row_10 = T_10(1,:);
mse_row_15 = T_15(1,:);
mse_row_20 = T_20(1,:);
mse_row_25 = T_25(1,:);
mse_row_50 = T_50(1,:);

x = [0,10,15,20,25,50];

bm3d_10 = mse_row_10{1,3};
bm3d_15 = mse_row_15{1,3};
bm3d_20 = mse_row_20{1,3};
bm3d_25 = mse_row_25{1,3};
bm3d_50 = mse_row_50{1,3};

ksvd_10 = mse_row_10{1,4};
ksvd_15 = mse_row_15{1,4};
ksvd_20 = mse_row_20{1,4};
ksvd_25 = mse_row_25{1,4};
ksvd_50 = mse_row_50{1,4};

foe_10 = mse_row_10{1,5};
foe_15 = mse_row_15{1,5};
foe_20 = mse_row_20{1,5};
foe_25 = mse_row_25{1,5};
foe_50 = mse_row_50{1,5};

epll_10 = mse_row_10{1,6};
epll_15 = mse_row_15{1,6};
epll_20 = mse_row_20{1,6};
epll_25 = mse_row_25{1,6};
epll_50 = mse_row_50{1,6};

y1 = [mse_row_10{1,2}, bm3d_10,bm3d_15,bm3d_20,bm3d_25,bm3d_50];
y2 = [mse_row_10{1,2}, ksvd_10,ksvd_15,ksvd_20,ksvd_25,ksvd_50];
y3 = [mse_row_10{1,2}, foe_10,foe_15,foe_20,foe_25,foe_50];
y4 = [mse_row_10{1,2}, epll_10,epll_15,epll_20,epll_25,epll_50];

h = figure;
hold on
h1 = plot(x,y1,'-s','MarkerSize',10);
h2 = plot(x,y2,'-s','MarkerSize',10);
h3 = plot(x,y3,'-s','MarkerSize',10);
h4 = plot(x,y4,'-s','MarkerSize',10);
hold off
set([h1,h2,h3,h4],'LineWidth',2)
ylabel('MSE')
xlabel('Noise Power')
legend('BM3D','KSVD','FOE','EPLL');
grid on
saveas(h,'./graphs/mse','png');