function varargout = process_pac_dynamic_sur2( varargin )
% PROCESS_PAC_DYNAMIC: Compute the Time resolved Phase-Amplitude Coupling
%
% DOCUMENTATION

% @=============================================================================
% 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: Soheila Samiee, Francois Tadel 2013
%
% Updates:
%   - 1.0.4:  Soheila
%   - 2.0.0: Soheila, based on dpac v.1.1.3 and surrogate 3, June 2016
%   - 2.1:   SS, Based on dpac v2.1.0, Nov. 2016
%   - 3.1:   SS, block resampling (N = 2)
%   - 3.3:   SS, number of blocks for resampling could be more than 2
%            (default =5 ), May 2017
eval(macro_method);
end

%% ===== GET DESCRIPTION =====
function sProcess = GetDescription() %#ok<DEFNU>
    % Description the process
    sProcess.Comment     = 'Surrogate tPAC - block resampling - version: 3.4';
    sProcess.FileTag     = '';
%     sProcess.Category    = 'Custom';
%     sProcess.SubGroup    = 'dPAC analysis';
    sProcess.Category    = 'Custom';
    sProcess.SubGroup    = {'Frequency','Time-resolved Phase-Amplitude Coupling'};
    sProcess.Index       = 660;
    % Definition of the input accepted by this process
    sProcess.InputTypes  = {'raw',      'data',     'results',  'matrix'};
    sProcess.OutputTypes = {'timefreq', 'timefreq', 'timefreq', 'timefreq'};
    sProcess.nInputs     = 1;
    sProcess.nMinFiles   = 1;

    % === TIME WINDOW
    sProcess.options.timewindow.Comment = 'Time:';
    sProcess.options.timewindow.Type    = 'timewindow';
    sProcess.options.timewindow.Value   = [];
    % === NESTING FREQ
    sProcess.options.nesting.Comment = 'Frequency for phase band (low):';
    sProcess.options.nesting.Type    = 'range';
    sProcess.options.nesting.Value   = {[8, 12], 'Hz', 2};
    % === NESTED FREQ
    sProcess.options.nested.Comment = 'Frequency for amplitude band (high):';
    sProcess.options.nested.Type    = 'range';
    sProcess.options.nested.Value   = {[40, 150], 'Hz', 2};
        % Band for fa
%     sProcess.options.label_fa.Comment = 'F_A frequency band:';
%     sProcess.options.label_fa.Type    = 'label';
    sProcess.options.fa_type.Comment = {'   Single band', ...
                                        '   More than one center frequencies (default: 20)' };
    sProcess.options.fa_type.Type    = 'radio';
    sProcess.options.fa_type.Value   = 1;

    % === WINDOW LENGTH
    sProcess.options.winLen.Comment = 'Length of sliding time window:';
    sProcess.options.winLen.Type    = 'value';
    sProcess.options.winLen.Value   = {1.10, 'S', 2};

    % === NUMBER OF SURROGATE DATASETS
    sProcess.options.Nsurrogate.Comment    = 'Number of surrogate datasets: ';
    sProcess.options.Nsurrogate.Type       = 'value';
    sProcess.options.Nsurrogate.Value      = {100, '', 0};

    % === SOURCES
    sProcess.options.label5.Comment = 'Sensors/sources to be investigated :';
    sProcess.options.label5.Type    = 'label';

    % === CLUSTERS
    sProcess.options.clusters.Comment = '';
    sProcess.options.clusters.Type    = 'scout_confirm';
    sProcess.options.clusters.Value   = {};
    sProcess.options.clusters.InputTypes = {'results'};

    % === SENSOR SELECTION
    sProcess.options.target_data.Comment    = 'Sensor types or names (empty=all): ';
    sProcess.options.target_data.Type       = 'text';
    sProcess.options.target_data.Value      = 'MEG, EEG';
    sProcess.options.target_data.InputTypes = {'data', 'raw'};
    % === SOURCE INDICES
    sProcess.options.target_res.Comment    = 'Source indices (empty=all): ';
    sProcess.options.target_res.Type       = 'text';
    sProcess.options.target_res.Value      = '';
    sProcess.options.target_res.InputTypes = {'results'};
    sProcess.options.label6.Comment = '(The indices will only be considered if the scouts are not selected)';
    sProcess.options.label6.Type    = 'label';

    % === ROW NAMES
    sProcess.options.target_tf.Comment    = 'Row names or indices (empty=all): ';
    sProcess.options.target_tf.Type       = 'text';
    sProcess.options.target_tf.Value      = '';
    sProcess.options.target_tf.InputTypes = {'timefreq', 'matrix'};

    % === LOOP METHOD
    sProcess.options.label1.Comment = 'Processing options [expert only]:';
    sProcess.options.label1.Type    = 'label';
    % === MAX_BLOCK_SIZE
    sProcess.options.max_block_size.Comment = 'Number of signals to process at once: ';
    sProcess.options.max_block_size.Type    = 'value';
    sProcess.options.max_block_size.Value   = {20, ' ', 0};

    % sProcess.options.filter_sensor.InputTypes = {'results'};
    % === AVERAGE OUTPUT FILES
    sProcess.options.label2.Comment = 'Output options:';
    sProcess.options.label2.Type    = 'label';
    sProcess.options.avgoutput.Comment = 'Save average PAC across trials';
    sProcess.options.avgoutput.Type    = 'checkbox';
    sProcess.options.avgoutput.Value   = 1;
%     % === SAVE PAC MAPS
%     sProcess.options.savefull.Comment = 'Save the full PAC maps';
%     sProcess.options.savefull.Type    = 'checkbox';
%     sProcess.options.savefull.Value   = 1;
end

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

%% ===== RUN =====
function OutputFiles = Run(sProcess, sInputsA) %#ok<DEFNU>
tic
    % Get options
    if isfield(sProcess.options, 'timewindow') && isfield(sProcess.options.timewindow, 'Value') && iscell(sProcess.options.timewindow.Value) && ~isempty(sProcess.options.timewindow.Value)
        OPTIONS.TimeWindow = sProcess.options.timewindow.Value{1};
    else
        OPTIONS.TimeWindow = [];
    end
    OPTIONS.FunctionVersion = sProcess.Comment;

    % Clusters
    if isfield(sProcess.options, 'clusters') && ~isempty(sProcess.options.clusters) && ~isempty(sProcess.options.clusters.Value)
        OPTIONS.Clusters = sProcess.options.clusters.Value;
    else
        OPTIONS.Clusters = [];
    end
    OPTIONS.BandNesting = sProcess.options.nesting.Value{1};
    OPTIONS.BandNested  = sProcess.options.nested.Value{1};
    OPTIONS.WinLen      = sProcess.options.winLen.Value{1};
    % Get target
    if  ~isempty(OPTIONS.Clusters)         % extract cluster
        OPTIONS.Target = OPTIONS.Clusters;
    elseif ismember(sInputsA(1).FileType, {'data','raw'}) && isfield(sProcess.options, 'target_data') && ~isempty(sProcess.options.target_data.Value)
        OPTIONS.Target = sProcess.options.target_data.Value;
    elseif strcmpi(sInputsA(1).FileType, 'results') && isfield(sProcess.options, 'target_res') && ~isempty(sProcess.options.target_res.Value)
        OPTIONS.Target = sProcess.options.target_res.Value;
    elseif ismember(sInputsA(1).FileType, {'timefreq', 'matrix'}) && isfield(sProcess.options, 'target_tf') && ~isempty(sProcess.options.target_tf.Value)
        OPTIONS.Target = sProcess.options.target_tf.Value;
    else
        OPTIONS.Target = [];
    end
    % All other options
    OPTIONS.MaxSignals   = sProcess.options.max_block_size.Value{1};
    if (strcmp(sInputsA(1).FileType,'data') && isempty(sProcess.options.target_data.Value)) || ...
            (strcmp(sInputsA(1).FileType,'results') && isempty(sProcess.options.target_res.Value))
        OPTIONS.isFullMaps   = 1;
    else
        OPTIONS.isFullMaps   = 0;
    end
    OPTIONS.isAvgOutput  = sProcess.options.avgoutput.Value;
    if (length(sInputsA) == 1)
        OPTIONS.isAvgOutput = 0;
    end
    OPTIONS.HighFreqs    = sProcess.options.fa_type.Value;
    OPTIONS.Nsur = sProcess.options.Nsurrogate.Value{1};

    % ===== INITIALIZE =====
    % Initialize output variables
    OutputFiles = {};
    sPAC_avg = [];
    nAvg = 0;
    % Initialize progress bar
    if bst_progress('isVisible')
        startValue = bst_progress('get');
    else
        startValue = 0;
    end
    % Options for LoadInputFile()
    if strcmpi(sInputsA(1).FileType, 'results')
        LoadOptions.LoadFull = 0;  % Load kernel-based results as kernel+data
    else
        LoadOptions.LoadFull = 1;  % Load the full file
    end
    LoadOptions.IgnoreBad   = 1;  % From raw files: ignore the bad segments
    LoadOptions.ProcessName = func2str(sProcess.Function);
    LoadOptions.TargetFunc = 'all';
    % Start the matlabpool for parallel processing in bst_pac

    % Loop over input files
    for iFile = 1:length(sInputsA)

        % ===== LOAD SIGNALS =====
        bst_progress('text', sprintf('PAC: Loading input file (%d/%d)...', iFile, length(sInputsA)));
        bst_progress('set', round(startValue + (iFile-1) / length(sInputsA) * 100));

        % Load input signals
        [sInput, nSignals, iRows] = bst_process('LoadInputFile', sInputsA(iFile).FileName, OPTIONS.Target, OPTIONS.TimeWindow, LoadOptions);
        if isempty(sInput) || isempty(sInput.Data)
            return;
        end

        % Get sampling frequency
        sRate = 1 / (sInput.Time(2) - sInput.Time(1));
        % Check the nested frequencies
        if (OPTIONS.BandNested(2) > sRate/3)
            % Warning
            strMsg = sprintf('Higher nesting frequency is too high (%d Hz) compared with sampling frequency (%d Hz): Limiting to %d Hz', round(OPTIONS.BandNested(2)), round(sRate), round(sRate/3));
            disp([10 'process_pac> ' strMsg]);
            bst_report('Warning', sProcess, [], strMsg);
            % Fix higher frequencyy
            OPTIONS.BandNested(2) = sRate/3;
        end
        % Check the extent of bandNested band
        if (OPTIONS.BandNested(2) <= OPTIONS.BandNested(1))
            bst_report('Error', sProcess, [], sprintf('Invalid frequency range: %d-%d Hz', round(OPTIONS.BandNested(1)), round(OPTIONS.BandNested(2))));
            continue;
        end

        % ===== COMPUTE PAC MEASURE =====
        % Number of blocks of signals
        MAX_BLOCK_SIZE = OPTIONS.MaxSignals;
        nBlocks = ceil(nSignals / MAX_BLOCK_SIZE);
        sPAC = [];
        % Display processing time
        disp(sprintf('Processing %d blocks of %d signals each.', nBlocks, MAX_BLOCK_SIZE));
        % Process each block of signals
        for iBlock = 1:nBlocks
