classdef featureMatrices < handle

% featureMatrices object for ECGwrapper
% -------------------------------------
%
% Description:
%
%
% Arguments: (specified as ECGwrapper('arg_name1', arg_val1, ... , 'arg_nameN', arg_valN) )
%
%     a. ECG specification:
%
%         a.1 Recording filename where ECG is stored in a valid format.
%
%           + recording_name: ECG recording to be classified.
%           + recording_format : Valid ECG format. (MIT, ISHNE, AHA, HES, MAT)
%
%     b. Operating modes
%
%
%     c. Modifiers
%
%
% Output:
%
% Examples:
%
%
% Limits and Known bugs:
%   Probably a lot :( ... but dont panic! send me feedback if you need
%   help.
%
% Author: Mariano Llamedo Soria (llamedom at frba.utn.edu.ar)
% Version: 0.1 beta
% Birthdate  : 20/3/2012
% Last update: 20/3/2012

    properties(GetAccess = private, Constant)
        sampling_rate = 360; %Hz
        fnPayload = {'featMat_clust' 'featMat_ldc' 'ECG' 'QRS_locations' 'RR_intervals'};
        fnFMfile = {'featMat_clust' 'featMat_ldc'};
        fnECGfile = {'ECG' 'QRS_locations' 'RR_intervals'};
        SmallValue = 0.0001;
    end

    properties ( Access = private )
        delayLP
        autovec
        q_filters
        LP
        LPsize
        scale_idx
        scales
        global_struct
    end

    properties (SetAccess = private, GetAccess = public)
        %read-only
        name
        class_labeling
        true_labels
        %auxiliary indexes
        file_count = 1;
        File_list = [];
        File_idx = [];
        QRS_idx = [];

    end

    properties
        %read-write
        recording_name
        recording_format
        progress_hdl
        tmp_path
        bCache
        cant_pids
        this_pid
        cant_iteration
        this_iteration
    end

    methods

        function obj = featureMatrices(class_labeling)

            %% Constants and definitions

            obj.name = 'feat_matrix_construction';
            obj.class_labeling = class_labeling;

        end

        function Start(obj)

            %% Calculate accesories signals

            %load low-pass preprocessing filter.
            load( ['LP Fs ' num2str(obj.sampling_rate) 'Hz - Fc 35Hz - 80db stop.mat' ]);
            obj.LPsize = length(LP.numerator);
            obj.delayLP = round((obj.LPsize-1)/2);
            obj.LP = LP;
            clear LP

            % Wavelet transform filter bank design
            obj.scales = 1:6;
            CantScales = length(obj.scales);
            MaxScales = max(obj.scales);
            obj.scale_idx= nan(MaxScales,1);

            filters_cache_filename = ['wt_filters_' num2str(MaxScales) ' scales_' num2str(obj.sampling_rate) ' Hz.mat' ];
            if( exist(filters_cache_filename, 'file') )
                aux = load( filters_cache_filename );
                obj.q_filters = aux.q_filters;
                clear aux
            else
                obj.q_filters = qs_filter_design(MaxScales, obj.sampling_rate);
            end

            for ii = 1:CantScales
                %indice para saber que escala corresponde a cada columna de MyWavelets
                obj.scale_idx(obj.scales(ii)) = ii;
            end

            % Update point
            obj.progress_hdl.checkpoint('PCA projection');

%             obj.autovec = PCA_proj_basis(obj.recording_name, obj.recording_format, obj.q_filters);

            obj.autovec = [-0.696609733483207 -0.403719836632163 -0.593081084444745;-0.487887339191286 -0.339521516938205 0.804171053814316;-0.526023595928265 0.849550135686930 0.0395441965529324;];

            % data structure to keep data through all iterations.
            obj.global_struct = [];

        end

        function payload = Process(obj, ECG, this_header, this_iter_ECG_relative_start_end_idx, this_iter_annotations)

            obj.progress_hdl.checkpoint('Label parsing');

            [QRS_locations, obj.true_labels ] = Annotation_process(this_iter_annotations, obj.recording_format, obj.class_labeling);

            % RR interval sequence
            RR_intervals = diff(QRS_locations, 1);
            RR_intervals = colvec([ RR_intervals(1); RR_intervals ]);

            dRR_intervals = diff(RR_intervals, 1);
            dRR_intervals = colvec([ dRR_intervals(2); dRR_intervals(2); dRR_intervals(2:end) ]);

            % interval resampling.
            RR_intervals = RR_intervals * obj.sampling_rate / this_header.freq;
            dRR_intervals = dRR_intervals * obj.sampling_rate / this_header.freq;

            %% Preprocessing

            % Update point
            obj.progress_hdl.checkpoint('Resampling');

            cant_QRS_locations = length(QRS_locations);
            this_iter_QRS_seq_idx = 1:cant_QRS_locations;

            %resample to obj.sampling_rate Hz
            ECG = resample(ECG, obj.sampling_rate, this_header.freq);
            QRS_locations = colvec(round(QRS_locations * obj.sampling_rate / this_header.freq));
        %         this_iter_true_labels = true_labels(this_iter_QRS_seq_idx);
            this_iter_ECG_resampled_size = size(ECG,1);

            if( this_header.nsig > 2 )
                %multilead approach.
                % project ECG and wtECG to obtain PCA components
                ECG = ECG * obj.autovec(:,1:2);
        %             wtECG = cellfun(@(a)( a * obj.autovec(:,1:2) ), mat2cell(wtECG, this_iter_ECG_resampled_size, obj.ECG_header.nsig, ones(1,CantScales) ), 'UniformOutput', false);
        %             wtECG = cell2mat(wtECG);
            end

            % Update point
            obj.progress_hdl.checkpoint('Filtering');

            %Low pass filtering @ 35Hz => ECG recovery
            ECG = filter(obj.LP, ECG);
            %delay compensation.
            ECG = [ zeros(obj.delayLP, size(ECG,2) ) ;ECG(obj.LPsize:end,:) ; zeros(obj.delayLP, size(ECG,2))];

            %Quito la linea de base.
        %     ECG = BaselineWanderRemovalMedian( ECG, obj.sampling_rate);
            ECG = BaselineWanderRemovalSplines( ECG, QRS_locations, obj.sampling_rate);

            % Update point
            obj.progress_hdl.checkpoint('Wavelet transform calculation');

            wtECG = qs_wt(ECG, obj.scales, obj.sampling_rate, obj.q_filters);

            %% Features Calculation

            % Update point
            obj.progress_hdl.checkpoint('Features calculation');

            this_iter_seq_idx = 1:length(QRS_locations);

            % log(RRactual)
            %%%%%%%%%%%%%%%
            featMat_ldc = log(colvec(RR_intervals(this_iter_QRS_seq_idx))/obj.sampling_rate);

            % Update point
            obj.progress_hdl.checkpoint('Features calculation');

            % log(RRpost)
            %%%%%%%%%%%%%%%

            featMat_ldc = [ featMat_ldc log(colvec(RR_intervals(min(cant_QRS_locations, this_iter_QRS_seq_idx+1)))/obj.sampling_rate)];

            % Update point
            obj.progress_hdl.checkpoint('Features calculation');

            % log(RRmean1)
            %%%%%%%%%%%%%%%

            aux_idx = arrayfun(@(a)( find(  QRS_locations >= (QRS_locations(a) - 1*60*obj.sampling_rate) & ...
                                            QRS_locations <= QRS_locations(a) )), ...
                               this_iter_seq_idx, 'UniformOutput', false);

            aux_featval = cellfun(@(a)(mean(RR_intervals(a))/obj.sampling_rate), aux_idx);
            featMat_ldc = [ featMat_ldc colvec(log(aux_featval)) ];

            % Update point
            obj.progress_hdl.checkpoint('Features calculation');

            % log(RRmean20)
            %%%%%%%%%%%%%%%

            aux_idx = arrayfun(@(a)( find(  QRS_locations >= (QRS_locations(a) - 20*60*obj.sampling_rate) & ...
                                            QRS_locations <= QRS_locations(a) )), ...
                               this_iter_QRS_seq_idx, 'UniformOutput', false);

            aux_featval = cellfun(@(a)(mean(RR_intervals(a))/obj.sampling_rate), aux_idx);
            featMat_ldc = [ featMat_ldc colvec(log(aux_featval)) ];

            % Update point
            obj.progress_hdl.checkpoint('Features calculation');

            % AutoCorr1PlanoWTZeroCross
            %%%%%%%%%%%%%%%
            % AutoCorr1PlanoWTModMaxPos
            %%%%%%%%%%%%%%%

            aux_idx = arrayfun(@(a)( max(1, QRS_locations(a) - round(0.08*obj.sampling_rate)): ...
                                     min(this_iter_ECG_resampled_size, QRS_locations(a) + round(0.08*obj.sampling_rate))) , ...
                               this_iter_seq_idx, 'UniformOutput', false);

            aux_featval = cellfun(@(a)(squeeze(wtECG(a,:,obj.scale_idx(4)))), aux_idx, 'UniformOutput', false);

            %calculate PCA matrix in this slices for feature
            %AutoCorr1PlanoWTModMaxPos
            autovec_slices = cellfun(@(a)(autovec_calculation(a)), aux_featval, 'UniformOutput', false );

            aux_idx = arrayfun(@(a)( max(1, QRS_locations(a) - round(0.13*obj.sampling_rate)): ...
                                     min(this_iter_ECG_resampled_size, QRS_locations(a) + round(0.2*obj.sampling_rate))) , ...
                               this_iter_seq_idx, 'UniformOutput', false);

            aux_featval = cellfun(@(a,b)(squeeze(wtECG(a,:,obj.scale_idx(4))) * b), aux_idx, autovec_slices, 'UniformOutput', false);
            [ aux_mp aux_zc ] = cellfun(@(a)(CalcModMaxPos(a(:,1))), aux_featval );

            featMat_ldc = [ featMat_ldc colvec(aux_zc) colvec(aux_mp) ];

            % Update point
            obj.progress_hdl.checkpoint('Features calculation');

            % AutoCorr2PlanoWTZeroCross
            %%%%%%%%%%%%%%%
            % AutoCorr2PlanoWTModMaxPos
            %%%%%%%%%%%%%%%

            [ aux_mp aux_zc ] = cellfun(@(a)(CalcModMaxPos(a(:,2))), aux_featval );

            featMat_ldc = [ featMat_ldc colvec(aux_zc) colvec(aux_mp) ];

            % Update point
            obj.progress_hdl.checkpoint('Features calculation');

            %clear some auxiliar variables.
            clear aux*

            %calculate clustering features.

            % log(RRactual)
            %%%%%%%%%%%%%%%
            featMat_clust = featMat_ldc(:,1);

            % Update point
            obj.progress_hdl.checkpoint('Features calculation');

            % log(RRant)
            %%%%%%%%%%%%%%%
            featMat_clust = [ featMat_clust log(colvec(RR_intervals(max(1, this_iter_QRS_seq_idx-1)))/obj.sampling_rate)];

            % Update point
            obj.progress_hdl.checkpoint('Features calculation');

            % log(Prematuridad_loc)
            %%%%%%%%%%%%%%%

            aux_idx = arrayfun(@(a)(max(1,a-1):min(cant_QRS_locations,a+1)), this_iter_QRS_seq_idx, 'UniformOutput', false);
            %tengo que contemplar los casos extremos
            if( length(aux_idx{1}) < 3 )
                aux_idx{1} = [1 aux_idx{1}];
            end
            if( length(aux_idx{end}) < 3 )
                aux_idx{end} = [aux_idx{end} cant_QRS_locations];
            end

            aux_featval = cellfun(@(a)( RR_intervals(a(2))/sum(RR_intervals(a)) ), aux_idx);
            featMat_clust = [ featMat_clust colvec(log(aux_featval)) ];

            % Update point
            obj.progress_hdl.checkpoint('Features calculation');

            % log(AbsRRlocalVar)
            %%%%%%%%%%%%%%%

            aux_idx = arrayfun(@(a)(max(1,a-1):min(cant_QRS_locations,a+1)), this_iter_QRS_seq_idx, 'UniformOutput', false);
            if( length(aux_idx{1}) < 3 )
                aux_idx{1} = [1 aux_idx{1}];
            end
            if( length(aux_idx{end}) < 3 )
                aux_idx{end} = [aux_idx{end} cant_QRS_locations];
            end

            aux_featval = cellfun(@(a)(obj.SmallValue+sum(abs(dRR_intervals(a)))/obj.sampling_rate), aux_idx);

            featMat_clust = [ featMat_clust colvec(log(aux_featval)) ];

            % Update point
            obj.progress_hdl.checkpoint('Features calculation');

            % log(RRmean20)
            %%%%%%%%%%%%%%%

            featMat_clust = [ featMat_clust featMat_ldc(:,4) ];

            % Update point
            obj.progress_hdl.checkpoint('Features calculation');

            % log(QRS_max_scale_proj12)
            %%%%%%%%%%%%%%%

            aux_idx = arrayfun(@(a)( max(1, QRS_locations(a) - round(0.08*obj.sampling_rate)): ...
                                     min(this_iter_ECG_resampled_size, QRS_locations(a) + round(0.08*obj.sampling_rate))) , ...
                               this_iter_seq_idx, 'UniformOutput', false);

            aux_featval = cellfun(@(a)(wtECG(a,:,:)), aux_idx, 'UniformOutput', false);

            % Update point
            obj.progress_hdl.checkpoint('Features calculation');

            aux_featval = cellfun(@(a)(squeeze(sum(abs(a(:,1,:))))'), aux_featval, 'UniformOutput', false );
            aux_featval = cellfun(@(a)(obj.scales * colvec(a) ./ sum(a)), aux_featval );

        %     iAreaAbs = squeeze(sum(abs(wtAux)))';
        %     MaxProjArea = obj.scales * iAreaAbs ./ sum(iAreaAbs);

            featMat_clust = [ featMat_clust colvec(log(aux_featval)) ];

            % Update point
            obj.progress_hdl.checkpoint('Features calculation');

            % AutoCorr1PlanoWTModMaxPos
            %%%%%%%%%%%%%%%

            aux_idx = arrayfun(@(a)( max(1, QRS_locations(a) - round(0.13*obj.sampling_rate)): ...
                                     min(this_iter_ECG_resampled_size, QRS_locations(a) + round(0.2*obj.sampling_rate))) , ...
                               this_iter_seq_idx, 'UniformOutput', false);

            aux_featval = cellfun(@(a,b)(squeeze(wtECG(a,:,obj.scale_idx(4))) * b), aux_idx, autovec_slices, 'UniformOutput', false);
            aux_featval = cellfun(@(a)(CalcModMaxPos(a(:,1))), aux_featval);

            featMat_clust = [ featMat_clust colvec(aux_featval) ];

            % Update point
            obj.progress_hdl.checkpoint('Features calculation');

            % CrossCorr_QRST_ModMaxVal13
            %%%%%%%%%%%%%%%

            aux_idx = arrayfun(@(a)( max(1, QRS_locations(a) - round(0.13*obj.sampling_rate)): ...
                                     min( [ this_iter_ECG_resampled_size, ...
                                            QRS_locations(a) + round(0.4*obj.sampling_rate), ...
                                            QRS_locations(a) + RR_intervals(a) - round(0.13*obj.sampling_rate) ] ) ), ...
                               this_iter_seq_idx, 'UniformOutput', false);

            if( this_header.nsig > 2 )
                % wtECG already projected in obj.autovec.
                aux_featval = cellfun(@(a)( CalcModMaxVal( squeeze(wtECG(a,:,obj.scale_idx(3))) ) ), aux_idx);
            else
                aux_featval = cellfun(@(a)( CalcModMaxVal( squeeze(wtECG(a,:,obj.scale_idx(3))) * obj.autovec ) ), aux_idx);
            end

            featMat_clust = [ featMat_clust colvec(aux_featval) ];

            %save ECG for user interface and cluster analysis
            for ii = 1:length(obj.fnPayload)
                payload.(obj.fnPayload{ii}) = eval(obj.fnPayload{ii});
            end

        end

        function allPayloads = Concatenate(obj, allPayloads, payload )

            if( isempty(allPayloads) )
                for ii = 1:length(obj.fnFMfile)
                     allPayloads.(obj.fnFMfile{ii}) = payload.(obj.fnFMfile{ii});
                end
            else
                for ii = 1:length(obj.fnFMfile)
                     allPayloads.(obj.fnFMfile{ii}) = [allPayloads.(obj.fnFMfile{ii}); payload.(obj.fnFMfile{ii})];
                end
            end

            [~, rec_filename] = fileparts(obj.recording_name);

            for ii = 1:length(obj.fnECGfile)
                 aux_payload.(obj.fnECGfile{ii}) = payload.(obj.fnECGfile{ii});
            end

            save([obj.tmp_path 'tmpfile_' rec_filename '_ECG_cantpids_' num2str(obj.cant_pids) '_' num2str(obj.this_pid) '_' num2str(obj.this_iteration) '_' num2str(obj.cant_iteration)  '.mat'], '-struct', 'aux_payload' );

            aux_ECG_filename = [obj.tmp_path 'tmpfile_' rec_filename '_ECG_cantpids_' num2str(obj.cant_pids) '_thispid_' num2str(obj.this_pid) '_iteration_' num2str(obj.this_iteration) '_of_' num2str(obj.cant_iteration)  '.mat'];
            %do this for cluster supercomputers reasons
            movefile(   [obj.tmp_path 'tmpfile_' rec_filename '_ECG_cantpids_' num2str(obj.cant_pids) '_' num2str(obj.this_pid) '_' num2str(obj.this_iteration) '_' num2str(obj.cant_iteration)  '.mat'], ...
                aux_ECG_filename, 'f' );

            cant_QRS = length(payload.QRS_locations);
            obj.QRS_idx = [obj.QRS_idx; uint16(colvec(1:cant_QRS))];
            obj.File_idx = [obj.File_idx; repmat(uint16(obj.file_count), cant_QRS, 1)];
            obj.File_list = [obj.File_list; cellstr(aux_ECG_filename)];
            obj.file_count = obj.file_count + 1;

        end

    end

end

function autovec = autovec_calculation(wtECGslice)

    mean_wtECGslice = mean(wtECGslice);
    wtECGslice_cov = cov( bsxfun( @minus, wtECGslice, mean_wtECGslice ));
    [autovec autoval] = eig(wtECGslice_cov);
    [~, autoval_idx] = sort(diag(autoval), 'descend');
    autovec = autovec(:,autoval_idx);

end

function [ ModMaxPos ZeroCross ] = CalcModMaxPos( wtSignal )

    ModMaxPos = nan;
    ZeroCross = nan;

    lwtSignal = size(wtSignal, 1);

    if( all( wtSignal == 0 ) )
        return
    end

    ac1 = conv(wtSignal(:,1), flipud(wtSignal(:,1)));

    ac1 = ac1(lwtSignal:end);
    ac1 = 1/ac1(1)*ac1;

    interp_idx = (1:0.1:lwtSignal)';

    ac1_interp = spline( 1:lwtSignal, ac1, interp_idx );
    iZeroCrossPos_ac1 = myzerocros(ac1);

    if( isempty(iZeroCrossPos_ac1))
        return
    else
        if( iZeroCrossPos_ac1(1) < lwtSignal )
            if( sign(ac1(iZeroCrossPos_ac1(1))) ~= sign(ac1(iZeroCrossPos_ac1(1)+1)) )
                ZeroCross = iZeroCrossPos_ac1(1) - ac1(iZeroCrossPos_ac1(1)) / ( ac1(iZeroCrossPos_ac1(1)+1) - ac1(iZeroCrossPos_ac1(1)) );
            else
                ZeroCross = (iZeroCrossPos_ac1(1)-1) - ac1(iZeroCrossPos_ac1(1)-1) / ( ac1(iZeroCrossPos_ac1(1)) - ac1(iZeroCrossPos_ac1(1)-1) );
            end
        else
            return
        end
    end

    ModMaxPos_ac1 = modmax( ac1_interp, 2, 0, 0 );

    if( ~isempty(ModMaxPos_ac1) )
        valid_ac1_max_idx = find(interp_idx(ModMaxPos_ac1) > ZeroCross);
        if( ~isempty(valid_ac1_max_idx) )
            ModMaxPos = interp_idx(ModMaxPos_ac1(valid_ac1_max_idx(1)));
        else
            return
        end
    else
        return
    end

end

function ModMaxVal = CalcModMaxVal( wtSignal )

    % std_wtSignal = std(wtSignal);
    % wtSignal = bsxfun( @times, wtSignal, 1./std_wtSignal);

    cc = conv(wtSignal(:,1), flipud(wtSignal(:,2)));

    [~, max_pos] = max(abs(cc));

    ModMaxVal = cc(max_pos);

end


function SenFig = drawSenFig(SenFig, Sen, Raw, Obs, FigOpt)

% DRAWSENFIG  Redraw one sensor figure.
% 	DRAWSENFIG(SENFIG, SEN, RAW, OBS, FIGOPT) updates all the handles in
% 	the handles structure SENFIG to reflect the observations OBS taken by
% 	sensor SEN, together with the raw data RAW. SENFIG is one sensor figure
% 	structure created with CREATESENFIG.
%
%   See also CREATESENFIG, DRAWMAPFIG.

%   Copyright 2008-2009 Joan Sola @ LAAS-CNRS.

% Sensor type:
% ------------
switch Sen.type

    % camera pinhole
    % --------------
    case {'pinHole','pinHoleDepth'}

        % 1. Raw data visualisation
        switch Raw.type
            case 'simu'
                drawRawPnts(SenFig, Raw);
                drawRawLines(SenFig, Raw);
            case 'dump'
                set(SenFig.img,'cdata',Raw.data.img);
                drawRawPnts(SenFig, Raw);
                drawRawLines(SenFig, Raw);
            case 'img'
                set(SenFig.img,'cdata',Raw.data.img);
            otherwise
        end

        % 2. Process only visible landmarks:
        % a - first erase lmks that were drawn but are no longer visible
        vis   = [Obs(:).vis];
        drawn = SenFig.drawn;
        erase = drawn & ~vis;
        if any(erase)
            set(SenFig.measure(erase),   'vis', 'off');
            set(SenFig.mean(erase),      'vis', 'off');
            set(SenFig.ellipse(erase,:), 'vis', 'off');
            set(SenFig.label(erase),     'vis', 'off');
            SenFig.drawn(erase) = false;
        end

        % b - now draw only visible landmarks
        for lmk = find(vis)

            SenFig.drawn(lmk) = true;
            % Landmark type:
            % --------------
            switch Obs(lmk).ltype

                case {'eucPnt'}  % Euclidean point
                    colors = FigOpt.sensor.colors.defPnt;
                    drawObsPnt(SenFig, Obs(lmk), colors, FigOpt.sensor.showEllip);

                case {'idpPnt','hmgPnt','ahmPnt','fhmPnt'}  % IDP and HMG points
                    colors = FigOpt.sensor.colors.othPnt;
                    drawObsPnt(SenFig, Obs(lmk), colors, FigOpt.sensor.showEllip);

                case {'plkLin','aplLin','idpLin','hmgLin', 'ahmLin'}  % lines
                    colors = FigOpt.sensor.colors.othLin;
                    drawObsLin(SenFig, Obs(lmk), colors, FigOpt.sensor.showEllip);

                    % ADD HERE FOR NEW LANDMARK TYPE
                case {'newLandmark'}
                    % do something

                otherwise
                    % Print an error and exit
                    error('??? Unable to display landmark ''%s'' with sensor ''%s''.',Obs(lmk).ltype,Sen.type);

            end % and of the "switch" on sensor type

        end

    % --------------
    case {'omniCam'}

        % 1. Raw data visualisation
        if strcmp(Raw.type,'simu')
            drawRawPnts(SenFig, Raw);
        else
            %drawRawImage(SenFig, Raw);
            imshow(Raw.img,'InitialMagnification', 'fit', 'Parent', SenFig.axes);
        end

        % 2. Process only visible landmarks:
        % a - first erase lmks that were drawn but are no longer visible
        vis   = [Obs(:).vis];
        drawn = SenFig.drawn;
        erase = drawn & ~vis;
        if any(erase)
            set(SenFig.measure(erase),   'vis', 'off');
            set(SenFig.mean(erase),      'vis', 'off');
            set(SenFig.ellipse(erase,:), 'vis', 'off');
            set(SenFig.label(erase),     'vis', 'off');
            SenFig.drawn(erase) = false;
        end
        % b - now draw only visible landmarks
        for lmk = find(vis)

            SenFig.drawn(lmk) = true;

            % Landmark type:
            % --------------
            switch Obs(lmk).ltype

                case {'eucPnt'}  % Euclidean point
                    colors = FigOpt.sensor.colors.defPnt;
                    drawObsPnt(SenFig, Obs(lmk), colors, FigOpt.sensor.showEllip);

                case {'idpPnt','hmgPnt','ahmPnt','fhmPnt'}  % IDP and HMG points
                    colors = FigOpt.sensor.colors.othPnt;
                    drawObsPnt(SenFig, Obs(lmk), colors, FigOpt.sensor.showEllip);

                case {'plkLin','aplLin','idpLin','hmgLin', 'ahmLin'}  % Plucker line
                    colors = FigOpt.sensor.colors.othLin;
                    drawObsLin(SenFig, Obs(lmk), colors, FigOpt.sensor.showEllip);

                    % ADD HERE FOR NEW LANDMARK TYPE
                case {'newLandmark'}
                    % do something

                otherwise
                    % Print an error and exit
                    error('??? Unable to display landmark ''%s'' with sensor ''%s''.',Obs(lmk).ltype,Sen.type);

            end % and of the "switch" on sensor type

        end

        % ADD HERE FOR INITIALIZATION OF NEW SENSORS's FIGURES
        % case {'newSensor'}
        % do something

        % unknown
        % -------
    otherwise
        error('??? Unknown sensor type ''%s''.',Sen.type);
end

%     end

% end

% ========== End of function - Start GPL license ==========

%   # START GPL LICENSE

%---------------------------------------------------------------------
%
%   This file is part of SLAMTB, a SLAM toolbox for Matlab.
%
%   SLAMTB 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.
%
%   SLAMTB 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 SLAMTB.  If not, see <http://www.gnu.org/licenses/>.
%
%---------------------------------------------------------------------

%   SLAMTB is Copyright:
%   Copyright (c) 2008-2010, Joan Sola @ LAAS-CNRS,
%   Copyright (c) 2010-2013, Joan Sola,
%   Copyright (c) 2014-2015, Joan Sola @ IRI-UPC-CSIC,
%   SLAMTB is Copyright 2009
%   by Joan Sola, Teresa Vidal-Calleja, David Marquez and Jean Marie Codol
%   @ LAAS-CNRS.
%   See on top of this file for its particular copyright.

%   # END GPL LICENSE


% Formatting_step3_align

clear all;close all;clc

%% set manually
M_stimset = GetFishStimset();
M_dir = GetFishDirectories();
save_masterdir = GetNestedDataDir();

%%
range_fish = GetFishRange();

for i_fish = range_fish,
    disp(['i_fish = ', num2str(i_fish)]);

    %% load data
    save_dir = fullfile(save_masterdir,['subject_' num2str(i_fish)]);
    load(fullfile(save_dir,'CoreInfo.mat'));
    load(fullfile(save_dir,'OptionalInfo.mat'));
    load(fullfile(save_dir,'AdditionalInfo.mat'));

    %% index processing
    if M_stimset(i_fish)==1, % fish 1-7
        % 'periods'
        M_period = {480,160,160,320,480,140,150,8,9,10,11,12,13,14,15,200}; %,{120,150,360}};% 8-15 are place holders
        periods = M_period{i_fish};

        % 'timelists'
        period = periods;
        nrep = floor(size(frame_keys,1)/period)-1;
        shift = period;
        timelists_raw = 1+shift:period*nrep+shift;

        % 'timelists_name'
        if i_fish<6,
            timelists_names = {'16 permut: B/W/phototaxis*2'};
        else % i_fish = 6 or 7
            timelists_names = {'phototaxis'};
        end

        % stimuluskey_raw
        stimuluskey_raw = round(frame_keys(:,17)');

    else % fish recorded with multiple protocols
        % 'stimset': struct that contains all info parsed from recorded params
        [stimset,stimuluskey_raw] = StimulusKeyPreprocessing(frame_keys,i_fish); % StimulusKeyPreprocessing(frame_keys,i_fish,'isplotting') to show plot

        % 'periods','timelists_names'
        periods = [stimset.period];
        timelists_names = {stimset.name};

        % very first 100 frames of experiment should be discarded (prone to artifacts)
        if stimset(1).starts(1)<100,
            if stimset(1).nReps(1)>1,
                stimset(1).starts(1) = stimset(1).starts(1)+stimset(1).period;
                stimset(1).nReps(1) = stimset(1).nReps(1)-1;
            elseif stimset(1).nReps(1)==1, % (n/a for current fish, improbable for future fish)
                stimset(1).ij(1,:) = [];
                stimset(1).rawstarts(1) = [];
                stimset(1).rawstops(1) = [];
                stimset(1).starts(1) = [];
                stimset(1).stops(1) = [];
                stimset(1).nReps(1) = [];
            end
        end

        % 'timelists' = list of time-frames numbers for a certain stimulus
        nTypes = length(stimset);
        timelists_raw = cell(1,nTypes); % time-lists, i.e. frame indices
        for i_type = 1:nTypes,
            nSets = length(stimset(i_type).starts);
            IX = [];
            for i_set = 1:nSets,
                IX = horzcat(IX,stimset(i_type).starts(i_set):stimset(i_type).stops(i_set));
            end
            timelists_raw{i_type} = IX;
        end
    end

    %% Behavioral data: fictive motor recordings
    rows = [7,8,9,13,14];
    Behavior_raw = frame_keys(:,rows)';
    Behavior_raw(2,:) = -Behavior_raw(2,:);

    %% save to nested directory
    save_dir_nested = fullfile(save_masterdir,['subject_' num2str(i_fish)]);

    filename = fullfile(save_dir_nested,'CoreInfo.mat');
    varList = {'timelists_raw','timelists_names','periods','stimuluskey_raw'}; % {'CellXYZ','anat_stack','fpsec'};
    save(filename,varList{:},'-append');

    filename = fullfile(save_dir_nested,'OptionalInfo.mat');
    varList = {'Behavior_raw'}; % {'numcell_full','CellXYZ_norm'};
    save(filename,varList{:},'-append');

    if exist('stimset','var'),
        filename = fullfile(save_dir_nested,'AdditionalInfo.mat');
        save(filename,'stimset','-append'); % {'frame_keys','IX_inval_anat'};
    end
end

%     % renameing
%     Behavior_raw = F;
%     stimuluskey_raw = stim_full_raw;
%     timelists = tlists_raw;
%     timelists_names = stimrangenames;


function write_wobj(OBJ,fullfilename)
% Write objects to a Wavefront OBJ file
%
% write_wobj(OBJ,filename);
%
% OBJ struct containing:
%
% OBJ.vertices : Vertices coordinates
% OBJ.vertices_texture: Texture coordinates
% OBJ.vertices_normal : Normal vectors
% OBJ.vertices_point  : Vertice data used for points and lines
% OBJ.material : Parameters from external .MTL file, will contain parameters like
%           newmtl, Ka, Kd, Ks, illum, Ns, map_Ka, map_Kd, map_Ks,
%           example of an entry from the material object:
%       OBJ.material(i).type = newmtl
%       OBJ.material(i).data = 'vase_tex'
% OBJ.objects  : Cell object with all objects in the OBJ file,
%           example of a mesh object:
%       OBJ.objects(i).type='f'
%       OBJ.objects(i).data.vertices: [n x 3 double]
%       OBJ.objects(i).data.texture:  [n x 3 double]
%       OBJ.objects(i).data.normal:   [n x 3 double]
%
% example reading/writing,
%
%   OBJ=read_wobj('examples\example10.obj');
%   write_wobj(OBJ,'test.obj');
%
% example isosurface to obj-file,
%
%   % Load MRI scan
%   load('mri','D'); D=smooth3(squeeze(D));
%   % Make iso-surface (Mesh) of skin
%   FV=isosurface(D,1);
%   % Calculate Iso-Normals of the surface
%   N=isonormals(D,FV.vertices);
%   L=sqrt(N(:,1).^2+N(:,2).^2+N(:,3).^2)+eps;
%   N(:,1)=N(:,1)./L; N(:,2)=N(:,2)./L; N(:,3)=N(:,3)./L;
%   % Display the iso-surface
%   figure, patch(FV,'facecolor',[1 0 0],'edgecolor','none'); view(3);camlight
%   % Invert Face rotation
%   FV.faces=[FV.faces(:,3) FV.faces(:,2) FV.faces(:,1)];
%
%   % Make a material structure
%   material(1).type='newmtl';
%   material(1).data='skin';
%   material(2).type='Ka';
%   material(2).data=[0.8 0.4 0.4];
%   material(3).type='Kd';
%   material(3).data=[0.8 0.4 0.4];
%   material(4).type='Ks';
%   material(4).data=[1 1 1];
%   material(5).type='illum';
%   material(5).data=2;
%   material(6).type='Ns';
%   material(6).data=27;
%
%   % Make OBJ structure
%   clear OBJ
%   OBJ.vertices = FV.vertices;
%   OBJ.vertices_normal = N;
%   OBJ.material = material;
%   OBJ.objects(1).type='g';
%   OBJ.objects(1).data='skin';
%   OBJ.objects(2).type='usemtl';
%   OBJ.objects(2).data='skin';
%   OBJ.objects(3).type='f';
%   OBJ.objects(3).data.vertices=FV.faces;
%   OBJ.objects(3).data.normal=FV.faces;
%   write_wobj(OBJ,'skinMRI.obj');
%
% Function is written by D.Kroon University of Twente (June 2010)

if(exist('fullfilename','var')==0)
    [filename, filefolder] = uiputfile('*.obj', 'Write obj-file');
    fullfilename = [filefolder filename];
end
[filefolder,filename] = fileparts( fullfilename);

comments=cell(1,4);
comments{1}=' Produced by Matlab Write Wobj exporter ';
comments{2}='';

fid = fopen(fullfilename,'w');
write_comment(fid,comments);

if(isfield(OBJ,'material')&&~isempty(OBJ.material))
    filename_mtl=fullfile(filefolder,[filename '.mtl']);
    fprintf(fid,'mtllib %s\n',filename_mtl);
    write_MTL_file(filename_mtl,OBJ.material)

end

if(isfield(OBJ,'vertices')&&~isempty(OBJ.vertices))
    write_vertices(fid,OBJ.vertices,'v');
end

if(isfield(OBJ,'vertices_point')&&~isempty(OBJ.vertices_point))
    write_vertices(fid,OBJ.vertices_point,'vp');
end

if(isfield(OBJ,'vertices_normal')&&~isempty(OBJ.vertices_normal))
    write_vertices(fid,OBJ.vertices_normal,'vn');
end

if(isfield(OBJ,'vertices_texture')&&~isempty(OBJ.vertices_texture))
    write_vertices(fid,OBJ.vertices_texture,'vt');
end

for i=1:length(OBJ.objects)
    type=OBJ.objects(i).type;
    data=OBJ.objects(i).data;
    switch(type)
        case 'usemtl'
            fprintf(fid,'usemtl %s\n',data);
        case 'f'
            check1=(isfield(OBJ,'vertices_texture')&&~isempty(OBJ.vertices_texture));
            check2=(isfield(OBJ,'vertices_normal')&&~isempty(OBJ.vertices_normal));
            if(check1&&check2)
                for j=1:size(data.vertices,1)
                    fprintf(fid,'f %d/%d/%d',data.vertices(j,1),data.texture(j,1),data.normal(j,1));
                    fprintf(fid,' %d/%d/%d', data.vertices(j,2),data.texture(j,2),data.normal(j,2));
                    fprintf(fid,' %d/%d/%d\n', data.vertices(j,3),data.texture(j,3),data.normal(j,3));
                end
            elseif(check1)
                for j=1:size(data.vertices,1)
                    fprintf(fid,'f %d/%d',data.vertices(j,1),data.texture(j,1));
                    fprintf(fid,' %d/%d', data.vertices(j,2),data.texture(j,2));
                    fprintf(fid,' %d/%d\n', data.vertices(j,3),data.texture(j,3));
                end
            elseif(check2)
                for j=1:size(data.vertices,1)
                    fprintf(fid,'f %d//%d',data.vertices(j,1),data.normal(j,1));
                    fprintf(fid,' %d//%d', data.vertices(j,2),data.normal(j,2));
                    fprintf(fid,' %d//%d\n', data.vertices(j,3),data.normal(j,3));
                end
            else
                for j=1:size(data.vertices,1)
                    fprintf(fid,'f %d %d %d\n',data.vertices(j,1),data.vertices(j,2),data.vertices(j,3));
                end
            end
        otherwise
            fprintf(fid,'%s ',type);
            if(iscell(data))
                for j=1:length(data)
                    if(ischar(data{j}))
                        fprintf(fid,'%s ',data{j});
                    else
                        fprintf(fid,'%0.5g ',data{j});
                    end
                end
            elseif(ischar(data))
                 fprintf(fid,'%s ',data);
            else
                for j=1:length(data)
                    fprintf(fid,'%0.5g ',data(j));
                end
            end
            fprintf(fid,'\n');
    end
end
fclose(fid);

function write_MTL_file(filename,material)
fid = fopen(filename,'w');
comments=cell(1,2);
comments{1}=' Produced by Matlab Write Wobj exporter ';
comments{2}='';
write_comment(fid,comments);

for i=1:length(material)
    type=material(i).type;
    data=material(i).data;
    switch(type)
        case('newmtl')
            fprintf(fid,'%s ',type);
            fprintf(fid,'%s\n',data);
        case{'Ka','Kd','Ks'}
            fprintf(fid,'%s ',type);
            fprintf(fid,'%5.5f %5.5f %5.5f\n',data);
        case('illum')
            fprintf(fid,'%s ',type);
            fprintf(fid,'%d\n',data);
        case {'Ns','Tr','d'}
            fprintf(fid,'%s ',type);
            fprintf(fid,'%5.5f\n',data);
        otherwise
            fprintf(fid,'%s ',type);
            if(iscell(data))
                for j=1:length(data)
                    if(ischar(data{j}))
                        fprintf(fid,'%s ',data{j});
                    else
                        fprintf(fid,'%0.5g ',data{j});
                    end
                end
            elseif(ischar(data))
                fprintf(fid,'%s ',data);
            else
                for j=1:length(data)
                    fprintf(fid,'%0.5g ',data(j));
                end
            end
            fprintf(fid,'\n');
    end
end

comments=cell(1,2);
comments{1}='';
comments{2}=' EOF';
write_comment(fid,comments);
fclose(fid);

function write_comment(fid,comments)
for i=1:length(comments), fprintf(fid,'# %s\n',comments{i}); end

function write_vertices(fid,V,type)
switch size(V,2)
    case 1
        for i=1:size(V,1)
            fprintf(fid,'%s %5.5f\n', type, V(i,1));
        end
    case 2
        for i=1:size(V,1)
            fprintf(fid,'%s %5.5f %5.5f\n', type, V(i,1), V(i,2));
        end
    case 3
        for i=1:size(V,1)
            fprintf(fid,'%s %5.5f %5.5f %5.5f\n', type, V(i,1), V(i,2), V(i,3));
        end
    otherwise
end
switch(type)
    case 'v'
        fprintf(fid,'# %d vertices \n', size(V,1));
    case 'vt'
        fprintf(fid,'# %d texture verticies \n', size(V,1));
    case 'vn'
        fprintf(fid,'# %d normals \n', size(V,1));
    otherwise
        fprintf(fid,'# %d\n', size(V,1));

end


function CstDefineFrequencydomainSolver(mws,startFreq,endFreq,samples)

ChangeSolverType = invoke(mws,'ChangeSolverType','HF Frequency Domain');

%'@ define frequency domain solver parameters
Mesh = invoke(mws,'Mesh');
invoke(Mesh,'SetCreator','High Frequency');
FDSolver = invoke(mws,'FDSolver');
invoke(FDSolver,'Reset');
invoke(FDSolver,'SetMethod','Tetrahedral','General purpose');
invoke(FDSolver,'OrderTet','Second');
invoke(FDSolver,'OrderSrf','First');
invoke(FDSolver,'Stimulation','All','All');
invoke(FDSolver,'ResetExcitationList');
invoke(FDSolver,'AutoNormImpedance','True');
invoke(FDSolver,'NormingImpedance','50');
invoke(FDSolver,'ModesOnly','False');
invoke(FDSolver,'ConsiderPortLossesTet','True');
invoke(FDSolver,'SetShieldAllPorts','False');
invoke(FDSolver,'AccuracyHex','1e-6');
invoke(FDSolver,'AccuracyTet','1e-4');
invoke(FDSolver,'AccuracySrf','1e-3');
invoke(FDSolver,'LimitIterations','False');
invoke(FDSolver,'MaxIterations','0');
invoke(FDSolver,'SetCalculateExcitationsInParallel','True','True','');
invoke(FDSolver,'StoreAllResults','False');
invoke(FDSolver,'StoreResultsInCache','False');
invoke(FDSolver,'UseHelmholtzEquation','True');
invoke(FDSolver,'LowFrequencyStabilization','True');
invoke(FDSolver,'Type','Auto');
invoke(FDSolver,'MeshAdaptionHex','False');
invoke(FDSolver,'MeshAdaptionTet','True');
invoke(FDSolver,'AcceleratedRestart','True');
invoke(FDSolver,'FreqDistAdaptMode','Distributed');
invoke(FDSolver,'NewIterativeSolver','True');
invoke(FDSolver,'TDCompatibleMaterials','False');
invoke(FDSolver,'ExtrudeOpenBC','False');
invoke(FDSolver,'SetOpenBCTypeHex','Default');
invoke(FDSolver,'SetOpenBCTypeTet','Default');
invoke(FDSolver,'AddMonitorSamples','True');
invoke(FDSolver,'CalcStatBField','False');
invoke(FDSolver,'CalcPowerLoss','True');
invoke(FDSolver,'CalcPowerLossPerComponent','False');
invoke(FDSolver,'StoreSolutionCoefficients','True');
invoke(FDSolver,'UseDoublePrecision','False');
invoke(FDSolver,'UseDoublePrecision_ML','True');
invoke(FDSolver,'MixedOrderSrf','False');
invoke(FDSolver,'MixedOrderTet','False');
invoke(FDSolver,'PreconditionerAccuracyIntEq','0.15');
invoke(FDSolver,'MLFMMAccuracy','Default');
invoke(FDSolver,'MinMLFMMBoxSize','0.3');
invoke(FDSolver,'UseCFIEForCPECIntEq','true');

invoke(FDSolver,'UseFastRCSSweepIntEq','false');
invoke(FDSolver,'UseSensitivityAnalysis','False');
% invoke(FDSolver,'SweepErrorThreshold','True','0.01');
invoke(FDSolver,'RemoveAllStopCriteria','Hex');
invoke(FDSolver,'AddStopCriterion','All S-Parameters','0.01','2','Hex','True');
invoke(FDSolver,'AddStopCriterion','Reflection S-Parameters','0.01','2','Hex','False');
invoke(FDSolver,'AddStopCriterion','Transmission S-Parameters','0.01','2','Hex','False');
invoke(FDSolver,'RemoveAllStopCriteria','Tet');

invoke(FDSolver,'AddStopCriterion','All S-Parameters','0.01','2','Tet','True');
invoke(FDSolver,'AddStopCriterion','Reflection S-Parameters','0.01','2','Tet','False');
invoke(FDSolver,'AddStopCriterion','Transmission S-Parameters','0.01','2','Tet','False');
% invoke(FDSolver,'All Probes','0.05','2','Tet','True');
invoke(FDSolver,'RemoveAllStopCriteria','Srf');

invoke(FDSolver,'AddStopCriterion','All S-Parameters','0.01','2','Srf','True');
invoke(FDSolver,'AddStopCriterion','Reflection S-Parameters','0.01','2','Srf','False');
invoke(FDSolver,'AddStopCriterion','Transmission S-Parameters','0.01','2','Srf','False');
invoke(FDSolver,'SweepMinimumSamples','3');
invoke(FDSolver,'SetNumberOfResultDataSamples','1001');

invoke(FDSolver,'SetResultDataSamplingMode','Automatic');
invoke(FDSolver,'SweepWeightEvanescent','1.0');
invoke(FDSolver,'AccuracyROM','1e-4');
invoke(FDSolver,'AddSampleInterval','','','1','Automatic','True');
invoke(FDSolver,'AddSampleInterval',num2str(startFreq),num2str(endFreq),num2str(samples),'Automatic','False');
% invoke(FDSolver,'AddSampleInterval','','','','Automatic','False');
invoke(FDSolver,'MPIParallelization','False');
invoke(FDSolver,'UseDistributedComputing','False');
invoke(FDSolver,'NetworkComputingStrategy','RunRemote');
invoke(FDSolver,'NetworkComputingJobCount','3');
invoke(FDSolver,'LimitCPUs','True');
invoke(FDSolver,'MaxCPUs','96');
invoke(FDSolver,'MaximumNumberOfCPUDevices','2');

IESolver = invoke(mws,'IESolver');
invoke(IESolver,'Reset');
invoke(IESolver,'UseFastFrequencySweep','True');
invoke(IESolver,'UseIEGroundPlane','False');
% invoke(IESolver,'SetRealGroundMaterialName','');
invoke(IESolver,'CalcFarFieldInRealGround','False');
invoke(IESolver,'RealGroundModelType','Auto');
invoke(IESolver,'PreconditionerType','Auto');
invoke(IESolver,'ExtendThinWireModelByWireNubs','False');

IESolver = invoke(mws,'IESolver');
invoke(IESolver,'SetFMMFFCalcStopLevel','0');
invoke(IESolver,'SetFMMFFCalcNumInterpPoints','6');
invoke(IESolver,'UseFMMFarfieldCalc','True');
invoke(IESolver,'SetCFIEAlpha','0.500000');
invoke(IESolver,'LowFrequencyStabilization','False');
invoke(IESolver,'LowFrequencyStabilizationML','True');
invoke(IESolver,'Multilayer','False');
invoke(IESolver,'SetiMoMACC_I','0.0001');

invoke(IESolver,'SetiMoMACC_M','0.0001');
invoke(IESolver,'DeembedExternalPorts','True');
invoke(IESolver,'SetOpenBC_XY','True');
invoke(IESolver,'OldRCSSweepDefintion','False');
invoke(IESolver,'SetAccuracySetting','Custom');
invoke(IESolver,'CalculateSParaforFieldsources','True');

invoke(IESolver,'ModeTrackingCMA','True');
invoke(IESolver,'NumberOfModesCMA','3');
invoke(IESolver,'StartFrequencyCMA','-1.0');
invoke(IESolver,'SetAccuracySettingCMA','Default');
invoke(IESolver,'FrequencySamplesCMA','0');
invoke(IESolver,'SetMemSettingCMA','Auto');

FDSolver = invoke(mws,'FDSolver');
invoke(FDSolver,'Start');

end


function output = callsdpt330(interfacedata)

% 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
[C,A,b,blk] = sdpt3struct2sdpt3block(F_struc,c,K);

% To be sure
global CACHE_SIZE   % cache size in kbytes
global LOOP_LEVEL   % loop unrolling level
CACHE_SIZE = options.sdpt3.CACHE_SIZE;
LOOP_LEVEL = options.sdpt3.LOOP_LEVEL;

A = svec(blk,A,ones(size(blk,1),1));

if options.savedebug
    save sdpt3debug blk A C b x0 options.sdpt3
end

showprogress('Calling SDPT3',options.showprogress);

solvertime = tic;
if options.verbose==0
    evalc('[obj,X,y,Z,gaphist,infeashist,info,Xiter,yiter,Ziter] =  sqlp(blk,A,C,b,[],x0,[],options.sdpt3);');
else
    [obj,X,y,Z,gaphist,infeashist,info,Xiter,yiter,Ziter] =  sqlp(blk,A,C,b,[],x0,[],options.sdpt3);
end
solvertime = toc(solvertime);

% Create dual variable in internal format
D_struc = [];
if K.l>0
    D_struc = [D_struc;X{1}(:)];
    if length(X)>1
        X = {X{2:end}};
    end
end
if K.q(1)>0
    D_struc = [D_struc;X{1}(:)];
    if length(X)>1
        X = {X{2:end}};
    end
end
if K.r(1)>0
    D_struc = [D_struc;X{1}(:)];
    if length(X)>1
        X = {X{2:end}};
    end
end
if K.s(1)>0
    top = 1;
    j = 1;
    for i = 1:1:length(K.s)
        Xi = X{j}(top:top+K.s(i)-1,top:top+K.s(i)-1);
        D_struc = [D_struc;Xi(:)];
        top = top + K.s(i);
        if top>length(X{j})
            j = j + 1;
            top = 1;
        end
    end
end

x = y;  % Our notation do not coincide ...

% Convert error code
switch info(1)
    case 0
        problem = 0; % No problems detected
    case -1
        problem = 5; % Lack of progress
    case {-2,-3,-4,-5}
        problem = 4; % Numerical problems
    case -6
        problem = 3; % Maximum iterations exceeded
    case -10
        problem = 7; % YALMIP sent incorrect input to solver
    case 1
        problem = 2; % Dual feasibility
    case 2
        problem = 1; % Primal infeasibility
    otherwise
        problem = -1; % Unknown error
end
infostr = yalmiperror(problem,interfacedata.solver.tag);

if options.savesolveroutput
    solveroutput.obj = obj;
    solveroutput.X = X;
    solveroutput.y = y;
    solveroutput.Z = Z;
    solveroutput.gaphist = gaphist;
    solveroutput.infeashist = infeashist;
    solveroutput.info = info;
    solveroutput.Xiter = Xiter;
    solveroutput.yiter = yiter;
    solveroutput.Ziter = Ziter;
else
    solveroutput = [];
end

if options.savesolverinput
    solverinput.blk = blk;
    solverinput.A   = A;
    solverinput.C   = C;
    solverinput.b   = b;
    solverinput.X0   = [];
    solverinput.y0   = x0;
    solverinput.Z0   = [];
    solverinput.options   = options.sdpt3;
else
    solverinput = [];
end

% Standard interface
output = createOutputStructure(x(:),D_struc,[],problem,infostr,solverinput,solveroutput,solvertime);


function z = uminus( x )

%   Disciplined convex programming information for UMINUS (-):
%      Unary minus may be used in DCPs without restrictions---with the
%      understanding, of course, that it produces a result with the
%      opposite cuvature to its argument; i.e., the negative of a convex
%      expression is concave, and vice versa.
%
%   Disciplined geometric programming information for UMINUS(-):
%      Negation of non-constant values may not be used in disciplined
%      geometric programs.

z = cvx( x.size_, -x.basis_ );
tt = cvx_isvalid( z, true );
if ~all( tt ),
    cvx_dcp_error( '-', 'unary', cvx_subsref( x, ~tt ) );
end

% Copyright 2005-2014 CVX Research, Inc.
% See the file LICENSE.txt for full copyright information.
% The command 'cvx_where' will show where this file is located.


function [ DetectionReview ] = DetectionReview(obj,event )
%UNTITLED Summary of this function goes here
%   Detailed explanation goes here
%obj = findobj('tag','EventExplorerMaster');
FO = guidata(obj);
%% Select the time windows to look at
%User input for this
numwins = 30; %number of windows to look at. determine to maximize sampling or have user input (FO.uinput.field?)
%Selecting from events:
%randevents = randsample(FO.EventTimes,numevents);
%Selecting from random times (RestrictInts takes way too long...)
set(findobj(FO.fig,'Type','uicontrol'),'Enable','off');
drawnow;
if ~isempty(FO.detectionints)
    [status,interval,index] = InIntervals(FO.data.lfp.timestamps,double(FO.detectionints));
    restrictedtimes = FO.data.lfp.timestamps(status);
else
    restrictedtimes = FO.data.lfp.timestamps;
end

%use InIntervals
set(findobj(FO.fig,'Type','uicontrol'),'Enable','on');
randevents = randsample(restrictedtimes,numwins);
%Find any events that are within winsize of another event to remove them?
%Also should look at windows in which no events were detected?  Maybe just
%random windows in the detectionwin
closeevents = abs(diff(randevents))<FO.winsize;
% numtries =0;
% while any(closeevents) && numtries<50
%     randevents(closeevents)
% end
%Problem:overlapping ints
%[ ints ] = MergeSeparatedInts( ints,minseparation )

%%
%Store the current user action
FO.currentuseraction = 'MarkEvents';
FO.markedevents = []; %clear any previously marked events
FO.viewmode = 'timepoint';
guidata(FO.fig, FO); %store the data in the figure

counter = numwins;
miss=[];hit=[];falsealarm=[];
lookedatwins = [];

%UI panel for event review
FO.EventPanel = uipanel('Title','Detection Review','FontSize',12,...
        'Position',[.65 .05 0.25 0.32]);
%Buttons for next loop or finishing
nextbtn = uicontrol('Parent',FO.EventPanel,...
    'Position',[200 20 150 40],'String','Next Window (Return)',...
          'Callback','uiresume(gcbf)');
qutbtn = uicontrol('Parent',FO.EventPanel,...
    'Position',[200 65 150 40],'String','Quit Early (Esc)',...
         'Callback','QUITLOOP=true;uiresume(gcbf)');
%Instruction text
instructtext = uicontrol('Parent',FO.EventPanel,...
    'Position',[25 100 400 100],'style','text',...
    'string',{'Instructions:',...
    '   - LEFT click missed events (o)','   - RIGHT click False Alarms (x)'},...
    'HorizontalAlignment','left');
%Display counter text
correcttext = uicontrol('Parent',FO.EventPanel,...
    'Position',[25 60 125 15],'style','text',...
    'string','Correct: 0','HorizontalAlignment','left');
misstext = uicontrol('Parent',FO.EventPanel,...
    'Position',[25 40 125 15],'style','text',...
    'string','Miss: 0','HorizontalAlignment','left');
FAtext = uicontrol('Parent',FO.EventPanel,...
    'Position',[25 20 125 15],'style','text',...
    'string','FA: 0','HorizontalAlignment','left');
countetext = uicontrol('Parent',FO.EventPanel,...
    'Position',[25 100 175 15],'style','text',...
    'string',['Windows Remaining: ',num2str(counter)],'HorizontalAlignment','left');

%The Event Review Loop
QUITLOOP = false;
while counter>0 && QUITLOOP~=true
    %EventUI(FO)   %function that will run the user interface
    thiseventtime = randevents(counter);
    FO.currevent = thiseventtime; guidata(FO.fig, FO);
    viewinfo = EventVewPlot;
    uiwait(FO.fig)  %Wait here until the user clicks quit or next

    lookedatwins = [lookedatwins; viewinfo.thiseventwin];
    inwinevents = viewinfo.inwinevents;

    %Tally the user selections for that window
    obj = findobj('tag','EventExplorerMaster');  FO = guidata(obj);
    if ~isempty(FO.markedevents)
        FO.markedevents(isnan(FO.markedevents(:,1)))=[]; %remove nans - bug fix
        miss = [miss; FO.markedevents(FO.markedevents(:,3)==1,1)];
        %This is messy to account for interp errors with 0-1 reference points
        if isempty(FO.markedevents(FO.markedevents(:,3)==3,1))
            newfalsealarms = [];
        elseif length(FO.markedevents(FO.markedevents(:,3)==3,1))==1 && length(inwinevents)==1
            newfalsealarms = inwinevents;
        else
            newfalsealarms = interp1(inwinevents,inwinevents,...
                FO.markedevents(FO.markedevents(:,3)==3,1),'nearest');
        end
        falsealarm = [falsealarm; newfalsealarms];
    end
    hit = [hit; inwinevents(~ismember(inwinevents,falsealarm))];
    FO.markedevents = [];  guidata(FO.fig, FO); %reset the marked events

    counter = counter-1;
    set(correcttext, 'String',['Correct: ',num2str(length(hit))]);
    set(misstext, 'String', ['Miss: ',num2str(length(miss))]);
    set(FAtext, 'String', ['FA: ',num2str(length(falsealarm))]);
    set(countetext, 'String', ['Windows Remaining: ',num2str(counter)]);
end
%Calculate total number of miss,hit,FA
numMiss = length(miss);
numHit = length(hit);
numFA = length(falsealarm);

%Calculate total amount of time/percentage of detection time (detectionintervals) looked at

%Put things in the output structure
DetectionReview.lookedatwins = lookedatwins;
DetectionReview.miss = miss;
DetectionReview.hit = hit;
DetectionReview.falsealarm = falsealarm;
DetectionReview.estMissperc = numMiss./(numHit+numMiss);
DetectionReview.estFAperc = numFA./(numHit+numFA);
DetectionReview.ReviewDate = today;
DetectionReview.EventsType = FO.EventName;

%UI: Done!  Would you like to save the results to (eventsfilename?)
%Make function that does this: SaveResults(FO,EEoutput)
if isfield(FO,'eventsfilename')
    button = questdlg(['Event Review Complete! Would you like to add the results to ',...
        FO.eventsfilename,'?'],'Good Job!');
    switch button
        case 'Yes'
            %Load the events file, add the field, save the events file
            try %Only do this if the correct named structure lives in the file
                eventsfile = load(FO.eventsfilename,FO.EventName);
                eventsfile.(FO.EventName).EventExplorer.DetectionReview = DetectionReview;
                save(FO.eventsfilename,'-struct','eventsfile',FO.EventName,'-append')
            catch
                warndlg({' Save failed... ',[FO.eventsfilename,' may not ',...
                    'contain a structure titled ',FO.EventName,'.'],...
                    'Or you may not have sudo priviliges...?'},'Oh No!')
            end
    end
end

%% Return to explorer mode
FO.viewmode = 'events';
FO.currentuseraction = 'none';
FO.currevent = 1;
set(FO.EventPanel,'Visible','off')
FO.DetectionReview = DetectionReview;
guidata(FO.fig, FO); %Save the detection review back to GUI data
end


% 2021 EDIT: This function is now deprecated. The current version of the shaped PRESS
% simulation is run_simPressShaped.m. The current version employs coherence selection,
% resulting in a 4x simulation speed increase.
%
% run_simPressShaped_fast_phCyc.m
% Jamie Near, McGill University 2018.
%
% USAGE:
% out=run_simPressShaped_fast_phCyc(spinSys);
%
% DESCRIPTION:
% This script simulates a PRESS experiment with fully shaped refocusing
% pulses.  Phase cycling of refocusing pulses is performed.  Furthermore,
% simulations are run at various locations in space to account for the
% within-voxel spatial variation of the metabolite signal.  Summation
% across phase cycles and spatial positions is performed.  To achieve
% faster perfomance compared to the original 'run_simPressShaped.m' function,
% this code uses the method described by Yan Zhang et al. Med Phys 2017;44(8):
% 4169-78.  Some additional acceleration is currently performed using parfor
% loops in both x and y directions.  To enable the use of the MATLAB
% parallel computing toolbox, initialize the multiple worked nodes using
% "matlabpool size X" where "X" is the number of available processing
% nodes.  If the parallel processing toolbox is not available, then replace
% the "parfor" loop with a "for" loop.
%
% INPUTS:
% To run this script, there is technically only one input argument:
% spinSys           = spin system to simulate
%
% However, the user should also edit the following parameters as
% desired before running the function:
% refocWaveform     = name of refocusing pulse waveform.
% refTp             = duration of refocusing pulses[ms]
% Bfield            = Magnetic field strength in [T]
% Npts              = number of spectral points
% sw                = spectral width [Hz]
% Bfield            = magnetic field strength [Tesla]
% lw                = linewidth of the output spectrum [Hz]
% thkX              = slice thickness of x refocusing pulse [cm]
% thkY              = slice thickness of y refocusing pulse [cm]
% fovX              = full simulation FOV in the x direction [cm]
% fovY              = full simulation FOV in the y direction [cm]
% nX                = number of spatial grid points to simulate in x-direction
% nY                = number of spatial grid points to simulate in y-direction
% taus              = vector of pulse sequence timings  [ms]
% refPhCyc1         = vector of phase cycling steps for 1st refocusing pulse [degrees]
% refPhCyc2         = vector of phase cycling steps for 2nd refocusing pulse [degrees]
%
% OUTPUTS:
% out               = Simulation results, summed over all space.

function out=run_simPressShaped_fast_phCyc(spinSys)

% ************INPUT PARAMETERS**********************************
refocWaveform='sampleRefocPulse.pta'; %name of refocusing pulse waveform.
refTp=3.5; %duration of refocusing pulses[ms]
flipAngle=137;  %Flip Angle of the refocusing pulses [degrees] (e.g. Use 180 for Siemens PRESS.  Use 137 for GE PRESS).
Npts=2048; %number of spectral points
sw=2000; %spectral width [Hz]
Bfield=3; %magnetic field strength [Tesla]
lw=2; %linewidth of the output spectrum [Hz]
thkX=1.66; %slice thickness of x refocusing pulse [cm]
thkY=1.66; %slice thickness of y refocusing pulse [cm]
fovX=2.4; %size of the full simulation Field of View in the x-direction [cm]
fovY=2.4; %size of the full simulation Field of View in the y-direction [cm]
nX=32; %Number of grid points to simulate in the x-direction
nY=32; %Number of grid points to simulate in the y-direction
tau1=30; %TE1 for first spin echo [ms]
tau2=105; %TE2 for second spin echo [ms]
refPhCyc1=[0,90]; %phase cycling steps for 1st refocusing pulse [degrees]
refPhCyc2=[0,90]; %phase cycling steps for 2nd refocusing pulse [degrees]
% ************END OF INPUT PARAMETERS**********************************

%set up spatial grid
x=linspace(-fovX/2,fovX/2,nX); %X positions to simulate [cm]
y=linspace(-fovY/2,fovY/2,nY); %y positions to simulate [cm]

%Load RF waveform
refRF=io_loadRFwaveform(refocWaveform,'ref',0);

gamma=42577000; %gyromagnetic ratio

%Load spin systems
load spinSystems
sys=eval(['sys' spinSys]);

%Resample refocusing RF pulse from 400 pts to 100 pts to reduce
%computational workload
refRF=rf_resample(refRF,100);

Gx=(refRF.tbw/(refTp/1000))/(gamma*thkX/10000); %[G/cm]
Gy=(refRF.tbw/(refTp/1000))/(gamma*thkY/10000); %[G/cm]

%Initialize structures:
d_temp=cell(length(x),length(refPhCyc1));
d=cell(length(refPhCyc1));

%loop through space: If you are using the parfor loops below, and you are
%using an older version of MATLAB (e.g.R2012), don't forget to initialize
%the parallel processing toolbox workers using 'matlabpool open N' (for N
%workers, 12 max).  I don't think this is necessary for newer version of
%MATLAB.

%First loop through all x-positions, simulating only the first refocusing
%pulse.
%First loop through x direction (first refoc pulse only);

%for X=1:length(x)  %Use this if you don't have the MATLAB parallel processing toolbox
parfor X=1:length(x)  %Use this if you have the MATLAB parallel processing toolbox
    for RP1=1:length(refPhCyc1)
        disp(['Executing X-position ' num2str(X) ' of ' num2str(length(x)) ', '...
            'First Refoc phase cycle ' num2str(RP1) ' of ' num2str(length(refPhCyc1)) '!!!']);
        d_temp{X}{RP1}=sim_press_shaped_fastRef1(Bfield,sys,tau1,tau2,refRF,refTp,x(X),Gx,refPhCyc1(RP1),flipAngle);
    end
end

%calculate the average density matrix (Doing this inside a separate for
%loop because I couldn't figure out how to do this inside the parfor loop):
for X=1:length(x)
    for RP1=1:length(refPhCyc1)
        d{RP1}=sim_dAdd(d{RP1},d_temp{X}{RP1});
    end
end

% %Initialize structures:
out_temp=cell(length(y),length(refPhCyc1),length(refPhCyc2));
out=struct([]);

%Now loop through y direction (second refoc pulse only);
%for Y=1:length(y) %Use this if you don't have the MATLAB parallel processing toolbox
parfor Y=1:length(y) %Use this if you do have the MATLAB parallel processing toolbox
    for RP1=1:length(refPhCyc1)
        for RP2=1:length(refPhCyc2)
            disp(['Executing Y-position ' num2str(Y) ' of ' num2str(length(y)) ', '...
                'First Refoc phase cycle ' num2str(RP1) ' of ' num2str(length(refPhCyc1)) ', '...
                'Second Refoc phase cycle ' num2str(RP2) ' of ' num2str(length(refPhCyc2)) '!!!']);
            out_temp{Y}{RP1}{RP2}=sim_press_shaped_fastRef2(d{RP1},Npts,sw,Bfield,lw,sys,tau1,tau2,...
                refRF,refTp,y(Y),Gy,refPhCyc2(RP2),flipAngle);
        end
    end
end

%Now combine the outputs;  Again, doing this inside a separate for loop
%becuase I can't figure out how to do this inside the parfor loop:
for Y=1:length(y)
    for RP1=1:length(refPhCyc1)
        for RP2=1:length(refPhCyc2)
            out=op_addScans(out,out_temp{Y}{RP1}{RP2},xor(RP1-1,RP2-1));
        end
    end
end

%For consistent scaling across different shaped simulations, we need to :
%1.  Scale down by the total number of simulations run (since these were
%    all added together.
numSims=(nX*nY*length(refPhCyc1)*length(refPhCyc2));
out=op_ampScale(out,1/numSims);

%2.  Scale by the total size of the simulated region, relative to the size
%    of the voxel.
voxRatio=(thkX*thkY)/(fovX*fovY);
out=op_ampScale(out,1/voxRatio);

end

%Nested Function #1
function d = sim_press_shaped_fastRef1(Bfield,sys,tau1,tau2,RF,tp,dx,Gx,phCyc1,flipAngle)
%
% USAGE:
% d = sim_press_shaped_fastRef1(n,sw,Bfield,linewidth,sys,tau1,tau2,RF,tp,dx,Gx,phCyc1,flipAngle)
%
% DESCRIPTION:
% This function simulates only the first bit of the PRESS experiment, up to
% the beginning of the second refocusing pulse.  The excitation is
% simulated as an instantaneous rotation, and the refocusing pulse is
% simulated as a shaped rotation.
%
% This code is designed to be used in highly-accelerated shaped simulations,
% using the method described by Yan Zhang et al. Med Phys 2017;44(8):
% 4169-78.
%
% This code enables the choice of the phase of the refocusing pulse.  This
% enables phase cycling of the refocusing pulses by repeating simulations
% with different editing pulse phases, which is necessary to remove phase
% artefacts from the editing pulses.  A four step phase cycling scheme is typically
% sufficient, where both refocusing pulses are phase cycled by 0 and 90 degrees, and
% the phase are combined in the following way:
%
% signal = ([0 90] - [0 0]) + ([90 0] - [90 90]);
%
% where, in [X Y], X is the phase of the first refocusing pulse and Y is
% the phase of the second refocusing pulse
%
% Finally, this code simulates the spectrum at a given point in space (x),
% given the values of the slice selection gradient (Gx).  In order
% to fully simulate the MEGA-PRESS experiment, you have to run this
% simulation many times at various points in space (x), followed by
% sim_press_shaped_fastRef2.m, at all points in space (y).
%
% INPUTS:
% n         = number of points in fid/spectrum
% sw        = desired spectral width in [Hz]
% Bfield    = main magnetic field strength in [T]
% linewidth = linewidth in [Hz]
% sys       = spin system definition structure
% tau1      = echo time 1 in [ms].
% tau2      = echo time 2 in [ms].
% RF        = RF pulse definition structure for refoc pulses (obtain using 'io_loadRFwaveform.m')
% tp        = RF pulse duration in [ms]
% dx        = position offset in x-direction (corresponding to first refocusing pulse) [cm]
% dy        = position offset in y-direction (corresponding to second refocusing pulse) [cm]
% Gx        = gradient strength for first selective refocusing pulse [G/cm]
% Gy        = gradient strength for second selective refocusing pulse [G/cm]
% phCycl    = initial phase of the first refocusing pulse in [degrees];
% phCycl2   = initial phase of the second refocusing pulse in [degrees];
% flipAngle = flip angle of refocusing pulses [degrees] (Optional.  Default = 180 deg)
%
% OUTPUTS:
% out       = simulated spectrum, in FID-A structure format, using PRESS
%             sequence.

if nargin<10
    flipAngle=180;
end

if tau1<tp/1000
    error('ERROR:  Echo-time 1 cannot be less than duration of refocusing pulse! ABORTING!!');
end
if tau2<tp/1000
    error('ERROR:  Echo-time 2 cannot be less than duration of refocusing pulse! ABORTING!!');
end

%Set water to centre
centreFreq=2.3;
for k=1:length(sys)
    sys(k).shifts=sys(k).shifts-centreFreq;
end

%Calculate Hamiltonian matrices and starting density matrix.
[H,d]=sim_Hamiltonian(sys,Bfield);

%Calculate new delays by subtracting the pulse duration from tau1 and tau2;
delays=zeros(2);
delays(1)=tau1-tp;
delays(2)=tau2-tp;
if sum(delays<0)
    error(['ERROR! The following taus are too short: ' num2str(find(delays<0)) '.']);
end

%BEGIN PULSE SEQUENCE************
d=sim_excite(d,H,'x');                                    %EXCITE
d=sim_evolve(d,H,delays(1)/2000);                            %Evolve by delays(1)/2
d=sim_shapedRF(d,H,RF,tp,flipAngle,90+phCyc1,dx,Gx);          %1st shaped 180 degree refocusing pulse
d=sim_evolve(d,H,(delays(1)+delays(2))/2000);                     %Evolve by (delays(1)+delays(2))/2
%END PULSE SEQUENCE**************

%After running this many times along x, the density matrices should be
%averaged, and then the average density matrix should be passed through
%'sim_press_shaped_fastRef2' at various different y-positions.

end

%Nested Function #2
function out = sim_press_shaped_fastRef2(d,n,sw,Bfield,linewidth,sys,tau1,tau2,RF,tp,dy,Gy,phCyc2,flipAngle)
%
% USAGE:
% out = sim_press_shaped_fastRef2(d,n,sw,Bfield,linewidth,sys,tau2,RF,tp,dy,Gy,phCyc2,flipAngle)
%
% DESCRIPTION:
% This function simulates only the last bit of the PRESS experiment, from the
% the beginning of the second refocusing pulse, to the end.  The refocusing
%pulse is simulated as a shaped rotation.
%
% This code is designed to be used in highly-accelerated shaped simulations,
% using the method described by Yan Zhang et al. Med Phys 2017;44(8):
% 4169-78.
%
% This code enables the choice of the phase of the refocusing pulse.  This
% enables phase cycling of the refocusing pulses by repeating simulations
% with different editing pulse phases, which is necessary to remove phase
% artefacts from the editing pulses.  A four step phase cycling scheme is typically
% sufficient, where both refocusing pulses are phase cycled by 0 and 90 degrees, and
% the phase are combined in the following way:
%
% signal = ([0 90] - [0 0]) + ([90 0] - [90 90]);
%
% where, in [X Y], X is the phase of the first refocusing pulse and Y is
% the phase of the second refocusing pulse
%
% Finally, this code simulates the spectrum at a given point in space (y),
% given the values of the slice selection gradient (Gy).  In order
% to fully simulate the MEGA-PRESS experiment, you have to first run
% sim_press_shaped_fastRef1.m at all points in space (x), followed by
% this code, at all points in space (y).
%
% INPUTS:
% d         = starting density matrix (obtained using 'sim_press_shaped_fastRef1.m')
% n         = number of points in fid/spectrum
% sw        = desired spectral width in [Hz]
% Bfield    = main magnetic field strength in [T]
% linewidth = linewidth in [Hz]
% sys       = spin system definition structure
% tau1      = echo time 1 in [ms].
% tau2      = echo time 2 in [ms].
% RF        = RF pulse definition structure for refoc pulses (obtain using 'io_loadRFwaveform.m')
% tp        = RF pulse duration in [ms]
% dx        = position offset in x-direction (corresponding to first refocusing pulse) [cm]
% dy        = position offset in y-direction (corresponding to second refocusing pulse) [cm]
% Gx        = gradient strength for first selective refocusing pulse [G/cm]
% Gy        = gradient strength for second selective refocusing pulse [G/cm]
% phCycl    = initial phase of the first refocusing pulse in [degrees];
% phCycl2   = initial phase of the second refocusing pulse in [degrees];
% flipAngle = flip angle of refocusing pulses [degrees] (Optional.  Default = 180 deg)
%
% OUTPUTS:
% out       = simulated spectrum, in FID-A structure format, using PRESS
%             sequence.

if nargin<14
    flipAngle=180;
end

if tau1<tp/1000
    error('ERROR:  Echo-time 1 cannot be less than duration of refocusing pulse! ABORTING!!');
end
if tau2<tp/1000
    error('ERROR:  Echo-time 2 cannot be less than duration of refocusing pulse! ABORTING!!');
end

%Set water to centre
centreFreq=2.3;
for k=1:length(sys)
    sys(k).shifts=sys(k).shifts-centreFreq;
end

%Calculate Hamiltonian matrices and starting density matrix.
[H]=sim_Hamiltonian(sys,Bfield);

%Calculate new delays by subtracting the pulse duration from tau1 and tau2;
delays=zeros(2);
delays(1)=tau1-tp;
delays(2)=tau2-tp;
if sum(delays<0)
    error(['ERROR! The following taus are too short: ' num2str(find(delays<0)) '.']);
end

%BEGIN PULSE SEQUENCE************
d=sim_shapedRF(d,H,RF,tp,flipAngle,90+phCyc2,dy,Gy);          %2nd shaped 180 degree refocusing pulse
d=sim_evolve(d,H,delays(2)/2000);                            %Evolve by delays(2)/2
[out,~]=sim_readout(d,H,n,sw,linewidth,90);      %Readout along y (90 degree phase);
%END PULSE SEQUENCE**************

%Correct the ppm scale:
out.ppm=out.ppm-(4.65-centreFreq);

%Fill in structure header fields:
out.seq='press';
out.te=tau1+tau2;
out.sim='shaped';

%Additional fields for compatibility with FID-A processing tools.
out.sz=size(out.specs);
out.date=date;
out.dims.t=1;
out.dims.coils=0;
out.dims.averages=0;
out.dims.subSpecs=0;
out.dims.extras=0;
out.averages=1;
out.rawAverages=1;
out.subspecs=1;
out.rawSubspecs=1;
out.flags.writtentostruct=1;
out.flags.gotparams=1;
out.flags.leftshifted=0;
out.flags.filtered=0;
out.flags.zeropadded=0;
out.flags.freqcorrected=0;
out.flags.phasecorrected=0;
out.flags.averaged=1;
out.flags.addedrcvrs=1;
out.flags.subtracted=1;
out.flags.writtentotext=0;
out.flags.downsampled=0;
out.flags.isFourSteps=0;

end


function varargout = classify_naive_bayes(meth, varargin)
% Naive Bayes classifier
%
% :Usage:
% ::
%
%     % set up model structure
%     bayes_model = classify_naive_bayes('setup', Y, Xi, [activation_cutoff, selectivity_cutoff]);
%
% :Inputs:
%
%   **Y:**
%        is full (not sparse); empty features will be eliminated
%
%   **Xi:**
%        is obs x classes, an indicator matrix of 1's and 0's
%
% :TEST: test classifier; make a prediction about classes from data
% ::
%
%    [class_est log_joint best_log map p_obs_act_given_class] = classify_naive_bayes('test', Y, bayes_model);
%
% :EVAL: evaluate classification accuracy
% ::
%
%    [prop_correct, confusion_mtx, misclass, prop_correct_by_class, chance, chance_95_ci] = classify_naive_bayes('eval', true_class, class_est, wh_obs);
%
% :APPARENT: apparent classification; with full dataset
% ::
%
%     bayes_model = classify_naive_bayes('apparent', Y, bayes_model);
%
% :XVAL: crossvalidate
% ::
%
%    xval = classify_naive_bayes('xval', Y, Xi);
%
%    bayes_model = classify_naive_bayes('write', bayes_model, Y, volInfo, conditionnames)
%    bayes_model = classify_naive_bayes('write', bayes_model, Y, MC_Setup.volInfo, MC_Setup.Xinms);
%
% To add feature abstraction step within xval:
% ::
%
%    xval = classify_naive_bayes('xval', Y, Xi, 0, .9, .05, 1, volInfo );
%
% :PLOT:
% ::
%
%    classify_naive_bayes('plot', bayes_model, ['pa|t', 'lr', 'lr surface',  'map plot', or 'class plot']);
%
% :Optional Inputs: (any order)
%
% Threshold (abs. value), and colors in cell array
% ::
%
%    classify_naive_bayes('plot', bayes_model, 'lr', .10, {[1 .7 0] [0 0 1]});
%
%
% :Examples:
% ::
%
%    [bayes_model, Y] = classify_naive_bayes('setup', Y, Xi);
%
%    % Test obs. 2
%    tic, [class_est log_joint best_log map] = classify_naive_bayes('test',Y(2,:), bayes_model); toc
%
%    % Get apparent classification rate and look at confusion matrix
%    tic, bayes_model = classify_naive_bayes('apparent', Y, bayes_model); toc
%    bayes_model.apparent.confusion_mtx
%
%    % Cross-validate
%    bayes_model.xval = classify_naive_bayes('xval', Y, Xi);
%    bayes_model.xval.confusion_mtx
%
% :Example 2:
% ::
%
%     Select features, and do apparent and cross-validated classification
%    Y = MC_Setup.unweighted_study_data';
%    wh = sum(Y) > 5;
%    Y = Y(:, wh);
%    whos Y
%    [bayes_model, Y] = classify_naive_bayes('setup', Y, Xi);
%    bayes_model = classify_naive_bayes('apparent', Y, bayes_model);
%    bayes_model.apparent.prop_correct, bayes_model.apparent.confusion_mtx
%    bayes_model.xval = classify_naive_bayes('xval', Y, Xi);
%    bayes_model.xval.prop_correct, bayes_model.xval.confusion_mtx
%
% :Example 3:
%    create_figure('hist'); hist(bayes_model.pa1_given_t, 100);
%    xval = classify_naive_bayes('xval', Y, Xi, .05, .3);
%
% Get results from key regions and run classifier only on those regions:
% ::
%
%     cl = classify_naive_bayes('plot', bayes_model, 'lr', .10, {[1 .7 0] [0 0 1]});
%    [studybyroi,studybyset] = Meta_cluster_tools('getdata',cl{1},dat',volInfo);
%    bayes_model_regions = classify_naive_bayes('setup', studybyroi, Xi);
%    bayes_model_regions = classify_naive_bayes('apparent', studybyroi,bayes_model_regions);
%    disp('Apparent confusion')
%    disp(bayes_model_regions.apparent.confusion_mtx)
%
%    bayes_model_regions.xval = classify_naive_bayes('xval', studybyroi, Xi);
%    disp('Cross-validated confusion')
%    disp(bayes_model_regions.xval.confusion_mtx)
%
%    fprintf('Proportion correct: Apparent: %3.0f%%  Xval: %3.0f%%\n', 100*bayes_model_regions.apparent.prop_correct, 100*bayes_model_regions.xval.prop_correct);
%    fprintf('Proportion correct by class: \t'); fprintf('%3.0f%%\t', 100*bayes_model_regions.xval.prop_correct_by_class);
%    fprintf('\n');

%    sz = cat(1,cl{1}(:).numVox);
%    cl{1}(sz < 10) = [];
%
%    subcl = subclusters_from_local_max(cl{1}, 10);
%
% ..
%    Tor Wager, Sept 2007
% ..

switch meth

    case 'setup'
        Y = full(varargin{1});
        Xi = varargin{2};

        activation_cutoff = [];
        selectivity_cutoff = [];
        return_full_ps = 1;         % for faster processing if not all fields are needed (i.e., in xval)

        if length(varargin) > 2

            activation_cutoff = varargin{3};
            selectivity_cutoff = varargin{4};

        end

        if length(varargin) > 4
            return_full_ps = varargin{5};
        end

        k = 1; % default k regularization param
        if length(varargin) > 5
            k = varargin{6};
        end

        % setup
        [Y, whkeep] = eliminate_empty_Y(Y);

        [bayes_model, Y] = classify_naive_bayes_setup(Y, Xi, activation_cutoff, selectivity_cutoff, whkeep, return_full_ps, k);

        gam = 1; % default gam prior weighting param
        if length(varargin) > 6
            gam = varargin{7};
        end
        bayes_model.params.gam = gam;

        varargout{1} = bayes_model;
        varargout{2} = Y;

    case 'apparent'

        Y = full(varargin{1});
        bayes_model = varargin{2};

        bayes_model.true_class = indic2condf(bayes_model.Xi);

        % test apparent classification rate
        [bayes_model.apparent.class_est bayes_model.apparent.log_joint bayes_model.apparent.best_log bayes_model.apparent.map bayes_model.apparent.pact_given_task] = ...
            classify_naive_bayes_test(Y, bayes_model);

        [bayes_model.apparent.prop_correct, bayes_model.apparent.confusion_mtx, bayes_model.apparent.misclass, ...
            bayes_model.apparent.prop_correct_by_class, bayes_model.apparent.chance, bayes_model.apparent.chance_95_ci] = ...
            classify_naive_bayes_eval(bayes_model.true_class, bayes_model.apparent.class_est);

        varargout{1} = bayes_model;

    case 'test'
        Y = full(varargin{1});
        bayes_model = varargin{2};

        [class_est log_joint best_log map p_obs_act_given_class] = classify_naive_bayes_test(Y, bayes_model);

        varargout{1} = class_est;
        varargout{2} = log_joint;
        varargout{3} = best_log;
        varargout{4} = map;
        varargout{5} = p_obs_act_given_class;

    case 'eval'
        bayes_model = varargin{1};
        class_est = varargin{2};
        wh_obs = [];
        if length(varargin) > 2, wh_obs = varargin{3}; end

        [prop_correct, confusion_mtx, misclass, prop_correct_by_class, chance, chance_95_ci] = classify_naive_bayes_eval(bayes_model, class_est, wh_obs);

        varargout{1} = prop_correct;
        varargout{2} = confusion_mtx;
        varargout{3} = misclass;
        varargout{4} = prop_correct_by_class;
        varargout{5} = chance;
        varargout{6} = chance_95_ci;

        case 'abstract'
          %bayes_model = classify_naive_bayes('abstract', Y, thresh, bayes_model, volInfo);

        Y = full(varargin{1});
        thresh = varargin{2};

        bayes_model = varargin{3};
        volInfo = varargin{4};

        reducedY = bayes_meta_feature_abstract(Y, thresh, bayes_model, volInfo);

        [bayes_model, Y] = classify_naive_bayes('setup', reducedY, bayes_model.Xi, bayes_model.params.activation_cutoff, bayes_model.params.selectivity_cutoff, 1, bayes_model.params.k);

        varargout{1} = bayes_model;
        varargout{2} = Y;

    case 'xval'
        Y = full(varargin{1});
        Xi = varargin{2};

        activation_cutoff = [];
        selectivity_cutoff = [];
        if length(varargin) > 2

            activation_cutoff = varargin{3};
            selectivity_cutoff = varargin{4};

        end

        k = .5;
        gam = 1;
        if length(varargin) > 4

            k = varargin{5};
            gam = varargin{6};

        end

        if length(varargin) > 6
            volInfo = varargin{7};
            do_feature_abstract = 1;
        end

        % initialize
        if do_feature_abstract, Yorig = Y; end

        [Y, whkeep] = eliminate_empty_Y(Y);

        Y = sparse(Y);

        nobs = size(Y, 1);
        fprintf('\nCross-validating %3.0f observations: 00000', nobs);

        log_joint = zeros(nobs, size(Xi, 2));
        map = zeros(nobs, size(Xi, 2));
        class_est = zeros(nobs, 1);
        best_log = zeros(nobs, 1);

        true_class = indic2condf(Xi);

        % Update volinfo for feature abstraction
% %         volInfo.wh_inmask = double(whkeep');
% %         volInfo.n_inmask = sum(whkeep);
% %         volInfo.image_indx(volInfo.image_indx) = whkeep';
% %         volInfo.xyzlist(~whkeep',:) = [];

        % run
        for i = 1:nobs

            if mod(i,10) == 0, fprintf(1,'\b\b\b\b\b%05d',i); end

            include_in_training = true(nobs, 1);
            include_in_training(i) = 0;

            % setup: does feature-selection as well, if requested; no
            % need to return Y, because ...
            bayes_model = classify_naive_bayes_setup(Y(include_in_training, :), Xi(include_in_training, :), activation_cutoff, selectivity_cutoff, whkeep, 0, k);

            bayes_model.params.k = k;
            bayes_model.params.gam = gam;

            testdata = Y(i, bayes_model.whkeep_from_notempty);

            % feature abstraction step
            if do_feature_abstract
                [reducedY, cl, cluster_indx] = bayes_meta_feature_abstract(Yorig(include_in_training, :), .1, .1, bayes_model, volInfo, 0);
                cluster_indx = cluster_indx(bayes_model.whkeep);

                bayes_model = classify_naive_bayes('setup', reducedY, Xi(include_in_training, :), 0, 0, gam, k);

                wh_cl = cluster_indx(testdata);
                wh_cl = unique(wh_cl); wh_cl(wh_cl == 0) = [];
                testdata = false(1, bayes_model.nfeatures);
                testdata(wh_cl) = 1;

            end

            [class_est(i) log_joint(i,:) best_log(i) map(i,:)] = classify_naive_bayes_test(testdata, bayes_model);
        end

        fprintf(1, '\n');

        [prop_correct, confusion_mtx, misclass, prop_correct_by_class, chance, chance_95_ci] = classify_naive_bayes_eval(true_class, class_est);

        varargout{1} = struct('class_est', class_est, 'log_joint', log_joint, 'best_log', best_log, 'map', map, ...
            'prop_correct', prop_correct, 'prop_correct_by_class', prop_correct_by_class, 'confusion_mtx', confusion_mtx, ...
            'misclass', misclass, 'chance', chance, 'chance_95_ci', chance_95_ci, 'params', bayes_model.params);

    case {'write', 'images', 'write_images'}
        bayes_model = varargin{1};
        Y = varargin{2};
        volInfo = varargin{3};
        conditionnames = varargin{4};

        bayes_model = write_images(bayes_model, Y, volInfo, conditionnames);
        varargout{1} = bayes_model;

    case {'plot', 'output'}
        bayes_model = varargin{1};
        disptype = varargin{2};

        cl = display_output(bayes_model, disptype, varargin{3:end});

        varargout{1} = cl;

    otherwise
        error('Unknown method (invalid first argument).');
end

end   % end main function

function [bayes_model, Y] = classify_naive_bayes_setup(Y, Xi, activation_cutoff, selectivity_cutoff, whkeep, return_full_ps, k)

% Y is data matrix, nobs observations x nfeatures variables
% in a meta-analysis, e.g., this is studies x voxels
%
% Each row of Y is an observation; if Y has one row, we're classifying an
% observation (e.g., one brain map)
%
% Xi are indicators for tasks (classes), ntasks columns of nobs elements
% (i.e., an nobs x ntasks matrix)

% eliminate features with no 'on' values (activations)

% k is regularization param, biases towards 0.5
if nargin < 7, k = 1; end

whkeep_from_notempty = whkeep;  % indicator of features after feature-selection, in index of not-empties

% feature selection, if requested
if ~isempty(selectivity_cutoff) && ~isempty(activation_cutoff)

    [Y, whkeep, whkeep_from_notempty] = select_features(Y, Xi, activation_cutoff, selectivity_cutoff, whkeep);

end

[nobs, nfeatures] = size(Y);
nclasses = size(Xi, 2);

if return_full_ps
    [priors, pa1_given_t, pa0_given_t, pt_given_act1, pt_given_act0, pa1_given_not_t] = ...
        bayes_get_probabilities(Y, Xi, k);

else
    % key results (not all) only; saves computation time in
    % crossvalidation.
    [priors, pa1_given_t, pa0_given_t] = bayes_get_probabilities(Y, Xi, k);

end

true_class = indic2condf(Xi);

bayes_model = struct('nobs', nobs, 'nfeatures', nfeatures, 'nclasses', nclasses, ...
    'whkeep', whkeep, 'whkeep_from_notempty', whkeep_from_notempty, ...
    'params', [], ...
    'priors', priors, 'pa1_given_t', pa1_given_t, 'pa0_given_t', pa0_given_t, ...
    'Xi', Xi, 'true_class', true_class);

bayes_model.params.gam = 1;  % gamma, weighting on prior vs. joint evidence
bayes_model.params.k = k;      % k, regularization of pa|t, bias towards 0.5.
bayes_model.params.activation_cutoff = activation_cutoff;
bayes_model.params.selectivity_cutoff = selectivity_cutoff;

if return_full_ps
    bayes_model.pt_given_act1 = pt_given_act1;
    bayes_model.pt_given_act0 = pt_given_act0;
    bayes_model.pa1_given_not_t = pa1_given_not_t;

end

end

function [class_est log_joint best_log map p_obs_act_given_class] = classify_naive_bayes_test(Y, bayes_model)

% Estimate classes, log joint p, and MAP estimates for a test data set

% P(task | act)
% The var. below is all that is needed to choose the best class.
% argmax log_joint
% do this for each observation

[nobs, nfeatures] = size(Y);

if nfeatures ~= bayes_model.nfeatures
    %  try eliminating zero voxels
    Y = Y(:, bayes_model.whkeep);
    [nobs, nfeatures] = size(Y);
end

if nfeatures ~= bayes_model.nfeatures
    error('Number of features (i.e., voxels/regions) in Y must equal that in bayes_model structure or that of original training data.');
end

log_joint = zeros(nobs, bayes_model.nclasses);
map = zeros(nobs, bayes_model.nclasses);

class_est = zeros(nobs, 1);
best_log = zeros(nobs, 1);

gam = bayes_model.params.gam;    % prior weighting factor; from 0 to nfeatures.

logpriors = log(bayes_model.priors);
%logpriors = logpriors(ones(nfeatures, bayes_model.nclasses));

for i = 1:nobs

    wh_active = Y(i,:)' > 0;

    % sum of log prob. of activation at each active voxel, plus sum of
    % log prob. of NOT activation at each inactive voxel (1 - p(activation))
    % Same as ln of product of all p(act state = observed state |
    % class) for all features

    % prob. that activity state is the observed one in the test vector,
    % given each class.
    % p(Fi = fi | C = c)
    % p(ACTi = acti | T = t)

    % % % %     p_obs_act_given_class(wh_active, :) = bayes_model.pt_given_act1(wh_active, :);         % p(active | t, obs active)
    % % % %     p_obs_act_given_class(~wh_active, :) = bayes_model.pt_given_act0(~wh_active, :);

    p_obs_act_given_class = zeros(nfeatures, bayes_model.nclasses);
    p_obs_act_given_class(wh_active, :) = bayes_model.pa1_given_t(wh_active, :);         % p(active | t, obs active)
    p_obs_act_given_class(~wh_active, :) = bayes_model.pa0_given_t(~wh_active, :);   % p(not active | t, obs not active)

    loglikelihood = sum(log(p_obs_act_given_class));
    log_joint(i,:) = gam * log(bayes_model.priors) + loglikelihood;      % log of joint prob. p(T, Y), p(task, act1, act2, act3, etc.)

    %log_joint(i,:) = sum(log(p_obs_act_given_class)); % proportional to the posterior.

    [best_log(i) class_est(i)] = max(log_joint(i,:));

    % Divide joint by evidence
    % get scaled estimate of p(task | activation)
    % These are Max. a posteriori estimates, and values may be > 1 because
    % we're assuming independence when that assumption is not likely to be true
    % * something still wrong with this
    % % % %
    % % % %         % evidence
    % % % %         log_evidence = sum(bayes_model.log_evidence_act(wh_active)) + sum(bayes_model.log_evidence_notact(~wh_active));
    % % % %         % because something is wrong, exp() gives Inf values a lot, so isn't
    % very sensible.
    % % % %         map(i,:) = log_joint(i,:) - log_evidence;  %exp(log_joint(i,:) - log_evidence);

    map(i,:) = exp(log_joint(i,:) ./ (gam + nfeatures) );  %exp(log_joint(i, :)); % will be very small
    %sum(log_joint) + nfeatures * logpriors;

    %map = map ./ sum(map);

end

end

function [prop_correct, m, misclass, prop_correct_by_class, chance, chance_95_ci] = classify_naive_bayes_eval(true_class, class_est, wh_obs)

if nargin < 3 || isempty(wh_obs)
    % assume we have all observations.
    wh_obs = 1:length(true_class);
end

[m,dp,corr,far,misclass] = confusion_matrix(true_class(wh_obs),class_est);

totaln = sum(m(:));
prop_correct = trace(m) ./ totaln;

nclasses = length(unique(true_class(true_class ~= 0)));

prop_correct_by_class = diag(m) ./ sum(m,2);

chance = 1 ./ nclasses;
chance_95_ci = [chance - sqrt(chance * (1 - chance) ./ totaln)   chance + sqrt(chance * (1 - chance) ./ totaln)];

end

function [Y, whkeep] = eliminate_empty_Y(Y)

fprintf('Eliminating any empty features. \n');

% using whkeep helps avoid out of memory errors.
whzero = (sum(Y) < eps);
whkeep = ~whzero;
Y = Y(:, whkeep);

end

function [Y, whkeep, whomsave] = select_features(Y, Xi, activation_cutoff, selectivity_cutoff, whkeep)

% select features
% -----------------------------------------------------------------

[nobs, nfeatures] = size(Y);
ntasks = size(Xi,2);

sumtasks = sum(Y); sumtasks(sumtasks==0) = 1;  % avoid warning for empty tasks

% p(task | activation) estimates
ptask = (Xi' * Y) ./ repmat(sumtasks,ntasks,1);

maxp = max(ptask);  % max prob of task across vars

pt = (Xi' * Y) ./ repmat(sum(Xi)',1,nfeatures);  % proportion of each task that activated in an area
ptmax = max(pt);
whomsave = maxp > (selectivity_cutoff) & ptmax > activation_cutoff;

if sum(whomsave) == 0
    disp('Warning: No variables meet feature selection criteria.');
    whomsave = (maxp == max(maxp));
end

whkeep(whkeep) = whomsave;  % indices of saved voxels in whole-brain (original) index vector
%whkeep = whkeep(whomsave);  % overall indices of saved voxels
Y = Y(:,whomsave);

%fprintf(1,'Selected: %3.0f ',nvars);
% get weights on voxels based on n activations.... ******

end

function bayes_model = write_images(bayes_model, Y, volInfo, conditionnames)
% bayes_model = write_images(bayes_model, MC_Setup.volInfo, MC_Setup.Xinms)

% Xi = bayes_model.Xi;

%[priors, pa1_given_t, pa0_given_t, varargout] = bayes_get_probabilities(Y, Xi, bayes_model.params.k);

nfeatures_orig = length(bayes_model.whkeep);

pa1_given_t = NaN * zeros(nfeatures_orig, bayes_model.nclasses);
pa1_given_not_t = NaN * zeros(nfeatures_orig, bayes_model.nclasses);

pt_given_act1 = NaN * zeros(nfeatures_orig, bayes_model.nclasses);
pt_given_act0 = NaN * zeros(nfeatures_orig, bayes_model.nclasses);

pa1_given_t(bayes_model.whkeep,:) = bayes_model.pa1_given_t;
pa1_given_not_t(bayes_model.whkeep,:) = bayes_model.pa1_given_not_t;

pt_given_act1(bayes_model.whkeep,:) = bayes_model.pt_given_act1;
pt_given_act0(bayes_model.whkeep,:) = bayes_model.pt_given_act0;

% now done in bayes_get_probabilities
% % pa1_given_not_t = (double(~(Xi)') * Y)' ./ repmat(sum(~Xi), size(Y, 2), 1);
% % pa1_given_not_t(pa1_given_not_t < eps) = 1 ./ bayes_model.nobs;
% % pa1_given_not_t(pa1_given_not_t == 1) = 1 - (1 ./ bayes_model.nobs);

%%%%pa1_given_not_t(~bayes_model.whkeep, :) = NaN;

disp('Writing images to disk in the current directory.');

for i = 1:bayes_model.nclasses
    pa1_given_tname{i} = ['pa|t_' conditionnames{i} '.img'];
    iimg_reconstruct_3dvol(pa1_given_t(:,i), volInfo, 'outname', pa1_given_tname{i});

    disp(pa1_given_tname{i})

    pa_given_nottname{i} = ['pa|nott_' conditionnames{i} '.img'];
    iimg_reconstruct_3dvol(pa1_given_not_t(:,i), volInfo, 'outname', pa_given_nottname{i});

    disp(pa_given_nottname{i})

    pt_given_aname{i} = ['pt|a_' conditionnames{i} '.img'];
    iimg_reconstruct_3dvol(pt_given_act1(:,i), volInfo, 'outname', pt_given_aname{i});

    disp(pt_given_aname{i})

    pt_given_notaname{i} = ['pt|nota_' conditionnames{i} '.img'];
    iimg_reconstruct_3dvol(pt_given_act0(:,i), volInfo, 'outname', pt_given_notaname{i});

    disp(pt_given_notaname{i})
end

% likelihood ratio at each voxel
% should penalize this in some way for low activations...
%pa1_given_t(pa1_given_t == 0) = 1;
%pa1_given_not_t(pa1_given_not_t == 0) = 1;

% higher numbers mean more association between activation and class.
% lr for 2 classes can both be positive IF not all obs. fall within class 1
% or class 2!!!
evidence_yes = (sum(Y) ./ bayes_model.nobs)';
evidence_yes = evidence_yes(:, ones(1, bayes_model.nclasses));

% % priors = NaN * zeros(nfeatures_orig, bayes_model.nclasses);
% % priors(bayes_model.whkeep,:) = bayes_model.priors(ones(bayes_model.nfeatures, bayes_model.nclasses));

% add .1 to regularize...this could be k param
lr = ( (pa1_given_t + .1) ./ (evidence_yes + .1) ) - 1;
%pt_given_act1 ./ pt_given_act0;
lr(lr < .05 & lr > -.05) = NaN;
%lr = log(lr);

for i = 1:bayes_model.nclasses
    lrname{i} = ['likeratio_' conditionnames{i} '.img'];
    iimg_reconstruct_3dvol(lr(:,i), volInfo, 'outname', lrname{i});

    disp(lrname{i})
end

bayes_model.image_output.volInfo = volInfo;
bayes_model.image_output.pa1_given_tname = pa1_given_tname;
bayes_model.image_output.pa_given_nottname = pa_given_nottname;

bayes_model.image_output.pt_given_aname = pt_given_aname;
bayes_model.image_output.pt_given_notaname = pt_given_notaname;

bayes_model.image_output.likerationame = lrname;

bayes_model.image_output.pa1_given_t = pa1_given_t;
bayes_model.image_output.pa1_given_not_t = pa1_given_not_t;
bayes_model.image_output.likeratio = lr;

end

function cl = display_output(bayes_model, disptype, varargin)
% display_output(bayes_model, disptype, varargin)

switch spm('Ver')
    case 'SPM2'
        % spm_defaults is a script
        disp('WARNING: spm defaults not set for spm2. Make sure your defaults are set correctly');

    case 'SPM5'
        % spm_defaults is a function
        spm_defaults()

    case 'SPM8'
        % spm_defaults is a function
        spm_defaults()

    otherwise
        % unknown SPM
        disp('Unknown version of SPM!');
        spm_defaults()
end

cl = [];
colors1 = [1 .7 0];
colors2 = [0 0 1];

for i = 1:length(varargin)
    if iscell(varargin{i})
        colors1 = varargin{i}{1};
        colors2 = varargin{i}{2};
    elseif length(varargin{i}) == 1
        thresh = varargin{i};
    end
end

switch disptype

    case {'pa|t', 'lr', 'pt|a'}

        overlay = which('spm2_single_subj_T1_scalped.img');
        spm_check_registration(repmat(overlay, bayes_model.nclasses, 1));

        for i = 1:bayes_model.nclasses

            switch disptype
                case 'pa|t'
                    cl{i} = mask2clusters(bayes_model.image_output.pa1_given_tname{i});
                    fprintf('Displayed: p(a | t) for each task.\n');

                    spm_orthviews_name_axis(bayes_model.image_output.pa1_given_tname{i}, i);

                case 'lr'
                    cl{i} = mask2clusters(bayes_model.image_output.likerationame{i});
                    fprintf('Displayed: regions with positive likelihood ratio for each task.\n');

                    spm_orthviews_name_axis(bayes_model.image_output.likerationame{i}, i);

                case 'pt|a'
                    cl{i} = mask2clusters(bayes_model.image_output.pt_given_aname{i});
                    fprintf('Displayed: p(t | a) for each task.\n');

                    spm_orthviews_name_axis(bayes_model.image_output.pt_given_aname{i}, i);

            end

            % threshold
            if exist('thresh', 'var')
                for j = 1:length(cl{i})
                    wh_omit = abs( cat(2, cl{i}(j).Z) ) < thresh;
                    cl{i}(j).XYZ(:, wh_omit) = [];
                    cl{i}(j).XYZmm(:, wh_omit) = [];
                    cl{i}(j).Z(:, wh_omit) = [];
                end

                clu = clusters2CLU(cl{i});
                cl{i} = tor_extract_rois([], clu, clu);

            end

            cluster_orthviews(cl{i}, 'handle', i, 'add');

        end

        switch disptype
            case 'pa|t'
                %spm_orthviews_change_colormap([.2 .2 .4], [1 1 0], [.9 .6 .1]);  %slate to orange to yellow
                spm_orthviews_change_colormap([.5 0 0], [1 1 0], [1 .5 0]);  %blue to yellow

            case  'lr'
                spm_orthviews_change_colormap(colors2, colors1);  %blue to white to yellow

            case 'pt|a'
                spm_orthviews_change_colormap(colors2, colors1);

        end

    case 'lr surface'

        poscm = colormap_tor([.2 .2 .4], [1 1 0], [.9 .6 .1]);  %slate to orange to yellow
        negcm = colormap_tor([0 0 1], [0 .3 1]);  % light blue to dark blue (shouldn't be needed!)

        for i = 1:bayes_model.nclasses

            cl{i} = mask2clusters(bayes_model.image_output.likerationame{i});

            fprintf('Displayed: regions with positive likelihood ratio for each task.\n');

            % Left medial and lateral surface
            % ----------------------------------------------------------
            create_figure('Brain Surface'); scn_export_papersetup(800);

            cluster_surf(cl{i}, 4, 'heatmap', 'colormaps', poscm, negcm, 'hires left');
            sh = cluster_surf(cl{i}, 4, 'heatmap', 'colormaps', poscm, negcm, 'brainstem');
            saveas(gcf, [bayes_model.image_output.likerationame{i}(1:end-4) '_left_medial'], 'png');

            view(270,0); lightRestoreSingle;
            saveas(gcf, [bayes_model.image_output.likerationame{i}(1:end-4) '_left_lateral'], 'png');

            % Right medial and lateral surface
            % ----------------------------------------------------------
            create_figure('Brain Surface'); cluster_surf(cl{i}, 4, 'heatmap', 'colormaps', poscm, negcm, 'hires right');
            sh = cluster_surf(cl{i}, 4, 'heatmap', 'colormaps', poscm, negcm, 'brainstem');
            scn_export_papersetup(800); saveas(gcf, [bayes_model.image_output.likerationame{i}(1:end-4) '_right_lateral'], 'png');

            view(270,0); lightRestoreSingle;
            saveas(gcf, [bayes_model.image_output.likerationame{i}(1:end-4) '_right_medial'], 'png');

            % Limbic
            % ---------------------------------------------------------
            create_figure('Brain Surface'); cluster_surf(cl{i}, 4, 'heatmap', 'colormaps', poscm, negcm, 'limbic');
            saveas(gcf, [bayes_model.image_output.likerationame{i}(1:end-4) '_limbic'], 'png');
            saveas(gcf, [bayes_model.image_output.likerationame{i}(1:end-4) '_limbic'], 'fig');
        end

    case 'map plot'

        % MAP plot

        colors = {'b' 'r' 'k' 'g' 'm'};
        create_figure('MAP Plot across observations');
        for i = 1:bayes_model.nclasses

            trueclassi = find(bayes_model.Xi(:,i) == 1);
            myclassmap = bayes_model.apparent.map(trueclassi, i);

            plot(bayes_model.apparent.map(:, i), 'color', colors{i});
            plot(trueclassi, bayes_model.apparent.map(find(bayes_model.Xi(:,i) == 1), i), 'o', 'Color', colors{i},'MarkerFaceColor',colors{i});

            incorrect_obs = ~(myclassmap == max(bayes_model.apparent.map(trueclassi, :), [], 2));
            incorrect_obs_vec = zeros(bayes_model.nobs, 1);
            incorrect_obs_vec(trueclassi(incorrect_obs)) = 1;

            plot(find(incorrect_obs_vec), myclassmap(incorrect_obs), 'kx', 'MarkerSize', 12, 'LineWidth', 2);
        end
        xlabel('Observations'); ylabel('log(MAP) (up to scaling constant)');

    case 'class plot'

        pr = bayes_model.priors;
        fr = [linspace(1, 0, 10)' linspace(0, 1, 10)'];   % frequencies of guesses, from always A to always B

        xfr = linspace(1, 0, 10);

        %hr_byclass = fr .* repmat(pr, size(fr, 1), 1)
        colors = {'b' 'r' 'k' 'g' 'm'};

        hr = fr * pr';

        create_figure('Summary Line Plot');
        %plot(xfr, hr_byclass);
        plot(xfr, hr, 'k', 'LineWidth', 2);

        phighestfreq = sum(bayes_model.xval.class_est == wh) ./ length(bayes_model.xval.class_est);  % p chosen for highest-frequency class
        plot(phighestfreq, bayes_model.xval.prop_correct, 'ko', 'LineWidth', 2, 'MarkerFaceColor', [.5 .5 .5], 'MarkerSize', 12);

        xx = repmat(phighestfreq, 2, bayes_model.nclasses);
        yy = [bayes_model.xval.prop_correct_by_class'; bayes_model.xval.prop_correct_by_class'];

        for i = 1:size(yy, 2)
            plot(xx(:,i), yy(:,i), 'o', 'Color', colors{i}, 'LineWidth', 2, 'MarkerFaceColor', colors{i}, 'MarkerSize', 12);
        end

        % Standard error of hit rate
        % var(hits for each category) = p*(1-p)*N for that category
        % where p is the prior p(correct) for that category, and N is the number of
        % choices in that category given the frequency of sampling that category.

        se_hr = sqrt( hr .* (1 - hr) ./ bayes_model.nobs );
        ci95_hr = 1.96 * se_hr;

        plot(xfr, hr + ci95_hr, 'k--', 'LineWidth', 1);
        plot(xfr, hr - ci95_hr, 'k--', 'LineWidth', 1);
        fill_around_line(hr, ci95_hr, 'k', xfr);

        ylabel('Correct classification rate');
        xlabel('Prop. choices from highest class');

        % N = fr .* bayes_model.nobs;     % for each of k categories, in a row

        % pq = repmat(pr .* (1 - pr), size(fr,1), 1);
        % varsum = sum(pq .* N, 2)
        %
        %
        % se_hr = sqrt( sum(repmat(pr .* (1 - pr), size(fr,1), 1) ./ (bayes_model.nobs .* fr), 2) )
        % se_hr(isinf(se_hr)) = 0;

    otherwise
        error('Unknown display option.')
end

end