function rMTAsaveInExcel(xls_filename, TSscore, deletedGenes, alpha_values, varargin)
% Save in Excel all the results calculated using rMTA.
%
% USAGE:
%
%    rMTAsaveInExcel(output_file, TSscore, deletedGenes, alpha_values, varargin)
%
% INPUTS:
%    xls_filename:       name of the resulting excel file
%    TSscore:            Transformation score by each transformation
%    deletedGenes:       The list of genes/reactions removed in each knock-out
%    alpha_values:       Numeric value or array. Parameter of the quadratic
%                        problem (default = alpha_1, alpha_2,...)
%
% OPTIONAL INPUTS:
%    gene_info:          Table with the gene names and additional
%                        information. It needs a column called 'gene',
%                        which contains the same information as the
%                        deletedGenes variable.
%    differ_genes:       Table with the gene differential expression
%                        information. It needs a column called 'gene',
%                        which contains the same information as the
%                        deletedGenes variable.
%    RankingGeneID:      Caracter array with the ID of the gene which score
%                        is desired to know.
%
% .. Authors:
%       - Luis V. Valcarcel, 08/03/2021, University of Navarra, CIMA & TECNUN School of Engineering.

p = inputParser;
% check required arguments
addRequired(p, 'xls_filename', @(x)ischar(x));
addRequired(p, 'TSscore');
addRequired(p, 'deletedGenes');
% Check optional arguments
addOptional(p, 'alpha_values', -1, @isnumeric);
% Add optional name-value pair argument
addParameter(p, 'gene_info', []);
addParameter(p, 'differ_genes', []);
addParameter(p, 'RankingGeneID', [], @(x)ischar(x));
% extract variables from parser
parse(p, xls_filename, TSscore, deletedGenes, alpha_values, varargin{:});
gene_info = p.Results.gene_info;
differ_genes = p.Results.differ_genes;
RankingGeneID = p.Results.RankingGeneID;
num_alphas = numel(alpha_values);
if alpha_values == -1
    alpha_values = 1:size(TSscore.bTS,2);
end

% In order to store the results of all the cases, a cell array is going to
% store a table with the results for each alpha
results = cell(size(alpha_values));

% threshold for viable results
th = -1e20; % not (-INF) == infeasible

for i = 1:num_alphas
    fprintf('\tProcess results for alpha = %1.2f \n',alpha_values(i));
    T = table(deletedGenes, TSscore.bTS(:,i), TSscore.mTS, TSscore.wTS(:,i),TSscore.rTS(:,i));
    T.Properties.VariableNames = {'gene','bTS','mTS','wTS','rTS'};

    if ~isempty(gene_info)
        % join to extra information of genes
        T = join(gene_info,T,'Keys','gene');
    end

    %sort by MOMA results
    T = sortrows(T,'rTS','descend');

    % store only viable genes
    T = T(T.bTS>th,:);
    results{i} = T;
end

fprintf('\tSelected the "%u" best solutions\n',sum(T.mTS>(th)));

%export to excel
LETTERS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
% Export results for the different alphas
for i = 1:num_alphas
    T = results{i};
    fprintf('\tWrite xls for alpha = %1.2f \n',alpha_values(i));
    sheet_name = ['alpha = ',num2str(alpha_values(i))];

    % write results
    writetable(T,xls_filename,'FileType','spreadsheet','Sheet',sheet_name,'WriteVariableNames',true);

    LETTER = LETTERS(size(T,2)+3);

    if exist('RankingGeneID','var') && ~isempty(RankingGeneID)
        % ranking of gene
        xlswrite(xls_filename,{['position ' RankingGeneID]},sheet_name,[LETTER '2']);
        xlswrite(xls_filename,{['=MATCH(' RankingGeneID ',A:A,0)-1']},sheet_name,[LETTER '3']);
        xlswrite(xls_filename,{'ranking genes with KO'},sheet_name,[LETTER '4']);
        xlswrite(xls_filename,{['=' LETTER '3/' num2str(size(T,1))]},sheet_name,[LETTER '5']);
        xlswrite(xls_filename,{'ranking genes model'},sheet_name, [LETTER '6']);
        xlswrite(xls_filename,{['=' LETTER '3/' num2str(numel(deletedGenes))]}, sheet_name,[LETTER '7']);
    end
end

fprintf('\tWrite selection of best genes\n');
num = numel(results{i}.gene);
TopGenes_ID = cell(num,num_alphas);
titles = cell(1,num_alphas);
for i = 1:num_alphas
    num = sum(results{i}.rTS > 0);
    TopGenes_ID(1:num,i) = results{i}.gene(1:num);
    titles{i} = ['alpha = ',num2str(alpha_values(i))];
end
xlswrite(xls_filename,titles,'TOP_GENES_ID','A1');
xlswrite(xls_filename,TopGenes_ID,'TOP_GENES_ID','A2');

TopGenes_join = TopGenes_ID(~cellfun(@isempty,TopGenes_ID(:,1)),1);
for i = 1:num_alphas
    TopGenes_join = intersect(TopGenes_join,TopGenes_ID(~cellfun(@isempty,TopGenes_ID(:,i)),i));
end

if ~isempty(differ_genes)
    differ_genes = differ_genes(~strcmp(differ_genes.gene,''),:);
    [~, idx, ~] = unique(differ_genes.gene);
    differ_genes = differ_genes(idx,:);
    differ_genes.Properties.RowNames = differ_genes.gene;

    idx = intersect(deletedGenes,differ_genes.gene);

    xlswrite(xls_filename,differ_genes.Properties.VariableNames,'TOP_GENES_diff_expr','A1');
    xlswrite(xls_filename,table2cell(differ_genes(idx,:)),'TOP_GENES_diff_expr','A2');
end