%             tic
            bst_progress('text', sprintf('PAC: File %d/%d - Block %d/%d', iFile, length(sInputsA), iBlock, nBlocks));
            bst_progress('set', round(startValue + (iFile-1)/length(sInputsA)*100 + iBlock/nBlocks*100));

            % Indices of the signals
            iSignals = (iBlock-1)*MAX_BLOCK_SIZE+1 : min(iBlock*MAX_BLOCK_SIZE, nSignals);

            % Get signals to process
            if ~isempty(sInput.ImagingKernel)
                Fblock = sInput.ImagingKernel(iSignals,:) * sInput.Data;
            else
                Fblock = sInput.Data(iSignals,:);
            end

            % Defining the options
            PACoptions.doInterpolation = 1;%0               % Applying interpolation in frequency and time domain
            PACoptions.logCenters = 0; %1                   % Choose the center frequencies for f_A with log space in faBand
            PACoptions.overlap = 0.5;                       % Time window over lap (0<= value <1)
            if OPTIONS.HighFreqs ==1
                PACoptions.nHighFreqs = 1;                  % Number of high frequency centers
                PACoptions.doInterpolation = 0;
            else
                PACoptions.nHighFreqs = 20; %4               % Number of high frequency centers
            end
            PACoptions.nSur = OPTIONS.Nsur;
            OPTIONS.PACoptions = PACoptions;

            % Estimating DPAC
            sPACblock = Compute(Fblock, sRate,  OPTIONS.BandNested, OPTIONS.BandNesting, OPTIONS.WinLen, PACoptions);

            % Check for errors
            if isempty(sPACblock)
                return;
            end
            % Initialize output structure
            nTime = length(sPACblock.TimeOut);
            if isempty(sPAC)
                sPAC.ValPAC      = [];
                sPAC.NestingFreq = [];
                sPAC.NestedFreq  = [];
                sPAC.PhasePAC    = [];
                sPAC.DynamicPAC     = zeros(nSignals, nTime, length(sPACblock.HighFreqs), OPTIONS.Nsur);
                sPAC.DynamicNesting = zeros(nSignals, nTime, length(sPACblock.HighFreqs), OPTIONS.Nsur);
                sPAC.DynamicPhase   = zeros(nSignals, nTime, length(sPACblock.HighFreqs), OPTIONS.Nsur);
                sPAC.HighFreqs = sPACblock.HighFreqs;

                meanInputTime  = OPTIONS.TimeWindow(1)+(sPACblock.TimeOut(end)+OPTIONS.WinLen/2)/2;
                meanOutputTime = (sPACblock.TimeOut(1)+sPACblock.TimeOut(end))/2;
                sPAC.TimeOut   = sPACblock.TimeOut + (meanInputTime - meanOutputTime);
            end
            % Copy block results to output structure
            sPAC.DynamicPAC(iSignals,:,:,:)     = permute(sPACblock.DynamicPAC,[3,2,1,4]);
            sPAC.DynamicNesting(iSignals,:,:,:) = permute(sPACblock.DynamicNesting,[3,2,1,4]);
            sPAC.DynamicPhase(iSignals,:,:,:)   = permute(sPACblock.DynamicPhase,[3,2,1,4]);
        end

        % ===== APPLY SOURCE ORIENTATION =====
        if strcmpi(sInput.DataType, 'results') && (sInput.nComponents > 1)
            % Number of values per vertex
            switch (sInput.nComponents)
                case 2
                    sPAC.ValPAC         = (sPAC.ValPAC(1:2:end,:,:)           + sPAC.ValPAC(2:2:end,:,:))           / 2;
                    sPAC.NestingFreq    = (sPAC.NestingFreq(1:2:end,:,:)      + sPAC.NestingFreq(2:2:end,:,:))      / 2;
                    sPAC.NestedFreq     = (sPAC.NestedFreq(1:2:end,:,:)       + sPAC.NestedFreq(2:2:end,:,:))       / 2;
                    sPAC.PhasePAC       = (sPAC.PhasePAC(1:2:end,:,:)         + sPAC.PhasePAC(2:2:end,:,:))         / 2;
                    sPAC.DynamicPAC     = (sPAC.DynamicPAC(1:2:end,:,:,:)     + sPAC.DynamicPAC(2:2:end,:,:,:))     / 2;
                    sPAC.DynamicNesting = (sPAC.DynamicNesting(1:2:end,:,:,:) + sPAC.DynamicNesting(2:2:end,:,:,:)) / 2;
                    sInput.RowNames     = sInput.RowNames(1:2:end);
                    sPAC.DynamicPhase   = (sPAC.DynamicPhase(1:2:end,:,:,:)   + sPAC.DynamicPhase(2:2:end,:,:,:))   / 2;

                case 3
                    sPAC.ValPAC         = (sPAC.ValPAC(1:3:end,:,:)           + sPAC.ValPAC(2:3:end,:,:)           + sPAC.ValPAC(3:3:end,:,:))           / 3;
                    sPAC.NestingFreq    = (sPAC.NestingFreq(1:3:end,:,:)      + sPAC.NestingFreq(2:3:end,:,:)      + sPAC.NestingFreq(3:3:end,:,:))      / 3;
                    sPAC.NestedFreq     = (sPAC.NestedFreq(1:3:end,:,:)       + sPAC.NestedFreq(2:3:end,:,:)       + sPAC.NestedFreq(3:3:end,:,:))       / 3;
                    sPAC.PhasePAC       = (sPAC.PhasePAC(1:3:end,:,:)         + sPAC.PhasePAC(2:3:end,:,:)         + sPAC.PhasePAC(3:3:end,:,:))         / 3;
                    sPAC.DynamicPAC     = (sPAC.DynamicPAC(1:3:end,:,:,:)     + sPAC.DynamicPAC(2:3:end,:,:,:)     + sPAC.DynamicPAC(3:3:end,:,:,:))     / 3;
                    sPAC.DynamicNesting = (sPAC.DynamicNesting(1:3:end,:,:,:) + sPAC.DynamicNesting(2:3:end,:,:,:) + sPAC.DynamicNesting(3:3:end,:,:,:)) / 3;
                    sInput.RowNames     = sInput.RowNames(1:3:end);
                    sPAC.DynamicPhase   = (sPAC.DynamicPhase(1:3:end,:,:,:)   + sPAC.DynamicPhase(2:3:end,:,:,:)   + sPAC.DynamicPhase(3:3:end,:,:,:))   / 3;

            end
        end

        % ===== SAVE FILE =====
        % Detect incomplete lists of sources
        isIncompleteResult = strcmpi(sInput.DataType, 'results') && (length(sInput.RowNames) * sInput.nComponents < nSignals);
        % Comment
        Comment = 'Sur. DynamicPAC';
        if iscell(sInput.RowNames)
            % Find the scout name
            scoutName = sInput.RowNames{1};
            k = strfind(scoutName,'.');
            if isempty(k)
                Comment = [Comment, ': ' scoutName];
            else
                Comment = [Comment, ': ' scoutName(1:k-1)];
            end

        elseif (length(sInput.RowNames) == 1)
            Comment = [Comment, ': #', num2str(sInput.RowNames(1))];
        elseif isIncompleteResult
            Comment = [Comment, ': ', num2str(length(sInput.RowNames)), ' sources'];
        end

        if OPTIONS.isFullMaps
            Comment = [Comment, ' (Full)'];
        end
        % Output data type: if there are not all the sources, switch the datatype to "scout"
        if isIncompleteResult
            sInput.DataType = 'scout';
            % Convert source indices to strings
            if ~iscell(sInput.RowNames)
                sInput.RowNames = cellfun(@num2str, num2cell(sInput.RowNames), 'UniformOutput', 0);
            end
        end
        % Save each as an independent file
        if ~OPTIONS.isAvgOutput
            nAvg = 1;
            OutputFiles{end+1} = SaveFile(sPAC, sInput.iStudy, sInputsA(iFile).FileName, sInput, Comment, nAvg, OPTIONS);
        else
            % Compute online average of the connectivity matrices
            if isempty(sPAC_avg)
                sPAC_avg.ValPAC      = sPAC.ValPAC       ./ length(sInputsA);
                sPAC_avg.NestingFreq = sPAC.NestingFreq  ./ length(sInputsA);
                sPAC_avg.NestedFreq  = sPAC.NestedFreq   ./ length(sInputsA);
                sPAC_avg.PhasePAC(:,:,:,nAvg+1)       =  sPAC.PhasePAC;
                sPAC_avg.DynamicPAC(:,:,:,nAvg+1)     =  sPAC.DynamicPAC;
                sPAC_avg.DynamicNesting(:,:,:,nAvg+1) =  sPAC.DynamicNesting;
                sPAC_avg.DynamicPhase(:,:,:,nAvg+1)   =  sPAC.DynamicPhase;

                sPAC_avg.TimeOut     = sPAC.TimeOut;
                sPAC_avg.HighFreqs   = sPAC.HighFreqs;
            else
                sPAC_avg.ValPAC      = sPAC_avg.ValPAC      + sPAC.ValPAC      ./ length(sInputsA);
                sPAC_avg.NestingFreq = sPAC_avg.NestingFreq + sPAC.NestingFreq ./ length(sInputsA);
                sPAC_avg.NestedFreq  = sPAC_avg.NestedFreq  + sPAC.NestedFreq  ./ length(sInputsA);
                sPAC_avg.PhasePAC(:,:,:,nAvg+1)       =  sPAC.PhasePAC;
                sPAC_avg.DynamicPAC(:,:,:,nAvg+1)     =  sPAC.DynamicPAC;
                sPAC_avg.DynamicPhase(:,:,:,nAvg+1)   =  sPAC.DynamicPhase;
                sPAC_avg.DynamicNesting(:,:,:,nAvg+1) =  sPAC.DynamicNesting;
            end
            nAvg = nAvg + 1;
        end
    end

    % ===== SAVE AVERAGE =====
    if OPTIONS.isAvgOutput
        % Output study, in case of average
        [tmp, iOutputStudy] = bst_process('GetOutputStudy', sProcess, sInputsA);
        % Save file
        OutputFiles{1} = SaveFile(sPAC_avg, iOutputStudy, [], sInput, Comment, nAvg, OPTIONS);
    end
end

%% ========================================================================
%  ===== SUPPORT FUNCTIONS ================================================
%  ========================================================================

%% ===== SAVE FILE =====
function NewFile = SaveFile(sPAC, iOuptutStudy, DataFile, sInput, Comment, nAvg, OPTIONS)
    % ===== PREPARE OUTPUT STRUCTURE =====
    % Create file structure
    FileMat = db_template('timefreqmat');
    FileMat.TF        = sPAC.ValPAC;
    FileMat.Comment   = Comment;
    FileMat.Method    = 'dpac';
    FileMat.Measure   = 'maxpac';
    FileMat.DataFile  = file_win2unix(DataFile);
    FileMat.nAvg      = nAvg;
    FileMat.Freqs     = 0;
    % All the PAC fields
    FileMat.sPAC = rmfield(sPAC, 'ValPAC');
    % Time vector
    FileMat.Time = sPAC.TimeOut;

    % Output data type and Row names
    if isempty(OPTIONS.Target)
        FileMat.DataType = sInput.DataType;
        FileMat.RowNames = sInput.RowNames;
    elseif strcmpi(sInput.DataType, 'results') && ~isempty(OPTIONS.Target)
        FileMat.DataType = 'matrix';
        if isnumeric(sInput.RowNames)
        	FileMat.RowNames = cellfun(@num2str, num2cell(sInput.RowNames), 'UniformOutput', 0);
        else
            FileMat.RowNames = sInput.RowNames;
        end
    else
        FileMat.DataType = sInput.DataType;
        FileMat.RowNames = sInput.RowNames;
    end
    % Atlas
    if ~isempty(sInput.Atlas)
        FileMat.Atlas = sInput.Atlas;
    end
    if ~isempty(sInput.SurfaceFile)
        FileMat.SurfaceFile = sInput.SurfaceFile;
    end
    % History: Computation
    FileMat = bst_history('add', FileMat, 'compute', 'PAC measure (see the field "Options" for input parameters)');
    % Save options in the file
    FileMat.Options = OPTIONS;

    % ===== SAVE FILE =====
    % Get output study
    sOutputStudy = bst_get('Study', iOuptutStudy);
    % File tag
%     if OPTIONS.isFullMaps
        fileTag = 'timefreq_dpac_fullmaps';
%     else
%         fileTag = 'timefreq_dpac';
%     end
    % Output filename
    NewFile = bst_process('GetNewFilename', bst_fileparts(sOutputStudy.FileName), fileTag);
    % Save file
    bst_save(NewFile, FileMat, 'v6');
    % Add file to database structure
    db_add_data(iOuptutStudy, NewFile, FileMat);
    toc
end

% ===== COMPUTE PAC MEASURE =====
function sPAC = Compute(Xinput, sRate, faBand, fpBand, winLen, Options)
% USAGE:
%   sPAC = dPACestimate(Xinput, sRate, faBand, fpBand, winLen, PLvector)
%
% INPUTS:
%    - Xinput:        [nChannels,nTime] signal to process
%    - sRate:         Sampling frequency (Hz)
%    - faBand:        Nested Band: Minimum and maximum frequency for extraction of frequency for amplitude
%    - fpBand:        Nesting Band: Minimum and maximum frequency for extraction of frequency for phase (Hz)
%    - winLen:        Length of each time window for coupling estimation(S) (default: 1 Sec)
%    - isFullMaps:    If 1, save the full directPAC maps
%
% OUTPUTS:   sPAC structure [for each signal]
%    - TimeOut:        Output time vector (Sec)
%    - HighFreqs:       Frequency for amplitude vector
%    - ValPAC:         [nChannels, nTimeOut] Maximum PAC strength in each  time point
%    - NestedFreq:     [nChannels, nTimeOut] Fnested corresponding to maximum synchronization index in each time point
%    - NestingFreq:    [nChannels, nTimeOut] Fnesting corresponding to maximum synchronization index in each time point
%    - phasePAC:       [nChannels, nTimeOut] Phase corresponding to maximum synchronization index in each time point
%    - DynamicNesting: [nNestedCenters,nTimeOut,nChannels] Estimated nesting frequency for all times, channels and nested intervals
%    - DynamicPAC:     [nNestedCenters,nTimeOut,nChannels] full array of PAC
%
% DESCRIPTION:
%   Estimation of Phase Amplitude Coupling (PAC) with DPAC method.
%
% Author:  Soheila Samiee, 2013
%

if (nargin < 4) || isempty(fpBand)
    fpBand = [4, 8];
end
if (nargin < 5) || isempty(winLen)
    winLen = 1;
end
if ~isfield(Options, 'overlap')
    Options.overlap = 0.5;
end

if fpBand(2)>faBand(1)
    fpBand(2) = faBand(1)/2;
    error_msg = ['Maximum of Fp should be less than half of the minimum of Fa!' 10 10 ...
        'max{Fp} modified to ', num2str(fpBand(2))];
    bst_report('Error', sProcess, [], error_msg);
    disp(['Warning: ' error_msg]);
end

if winLen < 2*1/fpBand(1)
    error_msg = ['Window length is short for extracting this minimum fp!' 10 ...
        'Either increase window length to: ',num2str(2*1/fpBand(1)), ' or increase minimum fp to; ', num2str(2/winLen)];
    bst_report('Error', sProcess, [], error_msg);
    disp(['Warning: ' error_msg]);
    winLen = 2*1/fpBand(1);
end

% ===== SETTING THE PARAMETERS =====
tStep = winLen*(1-Options.overlap);     % Time step for sliding window on time (Sec) (Overlap: 50%)
margin = 2;%1                       % Margin (in time) for filtering (Sec) --- default: 2sec -> changed to 1 sec in May12,2016
hilMar = 1/5;                       % Percentage of margin for Hilber transform
nTS = size(Xinput,2);               % Number of temporal samples of data
bandNestingLen= max(3,1/(winLen+margin));% Length of band nesting -- considering the resolution in FFT domain with available window length
isMirror = 0;                       % Mirroring the data in filtering
isRelax  = 1;                       % Attenuation of the filter in the stopband (1 => 40 dB, 0 => 60 dB)
minExtracFreq = max(1/winLen, fpBand(1));  % minimum frequency that could be extracted as nestingFreq
sProcess = 'Process_pac_dynamic_sur2';

