% [INPUT]
% ds = A structure representing the dataset.
% sn = A string representing the serial number of the result file.
% temp = A string representing the full path to the Excel spreadsheet used as template for the result file.
% out = A string representing the full path to the Excel spreadsheet to which the results are written, eventually replacing the previous ones.
% bw = An integer [21,252] representing the dimension of each rolling window (optional, default=252).
% bws = An integer [1,10] representing the number of steps between each rolling window (optional, default=10).
% fevd = A string representing the FEVD type used by the variance decomposition (optional, default='G'):
%   - 'G' for generalized FEVD;
%   - 'O' for orthogonal FEVD.
% lags = An integer [1,3] representing the number of lags of the VAR model used by the variance decomposition (optional, default=2).
% h = An integer [1,10] representing the prediction horizon used by the variance decomposition (optional, default=4).
% analyze = A boolean that indicates whether to analyse the results and display plots (optional, default=false).
%
% [OUTPUT]
% result = A structure representing the original dataset inclusive of intermediate and final calculations.
% stopped = A boolean that indicates whether the process has been stopped through user input.

function [result,stopped] = run_spillover(varargin)

    persistent ip;

    if (isempty(ip))
        ip = inputParser();
        ip.addRequired('ds',@(x)validateattributes(x,{'struct'},{'nonempty'}));
        ip.addRequired('sn',@(x)validateattributes(x,{'char'},{'nonempty' 'size' [1 NaN]}));
        ip.addRequired('temp',@(x)validateattributes(x,{'char'},{'nonempty' 'size' [1 NaN]}));
        ip.addRequired('out',@(x)validateattributes(x,{'char'},{'nonempty' 'size' [1 NaN]}));
        ip.addOptional('bw',252,@(x)validateattributes(x,{'double'},{'real' 'finite' 'integer' '>=' 21 '<=' 252 'scalar'}));
        ip.addOptional('bws',10,@(x)validateattributes(x,{'double'},{'real' 'finite' 'integer' '>=' 1 '<=' 10 'scalar'}));
        ip.addOptional('fevd','G',@(x)any(validatestring(x,{'G' 'O'})));
        ip.addOptional('lags',2,@(x)validateattributes(x,{'double'},{'real' 'finite' 'integer' '>=' 1 '<=' 3 'scalar'}));
        ip.addOptional('h',4,@(x)validateattributes(x,{'double'},{'real' 'finite' 'integer' '>=' 1 '<=' 10 'scalar'}));
        ip.addOptional('analyze',false,@(x)validateattributes(x,{'logical'},{'scalar'}));
    end

    ip.parse(varargin{:});

    ipr = ip.Results;
    ds = validate_dataset(ipr.ds,'Spillover');
    sn = ipr.sn;
    temp = validate_template(ipr.temp);
    out = validate_output(ipr.out);
    bw = ipr.bw;
    bws = ipr.bws;
    fevd = ipr.fevd;
    lags = ipr.lags;
    h = ipr.h;
    analyze = ipr.analyze;

    nargoutchk(1,2);

    [result,stopped] = run_spillover_internal(ds,sn,temp,out,bw,bws,fevd,lags,h,analyze);

end

function [result,stopped] = run_spillover_internal(ds,sn,temp,out,bw,bws,fevd,lags,h,analyze)

    result = [];
    stopped = false;
    e = [];

    indices = unique([1:bws:ds.T ds.T]);
    ds = initialize(ds,sn,bw,bws,indices,fevd,lags,h);

    rng(double(bitxor(uint16('T'),uint16('B'))));
    cleanup_1 = onCleanup(@()rng('default'));

    bar = waitbar(0,'Initializing spillover measures...','CreateCancelBtn',@(src,event)setappdata(gcbf(),'Stop',true));
    setappdata(bar,'Stop',false);
    cleanup_2 = onCleanup(@()delete(bar));

    pause(1);
    waitbar(0,bar,'Calculating spillover measures...');
    pause(1);

    try

        windows = extract_rolling_windows(ds.Returns,ds.BW);
        windows = windows(indices);
        windows_len = numel(windows);

        futures(1:windows_len) = parallel.FevalFuture;
        futures_max = 0;
        futures_results = cell(windows_len,1);

        for i = 1:windows_len
           futures(i) = parfeval(@main_loop,1,windows{i},ds.FEVD,ds.Lags,ds.H);
        end

        for i = 1:windows_len
            if (getappdata(bar,'Stop'))
                stopped = true;
                break;
            end

            [future_index,value] = fetchNext(futures);
            futures_results{future_index} = value;

            futures_max = max([future_index futures_max]);
            waitbar((futures_max - 1) / windows_len,bar);

            if (getappdata(bar,'Stop'))
                stopped = true;
                break;
            end
        end

    catch e
    end

    try
        cancel(futures);
    catch
    end

    if (~isempty(e))
        delete(bar);
        rethrow(e);
    end

    if (stopped)
        delete(bar);
        return;
    end

    pause(1);
    waitbar(1,bar,'Finalizing spillover measures...');
    pause(1);

    try
        ds = finalize(ds,futures_results);
    catch e
        delete(bar);
        rethrow(e);
    end

    pause(1);
    waitbar(1,bar,'Writing spillover measures...');
    pause(1);

    try
        write_results(ds,temp,out);
        delete(bar);
    catch e
        delete(bar);
        rethrow(e);
    end

    if (analyze)
        analyze_result(ds);
    end

    result = ds;

end

%% PROCESS

function ds = initialize(ds,sn,bw,bws,indices,fevd,lags,h)

    n = ds.N;
    t = ds.T;

    ds.Result = 'Spillover';
    ds.ResultDate = now(); %#ok<TNOW1>
    ds.ResultAnalysis = @(ds)analyze_result(ds);
    ds.ResultSerial = sn;

    ds.BW = bw;
    ds.BWS = bws;
    ds.FEVD = fevd;
    ds.H = h;
    ds.Lags = lags;
    ds.Windows = numel(indices);
    ds.WindowsIndices = indices;

    all_label = [' (' fevd ', H=' num2str(ds.H) ', LAGS=' num2str(ds.Lags) ')'];

    ds.LabelsIndicatorsSimple = {'SI'};
    ds.LabelsIndicators = {['SI' all_label]};

    ds.LabelsSheetsSimple = {'From' 'To' 'Net' 'Indicators'};
    ds.LabelsSheets = {['From' all_label] ['To' all_label] ['Net' all_label] 'Indicators'};

    ds.VarianceDecompositions = cell(t,1);

    ds.SpilloversFrom = NaN(t,n);
    ds.SpilloversTo = NaN(t,n);
    ds.SpilloversNet = NaN(t,n);

    ds.Indicators = NaN(t,numel(ds.LabelsIndicators));

    ds.ComparisonReferences = {'Indicators' [] strcat({'SP-'},ds.LabelsIndicatorsSimple)};

end

function window_results = main_loop(r,fevd,lags,h)

    window_results = struct();

    vd = variance_decomposition(r,fevd,lags,h);
    window_results.VarianceDecomposition = vd;

    [sf,st,sn,si] = spillover_metrics(vd);
    window_results.SpilloversFrom = sf;
    window_results.SpilloversTo = st;
    window_results.SpilloversNet = sn;
    window_results.SI = si;

end