if ~isempty(differ_genes) &&  ~isempty(RankingGeneID)

    fprintf('\tSUMMARY\n');

    differ_genes_aux = differ_genes(differ_genes.logFC>0,:);
    genes_FC_pos = differ_genes_aux.Properties.RowNames;

    SUMMARY = cell(7,2+5*num_alphas);
    table_aux_ranking = array2table(zeros(4,num_alphas));
    table_aux_ranking.Properties.RowNames = {'bTS','mTS','wTS','rTS'};
    table_aux_ranking.Properties.VariableNames = regexprep(strcat('alpha_',cellfun(@num2str,num2cell(alpha_values'),'UniformOutput',0)),'0.','0_');
    table_aux_ranking_FC_pos = table_aux_ranking;
    table_aux_sign = array2table(char(zeros(4,num_alphas)));
    table_aux_sign.Properties.RowNames = {'bTS','mTS','wTS','rTS'};
    table_aux_sign.Properties.VariableNames = regexprep(strcat('alpha_',cellfun(@num2str,num2cell(alpha_values'),'UniformOutput',0)),'0.','0_');
    num_KO = zeros(1,num_alphas);
    num_KO_FC_pos = zeros(1,num_alphas);
    for i = 1:num_alphas
        T = results{i};
        T.Properties.RowNames=T.gene;
        T2 = T(intersect(T.Properties.RowNames, genes_FC_pos),:);
        for j=1:size(table_aux_ranking,1)
            k = table_aux_ranking.Properties.RowNames{j};
            T = sortrows(T,k,'descend');
            T2 = sortrows(T2,k,'descend');
            try RankingGeneID = num2str(RankingGeneID); end
            table_aux_ranking{k,i} = find(strcmp(T.gene,RankingGeneID));
            try table_aux_ranking_FC_pos{k,i} = find(strcmp(T2.gene,RankingGeneID)); end
        end
        T = sortrows(T,'wTS','ascend');
        T2 = sortrows(T2,'wTS','ascend');
        table_aux_ranking{'wTS',i} = find(strcmp(T.gene,RankingGeneID));
        try table_aux_ranking_FC_pos{'wTS',i} = find(strcmp(T2.gene,RankingGeneID)); end
        aux = sign(T{RankingGeneID,table_aux_sign.Properties.RowNames})';
        aux2 = char(aux);
        aux2(:) = '+';
        aux2(aux<0) = '-';
        table_aux_sign{:,i} = aux2;
        num_KO(i) = size(T,1);
        num_KO_FC_pos(i) = size(T2,1);
    end

    table_aux_ranking_numKO = table2array(table_aux_ranking) ./ num_KO;
    table_aux_ranking_numDE = table2array(table_aux_ranking_FC_pos) ./ num_KO_FC_pos;
    table_aux_ranking_numgenes = table2array(table_aux_ranking) ./ length(deletedGenes);

    SUMMARY{1,2+0*num_alphas+1} = 'ranking';
    SUMMARY{1,2+1*num_alphas+1} = 'sign';
    SUMMARY{1,2+2*num_alphas+1} = 'ranking genes with KO';
    SUMMARY{1,2+3*num_alphas+1} = 'ranking genes with DE FC>0';
    SUMMARY{1,2+4*num_alphas+1} = 'ranking genes Recon1';

    SUMMARY(2,:) = [{'Case'} {'Score'} repmat(table_aux_ranking.Properties.VariableNames,1,5)];
    SUMMARY(3:6,2) = table_aux_ranking.Properties.RowNames;
    SUMMARY(3:6,3:end) = [table2cell(table_aux_ranking), table2cell(table_aux_sign), num2cell(table_aux_ranking_numKO), num2cell(table_aux_ranking_numDE), num2cell(table_aux_ranking_numgenes)];
    SUMMARY(end,2+2*num_alphas+(1:num_alphas)) = num2cell(num_KO);
    SUMMARY(end,2+3*num_alphas+(1:num_alphas)) = num2cell(num_KO_FC_pos);
    SUMMARY(end,2+4*num_alphas+(1:num_alphas)) = num2cell(length(deletedGenes));
    SUMMARY{1,1} = date();
    SUMMARY{end,1} = xls_filename;

    xlswrite(xls_filename,SUMMARY,'SUMMARY');
end

fprintf('DONE\n');

end


%% patchIND
% Below is a demonstration of the features of the |patchIND| function

%%
clear; close all; clc;

%% Syntax
% |[IND_F,IND_V]=patchIND(F,V,formOpt);|

%% Description
% UNDOCUMENTED
%% Examples
%
%%
%
% <<gibbVerySmall.gif>>
%
% _*GIBBON*_
% <www.gibboncode.org>
%
% _Kevin Mattheus Moerman_, <gibbon.toolbox@gmail.com>
%%
% _*GIBBON footer text*_
%
% License: <https://github.com/gibbonCode/GIBBON/blob/master/LICENSE>
%
% GIBBON: The Geometry and Image-based Bioengineering add-On. A toolbox for
% image segmentation, image-based modeling, meshing, and finite element
% analysis.
%
% Copyright (C) 2006-2022 Kevin Mattheus Moerman and the GIBBON contributors
%
% This program is free software: you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% (at your option) any later version.
%
% This program is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with this program.  If not, see <http://www.gnu.org/licenses/>.


function ret = spm_ov_roi(varargin)
% ROI tool - plugin for spm_orthviews
%
% With ROI tool it is possible to create new or modify existing mask images
% interactively. ROI tool can be launched via the spm_orthviews image
% context menu.
% While ROI tool is active, mouse buttons have the following functions:
% left       Reposition crosshairs
% middle     Perform ROI tool box selection according to selected edit mode at
%            crosshair position
% right      context menu
%
% Menu options and prompts explained:
% Launch     Initialise ROI tool in current image
%            'Load existing ROI image? (yes/no)'
%              If you want to modify an existing mask image (e.g. mask.img from
%              a fMRI analysis), press 'yes'. You will then be prompted to
%            'Select ROI image'
%              This is the image that will be loaded as initial ROI.
%              If you want to create a new ROI image, you will first be
%              prompted to
%            'Select image defining ROI space'
%              The image dimensions, voxel sizes and slice orientation will
%              be read from this image. Thus you can edit a ROI based on a
%              image with a resolution and slice orientation different from
%              the underlying displayed image.
%
% Once ROI tool is active, the menu consists of three parts: settings,
% edit operations and load/save operations.
% Settings
% --------
% Selection  Operation performed when pressing the middle mouse button or
% mode       by clustering operations.
%            'Set selection'
%              The selection made with the following commands will
%              be included in your ROI.
%            'Clear selection'
%              The selection made with the following commands will
%              be excluded from your ROI.
% Box size   Set size of box to be (de)selected when pressing the
%            middle mouse button.
% Polygon    Set number of adjacent slices selected by one polygon
% slices     drawing.
% Cluster    Set minimum cluster size for "Cleanup clusters" and
% size       "Connected cluster" operations.
% Erosion/   During erosion/dilation operations, the binary mask will be
% dilation   smoothed. At boundaries, this will result in mask values
% threshold  that are not exactly zero or one, but somewhere in
%            between. Whether a mask will be eroded (i.e. be smaller than
%            the original) or dilated (i.e. grow) depends on this
%            threshold. A threshold below 0.5 dilates, above 0.5 erodes a
%            mask.
% Edit actions
% ------------
% Polygon    Draw an outline on one of the 3 section images. Voxels
%            within the outline will be added to the ROI. The same
%            outline can be applied to a user-defined number of
%            consecutive slices around the current crosshair position.
% Threshold  You will be prompted to enter a [min max] threshold. Only
%            those voxels in the ROI image where the intensities of the
%            underlying image are within the [min max] range will survive
%            this operation.
% Connected  Select only voxels that are connected to the voxel at
% cluster    current crosshair position through the ROI.
% Cleanup    Keep only clusters that are larger than a specified cluster
% clusters   size.
% Erode/     Erode or dilate a mask, using the current erosion/dilation
% Dilate     threshold.
% Invert     Invert currently defined ROI
% Clear      Clear ROI, but keep ROI space information
% Add ROI from file(s)
%            Add ROIs from file(s) into current ROI set. According to the
%            current edit mode voxels unequal zero will be set or
%            cleared. The image files will be resampled and thus do not
%            need to have the same orientation or voxel size as the
%            original ROI.
% Save actions
% ------------
% Save       Save ROI image
% Save As    Save ROI image under a new file name
%            The images will be rescaled to 0 (out of mask) and 1 (in
%            mask).
% Quit       Quit ROI tool
%
% This routine is a plugin to spm_orthviews. For general help about
% spm_orthviews and plugins type
%             help spm_orthviews
% at the MATLAB prompt.
%__________________________________________________________________________
% Copyright (C) 2012 Wellcome Trust Centre for Neuroimaging

% Volkmar Glauche
% $Id: spm_ov_roi.m 6991 2017-01-19 13:09:51Z guillaume $

% Note: This plugin depends on the blobs set by spm_orthviews('addblobs',...)
% They should not be removed while ROI tool is active and no other blobs be
% added. This restriction may be removed when using the 'alpha' property
% to overlay blobs onto images.

global st;
if isempty(st)
    error('roi: This routine can only be called as a plugin for spm_orthviews!');
end

if nargin < 2
    error('roi: Wrong number of arguments. Usage: spm_orthviews(''roi'', cmd, volhandle, varargin)');
end

cmd = lower(varargin{1});
volhandle = varargin{2};

toset = [];
toclear = [];
tochange = [];
update_roi = false;

switch cmd
    case 'init'
        % spm_ov_roi('init', volhandle, Vroi, loadasroi, xyz)
        spm('pointer','watch');
        Vroi = spm_vol(varargin{3});
        switch varargin{4} % loadasroi
            case 1,
                roi = spm_read_vols(Vroi)>0;
                [x,y,z] = ndgrid(1:Vroi.dim(1),1:Vroi.dim(2),1:Vroi.dim(3));
                xyz = [x(roi(:))'; y(roi(:))'; z(roi(:))'];
            case {0,2} % ROI space image or SPM mat
                Vroi = rmfield(Vroi,'private');
                roi = false(Vroi.dim(1:3));
                Vroi.fname = fileparts(Vroi.fname); % save path
                xyz = varargin{5};
                if ~isempty(xyz)
                    ind = sub2ind(Vroi.dim(1:3),xyz(1,:),xyz(2,:),xyz(3,:));
                    roi(ind) = true;
                end;
        end;
        % reset data type to save disk space
        Vroi.dt(1) = spm_type('uint8');
        % reset scaling factor of Vroi handle
        Vroi.pinfo(1:2) = Inf;
        clear x y z

        % draw a frame only if ROI volume different from underlying GM volume
        if any(Vroi.dim(1:3)-st.vols{volhandle}.dim(1:3))|| ...
                any(Vroi.mat(:)-st.vols{volhandle}.mat(:))
            [xx1,yx1,zx1] = ndgrid(1            , 1:Vroi.dim(2), 1:Vroi.dim(3));
            [xx2,yx2,zx2] = ndgrid(Vroi.dim(1)  , 1:Vroi.dim(2), 1:Vroi.dim(3));
            [xy1,yy1,zy1] = ndgrid(1:Vroi.dim(1), 1            , 1:Vroi.dim(3));
            [xy2,yy2,zy2] = ndgrid(1:Vroi.dim(1), Vroi.dim(2)  , 1:Vroi.dim(3));
            [xz1,yz1,zz1] = ndgrid(1:Vroi.dim(1), 1:Vroi.dim(2), 1);
            [xz2,yz2,zz2] = ndgrid(1:Vroi.dim(1), 1:Vroi.dim(2), Vroi.dim(3));

            fxyz = [xx1(:)' xx2(:)' xy1(:)' xy2(:)' xz1(:)' xz2(:)'; ...
                    yx1(:)' yx2(:)' yy1(:)' yy2(:)' yz1(:)' yz2(:)'; ...
                    zx1(:)' zx2(:)' zy1(:)' zy2(:)' zz1(:)' zz2(:)'];
            clear xx1 yx1 zx1 xx2 yx2 zx2 xy1 yy1 zy1 xy2 yy2 zy2 xz1 yz1 zz1 xz2 yz2 zz2
            hframe = 1;
        else
            hframe = [];
            fxyz  = [];
        end;

        cb = cell(1,3);
        for k=1:3
            cb{k}=get(st.vols{volhandle}.ax{k}.ax,'ButtonDownFcn');
            set(st.vols{volhandle}.ax{k}.ax,...
                'ButtonDownFcn',...
                @(ob,ev)spm_ov_roi('bdfcn',volhandle,ob,ev));
        end;

        st.vols{volhandle}.roi = struct('Vroi',Vroi, 'xyz',xyz, 'roi',roi,...
                                        'hroi',1, 'fxyz',fxyz,...
                                        'hframe',hframe, 'mode','set',...
                                        'tool', 'box', ...
                                        'thresh',[60 140], 'box',[4 4 4],...
                                        'cb',[], 'polyslices',1, 'csize',5,...
                                        'erothresh',.5);
        st.vols{volhandle}.roi.cb = cb;

        if ~isempty(st.vols{volhandle}.roi.fxyz)
            if isfield(st.vols{volhandle}, 'blobs')
                st.vols{volhandle}.roi.hframe = numel(st.vols{volhandle}.blobs)+1;
            end;
            spm_orthviews('addcolouredblobs',volhandle, ...
                          st.vols{volhandle}.roi.fxyz,...
                          ones(size(st.vols{volhandle}.roi.fxyz,2),1), ...
                          st.vols{volhandle}.roi.Vroi.mat,[1 .5 .5]);
            st.vols{volhandle}.blobs{st.vols{volhandle}.roi.hframe}.max=1.3;
        end;
        update_roi=1;
        obj = findobj(0, 'Tag',  sprintf('ROI_1_%d', volhandle));
        set(obj, 'Visible', 'on');
        obj = findobj(0, 'Tag',  sprintf('ROI_0_%d', volhandle));
        set(obj, 'Visible', 'off');

    case 'edit'
        switch st.vols{volhandle}.roi.tool
            case 'box'
                spm('pointer','watch');
                pos = round(st.vols{volhandle}.roi.Vroi.mat\ ...
                            [spm_orthviews('pos'); 1]);
                tmp = round((st.vols{volhandle}.roi.box-1)/2);
                [sx,sy,sz] = meshgrid(-tmp(1):tmp(1), -tmp(2):tmp(2), -tmp(3):tmp(3));
                sel = [sx(:)';sy(:)';sz(:)']+repmat(pos(1:3), 1,prod(2*tmp+1));
                tochange = sel(:, (all(sel>0) &...
                                   sel(1,:)<=st.vols{volhandle}.roi.Vroi.dim(1) & ...
                                   sel(2,:)<=st.vols{volhandle}.roi.Vroi.dim(2) & ...
                                   sel(3,:)<=st.vols{volhandle}.roi.Vroi.dim(3)));
                update_roi = 1;

            case 'connect'
                spm_ov_roi('connect', volhandle)

            case 'poly'

                % @COPYRIGHT  :
      %             Copyright 1993,1994 Mark Wolforth and Greg Ward, McConnell
      %             Brain Imaging Centre, Montreal Neurological Institute, McGill
      %             University.
      %             Permission to use, copy, modify, and distribute this software
      %             and its documentation for any purpose and without fee is
      %             hereby granted, provided that the above copyright notice
      %             appear in all copies.  The authors and McGill University make
      %             no representations about the suitability of this software for
      %             any purpose.  It is provided "as is" without express or
      %             implied warranty.
                for k = 1:3
                    if st.vols{volhandle}.ax{k}.ax == gca
                        axhandle = k;
                        break;
                    end;
                end;
                line_color = [1 1 0];
                axes(st.vols{volhandle}.ax{axhandle}.ax);

                hold on;
                Xlimits = get (st.vols{volhandle}.ax{axhandle}.ax,'XLim');
                Ylimits = get (st.vols{volhandle}.ax{axhandle}.ax,'YLim');
                XLimMode = get(st.vols{volhandle}.ax{axhandle}.ax,'XLimMode');
                set(st.vols{volhandle}.ax{axhandle}.ax,'XLimMode','manual');
                YLimMode = get(st.vols{volhandle}.ax{axhandle}.ax,'YLimMode');
                set(st.vols{volhandle}.ax{axhandle}.ax,'YLimMode','manual');
                ButtonDownFcn = get(st.vols{volhandle}.ax{axhandle}.ax,'ButtonDownFcn');
                set(st.vols{volhandle}.ax{axhandle}.ax,'ButtonDownFcn','');
                UIContextMenu = get(st.vols{volhandle}.ax{axhandle}.ax,'UIContextMenu');
                set(st.vols{volhandle}.ax{axhandle}.ax,'UIContextMenu',[]);
                set(st.vols{volhandle}.ax{axhandle}.ax,'Selected','on');
                disp (['Please mark the ROI outline in the highlighted image' ...
                       ' display.']);
                disp ('Points outside the ROI image area will be clipped to');
                disp ('the image boundaries.');
                disp ('Left-Click on the vertices of the ROI...');
                disp ('Middle-Click to finish ROI selection...');
                disp ('Right-Click to cancel...');
                x=Xlimits(1);
                y=Ylimits(1);
                i=1;
                lineHandle = [];
                xc = 0; yc = 0; bc = 0;
                while ~isempty(bc)
                    [xc,yc,bc] = ginput(1);
                    if isempty(xc) || bc > 1
                        if bc == 3
                            x = []; y=[];
                        end;
                        if bc == 2 || bc == 3
                            bc = [];
                            break;
                        end;
                    else
                        if xc > Xlimits(2)
                            xc = Xlimits(2);
                        elseif xc < Xlimits(1)
                            xc = Xlimits(1);
                        end;
                        if yc > Ylimits(2)
                            yc = Ylimits(2);
                        elseif yc < Ylimits(1)
                            yc = Ylimits(1);
                        end;
                        x(i) = xc;
                        y(i) = yc;
                        i=i+1;
                        if ishandle(lineHandle)
                            delete(lineHandle);
                        end;
                        lineHandle = line (x,y,ones(1,length(x)), ...
                                           'Color',line_color,...
                                           'parent',st.vols{volhandle}.ax{axhandle}.ax,...
                                           'HitTest','off');
                    end;
                end

                if ishandle(lineHandle)
                    delete(lineHandle);
                end;
                if ~isempty(x)
                    spm('pointer','watch');
                    x(i)=x(1);
                    y(i)=y(1);
                    prms=spm_imatrix(st.vols{volhandle}.roi.Vroi.mat);
                    % Code from spm_orthviews('redraw') for determining image
                    % positions
                    is   = inv(st.Space);
                    cent = is(1:3,1:3)*st.centre(:) + is(1:3,4);
                    polyoff = [0 0 0];
                    switch axhandle
                        case 1,
                            M0 = [ 1 0 0 -st.bb(1,1)+1
                                   0 1 0 -st.bb(1,2)+1
                                   0 0 1 -cent(3)
                                   0 0 0 1];
                            polyoff(3) = st.vols{volhandle}.roi.polyslices/2;
                            polythick = prms(9);
                        case 2,
                            M0 = [ 1 0 0 -st.bb(1,1)+1
                                   0 0 1 -st.bb(1,3)+1
                                   0 1 0 -cent(2)
                                   0 0 0 1];
                            polyoff(2) = st.vols{volhandle}.roi.polyslices/2;
                            polythick = prms(8);
                        case 3,
                            if st.mode ==0,
                                M0 = [ 0 0 1 -st.bb(1,3)+1
                                       0 1 0 -st.bb(1,2)+1
                                       1 0 0 -cent(1)
                                       0 0 0 1];
                            else
                                M0 = [ 0 -1 0 +st.bb(2,2)+1
                                       0  0 1 -st.bb(1,3)+1
                                       1  0 0 -cent(1)
                                       0  0 0 1];
                            end;
                            polyoff(1) = st.vols{volhandle}.roi.polyslices/2;
                            polythick = abs(prms(7));
                    end;
                    polvx = st.vols{volhandle}.roi.Vroi.mat\(st.Space*(M0\...
                            [x(:)';y(:)'; zeros(size(x(:)')); ones(size(x(:)'))]));
                    % Bounding volume for polygon in ROI voxel space
                    [xbox,ybox,zbox] = ndgrid(max(min(floor(polvx(1,:)-polyoff(1))),1):...
                                              min(max(ceil(polvx(1,:)+polyoff(1))),...
                                                  st.vols{volhandle}.roi.Vroi.dim(1)),...
                                              max(min(floor(polvx(2,:)-polyoff(2))),1):...
                                              min(max(ceil(polvx(2,:)+polyoff(2))),...
                                                  st.vols{volhandle}.roi.Vroi.dim(2)),...
                                              max(min(floor(polvx(3,:)-polyoff(3))),1):...
                                              min(max(ceil(polvx(3,:)+polyoff(3))),...
                                                  st.vols{volhandle}.roi.Vroi.dim(3)));
                    % re-transform in polygon plane
                    xyzbox = M0*(st.Space\st.vols{volhandle}.roi.Vroi.mat)*[xbox(:)';ybox(:)';zbox(:)';...
                                        ones(size(xbox(:)'))];
                    xyzbox = xyzbox(:,abs(xyzbox(3,:))<=.6*polythick*...
                                    st.vols{volhandle}.roi.polyslices); % nearest neighbour to polygon
                    sel = logical(inpolygon(xyzbox(1,:),xyzbox(2,:),x,y));
                    xyz = st.vols{volhandle}.roi.Vroi.mat\(st.Space*(M0\xyzbox(:,sel)));
                    if ~isempty(xyz)
                        tochange = round(xyz(1:3,:));
                        update_roi = 1;
                    end;
                end;
                set(st.vols{volhandle}.ax{axhandle}.ax,...
                    'Selected','off', 'XLimMode',XLimMode, 'YLimMode',YLimMode,...
                    'ButtonDownFcn',ButtonDownFcn, 'UIContextMenu',UIContextMenu);
        end;
    case 'thresh'
        spm('pointer','watch');
        rind = find(st.vols{volhandle}.roi.roi);
        [x,y,z]=ind2sub(st.vols{volhandle}.roi.Vroi.dim(1:3),rind);
        tmp = round(st.vols{volhandle}.mat \ ...
                    st.vols{volhandle}.roi.Vroi.mat*[x'; y'; z'; ones(size(x'))]);
        dat = spm_sample_vol(st.vols{volhandle}, ...
                             tmp(1,:), tmp(2,:), tmp(3,:), 0);
        sel = ~((st.vols{volhandle}.roi.thresh(1) < dat) & ...
                (dat < st.vols{volhandle}.roi.thresh(2)));
        if strcmp(st.vols{volhandle}.roi.mode,'set')
            toclear = [x(sel)'; y(sel)'; z(sel)'];
        else
            toset   = [x(sel)'; y(sel)'; z(sel)'];
            toclear = st.vols{volhandle}.roi.xyz;
        end;
        update_roi = 1;

    case 'erodilate'
        spm('pointer','watch');
        V = zeros(size(st.vols{volhandle}.roi.roi));
        spm_smooth(double(st.vols{volhandle}.roi.roi), V, 2);
        [ero(1,:),ero(2,:),ero(3,:)] = ind2sub(st.vols{volhandle}.roi.Vroi.dim(1:3),...
                                               find(V(:)>st.vols{volhandle}.roi.erothresh));
        if strcmp(st.vols{volhandle}.roi.mode,'set')
            toset   = ero;
            toclear = st.vols{volhandle}.roi.xyz;
        else
            toclear = ero;
        end;
        update_roi = 1;

    case {'connect', 'cleanup'}
        spm('pointer','watch');
        [V,L] = spm_bwlabel(double(st.vols{volhandle}.roi.roi),6);
        sel = [];
        switch cmd
            case 'connect'
                pos = round(st.vols{volhandle}.roi.Vroi.mat\ ...
                            [spm_orthviews('pos'); 1]);
                sel = V(pos(1),pos(2),pos(3));
                if sel == 0
                    sel = [];
                end;
            case 'cleanup'
                numV = zeros(1,L);
                for k = 1:L
                    numV(k) = sum(V(:)==k);
                end;
                sel = find(numV>st.vols{volhandle}.roi.csize);
        end;
        if ~isempty(sel)
            ind1 = cell(1,numel(sel));
            for k=1:numel(sel)
                ind1{k} = find(V(:) == sel(k));
            end;
            ind = cat(1,ind1{:});
            conn = zeros(3,numel(ind));
            [conn(1,:),conn(2,:),conn(3,:)] = ...
                ind2sub(st.vols{volhandle}.roi.Vroi.dim(1:3),ind);

            if strcmp(st.vols{volhandle}.roi.mode,'set')
                toset   = conn;
                toclear = st.vols{volhandle}.roi.xyz;
            else
                toclear = conn;
            end;
        end;
        update_roi = 1;

    case 'invert'
        spm('pointer','watch');
        st.vols{volhandle}.roi.roi = ~st.vols{volhandle}.roi.roi;
        ind = find(st.vols{volhandle}.roi.roi);
        st.vols{volhandle}.roi.xyz = zeros(3,numel(ind));
        [st.vols{volhandle}.roi.xyz(1,:), ...
            st.vols{volhandle}.roi.xyz(2,:), ...
            st.vols{volhandle}.roi.xyz(3,:)] = ind2sub(st.vols{volhandle}.roi.Vroi.dim(1:3),ind);
        update_roi = 1;

    case 'clear'
        spm('pointer','watch');
        st.vols{volhandle}.roi.roi = false(size(st.vols{volhandle}.roi.roi));
        st.vols{volhandle}.roi.xyz=[];
        update_roi = 1;

    case 'addfile'
        V = spm_vol(spm_select([1 Inf],'image','Image(s) to add'));
        [x,y,z] = ndgrid(1:st.vols{volhandle}.roi.Vroi.dim(1),...
                         1:st.vols{volhandle}.roi.Vroi.dim(2),...
                         1:st.vols{volhandle}.roi.Vroi.dim(3));
        xyzmm = st.vols{volhandle}.roi.Vroi.mat*[x(:)';y(:)';z(:)'; ...
                            ones(1, prod(st.vols{volhandle}.roi.Vroi.dim(1:3)))];
        msk = false(1,prod(st.vols{volhandle}.roi.Vroi.dim(1:3)));
        for k = 1:numel(V)
            xyzvx = V(k).mat\xyzmm;
            dat = spm_sample_vol(V(k), xyzvx(1,:), xyzvx(2,:), xyzvx(3,:), 0);
            dat(~isfinite(dat)) = 0;
            msk = msk | logical(dat);
        end;
        [tochange(1,:),tochange(2,:),tochange(3,:)] = ind2sub(st.vols{volhandle}.roi.Vroi.dim(1:3),find(msk));
        clear xyzmm xyzvx msk
        update_roi = 1;

    case {'save','saveas'}
        if strcmp(cmd,'saveas') || ...
                exist(st.vols{volhandle}.roi.Vroi.fname, 'dir')
            flt = {'*.nii','NIfTI (1 file)';'*.img','NIfTI (2 files)'};
            [name,pth,idx] = uiputfile(flt, 'Output image');
            if ~ischar(pth)
                warning('spm:spm_ov_roi','Save cancelled');
                return;
            end;
            [p,n,e,v] = spm_fileparts(fullfile(pth,name));
            if isempty(e)
                e = flt{idx,1}(2:end);
            end;
            st.vols{volhandle}.roi.Vroi.fname = fullfile(p, [n e v]);
        end;
        spm('pointer','watch');
        spm_write_vol(st.vols{volhandle}.roi.Vroi, ...
                      st.vols{volhandle}.roi.roi);
        spm('pointer','arrow');
        return;

    case 'redraw'
        % do nothing
        return;

 %-------------------------------------------------------------------------
 % Context menu and callbacks
    case 'context_menu'
        item0 = uimenu(varargin{3}, 'Label', 'ROI tool');
        item1 = uimenu(item0, 'Label', 'Launch', 'Callback', ...
                       sprintf('%s(''context_init'', %d);', mfilename, volhandle), ...
                       'Tag',sprintf('ROI_0_%d', volhandle));
        item2 = uimenu(item0, 'Label', 'Selection mode', ...
                       'Visible', 'off', 'Tag', ['ROI_1_', num2str(volhandle)]);
        item2_1a = uimenu(item2, 'Label', 'Set selection', 'Callback', ...
                          sprintf('%s(''context_selection'', %d, ''set'');', ...
                                  mfilename, volhandle), ...
                          'Tag',sprintf('ROI_SELECTION_%d', volhandle), ...
                          'Checked','on');
        item2_1b = uimenu(item2, 'Label', 'Clear selection', 'Callback', ...
                          sprintf('%s(''context_selection'', %d,''clear'');', ...
                                  mfilename, volhandle), ...
                          'Tag', sprintf('ROI_SELECTION_%d', volhandle));
        item3 = uimenu(item0, 'Label', 'Box size', 'Callback', ...
                       sprintf('%s(''context_box'', %d);', mfilename, volhandle), ...
                       'Visible', 'off', 'Tag',sprintf('ROI_1_%d', volhandle));
        item4 = uimenu(item0, 'Label', 'Polygon slices', 'Callback', ...
                       sprintf('%s(''context_polyslices'', %d);', mfilename, volhandle), ...
                       'Visible','off', 'Tag', sprintf('ROI_1_%d', volhandle));
        item5 = uimenu(item0, 'Label', 'Cluster size', 'Callback', ...
                       sprintf('%s(''context_csize'', %d);', mfilename, volhandle), ...
                       'Visible','off', 'Tag',sprintf('ROI_1_%d', volhandle));
        item6 = uimenu(item0, 'Label', 'Erosion/Dilation threshold', 'Callback', ...
                       sprintf('%s(''context_erothresh'', %d);', mfilename, volhandle), ...
                       'Visible', 'off', 'Tag', sprintf('ROI_1_%d', volhandle));
        item7 = uimenu(item0, 'Label', 'Edit tool', ...
                       'Visible','off', 'Tag',sprintf('ROI_1_%d', volhandle), ...
                       'Separator','on');
        item7_1a = uimenu(item7, 'Label', 'Box tool', 'Callback', ...
                          sprintf('%s(''context_edit'', %d,''box'');', mfilename, volhandle), ...
                          'Tag',sprintf('ROI_EDIT_%d', volhandle), 'Checked','on');
        item7_1b = uimenu(item7, 'Label', 'Polygon tool', 'Callback', ...
                          sprintf('%s(''context_edit'', %d,''poly'');', mfilename, volhandle), ...
                          'Tag',sprintf('ROI_EDIT_%d', volhandle));
        item7_1c = uimenu(item7, 'Label', 'Connected cluster', 'Callback', ...
                          sprintf('%s(''context_edit'', %d,''connect'');', mfilename, volhandle), ...
                          'Tag',sprintf('ROI_EDIT_%d', volhandle));
        item8 = uimenu(item0, 'Label', 'Threshold', 'Callback', ...
                       sprintf('%s(''context_thresh'', %d);', mfilename, volhandle), ...
                       'Visible','off', 'Tag',sprintf('ROI_1_%d', volhandle));
        item10 = uimenu(item0, 'Label', 'Cleanup clusters', 'Callback', ...
                        sprintf('%s(''cleanup'', %d);', mfilename, volhandle), ...
                        'Visible','off', 'Tag',sprintf('ROI_1_%d', volhandle));
        item11 = uimenu(item0, 'Label', 'Erode/Dilate', 'Callback', ...
                        sprintf('%s(''erodilate'', %d);', mfilename, volhandle), ...
                        'Visible','off', 'Tag',sprintf('ROI_1_%d', volhandle));
        item12 = uimenu(item0, 'Label', 'Invert', 'Callback', ...
                        sprintf('%s(''invert'', %d);', mfilename, volhandle), ...
                        'Visible','off', 'Tag',sprintf('ROI_1_%d', volhandle));
        item13 = uimenu(item0, 'Label', 'Clear', 'Callback', ...
                        sprintf('%s(''clear'', %d);', mfilename, volhandle), ...
                        'Visible','off', 'Tag',sprintf('ROI_1_%d', volhandle));
        item14 = uimenu(item0, 'Label', 'Add ROI from file(s)', 'Callback', ...
                        sprintf('%s(''addfile'', %d);', mfilename, volhandle), ...
                        'Visible','off', 'Tag',sprintf('ROI_1_%d', volhandle));
        item15 = uimenu(item0, 'Label', 'Save', 'Callback', ...
                        sprintf('%s(''save'', %d);', mfilename, volhandle), ...
                        'Visible','off', 'Tag',sprintf('ROI_1_%d', volhandle),...
                        'Separator','on');
        item16 = uimenu(item0, 'Label', 'Save As', 'Callback', ...
                        sprintf('%s(''saveas'', %d);', mfilename, volhandle), ...
                        'Visible','off', 'Tag',sprintf('ROI_1_%d', volhandle));
        item17 = uimenu(item0, 'Label', 'Quit', 'Callback', ...
                        sprintf('%s(''context_quit'', %d);', mfilename, volhandle), ...
                        'Visible','off', 'Tag',sprintf('ROI_1_%d', volhandle));
        item18 = uimenu(item0, 'Label', 'Help', 'Callback', ...
                        sprintf('spm_help(''%s'');', mfilename));
        % add some stuff outside ROI tool menu
        iorient = findobj(get(st.vols{volhandle}.ax{1}.cm,'Children'), 'flat', 'Label', 'Orientation');
        item19 =  uimenu(iorient, 'Label', 'ROI Space', 'Callback', ...
                         sprintf('%s(''context_space'', %d);', mfilename, volhandle), ...
                         'Visible','off', 'Tag',sprintf('ROI_1_%d', volhandle));
        return;

    case 'context_init'
        Finter = spm_figure('FindWin', 'Interactive');
        spm_input('!DeleteInputObj',Finter);
        loadasroi = spm_input('Initial ROI','!+1','m',{'ROI image',...
                            'ROI space definition', 'SPM result'},[1 0 2],1);
        xyz = [];
        switch loadasroi
            case 1,
                imfname = spm_select(1, 'image', 'Select ROI image');
            case 0,
                imfname = spm_select(1, 'image', 'Select image defining ROI space');
            case 2,
                [SPM, xSPM] = spm_getSPM;
                xyz = xSPM.XYZ;
                imfname = SPM.Vbeta(1).fname;
        end;
        spm_input('!DeleteInputObj',Finter);
        feval('spm_ov_roi','init',volhandle,imfname,loadasroi,xyz);
        return;

    case 'context_selection'
        st.vols{volhandle}.roi.mode = varargin{3};
        obj = findobj(0, 'Tag', ['ROI_SELECTION_', num2str(volhandle)]);
        set(obj, 'Checked', 'off');
        set(gcbo, 'Checked', 'on');
        return;

    case 'context_edit'
        st.vols{volhandle}.roi.tool = varargin{3};
        obj = findobj(0, 'Tag', ['ROI_EDIT_', num2str(volhandle)]);
        set(obj, 'Checked', 'off');
        set(gcbo, 'Checked', 'on');
        return;

    case 'context_box'
        Finter = spm_figure('FindWin', 'Interactive');
        spm_input('!DeleteInputObj',Finter);
        box = spm_input('Selection size {vx vy vz}','!+1','e', ...
                        num2str(st.vols{volhandle}.roi.box), [1 3]);
        spm_input('!DeleteInputObj',Finter);
        st.vols{volhandle}.roi.box = box;
        return;

    case 'context_polyslices'
        Finter = spm_figure('FindWin', 'Interactive');
        spm_input('!DeleteInputObj',Finter);
        polyslices = spm_input('Polygon: slices around current slice','!+1','e', ...
                               num2str(st.vols{volhandle}.roi.polyslices), [1 1]);
        spm_input('!DeleteInputObj',Finter);
        st.vols{volhandle}.roi.polyslices = polyslices;
        return;

    case 'context_csize'
        Finter = spm_figure('FindWin', 'Interactive');
        spm_input('!DeleteInputObj',Finter);
        csize = spm_input('Minimum cluster size (#vx)','!+1','e', ...
                          num2str(st.vols{volhandle}.roi.csize), [1 1]);
        spm_input('!DeleteInputObj',Finter);
        st.vols{volhandle}.roi.csize = csize;
        return;

    case 'context_erothresh'
        Finter = spm_figure('FindWin', 'Interactive');
        spm_input('!DeleteInputObj',Finter);
        erothresh = spm_input('Erosion/Dilation threshold','!+1','e', ...
                              num2str(st.vols{volhandle}.roi.erothresh), [1 1]);
        spm_input('!DeleteInputObj',Finter);
        st.vols{volhandle}.roi.erothresh = erothresh;
        return;

    case 'context_space'
        spm_orthviews('space', volhandle, ...
            st.vols{volhandle}.roi.Vroi.mat, ...
            st.vols{volhandle}.roi.Vroi.dim(1:3));
        iorient = get(findobj(get(st.vols{volhandle}.ax{1}.cm,'Children'), ...
            'flat', 'Label', 'Orientation'),'children');
        if iscell(iorient)
            iorient = cell2mat(iorient);
        end
        set(iorient, 'Checked', 'Off');
        ioroi = findobj(iorient, 'Label','ROI Space');
        set(ioroi, 'Checked', 'On');
        return;

    case 'context_thresh'
        Finter = spm_figure('FindWin', 'Interactive');
        spm_input('!DeleteInputObj',Finter);
        thresh = spm_input('Threshold  {min max}','!+1','e', ...
                           num2str(st.vols{volhandle}.roi.thresh), [1 2]);
        spm_input('!DeleteInputObj',Finter);
        st.vols{volhandle}.roi.thresh = thresh;
        feval('spm_ov_roi', 'thresh', volhandle);
        return;

    case 'context_quit'
        obj = findobj(0, 'Tag',  sprintf('ROI_1_%d', volhandle));
        set(obj, 'Visible', 'off');
        obj = findobj(0, 'Tag',  sprintf('ROI_0_%d', volhandle));
        set(obj, 'Visible', 'on');
        spm_orthviews('rmblobs', volhandle);
        for k=1:3
            set(st.vols{volhandle}.ax{k}.ax,'ButtonDownFcn', st.vols{volhandle}.roi.cb{k});
        end;
        st.vols{volhandle} = rmfield(st.vols{volhandle}, 'roi');
        spm_orthviews('redraw');
        return;

    case 'bdfcn'
        if strcmpi(get(gcf,'SelectionType'), 'extend')
            spm_orthviews('roi','edit',volhandle);
        else
            for k=1:3
                if isequal(st.vols{volhandle}.ax{k}.ax, gca)
                    break;
                end
            end
            feval(st.vols{volhandle}.roi.cb{k});
        end
    otherwise
        fprintf('spm_orthviews(''roi'', ...): Unknown action %s', cmd);
        return;
end;

if update_roi
    if ~isempty(tochange) % change state according to mode
        if strcmp(st.vols{volhandle}.roi.mode,'set')
            toset = tochange;
        else
            toclear = tochange;
        end;
    end;
    % clear first, then set (needed for connect operation)
    if ~isempty(toclear)
        itoclear = sub2ind(st.vols{volhandle}.roi.Vroi.dim(1:3), ...
                           toclear(1,:), toclear(2,:), toclear(3,:));
        st.vols{volhandle}.roi.roi(itoclear) = false;
        if ~isempty(st.vols{volhandle}.roi.xyz)
            st.vols{volhandle}.roi.xyz = setdiff(st.vols{volhandle}.roi.xyz',toclear','rows')';
        else
            st.vols{volhandle}.roi.xyz = [];
        end;
    end;

    if ~isempty(toset)
        % why do we need this round()?? I don't know, but Matlab thinks
        % it is necessary
        itoset = round(sub2ind(st.vols{volhandle}.roi.Vroi.dim(1:3), ...
                               toset(1,:), toset(2,:), toset(3,:)));
        st.vols{volhandle}.roi.roi(itoset) = true;
        if ~isempty(st.vols{volhandle}.roi.xyz)
            st.vols{volhandle}.roi.xyz = union(st.vols{volhandle}.roi.xyz',toset','rows')';
        else
            st.vols{volhandle}.roi.xyz = toset;
        end;
    end;

    if isfield(st.vols{volhandle}, 'blobs')
        nblobs=length(st.vols{volhandle}.blobs);
        if nblobs>1
            blobstmp(1:st.vols{volhandle}.roi.hroi-1) = st.vols{volhandle}.blobs(1:st.vols{volhandle}.roi.hroi-1);
            blobstmp(st.vols{volhandle}.roi.hroi:nblobs-1) = st.vols{volhandle}.blobs(st.vols{volhandle}.roi.hroi+1:nblobs);
            st.vols{volhandle}.blobs=blobstmp;
        else
            if isempty(st.vols{volhandle}.roi.hframe) % save frame
                st.vols{volhandle}=rmfield(st.vols{volhandle},'blobs');
            end;
        end;
    end;
    if isfield(st.vols{volhandle}, 'blobs')
        st.vols{volhandle}.roi.hroi = numel(st.vols{volhandle}.blobs)+1;
    else
        st.vols{volhandle}.roi.hroi = 1;
    end;
    if isempty(st.vols{volhandle}.roi.xyz)  % initialised with empty roi
        spm_orthviews('addcolouredblobs', volhandle, ...
                      [1; 1; 1], 0, st.vols{volhandle}.roi.Vroi.mat,[1 3 1]);
    else
        spm_orthviews('addcolouredblobs', volhandle, ...
                      st.vols{volhandle}.roi.xyz, ones(size(st.vols{volhandle}.roi.xyz,2),1), ...
                      st.vols{volhandle}.roi.Vroi.mat,[1 3 1]); % use color that is more intense than standard rgb range
    end;
    st.vols{volhandle}.blobs{st.vols{volhandle}.roi.hroi}.max=2;
    spm_orthviews('redraw');
end;

spm('pointer','arrow');

function varargout = stack(cmd, varargin)

switch cmd
    case 'init'
        varargout{1}.st = cell(varargin{1},1);
        varargout{1}.top= 0;

    case 'isempty'
        varargout{1} = (varargin{1}.top==0);

    case 'push'
        stck = varargin{1};
        if (stck.top < size(stck.st,1))
            stck.top = stck.top + 1;
            stck.st{stck.top} = varargin{2};
            varargout{1}=stck;
        else
            error('Stack overflow\n');
        end;

    case 'pop'
        if stack('isempty',varargin{1})
            error('Stack underflow\n');
        else
            varargout{2} = varargin{1}.st{varargin{1}.top};
            varargin{1}.top = varargin{1}.top - 1;
            varargout{1} = varargin{1};
        end;
end;


function X = readbmpdata(info)
%READBMPDATA Read bitmap data
%   X = readbmpdata(INFO) reads image data from a BMP file.  INFO is a
%   structure returned by IMBMPINFO. X is a uint8 array that is 2-D for
%   1-bit, 4-bit, and 8-bit image data.  X is M-by-N-by-3 for 24-bit and
%   32-bit image data.

%   Copyright 1984-2011 The MathWorks, Inc.
%   $Revision: 1.1.6.5 $  $Date: 2011/05/13 17:34:37 $

offset = info.ImageDataOffset;
width = info.Width;
height = info.Height;
filename = info.Filename;

switch info.CompressionType

 case 'none'

  switch info.BitDepth
   case 1
    X = logical(bmpReadData1(filename, offset, width, height));

   case 4
    X = bmpReadData4(filename, offset, width, height);

   case 8
    X = bmpReadData8(filename, offset, width, height);

   case 16
    X = bmpReadData16(filename, offset, width, height);

   case 24
    X = bmpReadData24(filename, offset, width, height);

   case 32
    X = bmpReadData32(filename, offset, width, height);

  end

 case '8-bit RLE'
  X = bmpReadData8RLE(filename, offset, width, height);

 case '4-bit RLE'
  X = bmpReadData4RLE(filename, offset, width, height);

 case 'bitfields'
  error(message('MATLAB:audiovideo:readbmpdata:bitfieldUnsupported'));

 case 'Huffman 1D'
  error(message('MATLAB:audiovideo:readbmpdata:huffmanUnsupported'));

 case '24-bit RLE'
  error(message('MATLAB:audiovideo:readbmpdata:rleUnsupported'));

end

%%%
%%% bmpReadData8 --- read 8-bit bitmap data
%%%
function X = bmpReadData8(fname, offset, width, height)

% NOTE: BMP files are stored so that scanlines use a multiple of 4 bytes.
paddedWidth = 4*ceil(width/4);

fid = fopen(fname,'r','ieee-le');
status = fseek(fid,offset,'bof');
if status==-1
  fclose(fid);
  error(message('MATLAB:audiovideo:readbmpdata:invalidDataOffset'));
end
X = fread(fid,paddedWidth*abs(height),'*uint8');
status = fclose(fid);

count = length(X);
if (count ~= paddedWidth*abs(height))
    warning(message('MATLAB:audiovideo:readbmpdata:truncatedImageData'));
    % Fill in the missing values with zeros.
    X(paddedWidth*abs(height)) = 0;
end

if height>=0
  X = rot90(reshape(X, paddedWidth, height));
else
  X = [reshape(X, paddedWidth, abs(height))]';
end

if (paddedWidth ~= width)
    X = X(:,1:width);
end

%%%
%%% bmpReadData8RLE --- read 8-bit RLE-compressed bitmap data
%%%
function X = bmpReadData8RLE(fname, offset, width, height)

% NOTE: BMP files are stored so that scanlines use a multiple of 4 bytes.
paddedWidth = 4*ceil(width/4);

fid = fopen(fname,'r','ieee-le');
status = fseek(fid,offset,'bof');
if status==-1
  fclose(fid);
  error(message('MATLAB:audiovideo:readbmpdata:invalidDataOffset'));
end
inBuffer = fread(fid,'*uint8');
status = fclose(fid);

X = bmpdrle(inBuffer, paddedWidth, abs(height), 'rle8');

if height>=0
  X = rot90(X);
else
  X = X';
end
if (paddedWidth ~= width)
    X = X(:,1:width);
end

%%%
%%% bmpReadData4 --- read 4-bit bitmap data
%%%
function X = bmpReadData4(filename, offset, width, height)

% NOTE: BMP files are stored so that scanlines use a multiple of 4 bytes.
paddedWidth = 8*ceil(width/8);
numBytes = paddedWidth * abs(height) / 2; % evenly divides because of padding

fid = fopen(filename,'r','ieee-le');
status = fseek(fid,offset,'bof');
if status==-1
  fclose(fid);
  error(message('MATLAB:audiovideo:readbmpdata:invalidDataOffset'));
end
XX = fread(fid,numBytes,'*uint8');
status = fclose(fid);

count = length(XX);
if (count ~= numBytes)
    warning(message('MATLAB:audiovideo:readbmpdata:truncatedImageData'));
    % Fill in the missing values with zeros.
    X(numBytes) = 0;
end
XX = reshape(XX, paddedWidth / 2, abs(height));

X = repmat(uint8(0), paddedWidth, abs(height));
X(1:2:end,:) = bitslice(XX,5,8);
X(2:2:end,:) = bitslice(XX,1,4);

if height>=0
  X = rot90(X);
else
  X = X';
end
if (paddedWidth ~= width)
    X = X(:,1:width);
end

%%%
%%% bmpReadData4RLE --- read 4-bit RLE-compressed bitmap data
%%%
function X = bmpReadData4RLE(filename, offset, width, height)

% NOTE: BMP files are stored so that scanlines use a multiple of 4 bytes.
paddedWidth = 8*ceil(width/8);
numBytes = paddedWidth * abs(height) / 2; % evenly divides because of padding

fid = fopen(filename,'r','ieee-le');
status = fseek(fid,offset,'bof');
if status==-1
  fclose(fid);
  error(message('MATLAB:audiovideo:readbmpdata:invalidDataOffset'));
end
inBuffer = fread(fid,'*uint8');
status = fclose(fid);

if height>=0
  X = rot90(bmpdrle(inBuffer, paddedWidth, abs(height), 'rle4'));
else
  X = [bmpdrle(inBuffer, paddedWidth, abs(height), 'rle4')]';
end
if (paddedWidth ~= width)
    X = X(:,1:width);
end

%%%
%%% bmpReadData1 --- read 1-bit bitmap data
%%%
function X = bmpReadData1(filename, offset, width, height)

% NOTE: BMP files are stored so that scanlines use a multiple of 4 bytes.
paddedWidth = 32*ceil(width/32);
numPixels = paddedWidth * abs(height);  % evenly divides because of padding

% 1-bit BMP data has big-endian byte ordering
fid = fopen(filename,'r','ieee-be');
status = fseek(fid,offset,'bof');
if status==-1
  status = fclose(fid);
  error(message('MATLAB:audiovideo:readbmpdata:invalidDataOffset'));
end
[X, count] = fread(fid,paddedWidth*abs(height),'*ubit1');
status = fclose(fid);

if (count ~= numPixels)
    warning(message('MATLAB:audiovideo:readbmpdata:truncatedImageData'));
    % Fill in the missing values with zeros.
    X(numPixels) = 0;
end
X = reshape(X, paddedWidth, abs(height));

if height>=0
  X = rot90(X);
else
  X = X';
end

if (paddedWidth ~= width)
    X = X(:,1:width);
end

%%%
%%% bmpReadData16 --- read 16-bit bitmap data
%%%
function RGB = bmpReadData16(filename, offset, width, height)

% NOTE: BMP files are stored so that scanlines use a multiple of 4 bytes.
byteWidth = width;
paddedByteWidth = 4*ceil(byteWidth/4);
numBytes = paddedByteWidth * abs(height);

fid = fopen(filename,'r','ieee-le');
status = fseek(fid,offset,0);
%Check if seek is ok
X = fread(fid,numBytes,'*uint16');
status = fclose(fid);

count = length(X);
if (count ~= numBytes)
    warning(message('MATLAB:audiovideo:readbmpdata:truncatedImageData'));
    % Fill in the missing values with zeros.
    X(numBytes) = 0;
end

if height>=0
  X = rot90(reshape(X, paddedByteWidth, abs(height)));
else
  X = [reshape(X, paddedByteWidth, abs(height))]';
end

if (paddedByteWidth ~= byteWidth)
    X = X(:,1:byteWidth);
end

RGB(1:abs(height), 1:width, 1) = uint8(bitslice(X,11,15));
RGB(:,:,2) = uint8(bitslice(X,6,10));
RGB(:,:,3) = uint8(bitslice(X,1,5));

%Scale data for display
RGB = bitor(bitshift(RGB,3),bitshift(RGB,-2));

%%%
%%% bmpReadData24 --- read 24-bit bitmap data
%%%
function RGB = bmpReadData24(filename, offset, width, height)

% NOTE: BMP files are stored so that scanlines use a multiple of 4 bytes.
byteWidth = 3*width;
paddedByteWidth = 4*ceil(byteWidth/4);
numBytes = paddedByteWidth * abs(height);

fid = fopen(filename,'r','ieee-le');
status = fseek(fid,offset,'bof');
if status==-1
  fclose(fid);
  error(message('MATLAB:audiovideo:readbmpdata:invalidDataOffset'));
end
X = fread(fid,numBytes,'*uint8');
status = fclose(fid);

count = length(X);
if (count ~= numBytes)
    warning(message('MATLAB:audiovideo:readbmpdata:truncatedImageData'));
    % Fill in the missing values with zeros.
    X(numBytes) = 0;
end

if height>=0
  X = rot90(reshape(X, paddedByteWidth, abs(height)));
else
  X = [reshape(X, paddedByteWidth, abs(height))]';
end

if (paddedByteWidth ~= byteWidth)
    X = X(:,1:byteWidth);
end

RGB(1:abs(height), 1:width, 3) = X(:,1:3:end);
RGB(:, :, 2) = X(:,2:3:end);
RGB(:, :, 1) = X(:,3:3:end);

%%%
%%% bmpReadData32 --- read 32-bit bitmap data
%%%
function RGB = bmpReadData32(filename, offset, width, height)

% NOTE: BMP files are stored so that scanlines use a multiple of 4 bytes.
byteWidth = 4*width;
paddedByteWidth = 4*ceil(byteWidth/4);
numBytes = paddedByteWidth * abs(height);

fid = fopen(filename,'r','ieee-le');
status = fseek(fid,offset,'bof');
if status==-1
  fclose(fid);
  error(message('MATLAB:audiovideo:readbmpdata:invalidDataOffset'));
end
X = fread(fid,numBytes,'*uint8');
status = fclose(fid);

count = length(X);
if (count ~= numBytes)
    warning(message('MATLAB:audiovideo:readbmpdata:truncatedImageData'));
    % Fill in the missing values with zeros.
    X(numBytes) = 0;
end

if height>=0
  X = rot90(reshape(X, paddedByteWidth, abs(height)));
else
  X = [reshape(X, paddedByteWidth, abs(height))]';
end

if (paddedByteWidth ~= byteWidth)
    X = X(:,1:byteWidth);
end

RGB(1:abs(height), 1:width, 3) = X(:,1:4:end);
RGB(:, :, 2) = X(:,2:4:end);
RGB(:, :, 1) = X(:,3:4:end);


% edu.stanford.covert.io.editMatFileHeader and
% edu.stanford.covert.io.readMatFileHeader test cases
%
% Author: Jonathan Karr
% Author: Jared Jacobs
% Affilitation: Covert Lab, Department of Bioengineering, Stanford University
% Last updated: 9/14/2010
classdef editMatFileHeaderTest < TestCase
    methods
        function this = editMatFileHeaderTest(name)
            this = this@TestCase(name);
        end

        function testEditMatFileTimestamp(~)
            %generate mat file
            matFile = [tempname '.mat'];
            save(matFile, 'matFile', 'matFile');
            timestamp = edu.stanford.covert.io.readMatFileHeader(matFile);
            assertFalse(733899 == timestamp);

            %change timestamp
            edu.stanford.covert.io.editMatFileHeader(matFile, 733899, 'PCWIN', '5.0');
            [timestamp, platform, version] = ...
                edu.stanford.covert.io.readMatFileHeader(matFile);
            assertEqual(733899, timestamp);
            assertEqual('PCWIN', platform);
            assertEqual('5.0', version);

            %cleanup
            delete(matFile);
        end
    end
end


function obj = setFuncIndices(obj, index)
    % Sets indinces of the Nlp Function
    %
    % Parameters:
    %  index: the indices of the function @type integer

    % If an offset is not specified, set it to zero.

    assert(length(index) == obj.Dimension, ...
        'The length of the variable indices must be equal to the dimension of the NlpVariable object.');

    if ~isempty(obj.SummandFunctions)
        arrayfun(@(x) setFuncIndices(x, index), ...
            obj.SummandFunctions, 'UniformOutput', false);
    end

    obj.FuncIndices = index;
end


function Prefs = ReadPreferences()
%ReadPreferences Reads Preferences text file
%
% ////////////////////////////////////////////
% /// CLASSIFICATION: UNCLASSIFIED         ///
% ////////////////////////////////////////////
%
% INPUTS:
%   None
%
% OUTPUTS:
%  Prefs - Structure of Preferences
%
%VERSION:
%   1.0
%     - Tim Cox 20110525
%     - initial version.

Prefs = [];

%construct file name
[pathstr, name, ext] = fileparts(which('Taser'));
filename = sprintf('%s/TaserPreferences.txt',pathstr);

%determine if file exists
fid = fopen(filename,'r');

%return if fopen was unsucessful
if (fid < 3)
    return;
end

%parse file

%ImageOverviewFlag
tline = fgetl(fid);
Prefs.ImageOverview = str2double(tline(strfind(tline,'=')+1:end));
%SICD Location
tline = fgetl(fid);
Prefs.SICDLocation = strtrim(tline(strfind(tline,'=')+1:end));
%Max Resolution
tline = fgetl(fid);
Prefs.MaxResolution = str2double(tline(strfind(tline,'=')+1:end));
%Use Overview
tline = fgetl(fid);
Prefs.UseOverview = str2double(tline(strfind(tline,'=')+1:end));
%PHD NumPulses
tline = fgetl(fid);
Prefs.NumPulses = str2double(tline(strfind(tline,'=')+1:end));
%PHD ProcessingPulses
tline = fgetl(fid);
Prefs.ProcessingPulses = str2double(tline(strfind(tline,'=')+1:end));
%PHD dBCheck
tline = fgetl(fid);
Prefs.dBCheck = str2double(tline(strfind(tline,'=')+1:end));
%Complex Default Template Check
tline = fgetl(fid);
Prefs.ComplexDefaultCheck = str2double(tline(strfind(tline,'=')+1:end));
%Complex Template File
tline = fgetl(fid);
Prefs.ComplexTemplate = strtrim(tline(strfind(tline,'=')+1:end));
%PHD Default Template Check
tline = fgetl(fid);
Prefs.PHDDefaultCheck = str2double(tline(strfind(tline,'=')+1:end));
%PHD Template File
tline = fgetl(fid);
Prefs.PHDTemplate = strtrim(tline(strfind(tline,'=')+1:end));

fclose(fid);

% ////////////////////////////////////////////
% /// CLASSIFICATION: UNCLASSIFIED         ///
% ////////////////////////////////////////////


function [C,k] = str2cell_startup(str, delimiters, options)

% Option tells weather to keep leading whitespaces.
% (Trailing whitespaces are always removed)
if ~exist('options','var')
    options = '';
end

if ~strcmpi(options, 'keepblanks')
    str = strtrim(str);
end
str = deblank(str);

if ~exist('delimiters','var') || isempty(delimiters)
    delimiters{1} = sprintf('\n');
elseif ~iscell(delimiters)
    foo{1} = delimiters;
    delimiters = foo;
end

% Get indices of all the delimiters
k=[];
for kk=1:length(delimiters)
    k = [k, find(str==delimiters{kk})];
end
j = find(~ismember(1:length(str),k));

% The following line seems to hurt performance a little bit. It was
% meant to preallocate to speed things up but it does not seem to do that.
% C = repmat({blanks(max(diff([k,length(str)])))}, length(k)+1, 1);
C = {};
ii=1; kk=1;
while ii<=length(j)
    C{kk} = str(j(ii));
    ii=ii+1;
    jj=2;
    while (ii<=length(j)) && ((j(ii)-j(ii-1))==1)
        C{kk}(jj) = str(j(ii));
        jj=jj+1;
        ii=ii+1;
    end
    C{kk}(jj:end)='';
    kk=kk+1;
end
C(kk:end) = [];