% Mode = Options.Mode;                % Mode of estimating the coupling (line or map)
doInterpolation = Options.doInterpolation;  % Applying interpolation in frequency and time domain
logCenters = Options.logCenters;    % Choose the center frequencies for f_A with log space in faBand
nHighFreqs = Options.nHighFreqs;    % Number of high frequency centers
% Mode = 'map';                       % Mode of estimating the coupling (line or map)
% doInterpolation = 1;                % Applying interpolation in frequency and time domain
% logCenters = 0; %0                  % Choose the center frequencies for f_A with log space in faBand
% nHighFreqs = 20;%4;                 % Number of high frequency centers
missedPcount = 0;                   % Number of intervals that do not have peak in their F_A envelope PSD
mirrorEffectSample = 40;            % Number of samples that can be affected due to mirroring effect

N = 5; % Number of blocks for shuffling

% ==== ADDING MARGING TO THE DATA => AVOID EDGE ARTIFACT (FILTERS AND HILBERT TRANSFORM) ====
nMargin = fix(margin*sRate);
nHilMar = fix(nMargin*hilMar);

if (nTS/sRate < 2*winLen)
    error_msg = 'Data length should be at least twice of window length';
    bst_report('Error', 'process_pac_dynamic', [], error_msg);
    disp(['Error: ' error_msg]);
    sPAC = [];
    return
% elseif (nTS < nMargin)
%     tmp = repmat([Xinput(:,end-1:-1:2),Xinput],1,ceil(nMargin/nTS));
%     Xinput = [tmp(:,end-nMargin+1-size(Xinput,2):end-size(Xinput,2)), Xinput, tmp(:,1:nMargin)];
%     clear tmp
% else
%     Xinput = [Xinput(:,nMargin+1:-1:2), Xinput, Xinput(:,end-1:-1:end-nMargin)];
end

% Zero-padding signal for the margin
Xinput = [zeros(size(Xinput,1),nMargin), Xinput, zeros(size(Xinput,1),nMargin)];

% ==== SETTING THE PARAMETERS OF THE FILTERS ====
if nHighFreqs > 1 %strcmp(Mode,'map')
%     Fconst = (faBand(end)-faBand(1))/nHighFreqs;                     % Frequency distant between successive fAs
    if logCenters
        nestedCenters = logspace(log10(faBand(1)),log10(faBand(end)),nHighFreqs);
    else
        nestedCenters = linspace(faBand(1),faBand(end),nHighFreqs);
    end
    Fstep = diff(nestedCenters)/2;  % the range of frequency around each nested center
    Fstep = [Fstep(1),Fstep,Fstep(end)];
    Fstep = max(Fstep, fpBand(2)/2);  % Minimum band width is defined to cover the whole interval between consecutive centre frequencies and at the same time consider all coupled frequencies to it in the range of interest.
else
    nestedCenters = mean(faBand);
    Fstep    = abs(faBand-nestedCenters);
end

fArolloff = [];
fProlloff = [];%1          % roll off frequency for filtering
sPAC.HighFreqs = nestedCenters;
nFa = length(nestedCenters);
nSources = size(Xinput,1);
isources = 1:nSources;
nTime = fix((nTS-fix(winLen*sRate))/fix(tStep*sRate))+1;
TimeOut = winLen/2 : tStep : winLen/2+(nTime-1)*tStep;        % Sec
nSur = Options.nSur;                % Number of surrogate itterations
PAC = zeros(nFa,nTime,nSources,nSur);               % PAC measure
nestingFreq = zeros(nFa,nTime,nSources,nSur);
DynamicPhase= zeros(nFa,nTime,nSources);               % PAC measure

% ===== MAIN LOOP ON FA ===== %
% Filtering in Fa band before cutting into smaller time windows
% (=> Higherfrequency resolution + faster process)