function ds = finalize(ds,results)

    window_indices = ds.WindowsIndices;

    for i = 1:numel(results)
        result = results{i};
        index = window_indices(i);

        ds.VarianceDecompositions{index} = result.VarianceDecomposition;

        ds.SpilloversFrom(index,:) = result.SpilloversFrom;
        ds.SpilloversTo(index,:) = result.SpilloversTo;
        ds.SpilloversNet(index,:) = result.SpilloversNet;

        ds.Indicators(index) = result.SI;
    end

    if (ds.BWS > 1)
        n = ds.N;
        t = ds.T;
        x = ds.DatesNum;

        nan_indices = ~ismember((1:t).',window_indices);
        step_indices = find(nan_indices);
        step_indices_len = numel(step_indices);

        vd = ds.VarianceDecompositions;
        vd(cellfun(@isempty,vd)) = {NaN(n,n)};

        for i = 1:n
            for j = 1:n
                vd_ij = cellfun(@(vdf)vdf(i,j),vd);
                vd_ij(nan_indices) = spline(x(~nan_indices),vd_ij(~nan_indices),x(nan_indices));

                for k = 1:step_indices_len
                    step_index = step_indices(k);

                    vd_k = vd{step_index};
                    vd_k(i,j) = vd_ij(step_index);
                    vd{step_index} = vd_k;
                end
            end
        end

        for i = 1:step_indices_len
            step_index = step_indices(i);

            vd_i = vd{step_index};
            vd_i = bsxfun(@rdivide,vd_i,sum(vd_i,2));
            ds.VarianceDecompositions{step_index} = vd_i;

            [sf,st,sn,si] = spillover_metrics(vd_i);
            ds.SpilloversFrom(step_index,:) = sf;
            ds.SpilloversTo(step_index,:) = st;
            ds.SpilloversNet(step_index,:) = sn;
            ds.Indicators(step_index,1) = si;
        end
    end

    ds.SpilloversFrom = distress_data(ds.SpilloversFrom,ds.Defaults);
    ds.SpilloversTo = distress_data(ds.SpilloversTo,ds.Defaults);
    ds.SpilloversNet = distress_data(ds.SpilloversNet,ds.Defaults);

end

function write_results(ds,temp,out)

    [out_path,~,~] = fileparts(out);

    try
        if (exist(out_path,'dir') ~= 7)
            mkdir(out_path);
        end

        if (exist(out,'file') == 2)
            delete(out);
        end
    catch
        error('A system I/O error occurred while writing the results.');
    end

    copy_result = copyfile(temp,out,'f');

    if (copy_result == 0)
        error('The output file could not be created from the template file.');
    end

    dates_str = cell2table(ds.DatesStr,'VariableNames',{'Date'});

    for i = 1:(numel(ds.LabelsSheetsSimple) - 1)
        sheet = ds.LabelsSheetsSimple{i};
        measure = ['Spillovers' strrep(sheet,' ','')];

        tab = [dates_str array2table(ds.(measure),'VariableNames',ds.FirmNames)];
        writetable(tab,out,'FileType','spreadsheet','Sheet',sheet,'WriteRowNames',true);
    end

    tab = [dates_str array2table(ds.Indicators,'VariableNames',strrep(ds.LabelsIndicatorsSimple,' ','_'))];
    writetable(tab,out,'FileType','spreadsheet','Sheet',ds.LabelsSheetsSimple{end},'WriteRowNames',true);

    worksheets_batch(out,ds.LabelsSheetsSimple,ds.LabelsSheets);

end

%% PLOTTING

function analyze_result(ds)

    safe_plot(@(id)plot_index(ds,id));
    safe_plot(@(id)plot_spillovers(ds,id));
    safe_plot(@(id)plot_sequence(ds,id));

end

function plot_index(ds,id)

    si = smooth_data(ds.Indicators(:,1));

    f = figure('Name','Spillover Measures > Spillover Index','Units','normalized','Position',[100 100 0.85 0.85],'Tag',id);

    sub_1 = subplot(1,6,1:5);
    plot(sub_1,ds.DatesNum,si,'Color',[0.000 0.447 0.741]);
    set(sub_1,'XLim',[ds.DatesNum(1) ds.DatesNum(end)],'XTickLabelRotation',45);
    set(sub_1,'XGrid','on','YGrid','on');
    title(sub_1,ds.LabelsIndicators{1});

    if (ds.MonthlyTicks)
        date_ticks(sub_1,'x','mm/yyyy','KeepLimits','KeepTicks');
    else
        date_ticks(sub_1,'x','yyyy','KeepLimits');
    end

    sub_2 = subplot(1,6,6);
    boxplot(sub_2,si,'Notch','on','Symbol','k.');
    set(findobj(f,'type','line','Tag','Median'),'Color','g');
    set(findobj(f,'-regexp','Tag','\w*Whisker'),'LineStyle','-');
    set(sub_2,'TickLength',[0 0],'XTick',[],'XTickLabels',[]);

    figure_title(f,'Spillover Index');

    maximize_figure(f);

end

function plot_spillovers(ds,id)

    from = smooth_data(ds.SpilloversFrom);
    from = bsxfun(@rdivide,from,sum(from,2,'omitnan'));
    from(isnan(from)) = 0;

    to = smooth_data(ds.SpilloversTo);
    to = bsxfun(@rdivide,to,sum(to,2,'omitnan'));
    to(isnan(to)) = 0;

    net = smooth_data(ds.SpilloversNet);
    net = [min(net,[],2,'omitnan') max(net,[],2,'omitnan')];

    f = figure('Name','Spillover Measures > Spillovers','Units','normalized','Position',[100 100 0.85 0.85],'Tag',id);

    sub_1 = subplot(2,2,1);
    area(sub_1,ds.DatesNum,from,'EdgeColor',[0.000 0.447 0.741],'FaceColor','none');
    set(sub_1,'XLim',[ds.DatesNum(1) ds.DatesNum(end)],'XTickLabelRotation',45);
    set(sub_1,'YLim',[0 1],'YTick',0:0.2:1,'YTickLabels',arrayfun(@(x)sprintf('%.f%%',x),(0:0.2:1) .* 100,'UniformOutput',false));
    title(sub_1,'Spillovers From Others');

    sub_2 = subplot(2,2,3);
    area(sub_2,ds.DatesNum,to,'EdgeColor',[0.000 0.447 0.741],'FaceColor','none');
    set(sub_2,'XLim',[ds.DatesNum(1) ds.DatesNum(end)],'XTickLabelRotation',45);
    set(sub_2,'YLim',[0 1],'YTick',0:0.2:1,'YTickLabels',arrayfun(@(x)sprintf('%.f%%',x),(0:0.2:1) .* 100,'UniformOutput',false));
    title(sub_2,'Spillovers To Others');

    sub_3 = subplot(2,2,[2 4]);
    fill(sub_3,[ds.DatesNum; flipud(ds.DatesNum)],[net(:,1); fliplr(net(:,2))],[0.65 0.65 0.65],'EdgeColor','none','FaceAlpha',0.35);
    hold on;
        plot(sub_3,ds.DatesNum,mean(net,2),'Color',[0.000 0.447 0.741]);
        plot(sub_3,ds.DatesNum,zeros(ds.T,1),'Color',[1.000 0.400 0.400]);
    hold off;
    set(sub_3,'XLim',[ds.DatesNum(1) ds.DatesNum(end)],'XTickLabelRotation',45);
    set(sub_3,'YLim',[-1 1],'YTick',-1:0.2:1,'YTickLabels',arrayfun(@(x)sprintf('%.f%%',x),(-1:0.2:1) .* 100,'UniformOutput',false));
    set(sub_3,'XGrid','on','YGrid','on');
    title(sub_3,'Net Spillovers');

    if (ds.MonthlyTicks)
        date_ticks([sub_1 sub_2 sub_3],'x','mm/yyyy','KeepLimits','KeepTicks');
    else
        date_ticks([sub_1 sub_2 sub_3],'x','yyyy','KeepLimits');
    end

    figure_title(f,['Spillovers (' ds.FEVD ', H=' num2str(ds.H) ', LAGS=' num2str(ds.Lags) ')']);

    maximize_figure(f);

end

function plot_sequence(ds,id)

    n = ds.N;
    t = ds.T;
    dn = ds.DatesNum;
    mt = ds.MonthlyTicks;

    from_all = smooth_data(ds.SpilloversFrom);
    to_all = smooth_data(ds.SpilloversTo);
    net_all = smooth_data(ds.SpilloversNet);

    data = [repmat({dn},1,n); mat2cell(from_all,t,ones(1,n)); mat2cell(to_all,t,ones(1,n)); mat2cell(net_all,t,ones(1,n))];

    plots_title = [repmat({'From'},1,n); repmat({'To'},1,n); repmat({'Net'},1,n)];

    x_limits = [dn(1) dn(end)];

    ft = [from_all to_all];
    y_limits_from = [0 1];
    y_limits_to = plot_limits(max(max(ft)),0.1,0);
    y_limits_to(2) = ceil(y_limits_to(2) * 10) / 10;
    y_limits_net = [-1 1];

    y_ticks_from = 0:0.2:1;
    y_ticks_to = 0:0.2:y_limits_to(2);
    y_ticks_net = -1:0.2:1;
    y_tick_labels = @(x)sprintf('%.f%%',x * 100);

    core = struct();

    core.N = n;
    core.Data = data;
    core.Function = @(subs,data)plot_function(subs,data);

    core.OuterTitle = 'Spillover Measures > Spillovers Time Series';
    core.InnerTitle = 'Spillovers Time Series';
    core.SequenceTitles = ds.FirmNames;

    core.PlotsAllocation = [3 1];
    core.PlotsSpan = {1 2 3};
    core.PlotsTitle = plots_title;

    core.XDates = {mt mt mt};
    core.XGrid = {true true true};
    core.XLabel = {[] [] []};
    core.XLimits = {x_limits x_limits x_limits};
    core.XRotation = {45 45 45};
    core.XTick = {[] [] []};
    core.XTickLabels = {[] [] []};

    core.YGrid = {true true true};
    core.YLabel = {[] [] []};
    core.YLimits = {y_limits_from y_limits_to y_limits_net};
    core.YRotation = {[] [] []};
    core.YTick = {y_ticks_from y_ticks_to y_ticks_net};
    core.YTickLabels = {y_tick_labels y_tick_labels y_tick_labels};

    sequential_plot(core,id);

    function plot_function(subs,data)

        x = data{1};
        from = data{2};
        to = data{3};
        net = data{4};

        d = find(isnan(net),1,'first');

        if (isempty(d))
            xd = [];
        else
            xd = x(d) - 1;
        end

		sub_1 = subs(1);
        plot(sub_1,x,from,'Color',[0.000 0.447 0.741]);
        if (~isempty(xd))
            hold(sub_1,'on');
                plot(sub_1,[xd xd],get(sub_1,'YLim'),'Color',[1.000 0.400 0.400]);
            hold(sub_1,'off');
        end

		sub_2 = subs(2);
        plot(sub_2,x,to,'Color',[0.000 0.447 0.741]);
        hold(sub_2,'on');
            plot(sub_2,x,ones(numel(x),1),'Color',[1.000 0.400 0.400]);
            if (~isempty(xd))
                plot(sub_2,[xd xd],get(sub_2,'YLim'),'Color',[1.000 0.400 0.400]);
            end
        hold(sub_2,'off');

		sub_3 = subs(3);
        plot(sub_3,x,net,'Color',[0.000 0.447 0.741]);
        hold(sub_3,'on');
            plot(sub_3,x,zeros(numel(x),1),'Color',[1.000 0.400 0.400]);
            if (~isempty(xd))
                plot(sub_3,[xd xd],get(sub_3,'YLim'),'Color',[1.000 0.400 0.400]);
            end
        hold(sub_3,'off');

    end

end

%% VALIDATION

function out = validate_output(out)

    [path,name,extension] = fileparts(out);

    if (~strcmpi(extension,'.xlsx'))
        out = fullfile(path,[name extension '.xlsx']);
    end

end

function temp = validate_template(temp)

    sheets = {'From' 'To' 'Net' 'Indicators'};
    file_sheets = validate_xls(temp,'T');

    if (~all(ismember(sheets,file_sheets)))
        error(['The template must contain the following sheets: ' sheets{1} sprintf(', %s',sheets{2:end}) '.']);
    end

    worksheets_batch(temp,sheets);

end


function [EEGout cfg] = pop_sim_varmodel(EEG,typeproc,varargin)
%
% Simulate a Vector Autoregressive dynamical system
%
% Input:
% Optional:
%
%   EEG:            existing EEG dataset (configs will be retrived from
%                   here)
%   typeproc:       if 'nogui' don't generate GUI
%
%   <'Name',value> pairs as defined in sim_varmodel()
%
% Output:
%
%   EEGout:         Simulated EEG structure(s).
%                   Optionally this may be an array of
%                   EEG structs with the second struct
%                   being the ground truth model.
%   cfg:            Argument specification structure.
%
%
% See Also: sim_varmodel()
%
% References:
%
% [1] Mullen T (2010) The Source Information Flow Toolbox (SIFT):
%   Theoretical Handbook and User Manual.
%   Available at: http://www.sccn.ucsd.edu/wiki/Sift
%
% Author: Tim Mullen 2013, SCCN/INC, UCSD.
% Email:  tim@sccn.ucsd.edu
%

% This function is part of the Source Information Flow Toolbox (SIFT)
%
% This program 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.
%
% This program 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 this program; if not, write to the Free Software
% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

if nargin<1
    EEG = [];
end
if nargin<2
    typeproc = 0;
end
EEGout = EEG;
cfg    = [];

fcnName     = strrep(mfilename,'pop_','');
fcnHandle   = str2func(fcnName);

if isempty(hlp_checkeegset(EEG,{'cat'})) && isfield(EEG.CAT.configs,fcnName)
    % get default configuration (from prior use) and merge with varargin
    varargin = [hlp_struct2varargin(EEG.CAT.configs.(fcnName)) varargin];
end

if strcmpi(typeproc,'nogui')
    % get the default config from function and overload supplied args
    cfg = arg_tovals(arg_report('rich',fcnHandle,varargin),false);
else
    % render the GUI
    [PGh figh] = feval(['gui_' fcnName],varargin{:});

    if isempty(PGh)
        % user chose to cancel
        return;
    end

    % get the specification of the PropertyGrid
    ps = PGh.GetPropertySpecification;
    cfg = arg_tovals(ps,false);
end

drawnow;

if ~cfg.makeEEGset.arg_selection
    error('SIFT:sim_varmodel',['If using pop_' fcnName '(), you must enable the BuildEEGLABStructure option.\n' ...
                               'Use ' fcnName '() from the command-line to return a raw dataset']);
end

if strcmpi(typeproc,'cfg_only')
    return;
end

% execute the low-level function
[EEGout GroundTruth] = feval(fcnHandle,cfg);

if ~isempty(GroundTruth)
    EEGout = eeg_store(EEGout,GroundTruth,2);
end
if ~isempty(cfg)
    for k=1:length(EEGout)
        % store the configuration structure
        EEGout(k).CAT.configs.(fcnName) = cfg;
    end
end


classdef ApoapsisAltitudeOptimizationVariable < AbstractOptimizationVariable
    %ApoapsisAltitudeOptimizationVariable Summary of this class goes here
    %   Detailed explanation goes here

    properties
        varObj(1,1) ApoapsisAltitudeTermCondition = ApoapsisAltitudeTermCondition(0);

        lb(1,1) double = 0;
        ub(1,1) double = 0;

        useTf(1,1) = false;
    end

    methods
        function obj = ApoapsisAltitudeOptimizationVariable(varObj)
            obj.varObj = varObj;
            obj.varObj.optVar = obj;

            obj.id = rand();
        end

        function x = getXsForVariable(obj)
            x = [];

            if(obj.useTf)
                x = obj.varObj.apoalt;
            end
        end

        function [lb, ub] = getBndsForVariable(obj)
            lb = obj.lb(obj.useTf);
            ub = obj.ub(obj.useTf);
        end

        function [lb, ub] = getAllBndsForVariable(obj)
            lb = obj.lwrBnd;
            ub = obj.uprBnd;
        end

        function setBndsForVariable(obj, lb, ub)
            obj.lb = lb;
            obj.ub = ub;
        end

        function useTf = getUseTfForVariable(obj)
            useTf = obj.useTf;
        end

        function setUseTfForVariable(obj, useTf)
            obj.useTf = useTf;
        end

        function updateObjWithVarValue(obj, x)
            obj.varObj.apoalt = x;
        end

        function nameStrs = getStrNamesOfVars(obj, evtNum)
            nameStrs = {sprintf('Event %i Apoapsis Altitude Termination Condition', evtNum)};
        end
    end
end


function hash = hlp_cryptohash(data,fromfile)
% Compute an MD5 hash of a file, string or generic data structure.
% Hash = hlp_cryptohash(Data,FromFile)
%
% In:
%   Data : data to be hashed; can be a filename, a string, or any other MATLAB data structure.
%
%   FromFile : if true, data is interpreted as a file name (default: false)
%
% Out:
%   Hash : MD5 hash (decimal, lowercase) of the Data
%
% Examples:
%   % calculate an md5 hash of a file
%   hlp_cryptohash('myscript.m',true);
%
%   % calculate an md5 hash of a data structure
%   hlp_cryptohash(lastmodel);
%
% See also:
%   hlp_fingerprint, hlp_serialize
%
%					      	     Christian Kothe, Swartz Center for Computational Neuroscience, UCSD
%                                2010-10-10

% Copyright (C) Christian Kothe, SCCN, 2010, christian@sccn.ucsd.edu
%
% This program 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 2 of the
% License, or (at your option) any later version.
%
% This program 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 this program; if not,
% write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
% USA

% maximum amount of memory used inside the Java VM
max_java_memory = 2^26; % 64 MB

if nargin < 2
    fromfile = false; end
if ~(isequal(fromfile,true) || isequal(fromfile,false))
    error('The given FromFile argument must be true or false.'); end

if fromfile
    % take data as a file name
    if ~ischar(data)
        error('To represent a file name, Data should be a string.'); end
    if ~exist(data,'file')
        error('The file %s does not exist.',data); end
    f = fopen(data,'r');
    try
        data = fread(f,Inf);
        fclose(f);
    catch e
        try
            fclose(f);
        catch %#ok<CTCH>
        end
        rethrow(e);
    end
else
    % take data literally
    if ~ischar(data)
        data = hlp_serialize(data); end
end

% use Java to hash the data (idea from Michael Kleder)
hasher = java.security.MessageDigest.getInstance('MD5');
data = uint8(data);
if length(data) <= max_java_memory
    hasher.update(data);
else
    numsplits = ceil(length(data)/max_java_memory);
    for i=0:numsplits-1
        range = 1+floor(i*length(data)/numsplits) : min(length(data),floor((i+1)*length(data)/numsplits));
        hasher.update(data(range));
    end
end

hash = dec2hex(typecast(hasher.digest,'uint8'),2)';
hash = lower(hash(:)');


function nlp = IdentityMapConstraint(obj, nlp, src, tar, bounds, varargin)
    % Impose system specific constraints as NLP constraints for the
    % trajectory optimization problem
    %
    % Parameters:
    % nlp: an trajectory optimization NLP object of the system
    % @type TrajectoryOptimization
    % src: the NLP of the source domain system
    % @type TrajectoryOptimization
    % tar: the NLP of the target domain system
    % @type TrajectoryOptimization
    % bounds: a struct data contains the boundary values @type struct
    % varargin: extra arguments @type varargin

    % by default, identity map
    x = obj.States.x;
    xn = obj.States.xn;
    x_map = SymFunction(['xDiscreteMap' obj.Name],x-xn,{x,xn});

    addNodeConstraint(nlp, x_map, {'x','xn'}, 'first', 0, 0, 'Linear');

    if strcmp(obj.Plant.Type,'SecondOrder')
        dx = obj.States.dx;
        dxn = obj.States.dxn;
        dx_map = SymFunction(['dxDiscreteMap' obj.Name],dx-dxn,{dx,dxn});

        addNodeConstraint(nlp, dx_map, {'dx','dxn'}, 'first', 0, 0, 'Linear');
    end

end


function v=mocov_util_platform_version()
% return the version of Matlab or Octave
%
% v=mocov_util_platform_version()
%
% Output:
%   v               Vector with two (Matlab) or three (Octave) elements,
%                   for example 8.5 (for Matlab) or 4.0.3) for Octave)
%

    version_str=version();

    parts=regexp(version_str,'\s','split');
    first_part=parts{1};

    num_parts=regexp(first_part,'\.','split');
    v=cellfun(@str2num,num_parts);


%SETPOSTPROC (Re)set POSTPROC field of a datafile
%
%   A = SETPOSTPROC(A,MAPPING)
%   A = SETPOSTPROC(A)
%
% INPUT
%   A        - Datafile
%   POSTPROC - cell containing postprocessing mapping command
%
% OUTPUT
%   A       - Datafile
%
% DESCRIPTION
% Sets the mappings stored in A.POSTPROC. The size of the datafile
% A is set to the output size of PRMAPPING.
% A call without MAPPING clears A.POSTPROC. The size of the datafile
% A is reset to undefined (0).
%
% The mappings in A.POSTPROC may be extended by ADDPOSTPROC.
%
% Mappings in A.POSTPROC are stored only and executed just
% after A is converted from a DATAFILE into a PRDATASET.
%
% SEE ALSO (PRTools Guide)
% DATAFILES, SETPREPROC, ADDPOSTPROC.


function gpcf = gpcf_mask(varargin)
%GPCF_MASK  Create a mask covariance function
%
%  Description
%    GPCF = GPCF_MASK('PARAM1',VALUE1,'PARAM2,VALUE2,...)
%    creates a mask covariance function structure in
%    which the named parameters have the specified values. Any
%    unspecified parameters are set to default values.
%
%    GPCF = GPCF_MASK(GPCF,'PARAM1',VALUE1,'PARAM2,VALUE2,...)
%    modify a covariance function structure with the named
%    parameters altered with the specified values.
%
%    Mask covariance function returns correlation 1 if input
%    values X_i and X_j are both non zero and 0 otherwise.
%
%    Parameters for mask covariance function
%      selectedVariables - vector defining which inputs are used
%
%  See also
%    GP_SET, GPCF_*, PRIOR_*, MEAN_*

% Copyright (c) 2007-2010 Jarno Vanhatalo
% Copyright (c) 2008-2010 Jaakko Riihim�ki
% Copyright (c) 2010 Aki Vehtari

% This software is distributed under the GNU General Public
% License (version 3 or later); please refer to the file
% License.txt, included with the software, for details.

  ip=inputParser;
  ip.FunctionName = 'GPCF_MASK';
  ip.addOptional('gpcf', [], @isstruct);
  ip.addParamValue('selectedVariables',[], ...
                   @(x) isempty(x) || (isvector(x) && all(x>0)));
  ip.parse(varargin{:});
  gpcf=ip.Results.gpcf;

  if isempty(gpcf)
    % Initialize a covariance function structure
    init=true;
    gpcf.type = 'gpcf_mask';
  else
    % Modify a covariance function structure
    if ~isfield(gpcf,'type') && ~isequal(gpcf.type,'gpcf_mask')
      error('First argument does not seem to be a valid covariance function structure')
    end
    init=false;
  end

  if ~ismember('selectedVariables',ip.UsingDefaults)
    if ~isempty(ip.Results.selectedVariables)
      gpcf.selectedVariables = ip.Results.selectedVariables;
    elseif isfield(gpcf,'selectedVariables')
      gpcf=rmfield(gpcf,'selectedVariables');
    end
  end
  if init
    gpcf.fh.pak = @gpcf_mask_pak;
    gpcf.fh.unpak = @gpcf_mask_unpak;
    gpcf.fh.lp = @gpcf_mask_lp;
    gpcf.fh.lpg = @gpcf_mask_lpg;
    gpcf.fh.cfg = @gpcf_mask_cfg;
    gpcf.fh.ginput = @gpcf_mask_ginput;
    gpcf.fh.cov = @gpcf_mask_cov;
    gpcf.fh.trcov  = @gpcf_mask_trcov;
    gpcf.fh.trvar  = @gpcf_mask_trvar;
    gpcf.fh.recappend = @gpcf_mask_recappend;
  end

end

function [w,s] = gpcf_mask_pak(gpcf, w)
%GPCF_MASK_PAK  Combine GP covariance function parameters into
%              one vector.
%
%  Description
%    W = GPCF_MASK_PAK(GPCF) takes a covariance function
%    structure GPCF and combines the covariance function
%    parameters and their hyperparameters into a single row
%    vector W. This is a mandatory subfunction used for
%    example in energy and gradient computations.
%
%       w = []
%
%  See also
%    GPCF_MASK_UNPAK

  w = []; s = {};
end

function [gpcf, w] = gpcf_mask_unpak(gpcf, w)
%GPCF_MASK_UNPAK  Sets the covariance function parameters into
%                the structure
%
%  Description
%    [GPCF, W] = GPCF_MASK_UNPAK(GPCF, W) takes a covariance
%    function structure GPCF and a parameter vector W, and
%    returns a covariance function structure identical to the
%    input, except that the covariance parameters have been set
%    to the values in W. Deletes the values set to GPCF from W
%    and returns the modified W. This is a mandatory subfunction
%    used for example in energy and gradient computations.
%
%    Assignment is inverse of
%       w = []
%
%  See also
%   GPCF_MASK_PAK

end

function lp = gpcf_mask_lp(gpcf)
%GPCF_MASK_LP  Evaluate the energy of prior of covariance function parameters
%
%  Description
%    LP = GPCF_MASK_LP(GPCF) takes a covariance function
%    structure GPCF and returns log(p(th)), where th collects the
%    parameters. This is a mandatory subfunction used for example
%    in energy computations.
%
%  See also
%    GPCF_MASK_PAK, GPCF_MASK_UNPAK, GPCF_MASK_LPG, GP_E

  lp = 0;

end

function lpg = gpcf_mask_lpg(gpcf)
%GPCF_MASK_LPG  Evaluate gradient of the log prior with respect
%               to the parameters.
%
%  Description
%    LPG = GPCF_MASK_LPG(GPCF) takes a covariance function
%    structure GPCF and returns LPG = d log (p(th))/dth, where th
%    is the vector of parameters. This is a mandatory subfunction
%    used for example in gradient computations.
%
%  See also
%    GPCF_MASK_PAK, GPCF_MASK_UNPAK, GPCF_MASK_LP, GP_G

  lpg = [];

end

function DKff = gpcf_mask_cfg(gpcf, x, x2, mask,i1)
%GPCF_MASK_CFG  Evaluate gradient of covariance function
%               with respect to the parameters.
%
%  Description
%    DKff = GPCF_MASK_CFG(GPCF, X) takes a covariance function
%    structure GPCF, a matrix X of input vectors and returns
%    DKff, the gradients of covariance matrix Kff = k(X,X) with
%    respect to th (cell array with matrix elements). This is a
%    mandatory subfunction used in gradient computations.
%
%    DKff = GPCF_MASK_CFG(GPCF, X, X2) takes a covariance function
%    structure GPCF, a matrix X of input vectors and returns
%    DKff, the gradients of covariance matrix Kff = k(X,X2) with
%    respect to th (cell array with matrix elements). This subfunction
%    is needed when using sparse approximations (e.g. FIC).
%
%    DKff = GPCF_MASK_CFG(GPCF, X, [], MASK) takes a covariance
%    function structure GPCF, a matrix X of input vectors and
%    returns DKff, the diagonal of gradients of covariance matrix
%    Kff = k(X,X2) with respect to th (cell array with matrix
%    elements). This subfunction is needed when using sparse
%    approximations (e.g. FIC).
%
%    DKff = GPCF_MASK_CFG(GPCF, X, X2, [], i) takes a covariance function
%    structure GPCF, a matrix X of input vectors and returns
%    DKff, the gradients of covariance matrix Kff = k(X,X2), or k(X,X)
%    if X2 is empty, with respect to ith hyperparameter. This subfunction
%    is needed when using memory save option in gp_set.
%
%  See also
%    GPCF_MASK_PAK, GPCF_MASK_UNPAK, GPCF_MASK_LP, GP_G

  DKff = {};

end

function [DKff, lpg]  = gpcf_mask_ginput(gpcf, x, x2, i1)
%GPCF_MASK_GINPUT  Evaluate gradient of covariance function with
%                  respect to x.
%
%  Description
%    DKff = GPCF_MASK_GINPUT(GPCF, X) takes a covariance function
%    structure GPCF, a matrix X of input vectors and returns
%    DKff, the gradients of covariance matrix Kff = k(X,X) with
%    respect to X (cell array with matrix elements). This subfunction
%    is needed when computing gradients with respect to inducing
%    inputs in sparse approximations.
%
%    DKff = GPCF_MASK_GINPUT(GPCF, X, X2) takes a covariance
%    function structure GPCF, a matrix X of input vectors
%    and returns DKff, the gradients of covariance matrix Kff =
%    k(X,X2) with respect to X (cell array with matrix elements).
%    This subfunction is needed when computing gradients with
%    respect to inducing inputs in sparse approximations.
%
%    DKff = GPCF_MASK_GINPUT(GPCF, X, X2, i) takes a covariance
%    function structure GPCF, a matrix X of input vectors
%    and returns DKff, the gradients of covariance matrix Kff =
%    k(X,X2), or k(X,X) if X2 is empty, with respect to ith covariate
%    in X. This subfunction is needed when using memory saving option
%    in gp_set.
%
%  See also
%   GPCF_MASK_PAK, GPCF_MASK_UNPAK, GPCF_MASK_LP, GP_G

  [n, m] =size(x);

  if nargin == 2 || isempty(x2)
    ii1 = 0;
    for i=1:m
      for j = 1:n
        ii1 = ii1 + 1;
        DKff{ii1} = zeros(n);
        lpg(ii1) = 0;
      end
    end
  elseif nargin == 3 || nargin == 4
    ii1 = 0;
    for i=1:m
      for j = 1:n
        ii1 = ii1 + 1;
        DKff{ii1} = zeros(n, size(x2,1));
        lpg(ii1) = 0;
      end
    end
  end
  if nargin==4
    DKff=DKff{1};
  end
end

function C = gpcf_mask_cov(gpcf, x1, x2, varargin)
%GP_MASK_COV  Evaluate covariance matrix between two input vectors
%
%  Description
%    C = GP_MASK_COV(GP, TX, X) takes in covariance function of a
%    Gaussian process GP and two matrixes TX and X that contain
%    input vectors to GP. Returns covariance matrix C. Every
%    element ij of C contains covariance between inputs i in TX
%    and j in X. This is a mandatory subfunction used for example in
%    prediction and energy computations.
%
%  See also
%    GPCF_MASK_TRCOV, GPCF_MASK_TRVAR, GP_COV, GP_TRCOV

  if isempty(x2)
    x2=x1;
  end
  [n1,m1]=size(x1);
  [n2,m2]=size(x2);

  if m1~=m2
    error('the number of columns of X1 and X2 has to be same')
  end

  C=repmat(true,n1,n2);
  if isfield(gpcf, 'selectedVariables')
    for j = 1:length(gpcf.selectedVariables)
      jj=gpcf.selectedVariables(j);
      C = C & bsxfun(@and,x1(:,jj)~=0,x2(:,jj)'~=0);
    end
  else
    for j = 1:m1
      C = C & bsxfun(@and,x1(:,j)~=0,x2(:,j)'~=0);
    end
  end
  C=double(C);

end

function C = gpcf_mask_trcov(gpcf, x)
%GP_MASK_TRCOV  Evaluate training covariance matrix of inputs
%
%  Description
%    C = GP_MASK_TRCOV(GP, TX) takes in covariance function of a
%    Gaussian process GP and matrix TX that contains training
%    input vectors. Returns covariance matrix C. Every element ij
%    of C contains covariance between inputs i and j in TX. This
%    is a mandatory subfunction used for example in prediction
%    and energy computations.
%
%  See also
%    GPCF_MASK_COV, GPCF_MASK_TRVAR, GP_COV, GP_TRCOV

  [n,m]=size(x);

  C=repmat(true,n,n);
  if isfield(gpcf, 'selectedVariables')
    for j = 1:length(gpcf.selectedVariables)
      jj=gpcf.selectedVariables(j);
      C = C & bsxfun(@and,x(:,jj)~=0,x(:,jj)'~=0);
    end
  else
    for j = 1:m
      C = C & bsxfun(@and,x(:,j)~=0,x(:,j)'~=0);
    end
  end
  C=double(C);

end

function C = gpcf_mask_trvar(gpcf, x)
%GP_MASK_TRVAR  Evaluate training variance vector
%
%  Description
%    C = GP_MASK_TRVAR(GPCF, TX) takes in covariance function of a
%    Gaussian process GPCF and matrix TX that contains training
%    inputs. Returns variance vector C. Every element i of C
%    contains variance of input i in TX. This is a mandatory
%    subfunction used for example in prediction and energy
%    computations.
%
%  See also
%    GPCF_MASK_COV, GP_COV, GP_TRCOV

  [n,m]=size(x);
  C=true(n,1);
  if isfield(gpcf, 'selectedVariables')
    for j = 1:length(gpcf.selectedVariables)
      jj=gpcf.selectedVariables(j);
      C = C & x(:,jj)~=0;
    end
  else
    for j = 1:m
      C = C & x(:,j)~=0;
    end
  end
  C=double(C);

end

function reccf = gpcf_mask_recappend(reccf, ri, gpcf)
%RECAPPEND  Record append
%
%  Description
%    RECCF = GPCF_MASK_RECAPPEND(RECCF, RI, GPCF) takes a
%    covariance function record structure RECCF, record index RI
%    and covariance function structure GPCF with the current MCMC
%    samples of the parameters. Returns RECCF which contains
%    all the old samples and the current samples from GPCF.
%    This subfunction is needed when using MCMC sampling (gp_mc).
%
%  See also
%    GP_MC and GP_MC -> RECAPPEND

  if nargin == 2
    % Initialize the record
    reccf.type = 'gpcf_mask';

    % Initialize parameters
    reccf.coeffSigma2= [];

    % Set the function handles
    reccf.fh.pak = @gpcf_mask_pak;
    reccf.fh.unpak = @gpcf_mask_unpak;
    reccf.fh.lp = @gpcf_mask_lp;
    reccf.fh.lpg = @gpcf_mask_lpg;
    reccf.fh.cfg = @gpcf_mask_cfg;
    reccf.fh.cov = @gpcf_mask_cov;
    reccf.fh.trcov  = @gpcf_mask_trcov;
    reccf.fh.trvar  = @gpcf_mask_trvar;
    reccf.fh.recappend = @gpcf_mask_recappend;
  else
    % Append to the record
    if isfield(gpcf, 'selectedVariables')
      reccf.selectedVariables = gpcf.selectedVariables;
    end
  end
end


%PRCURSOR Show object ident.
%
%    PRCURSOR(H)
%
% Enable the datacursor in a scatterplot. This can be used to
% investigate the object identifier by clicking on the object.

% Copyright: D.M.J. Tax, D.M.J.Tax@37steps.com
% Faculty EWI, Delft University of Technology
% P.O. Box 5031, 2600 GA Delft, The Netherlands

function out = prcursor(h,event_obj)

% This file has to tasks:
% 1. to setup the datacursor (by enabling it and setting the callback
% function)
% 2. to provide the callback when a user pressed on an object

if ~exist('datacursormode','file')
	error('MATLAB 7.0 or newer is required (datacursormode.m is not available).');
end
if nargin ~=2
	% we are doing the setup:
	if nargin<1
		h = gcf;
	end
	dh = datacursormode(h);
	set(dh,'enable','on','updatefcn',@prcursor);

else
	% we are doing the callback:
	nr = get(event_obj,'dataindex');
	ud = get(get(event_obj,'target'),'UserData');
	if ~isempty(ud) & isfield(ud,'ident')
		nr = ud.ident(nr);
	end
	out = sprintf('obj. %d',nr);
end

return


function [ a, sX ] = size_compat( sX, sY )
a = true;
switch class( sX ),
    case 'double',
        if isempty( sX ) || all( sX == 0 ),
            sX = sY;
        elseif isempty( sY ) || all( sY == 0 ),
        elseif ~isequal( sX, sY ),

            % Feb 29, 2012. Special case:
            %   One represents the size a x b x c, where c = 1
            %   The other is a x b (since Matlab often automatically squeezes
            %   3D arrays to 2D if the 3rd dimension is a singletone)
%             if (length(sX) >= 3 && length(sX) == length(sY)+1 && sX(end)==1) || ...
%                     (length(sY) >= 3 && length(sY) == length(sX)+1 && sY(end)==1)
                % do nothing
            % March 2012, a better fix (also due to Graham Coleman)
            if min( length(sX),length(sY) ) >= 2
                truncA  = cutTrailingOnes(sX);
                truncB  = cutTrailingOnes(sY);
                a = isequal( truncA, truncB );
            else
                a = false;
            end
        end
    case 'cell',
        if ~isa( sY, 'cell' ) || numel( sX ) ~= numel( sY ) || isa( sX{1}, 'function_handle' ) && ~isequal( sX, sY ),
            a = false;
        elseif isa( sX{1}, 'function_handle' ),
            a = isequal( sX, sY );
        else
            for k = 1 : numel( sX ),
                [ ta, sX{k} ] = size_compat( sX{k}, sY{k} );
                a = a && ta;
            end
        end
    otherwise,
        a = isequal( sX, sY );
end
if ~a,
    sX = [];
end

function y = cutTrailingOnes( x )
%cuts the final ones of a vector, and columnize

lastNotOne = find( x(:)~=1, 1, 'last' );

%do not cut before 2nd position
lastNotOne = max( 2, lastNotOne );

y = x(1:lastNotOne);

% TFOCS v1.3 by Stephen Becker, Emmanuel Candes, and Michael Grant.
% Copyright 2013 California Institute of Technology and CVX Research.
% See the file LICENSE for full license information.


function writeMesh_off(fileName, vertices, faces)
%WRITEMESH_OFF Writes a mesh into a text file in OFF format.
%
%   writeMesh_off(FNAME, V, F)
%
%   Example
%   writeMesh_off
%
%   See also
%      meshes3d, readMesh_off, writeMesh_ply

% ------
% Author: David Legland
% e-mail: david.legland@inra.fr
% Created: 2018-04-26,    using Matlab 9.4.0.813654 (R2018a)
% Copyright 2018 INRA - Cepia Software Platform.

if ~ischar(fileName)
    error('First argument must contain the name of the file');
end

% optionnaly parses data
if isstruct(vertices)
    faces = vertices.faces;
    vertices = vertices.vertices;
end

% open file for writing text
f = fopen(fileName, 'wt');
if (f == -1)
	error('Couldn''t open the file %s', fileName);
end

% write the header line
fprintf(f, 'OFF\n');

% write number of vertices and of faces
nVertices = size(vertices, 1);
nFaces = size(faces, 1);
if iscell(faces)
    nFaces = length(faces);
end
fprintf(f, '%d %d\n', nVertices, nFaces);

% Write vertex info
format = '%g %g %g\n';
for iv = 1:nVertices
    fprintf(f, format, vertices(iv, :));
end

% Write face info
if isnumeric(faces)
    % simply write face vertex indices
    ns = size(faces, 2);
    format = ['%d' repmat(' %d', 1, ns) '\n'];
    for iFace = 1:nFaces
        fprintf(f, format, ns, faces(iFace, :)-1);
    end
else
    % if faces are stored in a cell array, the number of vertices in each
    % face may be different, and we need to process each face individually
    for iFace = 1:nFaces
        ns = length(faces{iFace});
        format = ['%d' repmat(' %d', 1, ns) '\n'];
        fprintf(f, format, ns, faces{iFace}-1);
    end
end

% close the file
fclose(f);


function arglist = hlp_getModelingApproaches(varargin)
% return a cell array (arglist) defining the available modeling approaches
% each entry of the cell array is a cell array of the form
% {'Approach Name' @function_handle}
% where @function_handle is the handle to the function implementing the
% approach
%
% hlp_getModelingApproaches('defNameOnly') returns only the name of the
%  default modeling approach (as a string)
%
% hlp_getModelingApproaches('mfileNameOnly',approachName) where approachName
%   is a string with the name of a valid approach, returns the m-file name
%   of the function implementing the specified approach
%
% Author: Tim Mullen, 2012 SCCN/INC/UCSD

defNameOnly = false;
approachName = '';

if nargin==1 && strcmp(varargin{1},'defNameOnly')
    defNameOnly = true;
end

if nargin==2 && ismember_bc('mfileNameOnly',varargin);
    approachName = varargin{2};
    if ~ischar(approachName)
        error('hlp_getModelingApproaches:badInput','Bad argument pair for ''mfileNameOnly''');
    end
end

% construct the arglist
arglist = { ...
            {'Segmentation VAR' @est_fitMVAR}, ...   % {'Kalman Filtering' {arg('arg_dummy',[],[],'replace this with @est_fitMVARKalman')}}, ...
            };

% determine the output
if defNameOnly
    arglist = arglist{1}{1};
elseif ~isempty(approachName)
    % get the function handle matching the desired approach name
    tmp = cellfun(@(x) fastif(isequal(x{1},approachName),x{2},''),arglist,'UniformOutput',false);
    tmp(cellfun(@isempty,tmp))=[];
    if isempty(tmp)
        error('hlp_getModelingApproachMfileName:badApproachName','Unknown modeling approach ''%s''',approachName);
    else
        % return function name as a string
        arglist = char(tmp{1});
    end
end


function varargout = real(varargin)
%REAL      Real part of a SPHEREFUN.
%
% See also SPHEREFUN/IMAG.

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

    [varargout{1:nargout}] = real@separableApprox(varargin{:});
end


function colors = RYB_colors(n)
% RYB_colors    A good diverging colormap.
% RYB_colors(n) returns an RGB matrix of n colors (default 7).
% These colors form a diverging colormap (colors easily perceived to lie
% on a sequence on either side of a central color).
%
% Example:
%   colormap(RYB_colors)
%
% See also YR_colors.

% Written by Tom Minka

if nargin < 1
  n = 7;
end

% In R: col2rgb(RYB.colors(3))
switch n
  case 7,
    colors = [
      215    61   41;
      252   141   89;
      254   224  144;
      255   255  191;
      224   243  248;
      145   191  219;
      69   117  180
      ]/255;
  case 6,
    colors = [
       215    61   41
       252   141   89
       254   224  144
       224   243  248
       145   191  219
        69   117  180
      ]/255;
  case 5,
    colors = [
       202    55   59
       253   174   97
       255   255  191
       171   217  233
        44   123  182
	]/255;
  case 4,
    colors = [
      202    55   59;
      253   174   97
      171   217  233;
       44   123  182
       ]/255;
   case 3,
     colors = [
       252   141   89;
       255   255  191;
       145   191  219
       ]/255;
end


%Install_lightspeed
% Compiles mex files for the lightspeed library.

% Written by Tom Minka
% (c) Microsoft Corporation. All rights reserved.

% thanks to Kevin Murphy for suggesting this routine.
% thanks to Ruben Martinez-Cantin for UNDERSCORE_LAPACK_CALL

% check that we are in the right directory
d = dir('install_lightspeed.m');
if length(d) == 0
	error('You must run install_lightspeed from within the lightspeed directory.  Use cd to change the current directory.');
end
fprintf('Compiling lightspeed 2.7 mex files...\n');

% Matlab version
v = sscanf(version,'%d.%d.%*s (R%d) %*s');
% v(3) is the R number
% could also use v(3)>=13
atleast65 = (v(1)>6 || (v(1)==6 && v(2)>=5));
atleast73 = (v(1)>7 || (v(1)==7 && v(2)>=3));
atleast75 = (v(1)>7 || (v(1)==7 && v(2)>=5));
atleast76 = (v(1)>7 || (v(1)==7 && v(2)>=6));
atleast78 = (v(1)>7 || (v(1)==7 && v(2)>=8)); % R2009a
atleast82 = (v(1)>8 || (v(1)==8 && v(2)>=2)); % R2013b
atleast83 = (v(1)>8 || (v(1)==8 && v(2)>=3)); % R2014a
if atleast73
	% largeArrayDims and mwSize were added in version 7.3 (R2006b)
	% http://www.mathworks.com/help/techdoc/rn/bqt6wtq.html
	flags = ' -largeArrayDims ';
else
	flags = ' -DmwSize=int -DmwIndex=int ';
end
if atleast83
	flags = [flags ' -silent '];
end
% To catch ANSI C violations with gcc:
%flags = [flags ' CFLAGS=''-Wall -std=c99'' '];
replace_repmat = ~atleast82;

% copy matlab's original repmat.m as xrepmat.m
if replace_repmat && exist('xrepmat.m') ~= 2
  w = fullfile(matlabroot,'toolbox','matlab','elmat','repmat.m');
  cmd = ['"' w '" xrepmat.m'];
  if ispc
    system(['copy ' cmd]);
  else
    system(['cp -rp ' cmd]);
  end
end

% these are done first to initialize mex
eval(['mex' flags '-c flops.c']);
eval(['mex' flags 'sameobject.c']);
eval(['mex' flags 'int_hist.c']);
eval(['mex' flags '-c mexutil.c']);
eval(['mex' flags '-c util.c']);

libdir = '';
if ispc || atleast83
	[compiler,options] = mexcompiler;
	libdir = options.LIBLOC;
	engmatopts = [compiler 'engmatopts.bat'];
elseif ismac
	options = struct;
	% this installer is set up for 64-bit MacOSX 10.6 with gcc-4.0
	% if you are using something else, run 'mex -v -c flops.c'
	% and use the output to change these strings
	options.COMPILER = 'gcc-4.0';
	options.COMPFLAGS = '-fno-common -no-cpp-precomp -arch x86_64 -isysroot /Developer/SDKs/MacOSX10.6.sdk -mmacosx-version-min=10.6  -fexceptions';
	options.OPTIMFLAGS = '-O -DNDEBUG';
else
	options = struct;
	options.COMPILER = 'cc';
	options.COMPFLAGS = '-fPIC';
	options.OPTIMFLAGS = '-O';
end

% Routines that use LAPACK
lapacklib = '';
blaslib = '';
lapackflags = flags;
if atleast78
	lapackflags = [lapackflags ' -DBLAS64'];
end
if ispc
  if strncmp(compiler,'MSVC',4)
    if atleast65
      % version >= 6.5
      lapacklib = fullfile(libdir,'libmwlapack.lib');
    end
  else
    lapacklib = fullfile(libdir,'libmwlapack.lib');
  end
  if atleast75
    blaslib = fullfile(libdir,'libmwblas.lib');
  end
  %%% Paste the location of libmwlapack.lib %%%
  %lapacklib = '';
  if ~exist(lapacklib,'file')
    lapacklib = 'dtrsm.c';
    fprintf('libmwlapack.lib was not found.  To get additional optimizations, paste its location into install_lightspeed.m\n');
  else
    fprintf('Using the lapack library at %s\n',lapacklib);
  end
else
  % in version 7.5, non-PC systems do not need to specify lapacklib,
  % but they must use an underscore when calling lapack routines
	% http://www.mathworks.com/help/techdoc/matlab_external/br_2m24-1.html
	lapackflags = [lapackflags ' -DUNDERSCORE_LAPACK_CALL'];
  if atleast76
    lapacklib = '-lmwlapack';
    blaslib = '-lmwblas';
  end
end
disp(['mex' lapackflags ' solve_triu.c "' lapacklib '" "' blaslib '"'])
eval(['mex' lapackflags ' solve_triu.c "' lapacklib '" "' blaslib '"']);
eval(['mex' lapackflags ' solve_tril.c "' lapacklib '" "' blaslib '"']);

if ispc
  % Windows
  %if exist('util.obj','file')
  eval(['mex' flags 'addflops.c flops.obj'])
	if atleast78
		[success,message] = mkdir('@double');
		eval(['mex' flags 'gammaln.c util.obj -outdir @double'])
	else
		eval(['mex' flags 'gammaln.c util.obj'])
	end
  eval(['mex' flags 'digamma.c util.obj'])
  eval(['mex' flags 'trigamma.c util.obj'])
  eval(['mex' flags 'tetragamma.c util.obj'])
	eval(['mex' flags 'setnonzeros.c'])
  if strncmp(compiler,'MSVC',4)
		clear random.dll randomseed randbinom randgamma sample_hist
    disp(['install_random.bat "' options.VSINSTALLDIR '" ' options.vcvarsopts]);
    system(['install_random.bat "' options.VSINSTALLDIR '" ' options.vcvarsopts]);
    eval(['mex' flags 'randomseed.c util.obj random.lib'])
    eval(['mex' flags 'randbinom.c mexutil.obj util.obj random.lib'])
    eval(['mex' flags 'randgamma.c mexutil.obj util.obj random.lib'])
    eval(['mex' flags 'sample_hist.c util.obj random.lib'])
  else
    fprintf('mexcompiler is not MSVC. The randomseed() function will have no effect.');
    eval(['mex' flags 'randomseed.c util.obj random.c'])
    eval(['mex' flags 'randbinom.c mexutil.obj util.obj random.c'])
    eval(['mex' flags 'randgamma.c mexutil.obj util.obj random.c'])
    eval(['mex' flags 'sample_hist.c util.obj random.c'])
  end
	if replace_repmat
		eval(['mex' flags 'repmat.c mexutil.obj'])
	else
		eval(['mex' flags 'repmat.c mexutil.obj -output xrepmat'])
	end
  try
    % standalone programs
    % compilation instructions are described at:
    % http://www.mathworks.com/access/helpdesk/help/techdoc/matlab_external/ch1_im15.html#27765
    if atleast78
			%disp('lightspeed''s matfile utility is not supported for this version of Matlab, but everything else should work.');
		elseif atleast65
      % -V5 is required for Matlab >=6.5
      mex('-f',engmatopts,'matfile.c','-V5');
    else
      mex('-f',engmatopts,'matfile.c');
    end
    % uncomment the line below if you want to build test_flops.exe
    % This program lets you check the flop counts on your processor.
    % mex('-f',engmatopts,'tests/test_flops.c');
  catch
    disp('Could not install the standalone programs.');
    disp(lasterr)
  end
else
  % UNIX
  eval(['mex' flags 'addflops.c flops.o'])
	if atleast78
		[success,message] = mkdir('@double');
		eval(['mex' flags 'gammaln.c util.o -lm -outdir @double'])
	else
		eval(['mex' flags 'gammaln.c util.o -lm'])
	end
  eval(['mex' flags 'digamma.c util.o -lm'])
  eval(['mex' flags 'trigamma.c util.o -lm'])
  eval(['mex' flags 'tetragamma.c util.o -lm'])
	eval(['mex' flags 'setnonzeros.c'])
  if ismac
    % thanks to Nicholas Butko for these mac-specific lines
		clear librandom.dylib randomseed randbinom randgamma sample_hist
		cmd = [options.COMPILER ' ' options.COMPFLAGS ' ' options.OPTIMFLAGS ' -c random.c; ' options.COMPILER ' ' options.COMPFLAGS ' -dynamiclib -Wl,-install_name,"`pwd`/librandom.dylib" -o librandom.dylib random.o'];
		disp(cmd);
		system(cmd)
    eval(['mex' flags 'randomseed.c util.o librandom.dylib -lm'])
    eval(['mex' flags 'randbinom.c mexutil.o util.o librandom.dylib -lm'])
    eval(['mex' flags 'randgamma.c mexutil.o util.o librandom.dylib -lm'])
    eval(['mex' flags 'sample_hist.c util.o librandom.dylib -lm'])
  else
    % this command only works on linux
		clear librandom.so randomseed randbinom randgamma sample_hist
    cmd = [options.COMPILER ' ' options.COMPFLAGS ' ' options.OPTIMFLAGS ' -c random.c; ' options.COMPILER ' ' options.COMPFLAGS ' -shared -Wl,-E -Wl,-soname,`pwd`/librandom.so -o librandom.so random.o'];
		disp(cmd);
		system(cmd)
    eval(['mex' flags 'randomseed.c util.o librandom.so -lm'])
    eval(['mex' flags 'randbinom.c mexutil.o util.o librandom.so -lm'])
    eval(['mex' flags 'randgamma.c mexutil.o util.o librandom.so -lm'])
    eval(['mex' flags 'sample_hist.c util.o librandom.so -lm'])
  end
	if replace_repmat
		eval(['mex' flags 'repmat.c mexutil.o'])
	else
		eval(['mex' flags 'repmat.c mexutil.o -output xrepmat'])
	end
  try
    % standalone programs
    if atleast78
			disp('lightspeed''s matfile utility is not supported for this version of Matlab');
		elseif atleast65
      % -V5 is required only for Matlab >=6.5
      mex -f matopts.sh matfile.c -V5
    else
      mex -f matopts.sh matfile.c
    end
    % uncomment the line below if you want to build test_flops.exe
    % This program lets you check the flop counts on your processor.
    % mex -f matopts.sh tests/test_flops.c
  catch
    disp('Could not install the standalone programs.');
    disp(lasterr);
    fprintf('Note: if matlab cannot find matopts.sh, your installation of matlab is faulty.\nIf you get this error, don''t worry, lightspeed should still work.');
  end
end

addpath(genpath(pwd))
fprintf('Done.\n');
fprintf('Type "test_lightspeed" to verify the installation.\n');


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% To run:  Edit user options in npscalc.m and execute the code.
%
% This code is intended to accompany the manuscript entitled:
%
% A simple approach to measure computed tomography (CT) modulation transfer
% function (MTF) and noise-power spectrum (NPS) using the American College
% of Radiology (ACR) accreditation phantom
%
% Please do not distribute.
%
% This program requires the following 4 files:
%   npscalc.m
%   SliceBrowser.m
%   SliceBrowser.fig
%   license.txt
%
% Purpose:  To calculate the 3D NPS using CT data of the American College
%           of Radiology (ACR) accreditation phantom.
%
% Input:    Two consecutive scans of the phantom are needed.  The program
%           requires a data directory to be selected in which there are
%           only two subdirectories containing only CT slices corresponding
%           to the third module of the phantom.  Be careful of partial
%           volume effects with surrounding modules.
%
%           i.e.,  datadir
%                      |-> scan 1 dir
%                      |           | -> only module 3 slices
%                      |
%                      |-> scan 2 dir
%                                  |-> only module 3 slices
%
% Copyright 2012 Saul N. Friedman
%   Distributed under the terms of the "New BSD License."  Please see
%   license.txt.
%
% N.B.:  SliceBrowser.m is an altered version of code by Marian Uhercik.
%        Please see license.txt.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% ======================================================================
%> SLICEBROWSER M-file for SliceBrowser.fig
%>       SliceBrowser is an interactive viewer of 3D volumes,
%>       it shows 3 perpendicular slices (XY, YZ, ZX) with 3D pointer.
%>   Input:  a) VOLUME - a 3D matrix with volume data
%>           b) VOLUME - a 4D matrix with volume data over time
%>   Control:
%>       - Clicking into the window changes the location of 3D pointer.
%>       - 3D pointer can be moved also by keyboard arrows.
%>       - Pressing +/- will switch to next/previous volume.
%>       - Pressing 1,2,3 will change the focus of current axis.
%>       - Pressing 'e' will print the location of 3D pointer.
%>       - Pressing 'c' switches between color-mode and grayscale.
%>       - Pressing 'q' switches scaling of axis (equal/normal).
%>   Example of usage:
%>       load mri.dat
%>       volume = squeeze(D);
%>       SliceBrowser(volume);
%>
%> Last modified by Saul N. Friedman
% ======================================================================
function varargout = SliceBrowser(varargin)

% Documentation generated GUIDE:
%
%SLICEBROWSER M-file for SliceBrowser.fig
%      SLICEBROWSER, by itself, creates a new SLICEBROWSER or raises the existing
%      singleton*.
%
%      H = SLICEBROWSER returns the handle to a new SLICEBROWSER or the handle to
%      the existing singleton*.
%
%      SLICEBROWSER('Property','Value',...) creates a new SLICEBROWSER using the
%      given property value pairs. Unrecognized properties are passed via
%      varargin to SliceBrowser_OpeningFcn.  This calling syntax produces a
%      warning when there is an existing singleton*.
%
%      SLICEBROWSER('CALLBACK') and SLICEBROWSER('CALLBACK',hObject,...) call the
%      local function named CALLBACK in SLICEBROWSER.M with the given input
%      arguments.
%
%      *See GUI Options on GUIDE's Tools menu.  Choose "GUI allows only one
%      instance to run (singleton)".
%
% See also: GUIDE, GUIDATA, GUIHANDLES

% Edit the above text to modify the response to help SliceBrowser

% Last Modified by GUIDE v2.5 01-Sep-2008 13:14:20

% Begin initialization code - DO NOT EDIT
gui_Singleton = 0;
gui_State = struct('gui_Name',       mfilename, ...
                   'gui_Singleton',  gui_Singleton, ...
                   'gui_OpeningFcn', @SliceBrowser_OpeningFcn, ...
                   'gui_OutputFcn',  @SliceBrowser_OutputFcn, ...
                   'gui_LayoutFcn',  [], ...
                   'gui_Callback',   []);
if nargin && ischar(varargin{1})
   gui_State.gui_Callback = str2func(varargin{1});
end

if nargout
    [varargout{1:nargout}] = gui_mainfcn(gui_State, varargin{:});
else
    gui_mainfcn(gui_State, varargin{:});
end
% End initialization code - DO NOT EDIT

% --- Executes just before SliceBrowser is made visible.
function SliceBrowser_OpeningFcn(hObject, eventdata, handles, varargin)
% This function has no output args, see OutputFcn.
% hObject    handle to figure
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
% varargin   unrecognized PropertyName/PropertyValue pairs from the
%            command line (see VARARGIN)

% Choose default command line output for SliceBrowser
handles.output = hObject;

% Update handles structure
guidata(hObject, handles);

% UIWAIT makes SliceBrowser wait for user response (see UIRESUME)
% uiwait(handles.figure1);

if (length(varargin) <=0)
    error('Input volume has not been specified.');
end;
volume = varargin{1};
if (ndims(volume) ~= 3 && ndims(volume) ~= 4)
    error('Input volume must have 3 or 4 dimensions.');
end;
handles.volume = volume;

handles.axis_equal = 0;
handles.color_mode = 1;
if (size(volume,4) ~= 3)
    handles.color_mode = 0;
end;

% set main wnd title
set(gcf, 'Name', 'Slice Viewer')

% init 3D pointer
vol_sz = size(volume);
if (ndims(volume) == 3)
    vol_sz(4) = 1;
end;
pointer3dt = floor(vol_sz/2)+1;
handles.pointer3dt = pointer3dt;
handles.vol_sz = vol_sz;

plot3slices(hObject, handles);

% stores ID of last axis window
% (0 means that no axis was clicked yet)
handles.last_axis_id = 0;

% Update handles structure
guidata(hObject, handles);

% --- Outputs from this function are returned to the command line.
function varargout = SliceBrowser_OutputFcn(hObject, eventdata, handles)
% varargout  cell array for returning output args (see VARARGOUT);
% hObject    handle to figure
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Get default command line output from handles structure
varargout{1} = handles.output;

% --- Executes on mouse press over axes background.
function Subplot1_ButtonDownFcn(hObject, eventdata, handles)
% hObject    handle to Subplot1 (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
% This object contains the XY slice

xaxis = getappdata(0,'uaxis');
yaxis = getappdata(0,'vaxis');
zaxis = getappdata(0,'waxis');
axlabel = getappdata(0,'axlabel');
deltayaxis = yaxis(2) - yaxis(1);
deltaxaxis = xaxis(2) - xaxis(1);
deltazaxis = zaxis(2) - zaxis(1);

%disp('Subplot1:BtnDown');
pt=get(gca,'currentpoint');
if length(xaxis) > 0 && length(yaxis)
    xpos=round((pt(1,2)+xaxis(end))/deltaxaxis); ypos=round((pt(1,1)+yaxis(end))/deltayaxis);
else
xpos=round(pt(1,2)); ypos=round(pt(1,1));
end
zpos = handles.pointer3dt(3);
tpos = handles.pointer3dt(4);
handles.pointer3dt = [xpos ypos zpos tpos];
handles.pointer3dt = clipointer3d(handles.pointer3dt,handles.vol_sz);
plot3slices(hObject, handles);
% store this axis as last clicked region
handles.last_axis_id = 1;
% Update handles structure
guidata(hObject, handles);

% --- Executes on mouse press over axes background.
function Subplot2_ButtonDownFcn(hObject, eventdata, handles)
% hObject    handle to Subplot2 (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
% This object contains the YZ slice

xaxis = getappdata(0,'uaxis');
yaxis = getappdata(0,'vaxis');
zaxis = getappdata(0,'waxis');
axlabel = getappdata(0,'axlabel');
deltayaxis = yaxis(2) - yaxis(1);
deltaxaxis = xaxis(2) - xaxis(1);
deltazaxis = zaxis(2) - zaxis(1);

%disp('Subplot2:BtnDown');
pt=get(gca,'currentpoint');
if length(xaxis) > 0 && length(zaxis)
    xpos=round((pt(1,2)+xaxis(end))/deltaxaxis); zpos=round((pt(1,1)+zaxis(end))/deltazaxis);
else
xpos=round(pt(1,2)); zpos=round(pt(1,1));
end
ypos = handles.pointer3dt(2);
tpos = handles.pointer3dt(4);
handles.pointer3dt = [xpos ypos zpos tpos];
handles.pointer3dt = clipointer3d(handles.pointer3dt,handles.vol_sz);
plot3slices(hObject, handles);
% store this axis as last clicked region
handles.last_axis_id = 2;
% Update handles structure
guidata(hObject, handles);

% --- Executes on mouse press over axes background.
function Subplot3_ButtonDownFcn(hObject, eventdata, handles)
% hObject    handle to Subplot3 (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
% This object contains the XZ slice

xaxis = getappdata(0,'uaxis');
yaxis = getappdata(0,'vaxis');
zaxis = getappdata(0,'waxis');
axlabel = getappdata(0,'axlabel');
deltayaxis = yaxis(2) - yaxis(1);
deltaxaxis = xaxis(2) - xaxis(1);
deltazaxis = zaxis(2) - zaxis(1);

%disp('Subplot3:BtnDown');
pt=get(gca,'currentpoint');
if length(xaxis) > 0 && length(zaxis)
    ypos=round((pt(1,1)+yaxis(end))/deltayaxis); zpos=round((pt(1,2)+zaxis(end))/deltazaxis);
else
zpos=round(pt(1,2)); ypos=round(pt(1,1));
end
xpos = handles.pointer3dt(1);
tpos = handles.pointer3dt(4);
handles.pointer3dt = [xpos ypos zpos tpos];
handles.pointer3dt = clipointer3d(handles.pointer3dt,handles.vol_sz);
plot3slices(hObject, handles);
% store this axis as last clicked region
handles.last_axis_id = 3;
% Update handles structure
guidata(hObject, handles);

% --- Executes on key press with focus on SliceBrowserFigure and no controls selected.
function SliceBrowserFigure_KeyPressFcn(hObject, eventdata, handles)
% hObject    handle to SliceBrowserFigure (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

%disp('SliceBrowserFigure_KeyPressFcn');
curr_char = int8(get(gcf,'CurrentCharacter'));
if isempty(curr_char)
    return;
end;

xpos = handles.pointer3dt(1);
ypos = handles.pointer3dt(2);
zpos = handles.pointer3dt(3);
tpos = handles.pointer3dt(4);
% Keys:
% - up:   30
% - down:   31
% - left:   28
% - right:   29
% - '1': 49
% - '2': 50
% - '3': 51
% - 'e': 101
% - plus:  43
% - minus:  45
switch curr_char
    case 99 % 'c'
        handles.color_mode = 1 - handles.color_mode;
        if (handles.color_mode ==1 && size(handles.volume,4) ~= 3)
            handles.color_mode = 0;
        end;

    case 113 % 'q'
        handles.axis_equal = 1 - handles.axis_equal;

    case 30
        switch handles.last_axis_id
            case 1
                xpos = xpos -1;
            case 2
                xpos = xpos -1;
            case 3
                zpos = zpos -1;
            case 0
        end;
    case 31
        switch handles.last_axis_id
            case 1
                xpos = xpos +1;
            case 2
                xpos = xpos +1;
            case 3
                zpos = zpos +1;
            case 0
        end;
    case 28
        switch handles.last_axis_id
            case 1
                ypos = ypos -1;
            case 2
                zpos = zpos -1;
            case 3
                ypos = ypos -1;
            case 0
        end;
    case 29
        switch handles.last_axis_id
            case 1
                ypos = ypos +1;
            case 2
                zpos = zpos +1;
            case 3
                ypos = ypos +1;
            case 0
        end;
    case 43
        % plus key
        tpos = tpos+1;
    case 45
        % minus key
        tpos = tpos-1;
    case 49
        % key 1
        handles.last_axis_id = 1;
    case 50
        % key 2
        handles.last_axis_id = 2;
    case 51
        % key 3
        handles.last_axis_id = 3;
    case 101
        disp(['[' num2str(xpos) ' ' num2str(ypos) ' ' num2str(zpos) ' ' num2str(tpos) ']']);
    otherwise
        return
end;
handles.pointer3dt = [xpos ypos zpos tpos];
handles.pointer3dt = clipointer3d(handles.pointer3dt,handles.vol_sz);
plot3slices(hObject, handles);
% Update handles structure
guidata(hObject, handles);

% --- Plots all 3 slices XY, YZ, XZ into 3 subplots
function [sp1,sp2,sp3] = plot3slices(hObject, handles)
% pointer3d     3D coordinates in volume matrix (integers)

handles.pointer3dt;
size(handles.volume);
value3dt = handles.volume(handles.pointer3dt(1), handles.pointer3dt(2), handles.pointer3dt(3), handles.pointer3dt(4));

text_str = ['[X:' int2str(handles.pointer3dt(1)) ...
           ', Y:' int2str(handles.pointer3dt(2)) ...
           ', Z:' int2str(handles.pointer3dt(3)) ...
           ', Time:' int2str(handles.pointer3dt(4)) '/' int2str(handles.vol_sz(4)) ...
           '], value:' num2str(value3dt)];
set(handles.pointer3d_info, 'String', text_str);
guidata(hObject, handles);

if (handles.color_mode ==1)
    sliceXY = squeeze(handles.volume(:,:,handles.pointer3dt(3),:));
    sliceYZ = squeeze(handles.volume(handles.pointer3dt(1),:,:,:));
    sliceXZ = squeeze(handles.volume(:,handles.pointer3dt(2),:,:));

    max_xyz = max([ max(sliceXY(:)) max(sliceYZ(:)) max(sliceXZ(:)) ]);
    min_xyz = min([ min(sliceXY(:)) min(sliceYZ(:)) min(sliceXZ(:)) ]);
    clims = [ min_xyz max_xyz ];
else
    sliceXY = squeeze(handles.volume(:,:,handles.pointer3dt(3),handles.pointer3dt(4)));
    sliceYZ = squeeze(handles.volume(handles.pointer3dt(1),:,:,handles.pointer3dt(4)));
    sliceXZ = squeeze(handles.volume(:,handles.pointer3dt(2),:,handles.pointer3dt(4)));

    max_xyz = max([ max(sliceXY(:)) max(sliceYZ(:)) max(sliceXZ(:)) ]);
    min_xyz = min([ min(sliceXY(:)) min(sliceYZ(:)) min(sliceXZ(:)) ]);
    clims = [ min_xyz max_xyz ];
end;
sliceZY = squeeze(permute(sliceYZ, [2 1 3]));

xaxis = getappdata(0,'uaxis');
yaxis = getappdata(0,'vaxis');
zaxis = getappdata(0,'waxis');
axlabel = getappdata(0,'axlabel');
if length(xaxis) > 0 && length(yaxis) && length(zaxis)
deltayaxis = yaxis(2) - yaxis(1);
deltaxaxis = xaxis(2) - xaxis(1);
deltazaxis = zaxis(2) - zaxis(1);
end

sp1 = subplot(2,2,1);
%colorbar;
if length(xaxis) > 0 && length(yaxis)
    imagesc([yaxis(1) yaxis(end)],[xaxis(1) xaxis(end)],sliceXY,clims)
else
imagesc(sliceXY, clims);
end
if (handles.axis_equal == 1)
    axis image;
else
    axis normal;
end;
title('Slice XY');
if exist('axlabel')
    xlabel(['Y',axlabel]);ylabel(['X',axlabel])
else
ylabel('X');xlabel('Y');
end
if length(xaxis) > 0 && length(yaxis)
    line([handles.pointer3dt(2).*deltayaxis - yaxis(end) handles.pointer3dt(2).*deltayaxis - yaxis(end)], [-size(handles.volume,1) size(handles.volume,1)]);
    line([-size(handles.volume,2) size(handles.volume,2)], [handles.pointer3dt(1).*deltaxaxis - xaxis(end) handles.pointer3dt(1).*deltaxaxis - xaxis(end)]);
else
line([handles.pointer3dt(2) handles.pointer3dt(2)], [0 size(handles.volume,1)]);
line([0 size(handles.volume,2)], [handles.pointer3dt(1) handles.pointer3dt(1)]);
end
%set(allchild(gca),'ButtonDownFcn',@Subplot1_ButtonDownFcn);
set(allchild(gca),'ButtonDownFcn','SliceBrowser(''Subplot1_ButtonDownFcn'',gca,[],guidata(gcbo))');

sp2 = subplot(2,2,2);
if length(xaxis) > 0 &&length(zaxis) > 0
   imagesc([zaxis(1) zaxis(end)],[xaxis(1) xaxis(end)],sliceXZ,clims)
else
imagesc(sliceXZ, clims);
end
if (handles.axis_equal == 1)
    axis image;
else
    axis normal;
end;
title('Slice XZ');
if exist('axlabel')
    ylabel(['X',axlabel]);xlabel(['Z',axlabel])
else
ylabel('X');xlabel('Z');
end
if length(xaxis) > 0 && length(zaxis)
    line([handles.pointer3dt(3).*deltazaxis - zaxis(end) handles.pointer3dt(3).*deltazaxis - zaxis(end)], [-size(handles.volume,1).*deltaxaxis size(handles.volume,1).*deltaxaxis]);
line([-size(handles.volume,3).*deltazaxis size(handles.volume,3).*deltazaxis], [handles.pointer3dt(1).*deltaxaxis - xaxis(end) handles.pointer3dt(1).*deltaxaxis - xaxis(end)]);
else
line([handles.pointer3dt(3) handles.pointer3dt(3)], [0 size(handles.volume,1)]);
line([0 size(handles.volume,3)], [handles.pointer3dt(1) handles.pointer3dt(1)]);
end
%set(allchild(gca),'ButtonDownFcn',@Subplot2_ButtonDownFcn);
set(allchild(gca),'ButtonDownFcn','SliceBrowser(''Subplot2_ButtonDownFcn'',gca,[],guidata(gcbo))');

sp3 = subplot(2,2,3);
if length(yaxis) > 0 && length(zaxis) > 0
   imagesc([yaxis(1) yaxis(end)],[zaxis(1) zaxis(end)],sliceZY,clims)
else
imagesc(sliceZY, clims);
end
if (handles.axis_equal == 1)
    axis image;
else
    axis normal;
end;
title('Slice ZY');
if exist('axlabel')
    ylabel(['Z',axlabel]);xlabel(['Y',axlabel])
else
ylabel('Z');xlabel('Y');
end

if length(yaxis) > 0 && length(zaxis)
    line([-size(handles.volume,2) size(handles.volume,2)], [handles.pointer3dt(3).*deltazaxis - zaxis(end) handles.pointer3dt(3).*deltazaxis - zaxis(end)]);
    line([handles.pointer3dt(2).*deltayaxis - yaxis(end) handles.pointer3dt(2).*deltayaxis - yaxis(end)], [-size(handles.volume,3).*deltazaxis size(handles.volume,3).*deltazaxis]);
else
line([0 size(handles.volume,2)], [handles.pointer3dt(3) handles.pointer3dt(3)]);
line([handles.pointer3dt(2) handles.pointer3dt(2)], [0 size(handles.volume,3)]);
end
%set(allchild(gca),'ButtonDownFcn',@Subplot3_ButtonDownFcn);
set(allchild(gca),'ButtonDownFcn','SliceBrowser(''Subplot3_ButtonDownFcn'',gca,[],guidata(gcbo))');

function pointer3d_out = clipointer3d(pointer3d_in,vol_size)
pointer3d_out = pointer3d_in;
for p_id=1:4
    if (pointer3d_in(p_id) > vol_size(p_id))
        pointer3d_out(p_id) = vol_size(p_id);
    end;
    if (pointer3d_in(p_id) < 1)
        pointer3d_out(p_id) = 1;
    end;
end;


function [ b, m1, m2, r11, r12, r21, r22, seed ] = p36_p_val ( ...
  action, b, m1, m2, r11, r12, r21, r22, seed )

%*****************************************************************************80
%
%% P36_P_VAL sets or gets parameters for problem 36.
%
%  Discussion:
%
%    If ACTION is 'SET', the parameters are input values, and set
%    the internal values.
%
%    If ACTION is 'GET', the parametrs are output values, copied from
%    the internal values.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    18 October 2011
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, string ACTION, specifies the action desired.
%    'SET' sets the parameters;
%    'GET' gets the current values.
%
%    Input/output, real B, M1, M2, R11, R12, R21, R22,
%    problem parameters.
%
%    Input/output, integer SEED, a seed for the random number generator.
%
  persistent b_save
  persistent m1_save
  persistent m2_save
  persistent r11_save
  persistent r12_save
  persistent r21_save
  persistent r22_save
  persistent seed_save

  if ( action(1) == 'G' || action(1) == 'g' )

    b = b_save;
    m1 = m1_save;
    m2 = m2_save;
    r11 = r11_save;
    r12 = r12_save;
    r21 = r21_save;
    r22 = r22_save;
    seed = seed_save;

  elseif ( action(1) == 'S' || action(1) == 's' )

    b_save = b;
    m1_save = m1;
    m2_save = m2;
    r11_save = r11;
    r12_save = r12;
    r21_save = r21;
    r22_save = r22;
    seed_save = seed;

  end

  return
end


% --- Executes on selection change in popupmenuColorMap.
function popupmenuColorMap_Callback(hObject, eventdata, handles)
    handles.data.cmapName = get(handles.popupmenuColorMap, 'String');
    handles.data.cmapName = handles.data.cmapName(get(handles.popupmenuColorMap, 'Value'));
    handles.data.cmap = feval(handles.data.cmapName{1, 1}, 256);
    colormap(handles.focusWindow, handles.data.cmap);
    colormap(handles.spectogramWindow, handles.data.cmap);
end


% PARSETXT - parse text input into cell array
%
% Usage: >> cellarray = parsetxt( txt, delims );
%
% Inputs:
%    txt    - input text
%    delims - optional char array of delimiters (default: [' ' ',' 9]);
%
% Note: commas, and simple quotes are ignored
%
% Author: Arnaud Delorme, CNL / Salk Institute, 18 April 2002

% Copyright (C) 18 April 2002 Arnaud Delorme, SCCN/INC/UCSD, scott@sccn.ucsd.edu
%
% This file is part of EEGLAB, see http://www.eeglab.org
% for the documentation and details.
%
% Redistribution and use in source and binary forms, with or without
% modification, are permitted provided that the following conditions are met:
%
% 1. Redistributions of source code must retain the above copyright notice,
% this list of conditions and the following disclaimer.
%
% 2. Redistributions in binary form must reproduce the above copyright notice,
% this list of conditions and the following disclaimer in the documentation
% and/or other materials provided with the distribution.
%
% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
% THE POSSIBILITY OF SUCH DAMAGE.

function cellarray = parsetxt(txt, delims);

if nargin < 1
	help parsetxt;
	return;
end;

if nargin < 2
    delims = [' ' ',' 9 '"' '''' ];
end

cellarray = {};
tmptxt = '';
for index =1:length(txt)
    if ~isempty(findstr(txt(index), delims))
        if ~isempty(tmptxt), cellarray = { cellarray{:}, tmptxt }; end
        tmptxt = '';
    else
        tmptxt = [ tmptxt txt(index) ];
    end
end;
if ~isempty(tmptxt)
    cellarray = { cellarray{:}, tmptxt };
end
return;