for ifreq=1:nFa
    % fA band
    bandNested = [nestedCenters(ifreq)-Fstep(ifreq),nestedCenters(ifreq)+Fstep(ifreq+1)];

    % Filtering in fA band
    Xnested = bst_bandpass_hfilter(Xinput, sRate,bandNested(1), bandNested(2), isMirror, isRelax, fArolloff);    % Filtering
    Xnested = Xnested(:,nMargin-nHilMar+1:end-nMargin+nHilMar);               % Removing part of the margin

    % Hilbert transform
    Z = hilbert(Xnested')';

    % Phase and envelope detection
    nestedEnv_total = abs(Z);             % Envelope of nested frequency rhythms
    nestedEnv_total = nestedEnv_total(:,nHilMar:end-nHilMar);              % Removing the margin

    % Loop on Time
    for iTime=1:nTime
        X = Xinput(:, (iTime-1)*fix(tStep*sRate)+[1:fix((2*margin+winLen)*sRate)]);
        nestedEnv = nestedEnv_total(:, (iTime-1)*fix(tStep*sRate)+[1:fix(winLen*sRate)]);

        % Time vector and number of samples
        nSample = size(nestedEnv,2);
        nFreq = 2^ceil(log2(nSample)+1);

        % Extraction of nesting frequency
        Ffft = abs(fft(nestedEnv-repmat(mean(nestedEnv,2),1,nSample),nFreq,2)).^2/nSample;
        freq = linspace(0,sRate,nFreq);
        x1 = X(:,nMargin+1:nMargin+fix(winLen*sRate));
        FfftSig = abs(fft(x1-repmat(mean(x1,2),1,nSample),nFreq,2)).^2/nSample;
        %%%

        % Finding the corresponding frequency component
        ind = bst_closest([minExtracFreq, fpBand(2)], freq);
        if freq(ind(1))<(minExtracFreq-diff(freq(1:2)))
            ind(1) = ind(1)+1;
        end
        if ind(2)>fpBand(2)
            ind(2) = ind(2)-1;
        end

                % Add previous and next point to the interval to give the algorithm
        % to find the local peaks even if they are in the first and last
        % point of interst in the spectrum
        if ind(1)>1
            ind(1) = ind(1)-1;
        end
        ind(2) = ind(2)+1;

        if freq(ind(1))<(minExtracFreq-diff(freq(1:2)))
            ind(1) = ind(1)+1;
        end

        indm = zeros(nSources,1);
        for iSource=1:nSources

            % Extracting the peak from envelope's PSD and then confirming
            % with a peak on the original signal
            [pks_env,locs_env] = findpeaks(Ffft(iSource,ind(1):ind(2)),'SORTSTR','descend');
            [pks_orig, locs_orig] = findpeaks(FfftSig(iSource,ind(1):ind(2)),'SORTSTR','descend');  % To check if a peak close to the coupled fp is available in the original signal
%             clear pks_env pks_orig

            % Ignore small peaks
            pks_orig = pks_orig/max(pks_orig);
            locs_orig = locs_orig(pks_orig>0.1);

            % Confirming the peak
            max_dist = max(1.5/winLen,1.5);     % maximum acceptable distance between peaks in evelope and the original signal's PSD
            count = 1;
            check_pks = 1;
            fp_loc = [];
            while check_pks && count<=length(locs_env)
                index = bst_closest(freq(locs_env(count)), freq(locs_orig));
                if abs(freq(locs_orig(index))-freq(locs_env(count)))<=max_dist
                    fp_loc = locs_env(count);
                    check_pks = 0;
                else
                    count = count+1;
                end
            end
            % If peak is not approved or no peak
            if isempty(fp_loc)
                fp_loc = 0;    % arbitrary value for fp  ==> will set the pac value to zero
                missedPcount = missedPcount +1;
            end

            indm(iSource) = fp_loc(1);
            clear pks_env locs_env
        end

        nestingFreq(ifreq,iTime,isources) = freq(ind(1)+indm-1);
        bandNesting = [max([squeeze(nestingFreq(ifreq,iTime,isources))-bandNestingLen/2,zeros(size(nestingFreq,3),1)],[],2),...
            squeeze(nestingFreq(ifreq,iTime,isources))+bandNestingLen/2];
        bandNesting(bandNesting<.15)=.15;

% Filtering in fP band
        if length(unique(bandNesting(:,1)))==1 && length(unique(bandNesting(:,2)))==1
            Xnesting = bst_bandpass_hfilter(X, sRate,bandNesting(1,1), bandNesting(1,2), isMirror, isRelax, fProlloff);    % Filtering
        else
            Xnesting = zeros(size(X));
            for i=1:length(isources)
                Xnesting(i,:) = bst_bandpass_hfilter(X(i,:), sRate, bandNesting(i,1), bandNesting(i,2),isMirror, isRelax, fProlloff);    % Filtering
            end
        end
        Xnesting = Xnesting(:,nMargin-nHilMar+1:fix((margin+winLen)*sRate)+nHilMar);              % Removing part of the margin
        % Hilbert transform
        Z = hilbert(Xnesting')';
        % Phase detection
        nestingPh = angle(Z-repmat(mean(Z,2),1,size(Z,2)));    % Phase of nesting frequency
        nestingPh = nestingPh(:,nHilMar:fix(winLen*sRate)+nHilMar-1);              % Removing the margin

        for ii=1:length(isources)
            iphase = find(diff(sign(nestingPh(ii,:) - nestingPh(ii,1)))==-2 | ...
                sign(nestingPh(ii,2:end)-nestingPh(ii,1))==0 | ...
                -(diff(sign(nestingPh(ii,:) - nestingPh(ii,1)))-1).*diff(nestingPh(ii,:)-nestingPh(ii,1)) >6 )-1;
            if isempty(iphase)
                iphase = length(nestingPh(ii,:));
            end
            amplitude = nestedEnv(ii,1:max(iphase));
            phase = nestingPh(ii,1:max(iphase));

            % Block resampling N=2
            numpoints=length(amplitude); %% number of sample points in raw signal

            if N==2
                minskip=numpoints/4; %% time lag must be at least this big
                maxskip=numpoints-numpoints/4; %% time lag must be smaller than this
                skip = ceil(minskip + (maxskip-minskip-1)*rand(2*nSur,1));
                skip(skip>maxskip)=[];
                skip(skip<minskip)=[];
                skip=skip(1:nSur,1);

                for iSur=1:nSur
                    surrogate_amplitude=[amplitude(skip(iSur):end) amplitude(1:skip(iSur)-1)];
                    PAC(ifreq,iTime,isources(ii),iSur) = sum(surrogate_amplitude.*exp(1i*phase),2)./max(iphase)./sqrt(mean(amplitude.^2,2));
                    DynamicPhase(ifreq,iTime,isources(ii),iSur) = angle(PAC(ifreq,iTime,isources(ii)));

                    if indm(ii)==0 % Fp not confirmed and arbitrary value for fp
                        PAC(ifreq,iTime,isources(ii),iSur) = 0;
                    end
                end

            elseif N>2 & N<numpoints/20
                % Block resampling N>2
%                 N = 10;
                numpoints=length(amplitude); %% number of sample points in raw signal
                block_len = fix(numpoints/N);
                remaining = numpoints - block_len*N;
                blocked_amp = reshape(amplitude(1:end-remaining),[block_len,N]);

                for iSur=1:nSur
                    order = randperm(N);
                    blocked_amp = blocked_amp(:,order);
                    surrogate_amplitude = [blocked_amp(:)', amplitude(end-remaining+1:end)];
                    PAC(ifreq,iTime,isources(ii),iSur) = sum(surrogate_amplitude.*exp(1i*phase),2)./max(iphase)./sqrt(mean(amplitude.^2,2));
                    DynamicPhase(ifreq,iTime,isources(ii),iSur) = angle(PAC(ifreq,iTime,isources(ii)));

                    if indm(ii)==0 % Fp not confirmed and arbitrary value for fp
                        PAC(ifreq,iTime,isources(ii),iSur) = 0;
                    end
                end

            else
                warning('Number of blocks should be at least 2, and less than 5% of data points')
            end
        end
    end
      disp(['iFreq: ', num2str(ifreq),' / ',num2str(nFa)]);
end

% ===== EXTRACTING THE PAC RELATED VALUES ===== %
[PACmax,maxInd] = max(abs(PAC),[],1);
% Fnested  = squeeze(nestedCenters(maxInd))';
% Sind     = repmat((1:nSources), nTime, 1);           % Source indices
% Tind     = repmat((1:nTime)', 1, nSources);              % Time indices
% linInd   = sub2ind(size(PAC),maxInd(:),Tind(:),Sind(:));
% Fnesting = reshape(nestingFreq(linInd),nTime,nSources)';
% phase    = reshape(angle(PAC(linInd)),nTime,nSources)'/pi*180;
% PACmax   = squeeze(PACmax)';

% ===== Interpolation in time domain for smoothing the results ==== %
if doInterpolation
    % Interpolation of PAC
    if nSources>1
        [X,Y,Z,W] = ndgrid(nestedCenters,TimeOut,[1:nSources], 1:nSur);
        ny = linspace(TimeOut(1), TimeOut(end), 2*length(TimeOut)-1);
        if logCenters
            nx = logspace(log10(nestedCenters(1)), log10(nestedCenters(end)), 2*nFa-1);
        else
            nx = linspace(nestedCenters(1), nestedCenters(end), 2*nFa-1);
        end
        [nX,nY,nZ,nW] = ndgrid(nx,ny,[1:nSources], 1:nSur);
        PAC = interpn(X,Y,Z,W,abs(PAC),nX,nY,nZ,nW,'linear',0);
        % make the notations similar
        tmp = nx; nx = ny;ny = tmp; clear tmp;

    else
        [X,Y,Z] = meshgrid(TimeOut,nestedCenters,[1:nSur]);
        nx = linspace(TimeOut(1), TimeOut(end), 2*nTime-1);
        if logCenters
            ny = logspace(log10(nestedCenters(1)), log10(nestedCenters(end)), 2*nFa-1);
        else
            ny = linspace(nestedCenters(1), nestedCenters(end), 2*nFa-1);
        end
        [nX,nY,nZ] = meshgrid(nx,ny,[1:nSur]);
        PAC = interp3(X,Y,Z,abs(squeeze(PAC)),nX,nY,nZ,'linear',0);
        PAC = permute(PAC,[1,2,4,3]);
    end
    TimeOut = nx;
    sPAC.HighFreqs = ny;
    clear nx nX nY nZ X Y Z

    % nestingFreq
    tmp = zeros(nFa*2-1, nTime, nSources, nSur);
    tmp(1:2:end,:,:,:) = nestingFreq;
    tmp(2:2:end,:,:,:) = nestingFreq(1:end-1,:,:,:);
    tmp2 = zeros(nFa*2-1, nTime*2-1, nSources, nSur);
    tmp2(:,1:2:end,:,:) = tmp;
    tmp2(:,2:2:end,:,:) = tmp(:,1:end-1,:,:);
    nestingFreq = tmp2;

    tmp = zeros(nFa*2-1, nTime, nSources, nSur);
    tmp(1:2:end,:,:,:) = DynamicPhase;
    tmp(2:2:end,:,:,:) = DynamicPhase(1:end-1,:,:,:);
    tmp2 = zeros(nFa*2-1, nTime*2-1, nSources, nSur);
    tmp2(:,1:2:end,:,:) = tmp;
    tmp2(:,2:2:end,:,:) = tmp(:,1:end-1,:,:);
    DynamicPhase = tmp2;
    clear tmp tmp2

end

Fnesting = [];
Fnested = [];
phase = [];

if missedPcount>0
disp(['Missed Peaks:',num2str(missedPcount),'/',num2str(nFa*nTime*nSources)])
end

% ===== OUTPUTS ===== %
if nTime >1
    sPAC.ValPAC = PACmax;
    sPAC.NestingFreq = Fnesting;
    sPAC.NestedFreq  = Fnested;
    sPAC.PhasePAC = phase;
    sPAC.TimeOut  = TimeOut;
    sPAC.DynamicPAC(:,:,1:nSources,:) = abs(PAC);
    sPAC.DynamicNesting(:,:,1:nSources,:)  = nestingFreq;
    sPAC.DynamicPhase(:,:,1:nSources,:)  = DynamicPhase;

        % == Generating two time points for Brainstorm structure ==
else
    sPAC.ValPAC = [PACmax(:), PACmax(:)];
    sPAC.NestingFreq = [Fnesting(:), Fnesting(:)];
    sPAC.NestedFreq  = [Fnested(:), Fnested(:)];
    sPAC.PhasePAC = [phase(:), phase(:)];
    sPAC.TimeOut  = [TimeOut, TimeOut+0.001];
    sPAC.DynamicPAC(:,1:2,1:nSources,:) = repmat(abs(PAC),[1,2,1]);
    sPAC.DynamicNesting(:,1:2,1:nSources,:)  = repmat(abs(nestingFreq),[1,2,1]);
    sPAC.DynamicPhase(:,1:2,1:nSources,:)  = repmat(abs(DynamicPhase),[1,2,1]);
end

end


function [A, b, C, d, results] = inflate_region(obstacles, A_bounds, b_bounds, start, varargin)
import iris.*;

p = inputParser();
p.addOptional('require_containment', false, @isscalar);
p.addOptional('error_on_infeasible_start', false, @isscalar);
p.addOptional('termination_threshold', 2e-2, @(x) x > 0);
p.addOptional('iter_limit', 100, @isnumeric);
p.parse(varargin{:});
options = p.Results;

if exist('+iris/inflate_regionmex', 'file')
  if ~iscell(obstacles)
    obs_cell = cell(1, size(obstacles, 3));
    for j = 1:size(obstacles, 3)
      obs_cell{j} = obstacles(:,:,j);
    end
    obstacles = obs_cell;

    % Note: could replace this with the following, but mat2cell is 100 times slower than the for loop
    % obstacles = mat2cell(obstacles, size(obstacles, 1), size(obstacles, 2), ones(1, size(obstacles, 3)));
  end

  if nargout > 4
    [A, b, C, d, p_history, e_history] = inflate_regionmex(obstacles, A_bounds, b_bounds, start, options);
    results = inflation_results();
    results.start = start;
    results.obstacles = obstacles;
    results.n_obs = numel(obstacles);
    results.e_history = e_history;
    results.p_history = p_history;
  else
    [A, b, C, d] = inflate_regionmex(obstacles, A_bounds, b_bounds, start, options);
  end
else
  if iscell(obstacles)
    padded = pad_obstacle_points(obstacles);
    obstacle_pts = cell2mat(reshape(padded, size(padded, 1), [], length(obstacles)));
  else
    obstacle_pts = obstacles;
  end
  [A, b, C, d, results] = inflate_region_fallback(obstacle_pts, A_bounds, b_bounds, start, options);
  results.obstacles = obstacles;
  if ~iscell(results.obstacles)
    results.obstacles = mat2cell(results.obstacles, size(results.obstacles, 1), size(results.obstacles, 2), ones(1, size(results.obstacles, 3)));
  end
end


function cv2tifs(y,f)
%CV2TIFS Decodes a TIFS2CV compressed image sequence.
%   Y = CV2TIFS(Y,F) decodes compressed sequence Y (a structure
%   generated by TIFS2CV) and creates a multiframe TIFF file F.
%
%   See also TIFS2CV.
%
%   Copyright 2002-2020 Gatesmark
%
%   This function, and other functions in the DIPUM Toolbox, are based
%   on the theoretical and practical foundations established in the
%   book Digital Image Processing Using MATLAB, 3rd ed., Gatesmark
%   Press, 2020.
%
%   Book website: http://www.imageprocessingplace.com
%   License: https://github.com/dipum/dipum-toolbox/blob/master/LICENSE.txt

% Get the number of frames, block size, and reconstruction quality.
fcnt = double(y.frames);
m = double(y.blksz);
q = double(y.quality);

% Reconstruct the first image in the sequence and store.
if q == 0
    r = double(huff2mat(y.video(1)));
else
    r = double(jpeg2im(y.video(1)));
end
imwrite(uint8(r),f,'Compression','none','WriteMode','overwrite');

% Get the frame size and motion vectors.
fsz = size(r);
mvsz = [fsz/m 2 fcnt];
mv = int16(huff2mat(y.motion));
mv = reshape(mv,mvsz);

% For frames except the first, get a motion conpensated prediction
% residual and add to the proper reference subimages.
for i = 2:fcnt
    if q == 0
        pe = double(huff2mat(y.video(i)));
    else
        pe = double(jpeg2im(y.video(i)) - 255);
    end
    peC = im2col(pe,[m m],'distinct');

    for col = 1:size(peC,2)
        u = 1 + mod(m * (col - 1),fsz(1));
        v = 1 + m * floor((col - 1) * m / fsz(1));
        rx = u - mv(1 + floor((u - 1)/m), 1 + floor((v - 1)/m), ...
           1, i);
        ry = v - mv(1 + floor((u - 1)/m), 1 + floor((v - 1)/m), ...
           2, i);

        subimage = r(rx:rx + m - 1,ry:ry + m - 1);
        peC(:,col) = subimage(:) - peC(:,col);
    end

    r = col2im(double(uint16(peC)),[m m],fsz,'distinct');
    imwrite(uint8(r),f,'Compression','none', ...
        'WriteMode','append');
end


function view = getFlatCoords(view)
%
% view = getFlatCoords(view)
%
% view: must be a flat view
%
% Loads gLocs2d and gLocs3d coordinates.  Keeps only those voxels
% that correspond to the inplane coordinates.  Sets FLAT
% fields: coordsRight, coordsLeft, grayCoordsRight,
% grayCoordsLeft.
%
% djh, 7/98
%
% djh, 8/4/99.  Use intersectCols instead of intersecting the indices.
% djh, 8/18/99.  loadGLocs now returns curvature.

if ~strcmp(view.viewType,'Flat')
    myErrorDlg('function getFlatCoords only for Flat view.');
end

pathStr=fullfile(viewDir(view),'coords');

if ~check4File(pathStr)
    imSize=[0,0];
    waitHandle = mrvWaitbar(0,'Computing flat coordinates.  Please wait...');
    for h = 1:2
        mrvWaitbar((h-1)/2)

        % Load gLocs2d and gLocs3d
        %
        if h==1
            [gLocs2d,gLocs3d,curvature,leftPath] = loadGLocs('left');
        else
            [gLocs2d,gLocs3d,curvature,rightPath] = loadGLocs('right');
        end

        if isempty(gLocs2d) | isempty(gLocs3d)
            coords{h} = [];
            grayCoords{h} = [];
        else
            % Compute imSize
            %
            imSize = max(imSize,(max(gLocs2d,[],2) - min(gLocs2d,[],2) + 1)');
            imSize = round(imSize);

            % Find gray nodes that are both in the inplanes and included
            % in the unfold.
            % gray.coords are the gray coords that lie in the inplanes.
            % gLocs3d are the gray coords in the unfold.
            hiddenGray = initHiddenGray;
            [grayCoordsTmp,gLocsIndices,coordsIndices] = ...
                intersectCols(gLocs3d,hiddenGray.coords);
            grayCoords{h} = grayCoordsTmp;

            % Flat locations corresponding to those voxels
            %
            coords{h} = gLocs2d(:,gLocsIndices);

            % Warning if there are any NaNs in the coords.
            NaNs = find(isnan(coords{h}(1,:)) | isnan(coords{h}(2,:)));
            if ~isempty(NaNs)
                myWarnDlg(['You have ',int2str(length(NaNs)),' NaNs in your flat coords.  ',...
                        'Those gray matter nodes will not be rendered in the FLAT view.']);
            end
        end
    end
    close(waitHandle)

    % Save to file
    %
    save(pathStr,'coords','grayCoords','imSize','leftPath','rightPath');
end

% Load Flat/coords and fill the fields
%
load(pathStr);
view.coords = coords;
view.grayCoords = grayCoords;
view.leftPath = leftPath;
view.rightPath = rightPath;
view.ui.imSize = imSize;


function [source] = ft_source2full(source)

% FT_SOURCE2FULL recreates the grid locations outside the brain in the source
% reconstruction, so that the source volume again describes the full grid.
% This undoes the memory savings that can be achieved using FT_SOURCE2SPARSE
% and makes it possible again to plot  the source volume and save it to an
% external file.
%
% Use as
%   [source] = ft_source2full(source)
%
% See also FT_SOURCE2SPARSE

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

ft_defaults

if ~isfield(source, 'inside')  || ...
   ~isfield(source, 'outside') || ...
   ~isfield(source, 'dim')
  ft_error('one of the required fields is missing in the source structure');
end

if ~isfield(source, 'pos') && (~isfield(source, 'xgrid') || ~isfield(source, 'ygrid') || ...
                               ~isfield(source, 'zgrid'))
  ft_error('the input data needs at least a ''pos'' field, or ''x/y/zgrid''');
end

if isfield(source, 'xgrid'),
  xgrid = source.xgrid;
  ygrid = source.ygrid;
  zgrid = source.zgrid;
  sparsepos = source.pos;

  % recreate the positions of the dipole grid
  [X, Y, Z] = ndgrid(xgrid, ygrid, zgrid);
  pos = [X(:) Y(:) Z(:)];
else
  %FIXME this assumes that the voxel data are ordered as if in a regularly spaced 3D grid,
  %but with only the inside voxels present
  ft_warning('assuming the voxel data to be ordered as if in a regularly spaced 3D grid');
  xgrid = 1:source.dim(1);
  ygrid = 1:source.dim(2);
  zgrid = 1:source.dim(3);

  %establish a homogeneous transformation matrix from voxels to headspace based on the sparse positions
  sparsepos = source.pos;
  ok  = 0;
  cnt = 0;
  while ok==0,
    cnt  = cnt+1;
    dpos = sparsepos - sparsepos(cnt*ones(size(sparsepos,1),1),:);
    [srt, indx] = sort(sum(dpos.^2,2));
    srt    = dpos(indx,:);
    tmpsrt = abs(srt(2:7,:));
    csrt   = tmpsrt*tmpsrt';
    sel    = find(sum(csrt==0)>=2);
    if numel(sel)>=3,
      ok = 1;
    end
  end
  tmppos  = sparsepos(indx([1 sel(:)'+1]),:);
  tmpdpos = dpos(indx([1 sel(:)'+1]),:);

  % FIXME the following is a bit experimental and not fully tested yet it works in general case rotation
  M         = pinv(tmpdpos(2:4,:));

  % get rotation such that maxima are on diagonal and positive
  m(1) = find(M(1,:)==max(abs(M(1,:))));
  m(2) = find(M(2,:)==max(abs(M(2,:))));
  m(3) = find(M(3,:)==max(abs(M(3,:))));
  [srt, indx] = sort(m);
  M    = M(indx,:);
  M    = M*diag(sign(diag(M)));
  sparsepos = sparsepos*M;

  % translation
  T         = -min(sparsepos,[],1)+1;
  sparsepos = sparsepos + T(ones(size(sparsepos,1),1), :);

  % recreate the positions of the dipole grid
  [X, Y, Z] = ndgrid(xgrid, ygrid, zgrid);
  pos = [X(:) Y(:) Z(:)];
  pos = ft_warp_apply(inv([M T(:);0 0 0 1]), pos);
end

Nsparse = length(source.inside);
siz     = source.dim;
Nfull   = prod(siz);

% determine the size that each slice takes in memory
sx = 1;
sy = siz(1);
sz = siz(1) * siz(2);

if isfield(source, 'inside') && isfield(source, 'outside') && size(source.pos,1)==Nfull
  % it contains all source positions
  inside = source.inside;
  outside = source.outside;
else
  % it only contains the inside source positions, which are all inside the brain
  % reconstruct the original inside and outside grid locations
  inside = zeros(Nsparse,1);
  for i=1:Nsparse
    fx = find(xgrid==sparsepos(i,1));
    fy = find(ygrid==sparsepos(i,2));
    fz = find(zgrid==sparsepos(i,3));
      inside(i) = (fx-1)*sx + (fy-1)*sy + (fz-1)*sz + 1;
  end
  outside = setdiff([1:Nfull]', inside);
end

fprintf('total number of dipoles        : %d\n', length(inside)+length(outside));
fprintf('number of dipoles inside  brain: %d\n', length(inside));
fprintf('number of dipoles outside brain: %d\n', length(outside));

% determine whether the source is old or new style
fnames = fieldnames(source);
if any(~cellfun('isempty', strfind(fnames, 'dimord'))),
  stype = 'new';
else
  stype = 'old';
end

if strcmp(stype, 'old')
  % original code
  % first do the non-trial fields
  source.dim = [1 length(inside) 1]; %to fool parameterselection
  [param]    = parameterselection('all', source);
  trlparam   = strmatch('trial', param);
  sel        = setdiff(1:length(param), trlparam);
  ind=find(ismember(param,'inside')); % find the index of 'inside' field
  % because its position varies with isfield('plvspctrm') vs. 'cohspctrm'
  param      = param(sel(ind));

  for j = 1:length(param)
    dat = getsubfield(source, param{j});
    if islogical(dat)
      tmp         = false(1,Nfull);
      tmp(inside) = dat;
    elseif iscell(dat)
      tmp          = cell(1,Nfull);
      tmp(inside)  = dat;
      %tmp(outside) = nan;
    else
      tmp         = nan(1,Nfull);
      tmp(inside) = dat;
    end
    source = setsubfield(source, param{j}, tmp);
  end

  % then do the trial fields
  if     isfield(source, 'trial' )
    for j = 1:length(source.trial)
      tmpsource     = source.trial(j);
      tmpsource.dim = source.dim; % to fool parameterselection
      tmpparam      = parameterselection('all', tmpsource);
      for k = 1:length(tmpparam)
        dat = getsubfield(tmpsource, tmpparam{k});
        if islogical(dat)
          tmp         = false(1,Nfull);
          tmp(inside) = dat;
        elseif iscell(dat)
          tmp          = cell(1,Nfull);
          tmp(inside)  = dat;
          %tmp(outside) = nan;
        else
          tmp         = nan(1,Nfull);
          tmp(inside) = dat;
        end
        tmpsource = setsubfield(tmpsource, tmpparam{k}, tmp);
      end
      tmpsource       = rmfield(tmpsource, 'dim');
      source.trial(j) = tmpsource;
    end
  elseif isfield(source, 'trialA')
    for j = 1:length(source.trialA)
      tmpsource     = source.trialA(j);
      tmpsource.dim = source.dim; % to fool parameterselection
      tmpparam      = parameterselection('all', tmpsource);
      for k = 1:length(tmpparam)
        dat = getsubfield(tmpsource, tmpparam{k});
        if islogical(dat)
          tmp         = false(1,Nfull);
          tmp(inside) = dat;
        elseif iscell(dat)
          tmp          = cell(1,Nfull);
          tmp(inside)  = dat;
          %tmp(outside) = nan;
        else
          tmp         = nan(1,Nfull);
          tmp(inside) = dat;
        end
        tmpsource = setsubfield(tmpsource, tmpparam{k}, tmp);
      end
      tmpsource        = rmfield(tmpsource, 'dim');
      source.trialA(j) = tmpsource;
    end
  elseif isfield(source, 'trialB')
    for j = 1:length(source.trialB)
      tmpsource     = source.trialB(j);
      tmpsource.dim = source.dim; % to fool parameterselection
      tmpparam      = parameterselection('all', tmpsource);
      for k = 1:length(tmpparam)
        dat = getsubfield(tmpsource, tmpparam{k});
        if islogical(dat)
          tmp         = false(1,Nfull);
          tmp(inside) = dat;
        elseif iscell(dat)
          tmp          = cell(1,Nfull);
          tmp(inside)  = dat;
          %tmp(outside) = nan;
        else
          tmp         = nan(1,Nfull);
          tmp(inside) = dat;
        end
        tmpsource = setsubfield(tmpsource, tmpparam{k}, tmp);
      end
      tmpsource        = rmfield(tmpsource, 'dim');
      source.trialB(j) = tmpsource;
    end
  end

  % and finally do the coherence-like matrices (size Nvox X Nvox)
  fn = fieldnames(source);
  for i=1:length(fn)
    d = getfield(source, fn{i});
    m = size(d, 1);
    n = size(d, 2);
    if m==Nsparse && n==Nsparse
      tmp = nan(Nfull,Nfull);
      tmp(inside,inside) = d;
      source = setfield(source, fn{i}, tmp);
    end
  end

  % update the inside and outside definitions
  source.inside  = inside;
  source.outside = outside;
  source.pos     = pos;
  source.dim     = siz;
elseif strcmp(stype, 'new')
  % new style conversion
  fn = fieldnames(source);
  for i=1:numel(fn)
    if any(size(source.(fn{i}))==Nsparse)
      if iscell(source.(fn{i}))
        indx = find(size(source.(fn{i}))==Nsparse);
        if all(indx==1)
          tmp            = cell(Nfull,1);
          tmp(inside,1)  = source.(fn{i});
          source.(fn{i}) = tmp;
        elseif all(indx==2)
          tmp            = cell(1,Nfull);
          tmp(1,inside)  = source.(fn{i});
          source.(fn{i}) = tmp;
        else
          ft_warning('sparse to full conversion failed for field %s\n', fn{i});
        end
      else
        indx = find(size(source.(fn{i}))==Nsparse);
        if all(indx==1)
          tmpsiz = [size(source.(fn{i})) 1];
          tmp    = nan([Nfull tmpsiz(2:end)]);
          tmp(inside,:,:,:,:) = source.(fn{i});
        elseif all(indx==2)
          tmpsiz = [size(source.(fn{i})) 1];
          tmp    = nan([tmpsiz(1) Nfull tmpsiz(3:end)]);
          tmp(:,inside,:,:,:) = source.(fn{i});
        elseif all(indx==[1 2])
          % bivariate matrix
          tmpsiz = [size(source.(fn{i})) 1];
          tmp    = nan([Nfull Nfull tmpsiz(3:end)]);
          tmp(inside,inside,:,:,:) = source.(fn{i});
        else
          ft_warning('sparse to full conversion failed for field %s\n', fn{i});
        end
      end
      % nothing to do
    end
  end

  % update the inside and outside definitions and pos
  source.inside  = inside;
  source.outside = outside;
  source.pos     = pos;

end
cfg = [];
% add version information to the configuration
try
  % get the full name of the function
  cfg.version.name = mfilename('fullpath');
catch
  % required for compatibility with MATLAB versions prior to release 13 (6.5)
  [st, i] = dbstack;
  cfg.version.name = st(i);
end
cfg.version.id = '$Id$';
% remember the configuration details of the input data
try, cfg.previous = source.cfg; end
% remember the exact configuration details in the output
source.cfg = cfg;


function rs = spec(s)

%tstoolbox/@signal/spec
%   Syntax:
%     * rs = spec(s)
%
%   compute power spectrum for real valued scalar signals. Multivariate
%   signals are accepted but may produce unwanted results as only the
%   spectrum of the first column is returned.
%
% Copyright 1997-2001 DPI Goettingen, License http://www.physik3.gwdg.de/tstool/gpl.txt

narginchk(1,1);

c = spec(s.core); 	% call real working routine for parent core object
rs = signal(c, s);	% special constructor calling syntax for working routines

a = getaxis(s, 1);
rs = setaxis(rs, 1, achse(unit(a)^(-1),0, samplerate(a)/dlens(s,1)));
rs = addhistory(rs, 'Calculated spectrum (spec)');
rs = setyunit(rs, yunit(s)^2);
rs = addcommandlines(rs, 's = spec(s');


function beamlet = createIMBeamlet(doseV, indV, beamNum, fullLength)
%"createIMBeamlet"
%   Take a vector of dose values and a vector of indices into the scan
%   array and build an IM.beamlets element.
%
%JRA 9/20/04
%
%Usage:
%   function beamlet = createIMBeamlet(doseV, indV, beamNum, fullLength)
%
% 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/>.

%Initialize the beamlet.
% IM = initIMRTProblem;
% beamlet = IM.beamlets;
beamlet = initBeamlet;
beamlet(1).format  = 'uint8';
beamlet(1).beamNum = beamNum;
beamlet(1).fullLength = fullLength;

if isempty(indV) || isempty(doseV)
%     disp(['Warning: a pencil beam in beam ' num2str(beamNum) ' does not contribute any dose to this structure.'])
    return;
end

ind2V = find(doseV);

if isempty(ind2V)
    valsV = [];
    maxVal = 0;
    smallVals = logical([]);
else
	valsV = doseV(ind2V);

	maxVal = max(valsV(:));

	%Bool vector to note low values, helps avoid uint8 roundoff error.
	smallVals = valsV < (maxVal/(2^8 - 1));

	%Store non small values as normal uint8s.
	valsV(~smallVals) = (valsV(~smallVals)/maxVal) * (2^8 - 1);

	%Store small values as uint8s with another factor of 256.
	valsV(smallVals) = valsV(smallVals)/(maxVal)*(2^8 - 1)*(2^8 - 1);
end

%Save vector to ID low values when builidng inflM later.
%Uses a logical packer to decrease size by about 8.

beamlet(1).lowDosePoints = packLogicals(smallVals);
beamlet(1).influence = uint8(valsV);
beamlet(1).indexV = uint32(indV(ind2V));
beamlet(1).maxInfluenceVal = maxVal;


function mask_normalize()
    global config mem;
    curr_layer_idx = config.misc.current_layer - 1;
    mem.activations{curr_layer_idx} = mem.activations{curr_layer_idx} ./ mem.mask_activations{curr_layer_idx};
end


function output = callsdpagmp(interfacedata)

% CALLSDPAGMP.m Call SDPA-GMP from YALMIP
% ----------------------------------------------------------------------- %
%        Author:    Giovanni Fantuzzi
%                   Department of Aeronautics
%                   Imperial College London
%       Created:    23/08/2016
%
%     Copyright (C) 2016  Giovanni Fantuzzi
%
%     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, see <http://www.gnu.org/licenses/>.
% ----------------------------------------------------------------------- %

% Retrieve needed data
options = interfacedata.options;
F_struc = interfacedata.F_struc;
c       = interfacedata.c;
K       = interfacedata.K;
x0      = interfacedata.x0;
ub      = interfacedata.ub;
lb      = interfacedata.lb;

% Bounded variables converted to constraints
if ~isempty(ub)
    [F_struc,K] = addStructureBounds(F_struc,K,ub,lb);
end

% Convert from internal (sedumi) format
[mDIM,nBLOCK,bLOCKsTRUCT,c,F] = sedumi2sdpa(F_struc,c,K);

if options.verbose==0
    options.sdpa_gmp.print = 'no';
else
    options.sdpa_gmp.print = 'display';
end

if options.savedebug
    ops = options.sdpa_gmp;
    save sdpa_gmpdebug mDIM nBLOCK bLOCKsTRUCT c F ops
end

if options.showprogress
    showprogress(['Calling ' interfacedata.solver.tag],options.showprogress);
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% CALL SDPA-GMP
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
solvertime = tic;
[objVal,x,X,Y,INFO] = sdpagmp(mDIM,nBLOCK,bLOCKsTRUCT,c,F,[],[],[],options.sdpa_gmp);
solvertime = toc(solvertime);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% From here onwards, like in YALMIP native callsdpa
% Create variables in YALMIP internal format
Primal = x;

Dual = [];
for i = 1:length(Y)
    Dual = [Dual;Y{i}(:)];
end

Slack = [];
if options.saveduals
    for i = 1:length(X)
        Slack = [Slack;X{i}(:)];
    end
end

switch (INFO.phasevalue)
    case 'pdOPT'
        problem = 0;
    case {'noINFO','pFEAS','dFEAS'}
        problem = 3;
    case {'pdFEAS'}
        problem = 4;
    case 'pFEAS_dINF'
        problem = 2;
    case 'pINF_dFEAS'
        problem = 1;
    case 'pUNBD'
        problem = 2;
    case 'dUNBD'
        problem = 1;
    case 'pdINF'
        problem = 12;
    otherwise
        problem = -1;
end
infostr = yalmiperror(problem,interfacedata.solver.tag);

if options.savesolveroutput
    solveroutput.objVal = objVal;
    solveroutput.x = x;
    solveroutput.X = X;
    solveroutput.Y = Y;
    solveroutput.INFO = INFO;
else
    solveroutput = [];
end

if options.savesolverinput
    solverinput.mDIM = mDIM;
    solverinput.nBLOCK=nBLOCK;
    solverinput.bLOCKsTRUCT=bLOCKsTRUCT;
    solverinput.c=c;
    solverinput.F=F;
else
    solverinput = [];
end

% Standard interface
output = createOutputStructure(Primal,Dual,[],problem,infostr,solverinput,solveroutput,solvertime);


% pop_apcluster() - Clusters STUDY ICs using Affinity Product method.
%                   In this method, IC measures, except equiv. dipoles, (ERP, ERSP...)
%                   are compared for each IC pair and their dissimilarity is multiplied
%                   together to form a combined pairwise dissimilarity matrix. This matrix
%                   is then normalized, weighted and added to the normalized and weighted
%                   IC equiv. dipole distance matrix. The final dissimilarity matrix is
%                   then clustered using affinity clustering  method.
%                   You can control the effect of equiv. dipole distances in
%                   the clustering by setting the 'Relative dipole weight'
%                   parameter in the pop-uo GUI. For example, by setting
%                   this value to 0.8, the final dissimilarity matrix will consist of 80%
%                   distance dissimilarity and 20% of other measures combined together.
%                   Please note that the number of returned clusters may slighty
%                   differ from the number requested in the GUI.
%
%
% Usage:
%     >> STUDY = pop_apcluster(STUDY, ALLEEG) % popup window
%
% See also:  std_apcluster(), std_apreclust(), , pop_apreclust(), apclusterK()
%
% Author: Nima Bigdely-Shamlo, SCCN/INC/UCSD, 2009

function [STUDY ALLEEG command] = pop_apcluster(STUDY, ALLEEG)
% command is used for keeping a history.
% disable measure checkboxes which are not present (calculated) in
% pre-clustering.

% ERP
if isfield(STUDY.preclust,'similarity') && isfield(STUDY.preclust.similarity,'erpCorr')
    erpEnable = 'on';
else
    erpEnable = 'off';
end;
erpChecked = strcmp(erpEnable, 'on'); % only check items if they are enabled.

% ERSP
if isfield(STUDY.preclust,'similarity') && isfield(STUDY.preclust.similarity,'erspCorr')
    erspEnable = 'on';
else
    erspEnable = 'off';
end;
erspChecked = strcmp(erspEnable, 'on'); % only check items if they are enabled.

% ITC
if isfield(STUDY.preclust,'similarity') && isfield(STUDY.preclust.similarity,'itcCorr')
    itcEnable = 'on';
else
    itcEnable = 'off';
end;
itcChecked = strcmp(itcEnable, 'on'); % only check items if they are enabled.

% dipole
if isfield(STUDY.preclust,'similarity') && isfield(STUDY.preclust.similarity,'compDistance')
    dipoleEnable = 'on';
else
    dipoleEnable = 'off';
end;
dipoleChecked = strcmp(dipoleEnable, 'on'); % only check items if they are enabled.

% spectra
if isfield(STUDY.preclust,'similarity') && isfield(STUDY.preclust.similarity,'specCorr')
    specEnable = 'on';
else
    specEnable = 'off';
end;
specChecked = strcmp(specEnable, 'on'); % only check items if they are enabled.

% scalp map
if isfield(STUDY.preclust,'similarity') && isfield(STUDY.preclust.similarity,'mapCorr')
    scalpEnable = 'on';
else
    scalpEnable = 'off';
end;

% popup GUI

returnedFromGui = inputgui( 'geometry', { [1 0.5]  [1 0.5] 1 1 [1 1 1] [1 1 1] [1 1 1] [1 1 1] [1 1 1] [1 1 1] 1 [3 1.5]}, ...
    'geomvert', [], 'uilist', { ...
    { 'style', 'text', 'string', 'Number of clusters to compute:'}, ...
    { 'style', 'edit', 'string', '10' 'tag' 'numberOfClusters' } , ...
       { 'style', 'text', 'string', 'Relative dipole weight (between 0 and 1):'}, ...
    { 'style', 'edit', 'string', '0.8' 'tag' 'numberOfClusters' } , ...
        { 'style', 'text', 'string', [ 'Select measuretures to be used in the clustering:' ] }, {}, ...
    {},{ 'Style', 'checkbox', 'string' 'Dipole' 'tag' 'scale' 'value' 1} , {}, ...
    {},{ 'Style', 'checkbox', 'string' 'ERP' 'tag' 'scale' 'value' erpChecked 'enable' erpEnable}, {},...
    {},{ 'Style', 'checkbox', 'string' 'ERSP' 'tag' 'scale' 'value' erspChecked 'enable' erspEnable}, {},...
    {},{ 'Style', 'checkbox', 'string' 'ITC' 'tag' 'scale' 'value' itcChecked 'enable' itcEnable}, {},...
    {},{ 'Style', 'checkbox', 'string' 'Spectra' 'tag' 'scale' 'value' specChecked 'enable' specEnable}, {}, ...
    {}, { 'Style', 'checkbox', 'string' 'Scalp map' 'tag' 'scale' 'value' 0 'enable' scalpEnable}, {},...
    {}, { 'Style', 'checkbox', 'string' 'Separate outliers (enter std.)' 'tag' 'scale' 'value' 1}, { 'style', 'edit', 'string', '3' 'tag' 'outlierSTD' }, ...

    }, 'helpcom','pophelp(''pop_mpcluster'');', 'title', 'Affinity Product clustering -- pop_apcluster()');

if isempty(returnedFromGui) % an empty returnedFromGui means the Cancel button has been pressed so nothing should be done.
    command = '';
    return; % Cancel button is pressed, so do nothing.
else

    % analysze answers returned from the GUI
    numberOfClusters = str2num(returnedFromGui{1});
    methodParameter = str2num(returnedFromGui{2});

    answers = cell2mat(returnedFromGui(3:end-1));
    measureNamesInGUIorder = {'dipole', 'erp', 'ersp', 'itc', 'spec', 'map'};
    measuresToUseInClustering = measureNamesInGUIorder(find(answers(1:end-1))); %#ok<FNDSB>

    if answers(end) % checkbox for outlier
        outlierSTD = str2num(returnedFromGui{end});
    else
        outlierSTD = Inf;
    end;

    STUDY = std_apcluster(STUDY, ALLEEG, numberOfClusters, outlierSTD, measuresToUseInClustering, methodParameter);

    % prepare 'command' variable for placing both in eeglab histry (accessible with eegh() ) and also
    % adding to  STUDY.history

    measuresInOneString = [];
    for i=1:length(measuresToUseInClustering)
        if i>1
            measuresInOneString = [measuresInOneString ' , ' '''' measuresToUseInClustering{i} ''''];
        else
            measuresInOneString = ['''' measuresToUseInClustering{1} ''''];
        end;
    end;

    % pop up the cluster edit and visualization .
    [STUDY commandFromPop_clustedit] = pop_clustedit(STUDY, ALLEEG);

    command = ['STUDY = std_apcluster(STUDY, ALLEEG, ' num2str(numberOfClusters) ', ' num2str(outlierSTD) ', {' measuresInOneString '} , ' num2str(methodParameter) ');'];
    command = [command '\n' commandFromPop_clustedit];     % add the command from pop_clustedit() to the history too.
    STUDY.history =  sprintf('%s\n%s',  STUDY.history, command);

end;


function [p params] = glm_getHrfParams(params, select);
%
% subParams = glm_getHrfParams(params, [select=0]);
%
% If you're using a pre-defined HRF for GLM analyses,
% such as 'boynton', 'spm', or 'dale&buckner', return those
% arguments which should be called for those canned function.
% These arguments are kept in a params.glmHRF_params field.
% If this field isn't found, or isn't the right size, ask
% the user to specify these, with appropriate defaults.
% Will also return the params struct with the glmHRF_params field
% set properly.
%
% If the select flag is passed in as 1, will put up a dialog for the
% user to specify the parameters. Otherwise, will set to reasonable
% defaults.
%
% ras, 01/2007.

% EDIT HISTORY:
% ras, 02/2007 -- added select flag, GUI dialogs.
% DY, 02/20/2007 -- removed checks for number of parameters such that
% whenever select ~=1, the parameters are automatically set to the
% defaults, even if there do appear to be the right number of parameters
% (since odd bugs can result if there are the right number of parameters,
% but the values are off).
% ras, 02/21/2007 -- Davie, I get your point that there is this possibility;
% however, since this function is used to retrieve stored values, your
% change would cause only the defaults to ever be used (see glm_hrf). It
% may be possible to change the way this function is called, but this would
% require additional logic (since you also need a check that the right
% number of HRF params are specified at HRF creation); and the possibility
% you're concerned about could only occur if you manually muck with the
% parameters -- none of the accessor functions can set the wrong # of
% params. So, I'm reverting for now, and we'll see if we need to do more.

p = [];

if ~exist('select', 'var') | isempty(select), select = 0;   end

% this is only needed for predefined HRF options
if ~ismember(params.glmHRF, [2 3 4]), return; end

% init params field
if ~isfield(params, 'glmHRF_params')
    params.glmHRF_params = [];
end

% get params
switch params.glmHRF
    case 2,     % boynton
        if select==1
            params.glmHRF_params = boyntonHIRF_dialog;
        elseif length(params.glmHRF_params) ~= 3 % wrong size
            % [n tau delta]
            params.glmHRF_params = [3 1.08 2.05];
        end

    case 3,     % spm
        if select==1
            params.glmHRF_params = spmHRF_dialog;
            %	p(1) - delay of response (relative to onset)	   6
            %	p(2) - delay of undershoot (relative to onset)    16
            %	p(3) - dispersion of response			   1
            %	p(4) - dispersion of undershoot			   1
            %	p(5) - ratio of response to undershoot		   6
            %	p(6) - onset (seconds)				   0
            %	p(7) - length of kernel (seconds)		  32
		elseif length(params.glmHRF_params) ~= 6 % wrong size
            maxT = max(params.timeWindow);
            params.glmHRF_params = [6 16 1 1 6 0 maxT];
        end

    case 4,     % dale & buckner
        if select==1
            params.glmHRF_params = daleBucknerHIRF_dialog;
            % [delta tau]
%             params.glmHRF_params = [2.25 1.25]; % more event-friendly
        elseif length(params.glmHRF_params) ~= 2 % wrong size
            params.glmHRF_params = [1.25 2.5]; % vals used in er_runSelxavgBlock (old code)
        end
end

p = params.glmHRF_params;

return
% /----------------------------------------------------------------/ %

% /----------------------------------------------------------------/ %
function p = boyntonHIRF_dialog;
% dialog to set [n tau delta] for boyntonHIRF function.
dlg(1).fieldName = 'eqn';
dlg(1).style = 'text';
dlg(1).string = 'HRF equation:';
dlg(1).value = 'h(t) = [(t/tau) ^ (n-1) * exp(-t/tau)] / [tau(n-1)!]';

dlg(2).fieldName = 'n';
dlg(2).style = 'edit';
dlg(2).string = 'n (integer):';
dlg(2).value = '3';

dlg(3).fieldName = 'tau';
dlg(3).style = 'edit';
dlg(3).string = 'tau (decay), secs:';
dlg(3).value = '1.08';

dlg(4).fieldName = 'delta';
dlg(4).style = 'edit';
dlg(4).string = 'delay (delta), secs:';
dlg(4).value = '2.05';

resp = generalDialog(dlg, 'Boynton HRF');
if isempty(resp)
    error('User canceled.')
end

p = [str2num(resp.n) str2num(resp.tau) str2num(resp.delta)];
return
% /----------------------------------------------------------------/ %

% /----------------------------------------------------------------/ %
function p = spmHRF_dialog;
% dialog to set the params for spm_hrf:
%	p(1) - delay of response (relative to onset)	   6
%	p(2) - delay of undershoot (relative to onset)    16
%	p(3) - dispersion of response			   1
%	p(4) - dispersion of undershoot			   1
%	p(5) - ratio of response to undershoot		   6
%	p(6) - onset (seconds)				   0
%	p(7) - length of kernel (seconds)		  32
dlg(1).fieldName = 'responseDelay';
dlg(end).style = 'edit';
dlg(end).string = 'delay of response (relative to onset)';
dlg(end).value = '6';

dlg(end+1).fieldName = 'undershootDelay';
dlg(end).style = 'edit';
dlg(end).string = 'delay of undershoot (relative to onset)';
dlg(end).value = '16';

dlg(end+1).fieldName = 'responseDispersion';
dlg(end).style = 'edit';
dlg(end).string = 'dispersion of response';
dlg(end).value = '1';

dlg(end+1).fieldName = 'undershootDispersion';
dlg(end).style = 'edit';
dlg(end).string = 'dispersion of response';
dlg(end).value = '1';

dlg(end+1).fieldName = 'ratio';
dlg(end).style = 'edit';
dlg(end).string = 'ratio of response to undershoot';
dlg(end).value = '6';

dlg(end+1).fieldName = 'onset';
dlg(end).style = 'edit';
dlg(end).string = 'onset (seconds)';
dlg(end).value = '0';

dlg(end+1).fieldName = 'kernelLength';
dlg(end).style = 'edit';
dlg(end).string = 'length of kernel (seconds)';
dlg(end).value = '32';

resp = generalDialog(dlg, 'SPM HRF');
if isempty(resp)
    error('User canceled.')
end

p = [str2num(resp.responseDelay) str2num(resp.undershootDelay) ...
     str2num(resp.responseDispersion) str2num(resp.undershootDispersion) ...
     str2num(resp.ratio) str2num(resp.onset) str2num(resp.kernelLength)];

return
% /----------------------------------------------------------------/ %

% /----------------------------------------------------------------/ %
function p = daleBucknerHIRF_dialog;
% dialog to set [delta tau] for fmri_hemodyn function.
dlg(1).fieldName = 'eqn';
dlg(1).style = 'text';
dlg(1).string = 'HRF equation:';
dlg(1).value = {'h(t>delta)  = ((t-delta)/tau)^2 * exp(-(t-delta)/tau)'; ...
                 'h(t<=delta) = 0;'};

dlg(2).fieldName = 'delta';
dlg(2).style = 'edit';
dlg(2).string = 'delay (delta), secs:';
dlg(2).value = '1.25';

dlg(3).fieldName = 'tau';
dlg(3).style = 'edit';
dlg(3).string = 'tau (decay), secs:';
dlg(3).value = '2.5';

resp = generalDialog(dlg, 'Dale & Buckner HRF');
if isempty(resp)
    error('User canceled.')
end

p = [str2num(resp.delta) str2num(resp.tau)];

return


function [sampled_graphs, accept_ratio, num_edges] = learn_struct_mcmc(data, ns, varargin)
% LEARN_STRUCT_MCMC  Monte Carlo Markov Chain search over DAGs assuming fully observed data
% [sampled_graphs, accept_ratio, num_edges] = learn_struct_mcmc(data, ns, ...)
%
% data(i,m) is the value of node i in case m.
% ns(i) is the number of discrete values node i can take on.
%
% sampled_graphs{m} is the m'th sampled graph.
% accept_ratio(t) = acceptance ratio at iteration t
% num_edges(t) = number of edges in model at iteration t
%
% The following optional arguments can be specified in the form of name/value pairs:
% [default value in brackets]
%
% scoring_fn - 'bayesian' or 'bic' [ 'bayesian' ]
%              Currently, only networks with all tabular nodes support Bayesian scoring.
% type       - type{i} is the type of CPD to use for node i, where the type is a string
%              of the form 'tabular', 'noisy_or', 'gaussian', etc. [ all cells contain 'tabular' ]
% params     - params{i} contains optional arguments passed to the CPD constructor for node i,
%              or [] if none.  [ all cells contain {'prior', 1}, meaning use uniform Dirichlet priors ]
% discrete   - the list of discrete nodes [ 1:N ]
% clamped    - clamped(i,m) = 1 if node i is clamped in case m [ zeros(N, ncases) ]
% nsamples   - number of samples to draw from the chain after burn-in [ 100*N ]
% burnin     - number of steps to take before drawing samples [ 5*N ]
% init_dag   - starting point for the search [ zeros(N,N) ]
%
% e.g., samples = my_learn_struct_mcmc(data, ns, 'nsamples', 1000);
%
%
% Modified by Mingyi Wang  (mingyiwang@hotmail.com) Sep 18, 2006 (based on Sonia Leach (SML)'s version ( 2/4/02, 9/5/03))
%
% Some bugs in update_ancestor_matrix() were fixed. This function can call mk_nbrs_of_digraph properly
%

[n ncases] = size(data);

% set default params
type = cell(1,n);
params = cell(1,n);
for i=1:n
 type{i} = 'tabular';
 %params{i} = { 'prior', 1};
 params{i} = { 'prior_type', 'dirichlet', 'dirichlet_weight', 1 };
end
scoring_fn = 'bayesian';
discrete = 1:n;
clamped = zeros(n, ncases);
nsamples = 100*n;
burnin = 5*n;
dag = zeros(n);

args = varargin;
nargs = length(args);
for i=1:2:nargs
 switch args{i},
  case 'nsamples',   nsamples = args{i+1};
  case 'burnin',     burnin = args{i+1};
  case 'init_dag',   dag = args{i+1};
  case 'scoring_fn', scoring_fn = args{i+1};
  case 'type',       type = args{i+1};
  case 'discrete',   discrete = args{i+1};
  case 'clamped',    clamped = args{i+1};
  case 'gconstraint', gconstraint=args{i+1};  %Added by mingyi
  case 'params',     if isempty(args{i+1}), params = cell(1,n); else params = args{i+1};  end

 end
end

% We implement the fast acyclicity check described by P. Giudici and R. Castelo,
% "Improving MCMC model search for data mining", submitted to J. Machine Learning, 2001.

% SML: also keep descendant matrix C
use_giudici = 1;
%use_giudici = 0; %Revised by MIngyi
if use_giudici
 [nbrs, ops, nodes, A] = mk_nbrs_of_digraph(dag);
else
 [nbrs, ops, nodes] = mk_nbrs_of_dag(dag);
 A = [];
end

num_accepts = 1;
num_rejects = 1;
T = burnin + nsamples;
accept_ratio = zeros(1, T);
num_edges = zeros(1, T);
sampled_graphs = cell(1, nsamples);
%sampled_bitv = zeros(nsamples, n^2);

for t=1:T
 [dag, nbrs, ops, nodes, A, accept] = take_step(dag, nbrs, ops, ...
                    nodes, ns, data, clamped, A, ...
                      scoring_fn, discrete, type, params);
 num_edges(t) = sum(dag(:));
 num_accepts = num_accepts + accept;
 num_rejects = num_rejects + (1-accept);
 accept_ratio(t) =  num_accepts/num_rejects;
 if t > burnin
   sampled_graphs{t-burnin} = dag;
   %sampled_bitv(t-burnin, :) = dag(:)';
 end
 fprintf('MCMC: %d/%d\n',t,T);
end

%%%%%%%%%

function [new_dag, new_nbrs, new_ops, new_nodes, A,  accept] = ...
   take_step(dag, nbrs, ops, nodes, ns, data, clamped, A,  ...
     scoring_fn, discrete, type, params, prior_w)

global gconstraint;   %Added by Mingyi
use_giudici = ~isempty(A);
if use_giudici
 [new_dag, op, i, j, new_A] =  pick_digraph_nbr(dag, nbrs, ops, nodes,A); % updates A
 [new_nbrs, new_ops, new_nodes] =  mk_nbrs_of_digraph(new_dag,new_A);
else
 d = sample_discrete(normalise(ones(1, length(nbrs))));
 new_dag = nbrs{d};
 op = ops{d};
 i = nodes(d, 1); j = nodes(d, 2);
 [new_nbrs, new_ops, new_nodes] = mk_nbrs_of_dag1(new_dag);
end
%For debug
% fprintf('op:%s,i:%d,j:%d\n',op,i,j);
% if ~acyclic(new_dag)
%     error('new dag must be acyclic!')
% end
% if size(find(diag(new_A)),1)>0
%   A=A
%   new_A=new_A
%   error('new A must be acyclic!')
%  end
%debug ends

bf =  bayes_factor(dag, new_dag, op, i, j, ns, data, clamped, scoring_fn, discrete, type, params);

%R = bf * (new_prior / prior) * (length(nbrs) / length(new_nbrs));
R = bf * (length(nbrs) / length(new_nbrs));
u = rand(1,1);
if u > min(1,R) % reject the move
 accept = 0;
 new_dag = dag;
 new_nbrs = nbrs;
 new_ops = ops;
 new_nodes = nodes;
else
 accept = 1;
 if use_giudici
    A = new_A; % new_A already updated in pick_digraph_nbr
 end
end

%%%%%%%%%

function bfactor = bayes_factor(old_dag, new_dag, op, i, j, ns, data, clamped, scoring_fn, discrete, type, params)

u = find(clamped(j,:)==0);
LLnew = score_family(j, parents(new_dag, j), type{j}, scoring_fn, ns, discrete, data(:,u), params{j});
LLold = score_family(j, parents(old_dag, j), type{j}, scoring_fn, ns, discrete, data(:,u), params{j});
bf1 = exp(LLnew - LLold);

if strcmp(op, 'rev')  % must also multiply in the changes to i's family
 u = find(clamped(i,:)==0);
 LLnew = score_family(i, parents(new_dag, i), type{i}, scoring_fn, ns, discrete, data(:,u), params{i});
 LLold = score_family(i, parents(old_dag, i), type{i}, scoring_fn, ns, discrete, data(:,u), params{i});
 bf2 = exp(LLnew - LLold);
else
 bf2 = 1;
end
bfactor = bf1 * bf2;

%%%%%%%% Giudici stuff follows %%%%%%%%%%

% SML: This now updates A as it goes from digraph it choses
function [new_dag, op, i, j, new_A] = pick_digraph_nbr(dag, digraph_nbrs, ops, nodes, A)

d = sample_discrete(normalise(ones(1, length(digraph_nbrs))));
%d = myunidrnd(length(digraph_nbrs),1,1);
i = nodes(d, 1); j = nodes(d, 2);
new_dag = digraph_nbrs(:,:,d);

op = ops{d};
new_A = update_ancestor_matrix(A, op, i, j, dag);
%for debug
% if op=='add'
%     if ~(dag(i,j)==0 & new_dag(i,j)==1)
%         fprintf('error add\n');
%     end
% end
% if op=='del'
%     if ~(dag(i,j)==1 & new_dag(i,j)==0)
%       fprintf('new dag del calculation is error!\n')
%     end
% end
% if op=='rev'
%     if ~(dag(i,j)==1 & dag(j,i)==0 & new_dag(i,j)==0 & new_dag(j,i)==1)
%       fprintf('new dag rev calculation is error!\n')
%     end
% end
% new_AA = reachability_graph(new_dag');
% if find(diag(new_AA)==1)
%     fprintf('cyclic\n');
% end
% if ~isequal(new_A,new_AA)
%    fprintf('new A calculation is error!\n')
% end
%debug ends

%%%%%%%%%%%%%%

function A = update_ancestor_matrix(A,  op, i, j, dag)

switch op
case 'add',
 A = do_addition(A,  op, i, j, dag);
case 'del',
 A = do_removal(A,  op, i, j, dag);
case 'rev',
 A = do_removal(A,  op, i, j, dag);
 A = do_addition(A,  op, j, i, dag);
end

%%%%%%%%%%%%

function A = do_addition(A, op, i, j, dag)

A(j,i) = 1;     % i is an ancestor of j
anci = find(A(i,:));
if ~isempty(anci)
 A(j,anci) = 1;   % all of i's ancestors are added to Anc(j)
end

descj = find(A(:,j));  %all the descendants of j are selected
if ~isempty(descj)
 for k=descj(:)'
   A(k,i) = 1;        % i is the ancestor of descj
   if ~isempty(anci)  % all of i's ancestors are also the ancestor of each descendant of j
       A(k,anci)=1;
   end
 end
end

%%%%%%%%%%%

function A = do_removal(A, op, i, j, dag)
descj = find(A(:,j));
A = update_row(A,i, j, dag);   % compute the A(j,:) row for dag i->j removal

if ~isempty(descj)
  order = topological_sort(dag);  %all the parent nodes are before to the children nodes
  [junk, perm] = sort(order);     %node i is perm(i)-TH in order
  descj_topnum = perm(descj);     %descj(i) is descj_topnum(i)-th in order

% SML: now re-sort descj by rank in descj_topnum
  [junk, perm] = sort(descj_topnum);
  descj = descj(perm);
  for k = descj(:)'
    A = old_update_row(A, k, dag);
  end
end

%%%%%%%%%

function A = update_row(A, i,j, dag)
% We compute row j of A
A(j, :) = 0;
ps = parents(dag, j);
ps=setdiff(ps,i);  % All the parents except i
if ~isempty(ps)
 A(j, ps) = 1;
end
for k=ps(:)'
 anck = find(A(k,:));
 if ~isempty(anck)
   A(j, anck) = 1;
 end
end

%%%%%%%%%

function A = old_update_row(A, j, dag)

% We compute row j of A
A(j, :) = 0;
ps = parents(dag, j);
if ~isempty(ps)
 A(j, ps) = 1;
end
for k=ps(:)'
 anck = find(A(k,:));
 if ~isempty(anck)
   A(j, anck) = 1;
 end
end

%%%%%%%%

function A = init_ancestor_matrix(dag)

order = topological_sort(dag);
A = zeros(length(dag));
for j=order(:)'
 A = update_row(A, j, dag);
end


function dataNdConcat = concat(this, dataNdArray, concatDims, tolerance)
%Concatenate an array of MrDataNd along one specified dimension
%
%   Y = MrDataNd()
%   dataNdConcat = Y.concat(dataNdArray, concatDims, tolerance)
%
% This is a method of class MrDataNd. It is very similar to combine with
% the difference that non-singleton dimensions can be concatenated (e.g.,
% volume 1-10 and 11-20).
% Note: Multi-dimensional concatenation is supported,
% but it will leave parts of the array empty
%   - e.g., volume 1-10 of slice 1-9 concatenated with volume 11-20
%     of slice 10-20 would leave the "cross-terms" of the dim Matrix like
%     slice 1-9 of volume 11-10 undefined (we set them to 0).
%
% NOTE: The order of data in the dataNdArray will *not* define the
%       concatenation order. Ratherthe actual values of the samplingPoints
%       in dimInfo along the concat dimensions specify the position of the
%       data.
%
% IN
%   dataNdArray     cell(nDatasets,1) of MrDataNd to be concatenated
%                       OR
%                   single MrDataNd object. In this case, the input object
%                   and the calling object will be concatenated
%   concatDims       index or string (label) of dimension along which data
%                   should be concatenated
%   tolerance                   dimInfos are only combined, if their
%                               information is equal for all but the
%                               concatDims (because only one
%                               representation is retained for those,
%                               usually from the first of the dimInfos).
%                               However, sometimes numerical precision,
%                               e.g., rounding errors, preclude the
%                               combination. Then you can increase this
%                               tolerance;
%                               default: single precision (eps('single')
%                               ~1.2e-7)
% OUT
%
% EXAMPLE
%   concat
%
%   See also MrDataNd MrDataNd.combine MrDimInfo.combine

% Author:   Lars Kasper
% Created:  2019-04-15
% Copyright (C) 2019 Institute for Biomedical Engineering
%                    University of Zurich and ETH Zurich
%
% This file is part of the TAPAS UniQC Toolbox, which is released
% under the terms of the GNU General Public License (GPL), version 3.
% You can redistribute it and/or modify it under the terms of the GPL
% (either version 3 or, at your option, any later version).
% For further details, see the file COPYING or
%  <http://www.gnu.org/licenses/>.

if nargin < 4
    tolerance = eps('single');
end

if ~iscell(dataNdArray)
    dataNdArray = {this, dataNdArray};
end

nImages = numel(dataNdArray);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Split all images in array along specified dimensions
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

dataNdArraySplit = {};
for iImage = 1:nImages
    dataNdArraySplit = [dataNdArraySplit; ...
        reshape(dataNdArray{iImage}.split('splitDims', concatDims), ...
        [], 1)];
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Combine all image arrays and reconcatenate!
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% Note that not the order here will be important, but the actual value of
% the slice position in dimInfo

dataNdConcat = dataNdArraySplit{1}.combine(dataNdArraySplit, concatDims, ...
    tolerance);


function options = rbfOptions

% RBFOPTIONS Default options for RBF network.

% MLTOOLS

options.outFunc = 'linear';
options.activeFunc = 'gaussian';
options.hiddenDim = 20;


function [B, tau] = mex_dgeqrf(A)
% B = mex_dgeqrf(A)
%
% Interface to LAPACK's DGEQRF function.
% Upper part of B is the triangular factor.
%
% 6-December 2009, Version 1.3
% Copyright (C) 2009, Haim Avron and Sivan Toledo.


%CROP_BORDERS Crop the borders of an image or stack of images
%
%   [B, vA, vB, bb_rel] = crop_borders(A, bcol, [padding])
%
%IN:
%   A - HxWxCxN stack of images.
%   bcol - Cx1 background colour vector.
%   padding - scalar indicating how much padding to have in relation to
%             the cropped-image-size (0<=padding<=1). Default: 0
%
%OUT:
%   B - JxKxCxN cropped stack of images.
%   vA     - coordinates in A that contain the cropped image
%   vB     - coordinates in B where the cropped version of A is placed
%   bb_rel - relative bounding box (used for eps-cropping)

% 06/03/15: Improved image cropping thanks to Oscar Hartogensis

function [A, vA, vB, bb_rel] = crop_borders(A, bcol, padding)
if nargin < 3
    padding = 0;
end
[h, w, c, n] = size(A);
if isscalar(bcol)
    bcol = bcol(ones(c, 1));
end
bail = false;
for l = 1:w
    for a = 1:c
        if ~all(col(A(:,l,a,:)) == bcol(a))
            bail = true;
            break;
        end
    end
    if bail
        break;
    end
end
bcol = A(ceil(end/2),w,:,1);
bail = false;
for r = w:-1:l
    for a = 1:c
        if ~all(col(A(:,r,a,:)) == bcol(a))
            bail = true;
            break;
        end
    end
    if bail
        break;
    end
end
bcol = A(1,ceil(end/2),:,1);
bail = false;
for t = 1:h
    for a = 1:c
        if ~all(col(A(t,:,a,:)) == bcol(a))
            bail = true;
            break;
        end
    end
    if bail
        break;
    end
end
bcol = A(h,ceil(end/2),:,1);
bail = false;
for b = h:-1:t
    for a = 1:c
        if ~all(col(A(b,:,a,:)) == bcol(a))
            bail = true;
            break;
        end
    end
    if bail
        break;
    end
end
% Crop the background, leaving one boundary pixel to avoid bleeding on resize
%v = [max(t-padding, 1) min(b+padding, h) max(l-padding, 1) min(r+padding, w)];
%A = A(v(1):v(2),v(3):v(4),:,:);
if padding == 0  % no padding
    padding = 1;
elseif abs(padding) < 1  % pad value is a relative fraction of image size
    padding = sign(padding)*round(mean([b-t r-l])*abs(padding)); % ADJUST PADDING
else  % pad value is in units of 1/72" points
    padding = round(padding);  % fix cases of non-integer pad value
end
if padding > 0  % extra padding
    % Create an empty image, containing the background color, that has the
    % cropped image size plus the padded border
    B = repmat(bcol,(b-t)+1+padding*2,(r-l)+1+padding*2);
    % vA - coordinates in A that contain the cropped image
    vA = [t b l r];
    % vB - coordinates in B where the cropped version of A will be placed
    vB = [padding+1, (b-t)+1+padding, padding+1, (r-l)+1+padding];
    % Place the original image in the empty image
    B(vB(1):vB(2), vB(3):vB(4), :) = A(vA(1):vA(2), vA(3):vA(4), :);
    A = B;
else  % extra cropping
    vA = [t-padding b+padding l-padding r+padding];
    A = A(vA(1):vA(2), vA(3):vA(4), :);
    vB = [NaN NaN NaN NaN];
end
% For EPS cropping, determine the relative BoundingBox - bb_rel
bb_rel = [l-1 h-b-1 r+1 h-t+1]./[w h w h];
end

function A = col(A)
A = A(:);
end


function [sfmL,sfm,names] = spm2delta(SPM,varargin)
% [d_hires,d_atTR,names] = spm2delta(SPM,varargin)
% or
% [onsets,delta_atTR,names] = spm2delta(SPM,varargin)
%
% Example: for SPM99
% [dL,d] = spm2delta([],Sess);

if length(varargin) > 0,
    Sess = varargin{1};,
    SPM.Sess = Sess;
else
    % SPM2
    Sess = SPM.Sess;
end

if iscell(Sess)

    ons = {};

    for i = 1:length(Sess)

        [sfc,sfm{i}] = downsample_delta(Sess{i}.sf,16);

        for j = 1:size(sfm{i},2)                % save onsets
            ons{end+1} = find(sfm{i}(:,j)) - 1; % in TRs, starting with 0
        end

        %sfmL{i} = cat(2,Sess{i}.sf{:});        % delta hires
    end

    sfm = cat(1,sfm{:});
    sfmL = ons;         % save onsets
    %sfmL = cat(1,sfmL{:}); % save delta_hires
    names = SPM.Sess{1}.name;
else

    % new spm2 way?
    ons = [];
    wh = [];, for i = 1:length(Sess), if isempty(Sess(i).U), wh(i) = 1;,end,end
        Sess(find(wh)) = [];

    delta = cell(1,length(Sess(1).U));

    for i = 1:length(Sess)
        sessons = [];
        for j = 1:length(Sess(i).U)
            ons{end+1} = Sess(i).U(j).ons/SPM.xY.RT;      % all onsets
            sessons{end+1} = Sess(i).U(j).ons/SPM.xY.RT;  % onsets this session
        end

        % add delta functions for this session to overall
        sessdelt = onsets2delta(sessons,size(SPM.xX.X,1) ./ length(Sess));
        for k = 1:size(sessdelt,2)

            % just in case some sessions do not have some
                    % regressors
            if k > length(delta)
            	delta{k} = zeros(size(delta{1}));
            end

            try
                delta{k} = cat(1,[delta{k}; sessdelt(:,k)]);
            catch
                 delta{k} = cat(1,[delta{k}; sessdelt(:,k)']);
            end
        end
    end
    sfmL = ons;
    sfm = delta;

    names = cat(2,SPM.Sess(1).U(:).name);
end

return


function hit_node(varargin)
%Function for generating Another metabolite-metabolite interaction network
%if the node is clicked in the figure that produced by createMetIntrcNetwork.
%Right click and left click has different properties.
%Left click :Generate sub-metabolite-metabolite network from the created figure,
%            this functionality was added for better looking at the created network
%            and showing flux values on edges lines.
%
%Right click :Generate metabolite metabolite network from model, this
%             functionality were added for creating metabolite-metabolite network using
%             clicked metabolite and model.
%The produced figure from main figure also has same property with main
%figure, and it is clickable too.

    f = varargin{1}.Parent.Parent;
    if (strcmp(f.SelectionType, 'normal') | strcmp(f.SelectionType, 'alt'))
        metofInt=varargin{1}.String;
        adjncMtrx=varargin{3};
        mets=varargin{4};
        model=varargin{5} ;
        excludedMets=varargin{7};
        fluxes=varargin{6};
        Graphtitle=varargin{8};
        nodecolour=varargin{9};
        Hnodecolour=varargin{10};
        fcont=varargin{11};
        scaleMin=varargin{12};
        scaleMax=varargin{13};
        nodeSize=varargin{14};
        HnodeSize=varargin{15};
        arrowSize=varargin{16};
        threshold=varargin{17};
        excNodesWithDeg=varargin{18};

% Define function of left click
        if strcmp(f.SelectionType, 'normal')
            idx=ismember(mets,metofInt);
            newadjMtrx=zeros(size(adjncMtrx));
            newadjMtrx(idx,:)=adjncMtrx(idx,:);
            newadjMtrx(:,idx)=adjncMtrx(:,idx);
            rows=any(newadjMtrx,1);
            cols=any(newadjMtrx,2);
            indofMets=rows'|cols;
            metnames=mets(indofMets);
            newadjMtrx=newadjMtrx(indofMets,indofMets);
            A=newadjMtrx;
            G = digraph(A,metnames);
            ttl=['Sub-', ' ',Graphtitle];
        end
% Define function of right click
        if strcmp(f.SelectionType, 'alt')
            IndexesofMets=find(ismember(model.mets,metofInt));
            metMatrix=~ismember(model.S(IndexesofMets,:),0);
            Rxns=model.rxns(any(metMatrix,1));
            FluxRes=fluxes(ismember(model.rxns,Rxns));
            metmatrix=~ismember(model.S(:,any(metMatrix,1)),0);
            metnames=model.mets(any(metmatrix,2));
            adjunc_mat=model.S(any(metmatrix,2),any(metMatrix,1));
            FluxMatrix  =FluxRes + 1e-6;
            FluxMatrix = repmat(FluxMatrix',size(adjunc_mat,1),1);
            adjunc_mat=adjunc_mat.*FluxMatrix;
            leftMatrix=adjunc_mat;
            leftMatrix(leftMatrix > 0)=0;
            rightMatrix=adjunc_mat;
            rightMatrix(rightMatrix < 0)=0;
            rightMatrix(rightMatrix > 0)=1;
            metMatrix=leftMatrix*rightMatrix';
            A=metMatrix*(-1);
            idx=ismember(metnames,metofInt);
            newadjMtrx=zeros(size(A));
            newadjMtrx(idx,:)=A(idx,:);
            newadjMtrx(:,idx)=A(:,idx);
            rows=any(newadjMtrx,1);
            cols=any(newadjMtrx,2);
            indofMets=rows'|cols;
            metnames=metnames(indofMets);
            newadjMtrx=newadjMtrx(indofMets,indofMets);
            A=newadjMtrx;
            G = digraph(A,metnames);
            ttl=Graphtitle;
        end

        G = rmnode(G,excludedMets);
        G.Edges.Weight(G.Edges.Weight < 1e-4)=1e-6;
        G.Edges.LWidths = (G.Edges.Weight-min(G.Edges.Weight))/(max(G.Edges.Weight)-min(G.Edges.Weight))+0.00001;
        G.Edges.LWidths((isnan(G.Edges.LWidths)))=1e-6;

        if threshold~=1e-7
            edesBelowTresholdIdx=find(G.Edges.Weight<threshold);
            G=rmedge(G,edesBelowTresholdIdx);
        end

        nodesIndegree=indegree(G);
        nodesoutdegree=outdegree(G);
        totalDegree=nodesIndegree+nodesoutdegree;
        G=rmnode(G,find(totalDegree<1));

        figure;
        hold on
        h=plot(G,'MarkerSize',(nodeSize+5),'NodeColor',nodecolour,'ArrowSize',(arrowSize+5),'NodeLabelMode','auto');
        layout(h,'layered','Direction','right')
        highlight(h,metofInt,'NodeColor',Hnodecolour,'MarkerSize',(HnodeSize+5));
        nl = h.NodeLabel;
        h.NodeLabel = '';
        xd = get(h, 'XData');
        yd = get(h, 'YData');
        title([metofInt,' ', 'Centred', ' ',ttl],'Interpreter', 'none');
        txt=text(xd, yd, nl, 'FontSize',8, 'FontWeight','bold', 'HorizontalAlignment','center', 'VerticalAlignment','middle');
        set(txt,'Interpreter', 'none');
        set(txt,'ButtonDownFcn',{@hit_node,A,metnames,model,fluxes,excludedMets,Graphtitle,nodecolour,Hnodecolour,fcont,scaleMin,scaleMax,nodeSize,HnodeSize,arrowSize,threshold,excNodesWithDeg});
        if  fcont==1
            h.LineWidth=1;
        elseif ~any(isnan(G.Edges.LWidths))
            h.EdgeLabel=G.Edges.Weight;
            h.LineWidth=G.Edges.LWidths*2.5;
            colormap jet(10)
            h.EdgeCData=G.Edges.Weight;
            hcb=colorbar;
            colorTitleHandle = get(hcb,'Title');

        else
            h.LineWidth=1;
            colormap jet(10)
            h.EdgeCData=G.Edges.Weight;
            hcb=colorbar;
            colorTitleHandle = get(hcb,'Title');
        end

        if  fcont==0
        if ~(scaleMax==1e-6 && scaleMin==0)
            caxis([scaleMin scaleMax])
            titleBar = ['Scaled Fluxes',' ', '(', num2str(scaleMin),' - ',num2str(scaleMax),')'];
            set(colorTitleHandle ,'String',titleBar,'FontWeight','Bold');
        else
            set(colorTitleHandle ,'String','Fluxes','FontWeight','Bold');
        end
        end

        set(gca,'XTickLabel',{' '});
        set(gca,'YTickLabel',{' '});
        set(gca,'YTick',[]);
        set(gca,'XTick',[]);
        set(gca,'XColor', 'none','YColor','none');
    end

end