function img2new = imSameHeight(img1, img2)
%
%
%        img2new = imSameHeight(img1, img2)
%
%
%        Input:
%           -img1: target size image
%           -img2: source image to adjust with the same height of image 1
%
%        Output:
%           -img2new: source image with the same height of img1
%
%     Copyright (C) 2011  Francesco Banterle
%
%     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/>.
%

[r1,c1,col1] = size(img1);
[r2,c2,col2] = size(img2);

c2new = round((c2*r1)/r2);
img2new = imresize(img2, [r1,c2new] , 'bilinear');

end


function evaluatePCKh(predidxs,bSave)
% implementation of PCKh measure,
% as defined in [Andriluka et al., CVPR'14]

fprintf('evaluatePCKh()\n');

if (nargin < 2)
    bSave = true;
end

range = 0:0.01:0.5;

tableDir = './latex'; if (~exist(tableDir,'dir')), mkdir(tableDir); end
plotsDir = './plots'; if (~exist(plotsDir,'dir')), mkdir(plotsDir); end
tableTex = cell(length(predidxs)+1,1);

% load ground truth
p = getExpParams(-1);
load([p.gtDir '/annolist_dataset_v12'],'annolist');  % do not have this file. so the author does not release ground truth?
load([p.gtDir '/mpii_human_pose_v1_u12'],'RELEASE');

annolist_test = annolist(RELEASE.img_train == 0);
% evaluate on the "single person" subset only
single_person_test = RELEASE.single_person(RELEASE.img_train == 0);
% convert to annotation list with a single pose per entry
[annolist_test_flat, single_person_test_flat] = flatten_annolist(annolist_test,single_person_test);
% represent ground truth as a matrix 2x14xN_images
gt = annolist2matrix(annolist_test_flat(single_person_test_flat == 1));
% compute head size
headSize = getHeadSizeAll(annolist_test_flat(single_person_test_flat == 1));

pckAll = zeros(length(range),16,length(predidxs));

for i = 1:length(predidxs);

    % load predictions
    p = getExpParams(predidxs(i));
    load([fileparts(p.predFilename) '/pred_keypoints_mpii' ],'pred');
    assert(length(annolist_test) == length(pred));
    pred_flat = flatten_annolist(pred,single_person_test);
    pred = annolist2matrix(pred_flat(single_person_test_flat == 1));
    % only gt is allowed to have NaN
    pred(isnan(pred)) = inf;

    % compute distance to ground truth joints
    dist = getDistPCKh(pred,gt,headSize);

    % compute PCKh
    pck = computePCK(dist,range);

    % plot results
    [row, header] = genTablePCK(pck(end,:),p.name);
    tableTex{1} = header;
    tableTex{i+1} = row;

    pckAll(:,:,i) = pck;

    auc = area_under_curve(scale01(range),pck(:,end));
    fprintf('%s, AUC: %1.1f\n',p.name,auc);
end

if (bSave)
    fid = fopen([tableDir '/pckh.tex'],'wt');assert(fid ~= -1);
    for i=1:length(tableTex),fprintf(fid,'%s\n',tableTex{i}); end; fclose(fid);
end

% plot curves
plotCurve(squeeze(pckAll(:,end,:)),range,predidxs,'PCKh total, MPII',[plotsDir '/pckh-total-mpii'],bSave,range(1:5:end));
plotCurve(squeeze(mean(pckAll(:,[1 6],:),2)),range,predidxs,'PCKh ankle, MPII',[plotsDir '/pckh-ankle-mpii'],bSave,range(1:5:end));
plotCurve(squeeze(mean(pckAll(:,[2 5],:),2)),range,predidxs,'PCKh knee, MPII',[plotsDir '/pckh-knee-mpii'],bSave,range(1:5:end));
plotCurve(squeeze(mean(pckAll(:,[3 4],:),2)),range,predidxs,'PCKh hip, MPII',[plotsDir '/pckh-hip-mpii'],bSave,range(1:5:end));
plotCurve(squeeze(mean(pckAll(:,[7 12],:),2)),range,predidxs,'PCKh wrist, MPII',[plotsDir '/pckh-wrist-mpii'],bSave,range(1:5:end));
plotCurve(squeeze(mean(pckAll(:,[8 11],:),2)),range,predidxs,'PCKh elbow, MPII',[plotsDir '/pckh-elbow-mpii'],bSave,range(1:5:end));
plotCurve(squeeze(mean(pckAll(:,[9 10],:),2)),range,predidxs,'PCKh shoulder, MPII',[plotsDir '/pckh-shoulder-mpii'],bSave,range(1:5:end));
plotCurve(squeeze(mean(pckAll(:,[13 14],:),2)),range,predidxs,'PCKh head, MPII',[plotsDir '/pckh-head-mpii'],bSave,range(1:5:end));

end


function  [img,coords] = makeMontage(imgCube, sliceList, fileName, numAcross, backVal)
%img = makeMontage(imgCube, [sliceList], [fileName], [numAcross], [backVal])
%
% Compiles a montage image from the images in imgCube.
% (imgCube is x by y by sliceNum)
% sliceList, if specified, determines which slices to extract.
% (it defaults to all slices if it's empty or omitted)
% The image can be displayed with something like:
%
%  figure; imagesc(img); colormap(gray); axis equal; axis off;
%
% If a fileName is specified, the image will be saved to that file.
% Note that the image file format type depends on the filename extension
% (see imwrite).
%
% see also makeMontageIfiles
if(~exist('sliceList', 'var')) sliceList = []; end
if(~exist('numAcross', 'var')) numAcross = []; end
if(~exist('fileName', 'var')) fileName = []; end
if(~exist('backVal', 'var')) backVal = []; end
if(size(imgCube,4)==3)
   [img(:,:,1),coords] = makeMontage(imgCube(:,:,:,1), sliceList, fileName, numAcross,backVal);
   img(:,:,2) = makeMontage(imgCube(:,:,:,2), sliceList, fileName, numAcross,backVal);
   img(:,:,3) = makeMontage(imgCube(:,:,:,3), sliceList, fileName, numAcross,backVal);
   return;
end

% Sanity check
if(any(size(imgCube)>10000))
    error('At least one dimension of input image is >10,000- refusing to continue...');
end

header = 0;
if(isempty(sliceList))
   sliceList = [1:size(imgCube, 3)];
end
sz = size(imgCube);
r = sz(1); c = sz(2);

nImages = length(sliceList);
if(isempty(numAcross))
    numAcross = ceil(sqrt(nImages)*sqrt((r/c)));
end
numDown = ceil(nImages/numAcross);
if(isempty(backVal)) backVal = 0; end
img = ones(r*numDown,c*numAcross)*backVal;
eval(['img = ',class(imgCube),'(img);']);
count = 0;
for ii = 1:length(sliceList)
    curSlice = sliceList(ii);
    count = count+1;
    x = rem(count-1, numAcross)*c;
    y = floor((count-1)/ numAcross)*r;
    img(y+1:y+r,x+1:x+c) = imgCube(:,:,curSlice);
    coords(ii,:) = [x+1,y+1];
end

if(~isempty(fileName))
    imgMin = min(img(:));
    imgMax = max(img(:));
    img2 = uint8(floor((double(img)+imgMin)./(imgMax-imgMin)*255));

    %figure; colormap(repmat([0:1/255:1]',1,3)); image(img2); axis image; axis off;
    [p,f,e] = fileparts(fileName);
    if(isempty(e))
        fileName = [fileName, '.jpg'];
    end
    imwrite(img2, fileName);
    disp(['Wrote montage to ' fullfile(pwd, fileName)]);
end


function errorxy(Data,varargin);
%----------------------------------------------------------------------------
% errorxy function           Plot graphs with error bars on both axes.
%                          (This is an improved version of errorxy_o.m).
% Input  : - Data matrix.
%          * an arbitrary pairs of keyword and value parameters, where
%            keyword is one of the followings:
%            (In case that the second parameter is a number then call the
%             old version errorxy_o).
%            'ColX'     - Column number containing X values, default is 1.
%            'ColY'     - Column number containing Y values, default is 2.
%            'ColXe'    - Column number containing error X values.
%                         If one parameter is given then assume upper
%                         and lower errors are the same, if two values
%                         are given then the first is the left and the
%                         second is the right errors,
%                         default is NaN (no X errors).
%            'ColYe'    - Column number containing error Y values.
%                         If one parameter is given then assume right
%                         and left errors are the same, if two values
%                         are given then the first is the lower and the
%                         second is the upper errors,
%                         default is 3.
%            'Marker'   - Marker type, see plot for options, default is 'o'.
%            'MarkSize' - Marker size, default is 6.
%            'EdgeColor'- Marker edge color, default is 'b'.
%            'FaceColor'- Marker face color, default is NaN.
%            'ColorEB'  - Error bars color, default is the same as 'EdgeColor'.
%            'LineEB'   - Error bars line type, default is '-'.
%            'WidthEB'  - Error bars line width, default is 0.5.
%            'EdgeEB'   - Length [ticks] of error bar edge, default is 0.
%            'Con'      - Connect data points {'y' | 'n'}, default is 'n'.
%            'ConColor' - Color of Connecting line, default is the
%                         same as 'EdgeColor'
%            'ConLine'  - Type of connecting line, default is '-'.
%            'ConWidth' - Width of connecting line, default is 0.5.
%            'Hist'     - Plot histogram {'y' | 'n'}, default is 'n'.
%            'HistFaceColor'- Histogram face color, default is the same as 'EdgeColor'.
%            'HistEdgeColor'- Histogram edge color, default is the same as 'EdgeColor'.
%            'ScaleX'   - X scale {'linear' | 'log'}, default is 'linear'.
%            'ScaleY'   - Y scale {'linear' | 'log'}, default is 'linear'.
%            'DirX'     - X direction {'normal' | 'reverse'}, default is 'normal'.
%            'DirY'     - Y direction {'normal' | 'reverse'}, default is 'normal'.
% Plot   : errorxy plot
% Example: errorxy(Data,'ColYe',[5 6],'Marker','^');
% Tested : Matlab 5.3
%     By : Eran O. Ofek               March 2004
%    URL : http://wise-obs.tau.ac.il/~eran/matlab.html
%----------------------------------------------------------------------------
Ndots = 450;    % approximate number of dots in paper

if (nargin>1 & ischar(varargin{1})==0),
   %--- call old erroxy_o ---
   errorxy_o(Data,varargin{:});
else
   %--- new version of errorxy ---

   Nvarg = length(varargin);
   if (0.5.*Nvarg~=floor(0.5.*Nvarg)),
      error('Illegal number of input arguments');
   end

   %--- set default values ---
   ColX         = 1;
   ColY         = 2;
   ColXe        = [NaN, NaN];
   ColYe        = [3, 3];
   Marker       = 'o';
   MarkSize     = 6;
   EdgeColor    = 'b';
   FaceColor    = 'n';
   ColorEB      = NaN;
   LineEB       = '-';
   WidthEB      = 0.5;
   EdgeEB       = 0;
   Con          = 'n';
   ConColor     = NaN;
   ConLine      = '-';
   ConWidth     = 0.5;
   Hist         = 'n';
   HistFaceColor= NaN;
   HistEdgeColor= NaN;
   ScaleX       = 'linear';
   ScaleY       = 'linear';
   DirX         = 'normal';
   DirY         = 'normal';

   for I=1:2:Nvarg-1,
      switch varargin{I}
       case 'ColX'
          ColX        = varargin{I+1};
       case 'ColY'
          ColY        = varargin{I+1};
       case 'ColXe'
          ColXe       = varargin{I+1};
       case 'ColYe'
          ColYe       = varargin{I+1};
       case 'Marker'
          Marker      = varargin{I+1};
       case 'MarkSize'
          MarkSize    = varargin{I+1};
       case 'EdgeColor'
          EdgeColor   = varargin{I+1};
       case 'FaceColor'
          FaceColor   = varargin{I+1};
       case 'ColorEB'
          ColorEB     = varargin{I+1};
       case 'LineEB'
          LineEB      = varargin{I+1};
       case 'WidthEB'
          WidthEB     = varargin{I+1};
       case 'EdgeEB'
          EdgeEB      = varargin{I+1};
       case 'Con'
          Con         = varargin{I+1};
       case 'ConColor'
          ConColor    = varargin{I+1};
       case 'ConLine'
          ConLine    = varargin{I+1};
       case 'ConWidth'
          ConWidth   = varargin{I+1};
       case 'Hist'
          Hist       = varargin{I+1};
       case 'HistFaceColor'
          HistFaceColor  = varargin{I+1};
       case 'HistEdgeColor'
          HistEdgeColor  = varargin{I+1};
       case 'ScaleX'
          ScaleX     = varargin{I+1};
       case 'ScaleY'
          ScaleY     = varargin{I+1};
       case 'DirX'
          DirX       = varargin{I+1};
       case 'DirY'
          DirY       = varargin{I+1};
       otherwise
          error('Unknown keyword option');
      end
   end

   %--- set NaN colors ---
   if (isnan(ColorEB)==1),
      ColorEB = EdgeColor;
   end
   if (isnan(ConColor)==1),
      ConColor = EdgeColor;
   end
   if (isnan(HistFaceColor)==1),
      HistFaceColor = EdgeColor;
   end
   if (isnan(HistEdgeColor)==1),
      HistEdgeColor = EdgeColor;
   end

   if (length(ColXe)==1),
      ColXe = [ColXe, ColXe];
   end
   if (length(ColYe)==1),
      ColYe = [ColYe, ColYe];
   end

   %---------------------
   %--- Hold handling ---
   %---------------------
   NextPlot = get(gcf,'NextPlot');
   hold on;
   box on;

   %-----------------------------------
   %--- Data span for edge plotting ---
   %-----------------------------------
   SpanX = max(Data(:,ColX)) - min(Data(:,ColX));
   SpanY = max(Data(:,ColY)) - min(Data(:,ColY));
   EdgeX = SpanX.*EdgeEB./Ndots;
   EdgeY = SpanY.*EdgeEB./Ndots;

   %----------------------
   %--- plot histogram ---
   %----------------------
   switch Hist
    case 'n'
       % do nothing
    case 'y'
       Hhist = bar(Data(:,ColX),Data(:,ColY));
       set(Hhist,'FaceColor',HistFaceColor,...
                 'EdgeColor',HistEdgeColor);
    otherwise
       error('Unknown Hist option');
   end

   %------------------------
   %--- plot data points ---
   %------------------------
   N = size(Data,1);

   for I=1:1:N,
      H         = plot(Data(I,ColX),Data(I,ColY),'o');
      Hpoint(I) = H;
      set(Hpoint,'Marker',Marker,...
            'MarkerSize',MarkSize,...
            'MarkerEdgeColor',EdgeColor,...
            'MarkerFaceColor',FaceColor);
      %--- plot Y error bars ---
      if (isnan(ColYe(1))==0),
         H        = plot([Data(I,ColX); Data(I,ColX)],...
                         [Data(I,ColY)-Data(I,ColYe(1)); Data(I,ColY)+Data(I,ColYe(2))],...
                         '-');
         Hyerr(I) = H;
         set(Hyerr,'Color',ColorEB,...
     	        'LineStyle',LineEB,...
   	        'LineWidth',WidthEB);

         %--- Error bar edge ---
         if (EdgeEB>0),
            %--- Y error bar lower edge ---
    	 H        = plot([Data(I,ColX)-EdgeX; Data(I,ColX)+EdgeX],...
                            [Data(I,ColY)-Data(I,ColYe(1)); Data(I,ColY)-Data(I,ColYe(1))],...
   		         '-');
            Heyel(I) = H;
   	 set(Heyel,'Color',ColorEB,...
     	           'LineStyle',LineEB,...
   	           'LineWidth',WidthEB);
            %--- Y error bar upper edge ---
    	 H        = plot([Data(I,ColX)-EdgeX; Data(I,ColX)+EdgeX],...
                            [Data(I,ColY)+Data(I,ColYe(2)); Data(I,ColY)+Data(I,ColYe(2))],...
   		         '-');
            Heyeu(I) = H;
   	 set(Heyeu,'Color',ColorEB,...
     	           'LineStyle',LineEB,...
   	           'LineWidth',WidthEB);
         end
      end
      %--- plot X error bars ---
      if (isnan(ColXe(1))==0),
         H        = plot([Data(I,ColX)-Data(I,ColXe(1)); Data(I,ColX)+Data(I,ColXe(2))],...
                         [Data(I,ColY); Data(I,ColY)],...
                         '-');
         Hxerr(I) = H;
         set(Hxerr,'Color',ColorEB,...
     	        'LineStyle',LineEB,...
   	        'LineWidth',WidthEB);

         %--- Error bar edge ---
         if (EdgeEB>0),
            %--- X error bar lower edge ---
    	 H        = plot([Data(I,ColX)-Data(I,ColXe(1)); Data(I,ColX)-Data(I,ColXe(1))],...
                            [Data(I,ColY)-EdgeY; Data(I,ColY)+EdgeY],...
   		         '-');
            Hexel(I) = H;
   	 set(Hexel,'Color',ColorEB,...
     	           'LineStyle',LineEB,...
   	           'LineWidth',WidthEB);
            %--- X error bar upper edge ---
    	 H        = plot([Data(I,ColX)+Data(I,ColXe(2)); Data(I,ColX)+Data(I,ColXe(2))],...
                            [Data(I,ColY)-EdgeY; Data(I,ColY)+EdgeY],...
   		         '-');
            Hexer(I) = H;
   	 set(Hexer,'Color',ColorEB,...
     	           'LineStyle',LineEB,...
   	           'LineWidth',WidthEB);
         end
      end

   end

   %---------------------------
   %--- connect data points ---
   %---------------------------
   switch Con
    case 'n'
       % do nothing
    case 'y'
       Hcon = plot(Data(:,ColX),Data(:,ColY),'-');
       set(Hcon,'Color',ConColor,...
    	     'LineStyle',ConLine,...
      	     'LineWidth',ConWidth);
    otherwise
       error('Unknown Con option');
   end

   %---------------------------
   %--- set scale/direction ---
   %---------------------------
   set(gca,'XScale',ScaleX,...
           'YScale',ScaleY,...
           'XDir',DirX,...
           'YDir',DirY);

   %--- return hold to riginal state ---
   set(gcf,'NextPlot',NextPlot);
end


function [minFlux, maxFlux, minFD, maxFD, GRvector, result, LP] = SteadyComFVA(modelCom, options, varargin)
% Flux variability analysis for community model at community steady-state for a range of growth rates.
% The function is capable of saving intermediate results and continuing from previous results
% if the file path is given in `options.saveFVA`. It also allows switch from single thread to parallel
% computation from intermediate results (but not the other way round).
%
% USAGE:
%    [minFlux, maxFlux, minFD, maxFD, GRvector, result, LP] = SteadyComFVA(modelCom, options, parameter, 'param1', value1, 'param2', value2, ...)
%
% INPUT:
%    modelCom:     A community COBRA model structure with the following fields (created using `createMultipleSpeciesModel`)
%                  (the first 5 fields are required, at least one of the last two is needed. Can be obtained using `getMultiSpecisModelId`):
%
%                    * S - Stoichiometric matrix
%                    * b - Right hand side
%                    * c - Objective coefficients
%                    * lb - Lower bounds
%                    * ub - Upper bounds
%                    * infoCom - structure containing community reaction info
%                    * indCom - the index structure corresponding to `infoCom`
%
% OPTIONAL INPUTS:
%    options:    struct with the following possible fields:
%
%                  * optGRpercent - A vector of percentages. Perform FVA at these percents of max. growth rate (Default = [99.99])
%                  * optBMpercent - Only consider solutions that yield at least a certain percentage of the optimal biomass (Default = 99.99)
%                  * rxnNameList - List of reactions (index row vector or subset of `*.rxns`) for which FVA is performed.
%                    (Default = biomass reaction of each species)
%                    Or a :math:`(N_{rxns} + N_{organism}) x K` matrix for FVA of `K` linear combinations of fluxes and/or abundances
%                    e.g., `[1; -2; 0]` for finding the max/min of :math:`1 v_1 - 2 v_2 + 0 v_3`
%                  * rxnFluxList - List of reactions (index vector or subset of `*.rxns`) whose fluxes are
%                    also returned along with the FVA result of each entry in `rxnNameList`
%                    (Default = biomass reaction of each species)
%                  * GRmax - maximum growth rate of the model (default to be found `SteadyCom.m`)
%                    (the two parameters below are usually determined by solving the problem during the program.
%                    Provide them only if you want to constrain the total biomass to a particular value)
%                  * BMmaxLB - lower bound for the total biomass (default 1)
%                  * BMmaxUB - upper bound for the total biomass (other parameters below)
%                  * saveFVA - If non-empty, become the filename to save the FVA results
%                    (default empty, not saving)
%                  * saveFre - save frequency. Save every `(#rxns for FVA) * saveFre` (default 0.1)
%                  * threads - for parallelization: > 1 for explicitly stating the no. of threads used,
%                    0 or -1 for using all available threads. Default 1.
%                    (Requires Matlab parallel toolbox)
%                  * verbFlag - Verbose output. 1 to have waitbar, >1 to have stepwise output (default 3)
%                  * loadModel - (`ibm_cplex` only) String of filename to be loaded. If non-empty, load the cplex
%                    model ('loadModel.mps'), basis ('loadModel.bas') and parameters ('loadModel.prm').
%                    (May add also other parameters in `SteadyCom` for calculating the maximum growth rate.)
%
%    parameter:  structure for solver-specific parameters.
%                'param1', value1, ...   name-value pairs for `solveCobraLP` parameters. See `solveCobraLP` for details
%
% OUTPUTS:
%    minFlux:    Minimum flux for each reaction
%    maxFlux:    Maximum flux for each reaction
%
% OPTIONAL OUTPUTS:
%    minFD:      :math:`rxnFluxList * rxnNameList` matrix containing the fluxes in `options.rxnFluxList`
%                corresponding to minimizing each reaction in `options.rxnNameList`
%    maxFD:      :math:`rxnFluxList * rxnNameList` matrix containing the fluxes in `options.rxnFluxList`
%                corresponding to maximizing each reaction in `options.rxnNameList`
%    GRvector:   a vector of growth rates at which FVA has been performed
%    result:     result structure from `SteadyCom`
%    LP:         `LP` problem structure (`Cplex LP` object for `ibm_cplex`)

[modelCom, ibm_cplex, feasTol, solverParams, parameters] = SteadyComSubroutines('initialize', modelCom, varargin{:});
% Initialization above
if nargin < 2 || isempty(options)
    options = struct();
end
% handle solveCobraLP name-value arguments that are specially treated in SteadyCom functions
[options, varargin] = SteadyComSubroutines('solveCobraLP_arg', options, parameters, varargin);

% get SteadyCom paramters. If a required parameter is in options, get its value, else equal to the
% default value in SteadyComSubroutines('getParams') if there is. Otherwise an empty matrix.
[GRmax, optGRpercent, rxnNameList, rxnFluxList, ...
    GRfx, BMmaxLB, BMmaxUB, ...
    verbFlag, loadModel, saveFVA, threads] = SteadyComSubroutines('getParams',  ...
    {'GRmax', 'optGRpercent', 'rxnNameList', 'rxnFluxList',...
    'GRfx','BMmaxLB','BMmaxUB', ...
    'verbFlag', 'loadModel','saveFVA','threads'}, ...
    options, modelCom);

[m, n] = size(modelCom.S);  % model size
nSp = numel(modelCom.indCom.spBm);  % number of organisms
nRxnSp = sum(modelCom.indCom.rxnSps > 0);  % number of organism-specific rxns

if ischar(rxnNameList)
    rxnNameList = {rxnNameList};
end
if iscell(rxnNameList) || (min(size(rxnNameList)) == 1 && size(rxnNameList, 1) < n)
    nRxnFVA = numel(rxnNameList);
else
    nRxnFVA = size(rxnNameList, 2);
end
if ischar(rxnFluxList)
    rxnFluxList = {rxnFluxList};
end

addRow = false;
GRgiven = false;
if isempty(GRmax)
    % get maximum growth rate
    [sol, result, LP] = SteadyCom(modelCom, options, varargin{:});
    if strcmp(result.stat,'infeasible')
        % infeasible model
        warning('The model is infeasible.');
        [minFlux, maxFlux] = deal(NaN(nRxnFVA, 1));
        [minFD, maxFD] = deal(NaN(numel(rxnFluxList), nRxnFVA));
        GRvector = NaN(numel(optGRpercent), 1);
        return
    end
    GRmax = result.GRmax;
    if ibm_cplex
        idRow = size(LP.Model.A, 1);  % row that constrains the total biomass
    else
        idRow = size(LP.A, 1);  % row that constrains the total biomass
    end
else
    % if GRmax is given, BMmaxLB and BMmaxUB should be included in options in this case to ensure feasibility
    if ibm_cplex && ~isempty(loadModel)
        % load Cplex model if loadModel is given
        LP = Cplex('SteadyComFVA');
        LP.readModel([loadModel '.mps']);
        LP.readBasis([loadModel '.bas']);
        LP.readParam([loadModel '.prm']);
        LP.DisplayFunc = [];
        fprintf('Load model ''%s'' successfully.\n', loadModel);
        addRow = true;
        if size(LP.Model.A, 1) > m + 2 * nRxnSp + nSp
            %try to find the row that constrains total biomass
            [ynRow, idRow] = ismember(sparse(ones(nSp, 1), (n + 1) : (n + nSp), ones(nSp, 1), 1, n + nSp),...
                LP.Model.A((m + 2 * nRxnSp + nSp + 1):end, 1:(n + nSp)),'rows');
            if ynRow
                idRow = m + 2 * nRxnSp + nSp + idRow;
            end
            addRow = ~ynRow;
        end
    else
        % get the LP structure using SteadyCom
        options2 = options;
        options2.LPonly = true;
        [~, ~, LP] = SteadyCom(modelCom, options2, varargin{:});
        addRow = true;  % no constraint on total biomass using LPonly option
    end
    result = struct('GRmax',GRmax,'vBM',[],'BM',[],'Ut',[],'Ex',[],'flux',[],'iter0',[],'iter',[],'stat','optimal');
    GRgiven = true;
end
if addRow
    % add a row for constraining the sum of biomass if not exist
    if ibm_cplex
        LP.addRows(BMmaxLB, sparse(ones(1, nSp), (n + 1) : (n + nSp), ones(1, nSp), 1, size(LP.Model.A, 2)), BMmaxUB, 'UnityBiomass');
        idRow = size(LP.Model.A, 1);
    else
        LP.A = [LP.A; sparse([ones(nSp, 1); 2 * ones(nSp, 1)], repmat((n + 1):(n + nSp), 1, 2),...
            ones(nSp * 2, 1), 2, size(LP.A, 2))];
        LP.b = [LP.b; BMmaxUB; BMmaxLB];
        LP.csense = [LP.csense, 'LG'];
        idRow = size(LP.A, 1);
    end
else
    % using BMmaxLB and BMmaxUB stored in the LP if not given in options
    if ~isfield(options,'BMmaxLB')  % take from LP if not supplied
        if ibm_cplex
            BMmaxLB = LP.Model.lhs(idRow);
        else
            BMmaxLB = LP.b(idRow);
        end
    end
    if ~isfield(options,'BMmaxUB')  % take from LP if not supplied
        if ibm_cplex
            BMmaxUB = LP.Model.rhs(idRow);
        else
            BMmaxUB = LP.b(idRow - 1);
        end
    end
    % not allow the max. biomass to exceed the one at max growth rate,
    % can happen if optBMpercent < 100. May dismiss this constraint or
    % manually supply BMmaxUB in the options if sum of biomass should be variable
    if ibm_cplex
        LP.Model.lhs(idRow) = BMmaxLB;
        LP.Model.rhs(idRow) = BMmaxUB;
    else
        LP.b(idRow) = BMmaxLB;
        LP.b(idRow - 1) = BMmaxUB;
    end
end
if ibm_cplex
    LP = setCplexParam(LP, solverParams);  % set Cplex parameters
    % update the LP to ensure the current growth rate is constrained
    LP.Model.A = SteadyComSubroutines('updateLPcom', modelCom, GRmax, GRfx, [], LP.Model.A, []);
    LP.Model.sense = 'minimize';
    LP.Model.obj(:) = 0;
    LP.solve();
    dev = checkSolFeas(LP);
else
    % update the LP to ensure the current growth rate is constrained
    LP.A = SteadyComSubroutines('updateLPcom', modelCom, GRmax, GRfx, [], LP.A, []);
    LP.c(:) = 0;
    LP.osense = 1;
    sol = solveCobraLP(LP, varargin{:});
    dev = checkSolFeas(LP, sol);
    if isfield(sol, 'basis')
        LP.basis = sol.basis;  % reuse basis
    end
end
% check and adjust for feasibility
% (LP from SteadyCom should pass this automatically as the row has been added in SteadyComCplex)
kBMadjust = 0;
while ~(dev <= feasTol) && kBMadjust < 10
    kBMadjust = kBMadjust + 1;
    % relax the required sum of biomass
    if ibm_cplex
        LP.Model.lhs(idRow) = BMmaxLB * (1 - feasTol/(11 - kBMadjust));
        LP.solve();
        dev = checkSolFeas(LP);
    else
        LP.b(idRow) = BMmaxLB * (1 - feasTol/(11 - kBMadjust));
        sol = solveCobraLP(LP, varargin{:});
        dev = checkSolFeas(LP, sol);
        if isfield(sol, 'basis')
            LP.basis = sol.basis;  % reuse basis
        end
    end
    if verbFlag
        fprintf('BMmax adjustment: %d\n', kBMadjust);
    end
end
if ~(dev <= feasTol)  % dev can be NaN, which still means infeasibility. So use ~(dev <= feasTol) instead of dev > feasTol
    warning('Model not feasible.')
    [minFlux, maxFlux] = deal(NaN(nRxnFVA, numel(optGRpercent)));
    [minFD, maxFD] = deal(NaN(numel(rxnFluxList), nRxnFVA, numel(optGRpercent)));
    GRvector = NaN(numel(optGRpercent), 1);
    result.stat = 'infeasible';
    return
end
if GRgiven
    % assign result structure if in the rare case of given growth rate
    if ibm_cplex
        flux = LP.Solution.x;
    else
        flux = sol.full;
    end
    result.vBM = flux(modelCom.indCom.spBm);
    result.BM = flux(n+1:n+nSp);
    % two different types of indexing
    if size(modelCom.indCom.EXcom, 2) == 2
        % uptake and excretion reactions separated
        result.Ut = flux(modelCom.indCom.EXcom(:,1));
        result.Ex = flux(modelCom.indCom.EXcom(:,2));
    else
        % uptake and excretion in one exchange reaction
        [result.Ut, result.Ex] = deal(flux(modelCom.indCom.EXcom(:,1)));
        result.Ut(result.Ut > 0) = 0;
        result.Ut = -result.Ut;
        result.Ex(result.Ex < 0) = 0;
    end
    result.flux = flux(1:n);
end
% update BMmaxLB and BMmaxUB for FVA at each given growth rate
if ~isfield(options, 'BMmaxLB')
    if ibm_cplex
        options.BMmaxLB = LP.Model.lhs(idRow);
    else
        options.BMmaxLB = LP.b(idRow);
    end
end
if ~isfield(options, 'BMmaxUB')
    if ibm_cplex
        options.BMmaxUB = LP.Model.rhs(idRow);
    else
        options.BMmaxUB = LP.b(idRow - 1);
    end
end

GRvector = GRmax * optGRpercent / 100;
if ~isempty(saveFVA)
    % decide number of digits in the save name
    if numel(optGRpercent) == 1
        kDisp = 2;
    else
        d = min(GRvector(2:end) - GRvector(1:end-1));
        if d < 1
            kDisp = abs(floor(log10(abs(d))));
        else
            kDisp = 0;
        end
    end
    directory = strsplit(saveFVA,filesep);
    if numel(directory) > 1
        % not saving in the current directory. Create the directory.
        directory = strjoin([{pwd}, directory(1:end-1)],filesep);
        if ~exist(directory, 'dir')
            mkdir(directory);
        end
    end
    if ibm_cplex
        LPmodel = LP.Model;  % the Cplex dynamic object is not good for saving
        LPstart = LP.Start;
        save([saveFVA '_model.mat'], 'LPmodel', 'LPstart', 'options', 'solverParams', 'parameters');
        clear LPmodel LPstart
    else
        save([saveFVA '_model.mat'], 'LP', 'options', 'solverParams', 'parameters');
    end
end

[minFlux, maxFlux] = deal(zeros(nRxnFVA, numel(GRvector)));
[minFD, maxFD] = deal(zeros(numel(rxnFluxList), nRxnFVA, numel(GRvector)));

%parallel computation
try
    p = gcp('nocreate');
    if isempty(p)
        if threads > 1
            %given explicit no. of threads
            parpool(ceil(threads));
        elseif threads ~= 1
            %default max no. of threads (input 0 or -1 etc)
            parpool;
        end
    end
catch
    %No parallel pool existent
end
%perform FVA at each growth rate
for j = 1:numel(GRvector)
    optionsJ = options;
    optionsJ.GR = GRvector(j);
    if ~isempty(saveFVA)
        optionsJ.saveFVA = sprintf(['%s_GR%.' num2str(kDisp) 'f'], saveFVA, GRvector(j));
    end
    [minFluxJ,maxFluxJ,minFDj,maxFDj,LP] = SteadyComFVAgr(modelCom,optionsJ, LP, varargin{:});
    minFlux(:, j) = minFluxJ;
    maxFlux(:, j) = maxFluxJ;
    minFD(:,:, j) = minFDj;
    maxFD(:,:, j) = maxFDj;
end

end


clear;
%% load file
gcp;                            % start cluster
addpath(genpath('utilities'));
addpath(genpath('deconvolution'));

nam = 'demoMovie.tif';          % insert path to tiff stack here
sframe=1;						% user input: first frame to read (optional, default 1)
num2read=2000;					% user input: how many frames to read   (optional, default until the end)
Y = read_file(nam,sframe,num2read);

%Y = Y - min(Y(:));
if ~isa(Y,'single');    Y = single(Y);  end         % convert to single

[d1,d2,T] = size(Y);                                % dimensions of dataset
d = d1*d2;                                          % total number of pixels

%% Set parameters

K = 40;                                           % number of components to be found
tau = 5;                                          % std of gaussian kernel (half size of neuron)
p = 2;

options = CNMFSetParms(...
    'd1',d1,'d2',d2,...                         % dimensionality of the FOV
    'p',p,...                                   % order of AR dynamics
    'gSig',tau,...                              % half size of neuron
    'merge_thr',0.80,...                        % merging threshold
    'nb',2,...                                  % number of background components
    'min_SNR',3,...                             % minimum SNR threshold
    'space_thresh',0.5,...                      % space correlation threshold
    'cnn_thr',0.2...                            % threshold for CNN classifier
    );
%% Data pre-processing

[P,Y] = preprocess_data(Y,p);
%% fast initialization of spatial components using greedyROI and HALS

[Ain,Cin,bin,fin,center] = initialize_components(Y,K,tau,options,P);  % initialize

% display centers of found components
Cn =  correlation_image(Y); %reshape(P.sn,d1,d2);  %max(Y,[],3); %std(Y,[],3); % image statistic (only for display purposes)
figure;imagesc(Cn);
    axis equal; axis tight; hold all;
    scatter(center(:,2),center(:,1),'mo');
    title('Center of ROIs found from initialization algorithm');
    drawnow;

    %% manually refine components (optional)
refine_components = false;  % flag for manual refinement
if refine_components
    [Ain,Cin,center] = manually_refine_components(Y,Ain,Cin,center,Cn,tau,options);
end

%% update spatial components
Yr = reshape(Y,d,T);
[A,b,Cin] = update_spatial_components(Yr,Cin,fin,[Ain,bin],P,options);

%% update temporal components
P.p = 0;    % set AR temporarily to zero for speed
[C,f,P,S,YrA] = update_temporal_components(Yr,A,b,Cin,fin,P,options);

%% classify components

rval_space = classify_comp_corr(Y,A,C,b,f,options);
ind_corr = rval_space > options.space_thresh;           % components that pass the correlation test
                                        % this test will keep processes

%% further classification with cnn_classifier
try  % matlab 2017b or later is needed
    [ind_cnn,value] = cnn_classifier(A,[d1,d2],'cnn_model',options.cnn_thr);
catch
    ind_cnn = true(size(A,2),1);                        % components that pass the CNN classifier
end

%% event exceptionality

fitness = compute_event_exceptionality(C+YrA,options.N_samples_exc,options.robust_std);
ind_exc = (fitness < options.min_fitness);

%% select components

keep = (ind_corr | ind_cnn) & ind_exc;

%% display kept and discarded components
A_keep = A(:,keep);
C_keep = C(keep,:);
figure;
    subplot(121); montage(extract_patch(A(:,keep),[d1,d2],[30,30]),'DisplayRange',[0,0.15]);
        title('Kept Components');
    subplot(122); montage(extract_patch(A(:,~keep),[d1,d2],[30,30]),'DisplayRange',[0,0.15])
        title('Discarded Components');
%% merge found components
[Am,Cm,K_m,merged_ROIs,Pm,Sm] = merge_components(Yr,A_keep,b,C_keep,f,P,S,options);

%%
display_merging = 1; % flag for displaying merging example
if and(display_merging, ~isempty(merged_ROIs))
    i = 1; %randi(length(merged_ROIs));
    ln = length(merged_ROIs{i});
    figure;
        set(gcf,'Position',[300,300,(ln+2)*300,300]);
        for j = 1:ln
            subplot(1,ln+2,j); imagesc(reshape(A_keep(:,merged_ROIs{i}(j)),d1,d2));
                title(sprintf('Component %i',j),'fontsize',16,'fontweight','bold'); axis equal; axis tight;
        end
        subplot(1,ln+2,ln+1); imagesc(reshape(Am(:,K_m-length(merged_ROIs)+i),d1,d2));
                title('Merged Component','fontsize',16,'fontweight','bold');axis equal; axis tight;
        subplot(1,ln+2,ln+2);
            plot(1:T,(diag(max(C_keep(merged_ROIs{i},:),[],2))\C_keep(merged_ROIs{i},:))');
            hold all; plot(1:T,Cm(K_m-length(merged_ROIs)+i,:)/max(Cm(K_m-length(merged_ROIs)+i,:)),'--k')
            title('Temporal Components','fontsize',16,'fontweight','bold')
        drawnow;
end

%% refine estimates excluding rejected components

Pm.p = p;    % restore AR value
[A2,b2,C2] = update_spatial_components(Yr,Cm,f,[Am,b],Pm,options);
[C2,f2,P2,S2,YrA2] = update_temporal_components(Yr,A2,b2,C2,f,Pm,options);

%% do some plotting

[A_or,C_or,S_or,P_or] = order_ROIs(A2,C2,S2,P2); % order components
K_m = size(C_or,1);
[C_df,~] = extract_DF_F(Yr,A_or,C_or,P_or,options); % extract DF/F values (optional)

figure;
[Coor,json_file] = plot_contours(A_or,Cn,options,1); % contour plot of spatial footprints
%savejson('jmesh',json_file,'filename');        % optional save json file with component coordinates (requires matlab json library)

%% display components

plot_components_GUI(Yr,A_or,C_or,b2,f2,Cn,options);

%% make movie
if (0)
    make_patch_video(A_or,C_or,b2,f2,Yr,Coor,options)
end


function vi = variablesPack(x,z)

d = size(x,2);
nx = size(x,1);
nz = size(z,1);

[x_indizes, z_indizes] = ocl.collocation.indizes(nx,nz,d);

vi = zeros(d*(nx+nz), 1);

vi(x_indizes) = x;
vi(z_indizes) = z;

end


%CHAR	create string representation of quaternion object

% Ryan Steindl based on Robotics Toolbox for MATLAB (v6 and v9)
%
% Copyright (C) 1993-2011, by Peter I. Corke
%
% This file is part of The Robotics Toolbox for MATLAB (RTB).
%
% RTB is free software: you can redistribute it and/or modify
% it under the terms of the GNU Lesser General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% (at your option) any later version.
%
% RTB 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 Lesser General Public License for more details.
%
% You should have received a copy of the GNU Leser General Public License
% along with RTB.  If not, see <http://www.gnu.org/licenses/>.
%
% http://www.petercorke.com

%	Copright (C) Peter Corke 1999
function s = char(q)

	s = [num2str(q.s), ' <' num2str(q.v(1)) ', ' num2str(q.v(2)) ', '   num2str(q.v(3)) '>'];


function test_suite = test_intersectRayPolygon3d
%TESTINTERSECTRAYPOLYGON3D  One-line description here, please.
%
%   output = testIntersectRayPolygon3d(input)
%
%   Example
%   testIntersectRayPolygon3d
%
%   See also
%
%
% ------
% Author: David Legland
% e-mail: david.legland@grignon.inra.fr
% Created: 2011-05-22,    using Matlab 7.9.0.529 (R2009b)
% Copyright 2011 INRA - Cepia Software Platform.

test_suite = functiontests(localfunctions);

function testTriangle(testCase) %#ok<*DEFNU>

pts3d = [3 0 0; 0 6 0;0 0 9];
line1 = [0 0 0 1 2 3];

inter = intersectRayPolygon3d(line1, pts3d);
exp = [1 2 3];
testCase.assertEqual(exp, inter);

line2 = [10 0 0 1 2 3];

[inter, inside] = intersectRayPolygon3d(line2, pts3d); %#ok<ASGLU>
testCase.assertFalse(inside);

function testReverseTriangle(testCase)

pts3d = [3 0 0; 0 6 0;0 0 9];
line1 = [0 0 0 1 2 3];

inter = intersectRayPolygon3d(line1, pts3d);
exp = [1 2 3];
testCase.assertEqual(exp, inter);

function testLineArray(testCase)

pts3d = [3 0 0;0 0 9; 0 6 0];
lines = [0 0 0 3 6 9 ; 10 0 0 3 6 9; 3 6 9 3 6 9];

[inters, inside] = intersectRayPolygon3d(lines, pts3d);
testCase.assertEqual(3, size(inters, 1));
testCase.assertEqual([true;false;false], inside);


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Spherical Harmonic Modeling and Analysis Toolkit (SPHARM-MAT) is a 3D
% shape modeling and analysis toolkit.
% It is a software package developed at Shenlab in Center for Neuroimaging,
% Indiana University (SpharmMat@gmail.com, http://www.iupui.edu/~shenlab/)
% It is available to the scientific community as copyright freeware
% under the terms of the GNU General Public Licence.
%
% Copyright 2009, 2010, ShenLab, Center for Neuroimaging, Indiana University
%
% This file is part of SPHARM-MAT.
%
% SPHARM-MAT 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.
%
% SPHARM-MAT 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 SPHARM-MAT. If not, see <http://www.gnu.org/licenses/>.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

disp('Ex0601: Perform T Test')

SpharmMatDir = '../';
CodeDir = [SpharmMatDir 'code'];
addpath(CodeDir);

DataFolder = 'data';

%% (1) Input data directory
InDataDir = [SpharmMatDir DataFolder '/Ex0601/hip06_reg'];

%% (2) List input file names
inFiles = dir([InDataDir '/*_reg.mat']); inNames={};
for i=1:length(inFiles)
    inNames{end+1} = [InDataDir '/' inFiles(i).name];
end

%% (3) Display input objects (optional)
    %Available values for Space- 'object';'param';'both'
dispConfs.Space = 'object';
    % Available values for Mesh- 'orig';'quad32';'quad64';'quad128';'quad256';'quad512';'icosa1';'icosa2'; ...
    %    'icosa3';'icosa4';'icosa5';'icosa6'
dispConfs.Mesh = 'quad64';
    % Available values for Shape- 'solid';'mesh';'both'
dispConfs.Shade = 'both';
    % Available values for Overlay- 'none';'adc_paramap'
dispConfs.Overlay = 'adc_paramap';
    % Available values for Export- 'screen';'png';'both'
dispConfs.Export = 'png';
dispConfs.Degree = [];
dispConfs.Template = '';

SpharmMatUtilDisplayObjs(dispConfs, inNames, CodeDir);

%% (4) Perform T Test
confs.Atlas = [SpharmMatDir DataFolder '/Ex0601/hip07_atlas/atlas.mat'];
confs.Smoothing_FWHM =5;
    % Available values for EqualVariance- 'Yes';'No'
confs.EqualVariance = 'Yes';
    % Available values for Signal-
    % 'vl_defm_org';'vl_defm_nrm';'vl_defm_pca';'vl_defm_fld'
confs.Signal = 'vl_defm_nrm';
    % Available values for SampleMesh-
    % 'icosa1';'icosa2';'icosa3';'icosa4';'icosa5';'icosa6'
confs.SampleMesh = 'icosa3';
confs.OutputNamePrefix = 't_map';
confs.GroupIDs = 'Ctrl,PT';
confs.OutDirectory = [SpharmMatDir DataFolder '/Ex0601/hip08_stat'];

if ~exist(confs.OutDirectory,'dir')
    mkdir(confs.OutDirectory);
end

inFiles = dir([InDataDir '/a*_reg.mat']); inNames1={};
for i=1:length(inFiles)
    inNames1{end+1} = [InDataDir '/' inFiles(i).name];
end

inFiles = dir([InDataDir '/b*_reg.mat']); inNames2={};
for i=1:length(inFiles)
    inNames2{end+1} = [InDataDir '/' inFiles(i).name];
end

SpharmMatStatAnalysis(confs, inNames1, inNames2, 't_map', CodeDir);

%% (5) Display output objects (optional)
dispConfs.Threshold_p_value = 0.05;
    % Available values for Overlay- 'p-value';'t-map'
dispConfs.Overlay = 'p-value';
    % Available values for Colormap-
    % 'jet';'hot';'summer';'cool';'autumn';'winter';'spring'
dispConfs.Colormap = 'jet';

inFile = dir([confs.OutDirectory '/t_map_*.mat']); inName={};
for i=1:length(inFile)
    inName{end+1} = [confs.OutDirectory '/' inFile(i).name];
end

h = figure('Name', 'Render Statistical Results','NumberTitle', 'off');
cameratoolbar(h, 'Show');
SpharmMatDisplayStat(dispConfs, inName, 'res_t_map', h, CodeDir);

dispConfs.Overlay = 't-map';

h = figure('Name', 'Render Statistical Results','NumberTitle', 'off');
cameratoolbar(h, 'Show');
SpharmMatDisplayStat(dispConfs, inName, 'res_t_map', h, CodeDir);

rmpath(CodeDir);

clear;


function PlotMap(cfig, map, path, scan, scanIdx)

world   = map.points;
scan = Transform(scan, path(:,end));

worldColor = [1 0 1];
scanColor = 'green';

% Plot
cfig(1); clf;
set(gca, 'color', [0,0,0]);
hold on; grid on; axis equal;

plot(world(:,1), world(:,2), '+', 'MarkerSize', 0.5, 'color', worldColor);
plot(scan(:,1),  scan(:,2),  '.', 'MarkerSize', 2, 'color', scanColor);
plot(path(1,:),  path(2,:),  '-.', 'LineWidth', 1, 'color', [0 1 1]);
for i = 1:20:length(scan)
    line([path(1,end), scan(i,1)], [path(2,end), scan(i,2)], 'color', [0.75 0.996 0.242]);
end
title(['Scan: ', num2str(scanIdx)]);
drawnow

% frame = getframe(cfig);
% cda = frame.cdata;
% frame.cdata = cda(30:1199, 270:2349, :);
% writeVideo(vip, frame);


function test_failed = test_libltfat_dgtreal_long(varargin)
test_failed = 0;

fprintf(' ===============  %s ================ \n',upper(mfilename));

definput.flags.complexity={'double','single'};
[flags]=ltfatarghelper({},definput,varargin);
dataPtr = [flags.complexity, 'Ptr'];

[~,~,enuminfo]=libltfatprotofile;
phaseconv = enuminfo.ltfat_phaseconvention;

fftwflags = struct('FFTW_MEASURE',0,'FFTW_ESTIMATE',64,'FFTW_PATIENT',32,'FFTW_DESTROY_INPUT',1,...
    'FFTW_UNALIGNED',2,'FFTW_EXHAUSTIVE',8,'FFTW_PRESERVE_INPUT',16);

Larr  = [350 350   9   1];
aarr  = [ 10  10   9   1];
Marr  = [ 35  35   3   1];
Warr  = [  1   3   3   1];

for idx = 1:numel(Larr)
    L = Larr(idx);
    W = Warr(idx);
    a = aarr(idx);
    M = Marr(idx);
    M2 = floor(M/2) + 1;

    N = L/a;

    g = randn(L,1,flags.complexity);
    c = cast(randn(M2,N,W)+1i*randn(M2,N,W),flags.complexity);
    cout = complex2interleaved(c);
    f = randn(L,W,flags.complexity);

    fPtr = libpointer(dataPtr,f);
    gPtr = libpointer(dataPtr,g);
    coutPtr = libpointer(dataPtr,cout);

    truec = dgtreal(f,g,a,M);

    funname = makelibraryname('dgtreal_long',flags.complexity,0);
    status = calllib('libltfat',funname,fPtr,gPtr,L,W,a,M,phaseconv.LTFAT_FREQINV,coutPtr);

    res = norm(reshape(truec,M2,N*W) - interleaved2complex(coutPtr.Value),'fro');
    [test_failed,fail]=ltfatdiditfail(res+status,test_failed);
    fprintf(['DGTREAL FREQINV       L:%3i, W:%3i, a:%3i, M:%3i %s %s %s\n'],L,W,a,M,flags.complexity,ltfatstatusstring(status),fail);

    truec = dgtreal(f,g,a,M,'timeinv');
    status = calllib('libltfat',funname,fPtr,gPtr,L,W,a,M,phaseconv.LTFAT_TIMEINV,coutPtr);

    res = norm(reshape(truec,M2,N*W) - interleaved2complex(coutPtr.Value),'fro');
    [test_failed,fail]=ltfatdiditfail(res+status,test_failed);
    fprintf(['DGTREAL TIMEINV       L:%3i, W:%3i, a:%3i, M:%3i %s %s %s\n'],L,W,a,M,flags.complexity,ltfatstatusstring(status),fail);

    % With plan
    c = cast(randn(M2,N,W)+1i*randn(M2,N,W),flags.complexity);
    cout = complex2interleaved(c);
    coutPtr = libpointer(dataPtr,cout);

    plan = libpointer();
    funname = makelibraryname('dgtreal_long_init',flags.complexity,0);
    statusInit = calllib('libltfat',funname,gPtr,L,W,a,M,fPtr,coutPtr,phaseconv.LTFAT_FREQINV,fftwflags.FFTW_MEASURE,plan);

    funname = makelibraryname('dgtreal_long_execute',flags.complexity,0);
    statusExecute = calllib('libltfat',funname,plan);

    funname = makelibraryname('dgtreal_long_done',flags.complexity,0);
    statusDone = calllib('libltfat',funname,plan);

    truec = dgtreal(f,g,a,M);
    res = norm(reshape(truec,M2,N*W) - interleaved2complex(coutPtr.Value),'fro');
    [test_failed,fail]=ltfatdiditfail(res+statusInit,test_failed);
    fprintf(['DGTREAL FREQINV WP    L:%3i, W:%3i, a:%3i, M:%3i %s %s %s\n'],L,W,a,M,flags.complexity,ltfatstatusstring(status),fail);

    %%%%%%
    c = cast(randn(M2,N,W)+1i*randn(M2,N,W),flags.complexity);
    cout = complex2interleaved(c);
    coutPtr = libpointer(dataPtr,cout);

    plan = libpointer();
    funname = makelibraryname('dgtreal_long_init',flags.complexity,0);
    statusInit = calllib('libltfat',funname,gPtr,L,W,a,M,fPtr,coutPtr,phaseconv.LTFAT_TIMEINV,fftwflags.FFTW_MEASURE,plan);

    funname = makelibraryname('dgtreal_long_execute',flags.complexity,0);
    statusExecute = calllib('libltfat',funname,plan);

    funname = makelibraryname('dgtreal_long_done',flags.complexity,0);
    statusDone = calllib('libltfat',funname,plan);

    truec = dgtreal(f,g,a,M,'timeinv');
    res = norm(reshape(truec,M2,N*W) - interleaved2complex(coutPtr.Value),'fro');
    [test_failed,fail]=ltfatdiditfail(res+statusInit,test_failed);
    fprintf(['DGTREAL TIMEINV WP    L:%3i, W:%3i, a:%3i, M:%3i %s %s %s\n'],L,W,a,M,flags.complexity,ltfatstatusstring(status),fail);

    % With plan, new array
    c = cast(randn(M2,N,W)+1i*randn(M2,N,W),flags.complexity);
    cout = complex2interleaved(c);
    coutPtr = libpointer(dataPtr,cout);

    plan = libpointer();
    nullPtr = libpointer();
    funname = makelibraryname('dgtreal_long_init',flags.complexity,0);
    statusInit = calllib('libltfat',funname,gPtr,L,W,a,M,nullPtr,nullPtr,phaseconv.LTFAT_FREQINV,fftwflags.FFTW_ESTIMATE,plan);

    funname = makelibraryname('dgtreal_long_execute_newarray',flags.complexity,0);
    statusExecute = calllib('libltfat',funname,plan,fPtr,coutPtr);

    funname = makelibraryname('dgtreal_long_done',flags.complexity,0);
    statusDone = calllib('libltfat',funname,plan);

    truec = dgtreal(f,g,a,M);
    res = norm(reshape(truec,M2,N*W) - interleaved2complex(coutPtr.Value),'fro');
    [test_failed,fail]=ltfatdiditfail(res+statusInit,test_failed);
    fprintf(['DGTREAL FREQINV WP NA L:%3i, W:%3i, a:%3i, M:%3i %s %s %s\n'],L,W,a,M,flags.complexity,ltfatstatusstring(status),fail);

    %%%%%%
    c = cast(randn(M2,N,W)+1i*randn(M2,N,W),flags.complexity);
    cout = complex2interleaved(c);
    coutPtr = libpointer(dataPtr,cout);

    plan = libpointer();
    funname = makelibraryname('dgtreal_long_init',flags.complexity,0);
    statusInit = calllib('libltfat',funname,gPtr,L,W,a,M,nullPtr,nullPtr,phaseconv.LTFAT_TIMEINV,fftwflags.FFTW_ESTIMATE,plan);

    funname = makelibraryname('dgtreal_long_execute_newarray',flags.complexity,0);
    statusExecute = calllib('libltfat',funname,plan,fPtr,coutPtr);

    funname = makelibraryname('dgtreal_long_done',flags.complexity,0);
    statusDone = calllib('libltfat',funname,plan);

    truec = dgtreal(f,g,a,M,'timeinv');
    res = norm(reshape(truec,M2,N*W) - interleaved2complex(coutPtr.Value),'fro');
    [test_failed,fail]=ltfatdiditfail(res+statusInit,test_failed);
    fprintf(['DGTREAL TIMEINV WP NA L:%3i, W:%3i, a:%3i, M:%3i %s %s %s\n'],L,W,a,M,flags.complexity,ltfatstatusstring(status),fail);
end


% MKLTRNLS  Solve a NLS using MKLTRNLS
%
% THIS IS A LOW LEVEL FUNCTION - USE opti_mkltrnls() INSTEAD!
%
% mktrnls uses the Intel MKL Trust Region NLS Solver.
%
%   [x,fval,exitflag,iter] = mktrnls(fun,grad,x0,ydata,lb,ub,opts)
%
%   Input arguments:
%       fun - nonlinear fitting function handle
%       grad - gradient of nonlinear fitting function handle (optional)
%       x0 - initial solution guess
%       ydata - fitting data
%       lb - decision variable lower bounds (optional)
%       ub - decision variable upper bounds (required if lb present)
%       opts - solver options (see below)
%
%   Return arguments:
%       x - solution vector
%       fval - objective value at the solution
%       exitflag - exit status (see below)
%       iter - number of iterations taken by the solver
%       feval - number of function evaluations used by the solver
%
%   Option Fields (all optional):
%       display - solver display level [0,1,2]
%       maxiter - maximum solver iterations
%       maxtime - maximum solver execution time
%       tolafun - absolute function tolerance
%       tolrfun - relative function tolerance
%       iterfun - Iteration Callback Function, stop = iterfun(iter,fval,x)
%
%   Return Status:
%       1 - optimal
%       0 - maximum iterations exceeded
%      -1 - could not converge / tolerance too small
%      -2 - singular or other error
%      -3 - unknown error
%
%
%
%   Copyright (C) 2012 Jonathan Currie (I2C2)


classdef ncs_wc_reader < handle
	properties
        sr
        max_segments
        raw_filename
        %channel
        opened_file
        recmin
        recmax
        num_scale_factor
        TimeStamps
        dt
    end
	methods
        function obj = ncs_wc_reader(par, raw_filename)
            obj.raw_filename = raw_filename;
            obj.opened_file = fopen(obj.raw_filename, 'r', 'l');
            fseek(obj.opened_file,16384,'bof');                       %Skip Header, put pointer to the first record
            TimeStamps = fread(obj.opened_file,inf,'int64',(4+4+4+2*512));          %Read all TimeStamps, in us
            fseek(obj.opened_file, 16384+8+4+4+4, 'bof');                             %put pointer to the beginning of data

            % conversion to datapoints
            tsdiff = diff(TimeStamps);
            obj.dt = median(tsdiff);
            min_dt = min(diff(TimeStamps));
            if min_dt<=0 %check for corrupt TimeStamps that would violate the monotonic increase
                warning('corrupt TimeStamps - attempting correction')
                ind = find(tsdiff <= 0); %corrupt TimeStamps are usually too low
                TimeStamps(ind+1) = TimeStamps(ind) + obj.dt;
                clear tsdiff;
                min_dt = min(diff(TimeStamps));
                if min_dt <= 0
                    error('Automated correction of corrupt TimeStamps failed - try manually!');
                end
            end

            %remove the following if statement to keep unix time in new
            %versions
            if TimeStamps(1) > 1e+15 %reset time to the beggining of the file
                TimeStamps = TimeStamps - TimeStamps(1);
            end
            obj.sr = 512*1e6/obj.dt;            % sampling rate (in Hz).
            time0 = TimeStamps(1);

            tmin = time0 + par.tmin*1e6; %min time to read (in micro-sec)
            index_tinitial = find(tmin > TimeStamps);
            if isempty(index_tinitial) == 1
                index_tinitial = 0;
            else
                index_tinitial = index_tinitial(end)-1;
            end

            if strcmp(par.tmax,'all')
                index_tfinal = length(TimeStamps);
                tmax = TimeStamps(end);
            else
                tmax = time0 + par.tmax*1e6;                   %max time to read (in micro-sec)
                index_tfinal = find(tmax < TimeStamps);
                if isempty(index_tfinal) ==1;
                    index_tfinal = length(TimeStamps);
                else
                    index_tfinal = index_tfinal(1);
                end
            end
            fseek(obj.opened_file,16384+8+4+4+4+index_tinitial*(8+4+4+4+2*512),'bof');

            lts = index_tfinal - index_tinitial;
            %Segments the data in par.segments pieces
            obj.max_segments = ceil((tmax - tmin)/ ...
                    (par.segments_length * 1e6 * 60));         %number of segments in which data is cutted
            segmentLength = floor (lts/obj.max_segments);

            tmin = 1 : segmentLength :lts;
            tmin = tmin(1:obj.max_segments);
            tmax = tmin - 1;
            tmax = tmax (2:end);
            tmax = [tmax, lts];
            obj.recmax = tmax;
            obj.recmin = tmin;
            obj.TimeStamps = TimeStamps;
            header = textread(obj.raw_filename,'%s',50);
			obj.num_scale_factor = 1e6 * str2num(header{find(strcmp('-ADBitVolts', header))+1});
			%for older versions:
            %scale_factor = textread(obj.raw_filename,'%s',43);
			%if(str2num(scale_factor{41})*1e6 > 0.5)
            %    obj.num_scale_factor = 1e6 * str2num(scale_factor{43}); %for the new CSC format
            %else
            %    obj.num_scale_factor = 1e6 * str2num(scale_factor{41}); %for the old CSC format
            %end

        end

        function [sr,max_segments,with_raw,with_spikes] = get_info(obj)
        	sr = obj.sr;
            max_segments = obj.max_segments;
            with_raw = true;
            with_spikes = false;
        end

        function index_ts = index2ts(obj,index,i)
            index_ts = (obj.TimeStamps(obj.recmin(i)+floor(index/512))'+mod(index,512)/512*obj.dt)/1000;
        end

        function x = get_segment(obj,i)

            Samples = fread(obj.opened_file,512*(obj.recmax(i)- ...
                obj.recmin(i)+1),'512*int16=>int16',8+4+4+4); %the recmax are in timestamps indexs

            x = double(Samples(:))' *  obj.num_scale_factor;

            if i == obj.max_segments
                fclose(obj.opened_file);
            end
        end
    end
end


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% MRiLab auto generated file: DO NOT EDIT!     %
% Generated by MRiLab "DoWriteXML2m" Generator %
% MRiLab Version 1.3                           %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [Obj, VObj]=VObj_SphereHead(flag)
%====================================
VObj.Gyro=[267538030.3797];
AttributeOpt={'Normal','MT','ME','GM'};
VObj.Model=AttributeOpt{1};
VObj.Name='VObj_SphereHead';
VObj.Notes='3D Sphere Head Object';
VObj.Type='Water';
VObj.TypeNum=[1];
VObj.XDim=[90];
VObj.XDimRes=[0.002];
VObj.YDim=[90];
VObj.YDimRes=[0.002];
VObj.ZDim=[90];
VObj.ZDimRes=[0.002];
[x,y,z]=meshgrid(((-(VObj.XDim-1)/2):((VObj.XDim-1)/2))*VObj.XDimRes,((-(VObj.YDim-1)/2):((VObj.YDim-1)/2))*VObj.YDimRes,((-(VObj.ZDim-1)/2):((VObj.ZDim-1)/2))*VObj.ZDimRes);
rho=zeros([size(x) VObj.TypeNum]);
t1=zeros([size(x) VObj.TypeNum]);
t2=zeros([size(x) VObj.TypeNum]);
t2star=zeros([size(x) VObj.TypeNum]);
chemshift=zeros(1,VObj.TypeNum);
typeflag=zeros(1,VObj.TypeNum);
lineshapeflag=zeros(1,VObj.TypeNum);
k=zeros([size(x) VObj.TypeNum^2]);
econ=zeros([size(x) 3]);
massden=zeros(size(x));
%====================================
Alpha=[1];
Color='b';
Notes='Cube Object';
p.CenterX=[0.015];
p.CenterY=[0.015];
p.CenterZ=[0.015];
p.Length=[0.03];
ChemShift=[0];
ECon=[0.5 0.5 0.5];
K=[10];
LineShapeFlag=[0];
MassDen=[1000];
Rho=[1];
T1=[1];
T2=[0.1];
T2Star=[0.01];
TypeFlag=[0];
TypeIdx=[1];
[mask, fvc]=VObjCube(p,x,y,z,flag);
Obj{1,1}=fvc;
Obj{2,1}=Color;
Obj{3,1}=Alpha;
rho(:,:,:,TypeIdx)=rho(:,:,:,TypeIdx)+mask.*Rho;
t1(:,:,:,TypeIdx)=t1(:,:,:,TypeIdx)+mask.*T1;
t2(:,:,:,TypeIdx)=t2(:,:,:,TypeIdx)+mask.*T2;
t2star(:,:,:,TypeIdx)=t2star(:,:,:,TypeIdx)+mask.*T2Star;
massden=massden+mask.*MassDen;
econ(:,:,:,1)=econ(:,:,:,1)+mask.*ECon(1);
econ(:,:,:,2)=econ(:,:,:,2)+mask.*ECon(2);
econ(:,:,:,3)=econ(:,:,:,3)+mask.*ECon(3);
chemshift(TypeIdx)=ChemShift;
typeflag(TypeIdx)=TypeFlag;
lineshapeflag(TypeIdx)=LineShapeFlag;
if ~strcmp(VObj.Model,'Normal')
for i=1:VObj.TypeNum
k(:,:,:,(TypeIdx-1)*VObj.TypeNum+i)=k(:,:,:,(TypeIdx-1)*VObj.TypeNum+i)+mask.*K(i);
end
end
p=[];
%--------------------
Alpha=[1];
Color='r';
Notes='Sphere Object';
p.CenterX=[0];
p.CenterY=[-0.04];
p.CenterZ=[0];
p.FaceNum=[20];
p.Radius=[0.04];
ChemShift=[0];
ECon=[0.5 0.5 0.5];
K=[10];
LineShapeFlag=[0];
MassDen=[1000];
Rho=[1];
T1=[1];
T2=[0.1];
T2Star=[0.01];
TypeFlag=[0];
TypeIdx=[1];
[mask, fvc]=VObjSphere(p,x,y,z,flag);
Obj{1,2}=fvc;
Obj{2,2}=Color;
Obj{3,2}=Alpha;
rho(:,:,:,TypeIdx)=rho(:,:,:,TypeIdx)+mask.*Rho;
t1(:,:,:,TypeIdx)=t1(:,:,:,TypeIdx)+mask.*T1;
t2(:,:,:,TypeIdx)=t2(:,:,:,TypeIdx)+mask.*T2;
t2star(:,:,:,TypeIdx)=t2star(:,:,:,TypeIdx)+mask.*T2Star;
massden=massden+mask.*MassDen;
econ(:,:,:,1)=econ(:,:,:,1)+mask.*ECon(1);
econ(:,:,:,2)=econ(:,:,:,2)+mask.*ECon(2);
econ(:,:,:,3)=econ(:,:,:,3)+mask.*ECon(3);
chemshift(TypeIdx)=ChemShift;
typeflag(TypeIdx)=TypeFlag;
lineshapeflag(TypeIdx)=LineShapeFlag;
if ~strcmp(VObj.Model,'Normal')
for i=1:VObj.TypeNum
k(:,:,:,(TypeIdx-1)*VObj.TypeNum+i)=k(:,:,:,(TypeIdx-1)*VObj.TypeNum+i)+mask.*K(i);
end
end
p=[];
%--------------------
VObj.Rho=rho;
VObj.T1=t1;
VObj.T2=t2;
VObj.T2Star=t2star;
VObj.ECon=econ;
VObj.MassDen=massden;
VObj.ChemShift=chemshift;
if strcmp(VObj.Model,'GM')
k=reshape(k,[size(x), VObj.TypeNum, VObj.TypeNum]);
VObj.TypeFlag=typeflag;
VObj.LineShapeFlag=lineshapeflag;
end
if ~strcmp(VObj.Model,'Normal')
VObj.K=k;
end
end


% Function to estimate the glottal parameters: NAQ, QOQ, H1-H2, HRF and PSP
%
% Octave compatible
%
% Description
%  This function can be used to estimate a range of conventional glottal
%  source parameters often used in the literature. This includes: the
%  normalised amplitude quotient (NAQ), the quasi-open quotient (QOQ), the
%  difference in amplitude of the first two harmonics of the differentiated
%  glottal source spectrum (H1-H2), the harmonic richness factor (HRF) and
%  the parabolic spectral parameter (PSP)
%
% Inputs
%  gf       : [samples] [Nx1] Glottal flow estimation
%  gfd      : [samples] [Nx1] Glottal flow derivative estimation
%  fs       : [Hz]      [1x1] sampling frequency
%  GCI      : [s]       [Mx1] Glottal closure instants
%
% Outputs
%  NAQ      : [s,samples] [Mx2] Normalised amplitude quotient
%  QOQ      : [s,samples] [Mx2] Quasi-open quotient
%  H1H2     : [s,dB]      [Mx2] Difference in glottal harmonic amplitude
%  HRF      : [s,samples] [Mx2] Harmonic richness factor
%  PSP      : [s,samples] [Mx2] Parabolic spectral parameter
%
% Example
%  Please see the HOWTO_glottalsource.m example file.
%
% References
%  [1] Alku, P., B ackstrom, T., and Vilkman, E. Normalized amplitude quotient
%     for parameterization of the glottal flow. Journal of the Acoustical
%     Society of America, 112(2):701–710, 2002.
%  [2] Hacki, T. Klassifizierung von glottisdysfunktionen mit hilfe der
%     elektroglottographie. Folia Phoniatrica, pages 43–48, 1989.
%  [3] Alku, P., Strik, H., and Vilkman, E. Parabolic spectral parameter -
%     A new method for quantification of the glottal flow. Speech
%     Communication, 22(1):67–79, 1997.
%  [4] Hanson, H. M. Glottal characteristics of female speakers: Acoustic
%     correlates. Journal of the Acoustical Society of America,
%     10(1):466–481, 1997.
%  [5] Childers, D. G. and Lee, C. K. Voice quality factors: Analysis,
%     synthesis and perception. Journal of the Acoustical Society of
%     America, 90(5):2394–2410, 1991.
%
% Copyright (c) 2013 Trinity College Dublin
%
% License
%  This file is under the LGPL license,  you can
%  redistribute it and/or modify it under the terms of the GNU Lesser General
%  Public License as published by the Free Software Foundation, either version 3
%  of the License, or (at your option) any later version. This file 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 Lesser General Public License for more
%  details.
%
% This function is part of the Covarep project: http://covarep.github.io/covarep
%
% Author
%  John Kane kanejo@tcd.ie

function [NAQ,QOQ,H1H2,HRF,PSP] = get_vq_params(gf,gfd,fs,GCI)

GCI = round(GCI*fs)+1;

%% Initial settings
F0min=20;
F0max=500;
NAQ=zeros(1,length(GCI));
QOQ=zeros(1,length(GCI));
H1H2=zeros(1,length(GCI));
HRF=zeros(1,length(GCI));
PSP=zeros(1,length(GCI));

glot_shift=round(0.5/1000*fs);
qoq_level=0.5; % Threshold for QOQ estimation
T0_num=3; % Number of local glottal pulses to be used for harmonic spectrum
min_harm_num=5;
HRF_freq_max=5000; % Maximum frequency used for harmonic measurement
PSP_fft_size=2048; % As per Alku et al (1997)

%% Do processing
for n=1:length(GCI)

    % Get glottal pulse compensated for zero-line drift
    if n==1
        start=1;
        stop=GCI(n);
        T0=GCI(n+1)-GCI(n);
    else start=GCI(n-1);
        stop=GCI(n);
        T0=GCI(n)-GCI(n-1);
    end
    F0=fs/T0;

    if isinf(F0)==0 && T0~=0 && F0 > F0min && F0<F0max

        % Compensate for zero-line drift in the glottal flow pulse
        gf_comb=[gf(start) gf(stop)];
        if start~=stop
            if length(gf_comb)>1
                line=interp1(linspace(1,stop-start+1,2),gf_comb, ...
                    1:stop-start+1);
            else line=gf_comb;
            end
        else line=0;
        end
        gf_seg=gf(start:stop);
        gf_seg_comp=gf_seg(:)-line(:);

        if stop+glot_shift <= length(gfd)
            stop2=stop+glot_shift;
        else
            stop2=stop;
        end
        gfd_seg=gfd(start:stop2);

        % Get NAQ and QOQ
        d_peak = max(abs(gfd_seg));
        [f_ac,max_idx] = max(gf_seg_comp);
        Amid=f_ac*qoq_level;
        [T1,T2] = findAmid_t(gf_seg_comp,Amid,max_idx);

        NAQ(n) = (f_ac/d_peak)/T0;
        QOQ(n)=(T2-T1)/(fs/F0);

         % Get frame positions for H1-H2 parameter
        if GCI(n)-round((T0*T0_num)/2) > 0
            f_start = GCI(n)-round((T0*T0_num)/2);
        else f_start=1;
        end
        if GCI(n)+round((T0*T0_num)/2) <= length(gfd)
            f_stop = GCI(n)+round((T0*T0_num)/2);
        else f_stop=length(gfd);
        end
        f_frame=gfd(f_start:f_stop);
        f_frame=f_frame.*(2^15);
        f_win=f_frame(:).*hamming(length(f_frame));
        f_spec = mag2db(abs(fft(f_win,fs)));

         % Get H1-H2/HRF measurements
        [h_idx,h_amp]=v_findpeaks(f_spec,[],F0/2);
        HRF_harm_num=floor(HRF_freq_max/F0);
        if length(h_idx) >= min_harm_num
            [~,f0_idx] = min(abs(bsxfun(@minus,h_idx,(1:HRF_harm_num)*F0)),[],1);
            H1H2(n)=h_amp(f0_idx(1))-h_amp(f0_idx(2));
            HRF(n) = sum(h_amp(f0_idx(2:end)))/h_amp(f0_idx(1));
        end

        % Get PSP
        if n>1
           start=GCI(n-1);
           stop=GCI(n);
           gf_seg=gf(start:stop);
           gf_seg=gf_seg-min(gf_seg); % Adjust minimum to zero
           gf_seg=gf_seg/max(gf_seg); % Scale to unity
           gf_seg(PSP_fft_size)=0; % Zero-pad
           X = mag2db(abs(fft(gf_seg))); % Spectrum
           X = X(linspace(1,fs,length(X))<=fs/2); % Use up to the nyquist
           a = psp_get_a(X);

           % Estimate measure of maximal spectral decay
           a_max_sig=1/round(fs/F0)*ones(1,round(fs/F0));
           a_max_sig(PSP_fft_size)=0; % Zero-pad
           X_a_max = mag2db(abs(fft(a_max_sig))); % Spectrum
           X_a_max = X_a_max(linspace(1,fs,length(X_a_max))<=fs/2); % Use up to the nyquist
           a_max = psp_get_a(X_a_max);

           PSP(n)=a/a_max;
        end

    end

end

%% Add in time to parameters
NAQ=[GCI(:)/fs NAQ(:)];
QOQ=[GCI(:)/fs QOQ(:)];
H1H2=[GCI(:)/fs H1H2(:)];
HRF=[GCI(:)/fs HRF(:)];
PSP=[GCI(:)/fs PSP(:)];

function [T1,T2] = findAmid_t(glot_adj,Amid,Tz)

% Function to find the start and stop positions of the quasi-open phase.

T1=0;
T2=0;
if Tz~=0
    n=Tz;

    while glot_adj(n) > Amid && n > 3
        n=n-1;
    end
    T1=n;
    n=Tz;

    while glot_adj(n) > Amid && n < length(glot_adj)-2
        n=n+1;
    end
    T2=n;
end

function a = psp_get_a(X)

% Function to calculate a coefficient, as per section 3.2.1 in Alku et al
% (1997)

%% Initial settings
NE_min = 0.01;
flag = 0;
N = 3; % An set to an initial value of 3, as per Alku et al (1997)
X=X(:);

% pre-allocate&compute
X2=X.^2;
k2 = ((0:100).^2)';

% init sum
k2_sum=sum(k2(1:N-1));
k4_sum=sum(k2(1:N-1).^2);
X_k1_sum = sum(X(1:N-1));
X_k1_k2_sum = sum(X((1:N-1)).*k2(1:N-1));
X2_k1_sum = sum(X2((1:N-1)));

%% Do processing
while flag == 0

    % re-allocate if necessary
    if N>numel(k2)
        k2 = ((0:numel(k2)*2).^2)';
    end

    % iteratively built sum
    k2_sum= k2_sum + k2(N);
    k4_sum= k4_sum + k2(N)*k2(N);
    X_k1_sum = X_k1_sum + X(N);
    X_k1_k2_sum = X_k1_k2_sum + X(N)*k2(N);
    X2_k1_sum = X2_k1_sum + X2(N);

    % Equation 5
    a = (N*X_k1_k2_sum-X_k1_sum.*k2_sum)/(N*k4_sum-k2_sum.^2);

    X_k1_a_k2 = X(1:N)-a*k2(1:N);

    % Equation 4
    b = 1/N*sum(X_k1_a_k2);
    NE = (sum((X_k1_a_k2-b).^2))/X2_k1_sum;

    % Increment or stop
    if NE < NE_min
        N = N+1;
    else
        flag = 1;
    end
end


function startNodeIdx = flatFindStartNodeIndex(mesh)
%
%  startNodeIdx = flatFindStartNodeIndex(mesh)
%
%Author: Wandell
%Purpose:
%  Use the data in the flat.mat type file for the mesh and determine the
%  index into the start node used for flattening the Layer 0 (white matter
%  boundary) mesh.
%
%  Called from flatAdjustSpacing

% We need to make sure that the inputs to assignToNearest each have 3 rows
if ~isequal(size(mesh.uniqueVertices, 1), 3),
    mesh.uniqueVertices = mesh.uniqueVertices';
end
if ~isequal(size(mesh.startCoords, 1), 3),
    mesh.startCoords = mesh.startCoords';
end

% This is the line used in mrFlatMesh.  There it is used before we have
% clipped down the matrices.
[startNodeIdx,snDist]=assignToNearest(mesh.uniqueVertices,mesh.startCoords);

% Not sure this is the right function.
% l = find(ismember(round(mesh.uniqueVertices),mesh.startCoords,'rows'));
% startNodeIdx = l(1);

return;


function featureS = calcRadiomicsForImgType(volOrig3M,maskBoundingBox3M,paramS,gridS)
%calcRadiomicsForImgType.m
%Derive user-defined image type and extract radiomics features.
%
%AI 3/28/19
%AI 5/1/19    Turned off flag for diffAvg since this is equivalent to dissimilarity.

% Voxel volume for Total Energy calculation
xValsV = gridS.xValsV;
yValsV = gridS.yValsV;
zValsV = gridS.zValsV;
PixelSpacingX = gridS.PixelSpacingV(1);
PixelSpacingY = gridS.PixelSpacingV(2);
PixelSpacingZ = gridS.PixelSpacingV(3);
VoxelVol = PixelSpacingX*PixelSpacingY*PixelSpacingZ*1000; % convert cm to mm

whichFeatS = paramS.whichFeatS;
featureS = struct;

% Get image types with various parameters
fieldNamC = fieldnames(paramS.imageType);
imageTypeC = {};
for iImg = 1:length(fieldNamC)
    for iFilt = 1:length(paramS.imageType.(fieldNamC{iImg}))
        filtParamS = struct();
        filtParamS.imageType = fieldNamC{iImg};
        filtParamS.paramS = paramS.imageType.(fieldNamC{iImg})(iFilt);
        imageTypeC{end+1} = filtParamS;
    end
end

% ---- Calc. shape features (same across img. types) ----
tic
if whichFeatS.shape.flag
    rcsV = [];
    if isfield(paramS,'shapeParamS') && isfield(paramS.shapeParamS,'rcs')
        rcsV = paramS.shapeParamS.rcs.';
    end
    featureS.shapeS = getShapeParams(maskBoundingBox3M, ...
        {xValsV, yValsV, zValsV},rcsV);
end
toc

%% Loop over image types
maskOrig3M = maskBoundingBox3M;
for k = 1:length(imageTypeC)

    %Generate volume based on original/derived imageType
    if strcmpi(imageTypeC{k}.imageType,'original')
        if isfield(paramS,'toQuantizeFlag')
            quantizeFlag = paramS.toQuantizeFlag;
        else
            quantizeFlag = 0;
        end
        minClipIntensity = [];
        maxClipIntensity = [];
        if isfield(paramS,'textureParamS') && ...
                isfield(paramS.textureParamS,'minClipIntensity')
            minClipIntensity = paramS.textureParamS.minClipIntensity;
        end
        if isfield(paramS,'textureParamS') && ...
                isfield(paramS.textureParamS,'maxClipIntensity')
            maxClipIntensity = paramS.textureParamS.maxClipIntensity;
        end
        volToEval = volOrig3M;
        [minr, maxr, minc, maxc, mins, maxs] = compute_boundingbox(maskBoundingBox3M);
        maskBoundingBox3M = maskOrig3M(minr:maxr, minc:maxc, mins:maxs);
        volToEval = volToEval(minr:maxr, minc:maxc, mins:maxs);
    else
        %Add voxel size in mm to paramS
        voxSizV = [PixelSpacingX, PixelSpacingY, PixelSpacingZ]*10; %convert cm to mm
        imageTypeC{k}.paramS.VoxelSize_mm.val = voxSizV;
        if paramS.whichFeatS.padding.flag && ...
                ~strcmpi(whichFeatS.padding.method,'none')
            paddingSizeV = paramS.whichFeatS.padding.size;
            if length(paddingSizeV)==2
                paddingSizeV(end+1) = 0;
            end
            imageTypeC{k}.paramS.padding.size = paddingSizeV;
        else
            imageTypeC{k}.paramS.padding.size = [0,0,0];
        end

        outS = processImage(imageTypeC{k}.imageType,volOrig3M,maskOrig3M,...
            imageTypeC{k}.paramS);
        [minr, maxr, minc, maxc, mins, maxs] = compute_boundingbox(maskOrig3M);
        maskBoundingBox3M = maskOrig3M(minr:maxr, minc:maxc, mins:maxs);
        derivedImgName = fieldnames(outS);
        volToEval = outS.(derivedImgName{1});
        volToEval = volToEval(minr:maxr, minc:maxc, mins:maxs);
        if whichFeatS.texture.flag
            % always quantize the derived image for texture calc.
            quantizeFlag = true;
        else
            if isfield(paramS,'toQuantizeFlag')
                quantizeFlag = paramS.toQuantizeFlag;
            else
                quantizeFlag = false;
            end
        end
        minClipIntensity = []; % no clipping imposed for derived images
        maxClipIntensity = [];
        if isfield(paramS,'textureParamS') && ...
                isfield(paramS.textureParamS,'minClipIntensity')
            minClipIntensity = paramS.textureParamS.minClipIntensity;
        end
        if isfield(paramS,'textureParamS') && ...
                isfield(paramS.textureParamS,'maxClipIntensity')
            maxClipIntensity = paramS.textureParamS.maxClipIntensity;
        end
    end

    % Volume without NaNs for Peak/Valley computation
    volToEvalOrigM = volToEval;

    % Quantize the volume of interest
    if quantizeFlag
        numGrLevels = [];
        binwidth = [];

        if isfield(paramS,'textureParamS')
            if isfield(paramS.textureParamS,'numGrLevels')
                numGrLevels = paramS.textureParamS.numGrLevels;
            end
            if isfield(imageTypeC{k}.paramS,'textureParamS') && ...
                    isfield(imageTypeC{k}.paramS.textureParamS,'numGrLevels')
                numGrLevels = imageTypeC{k}.paramS.textureParamS.numGrLevels;
            end
            if isfield(paramS.textureParamS,'binwidth')
                binwidth = paramS.textureParamS.binwidth;
            end
            if isfield(imageTypeC{k}.paramS,'textureParamS') &&...
                    isfield(imageTypeC{k}.paramS.textureParamS,'binwidth')
                binwidth = imageTypeC{k}.paramS.textureParamS.binwidth;
            end
        end

        % Don't use intensities outside the ROI in discretization
        volToEval(~maskBoundingBox3M) = NaN;
        quantizedM = imquantize_cerr(volToEval,numGrLevels,...
            minClipIntensity,maxClipIntensity,binwidth);
        % Reassign the number of gray levels in case they were computed for the
        % passed binwidth
        numGrLevels = max(quantizedM(:));
        %paramS.textureParamS.numGrLevels = numGrLevels;

    else
        quantizedM = volToEval;
    end

    quantizedM(~maskBoundingBox3M) = NaN;
    numVoxels = sum(~isnan(quantizedM(:)));

    %Feature calculation
    outFieldName = createFieldNameFromParameters...
        (imageTypeC{k}.imageType,imageTypeC{k}.paramS);

    % --- 1. First-order features ---
    if whichFeatS.firstOrder.flag
        binWidthEntropy = [];
        binNumEntropy = [];
        offsetForEnergy = 0;
        if isfield(paramS.firstOrderParamS,'offsetForEnergy')
            offsetForEnergy = paramS.firstOrderParamS.offsetForEnergy;
        end
        if isfield(paramS.firstOrderParamS,'binWidthEntropy') && ...
            ~isempty(paramS.firstOrderParamS.offsetForEnergy)
            binWidthEntropy = paramS.firstOrderParamS.binWidthEntropy;
        end
        if isfield(paramS.firstOrderParamS,'binNumEntropy') && ...
                ~isempty(paramS.firstOrderParamS.binNumEntropy)
            binNumEntropy = paramS.firstOrderParamS.binNumEntropy;
        end
        if isfield(imageTypeC{k}.paramS,'firstOrderParamS') && ...
                isfield(imageTypeC{k}.paramS.firstOrderParamS,'offsetForEnergy')
            offsetForEnergy = imageTypeC{k}.paramS.firstOrderParamS.offsetForEnergy;
        end
        if isfield(imageTypeC{k}.paramS,'binWidthEntropy') && ...
                isfield(imageTypeC{k}.paramS.firstOrderParamS,'binWidthEntropy')
            binWidthEntropy = imageTypeC{k}.paramS.firstOrderParamS.binWidthEntropy;
        end
        if isfield(imageTypeC{k}.paramS,'binNumEntropy') && ...
                isfield(imageTypeC{k}.paramS.firstOrderParamS,'binNumEntropy')
            binNumEntropy = imageTypeC{k}.paramS.firstOrderParamS.binNumEntropy;
        end
        volV = volToEval(logical(maskBoundingBox3M));
        volV = volV(:);
        featureS.(outFieldName).firstOrderS = radiomics_first_order_stats...
            (volV, VoxelVol,offsetForEnergy,binWidthEntropy,binNumEntropy);
    end

    %---2. Higher-order (texture) features ----

    %Get directionality and avg type
    if any([whichFeatS.glcm.flag,whichFeatS.glrlm.flag,whichFeatS.gtdm.flag,...
            whichFeatS.gldm.flag,whichFeatS.glszm.flag])

        directionality = paramS.textureParamS.directionality;
        avgType = paramS.textureParamS.avgType;
        switch lower(directionality)
            case '2d'
                dirctn = 2;
            case '3d'
                dirctn = 1;
            otherwise
                error('Invalid input. Directionality must be "2D" or "3D"');
        end

        switch lower(avgType)
            case 'texturematrix'
                %Haralick features with combined cooccurrence matrix
                cooccurType = 1;
            case 'feature'
                %'Haralick features from separate cooccurrence matrix per direction, averaged'
                cooccurType = 2;
            otherwise
                error('Invalid input. Directionality must be "2D" or "3D"');
        end

        %numGrLevels = paramS.textureParamS.numGrLevels;
        voxelOffset = paramS.textureParamS.voxelOffset;

        % a. GLCM
        if whichFeatS.glcm.flag

            featC = whichFeatS.glcm.featureList;
            glcmFlagS = getHaralickFlags(featC);
            featureS.(outFieldName).glcmFeatS = get_haralick(dirctn, voxelOffset, cooccurType, quantizedM, ...
                numGrLevels, glcmFlagS);

        end

        % b. GLRLM
        if whichFeatS.glrlm.flag
            featC = whichFeatS.glrlm.featureList;
            rlmFlagS = getRunLengthFlags(featC);
            rlmType = cooccurType;
            featureS.(outFieldName).rlmFeatS = get_rlm(dirctn, rlmType, quantizedM, ...
                numGrLevels, numVoxels, rlmFlagS);
        end

        %c. GTDM
        if whichFeatS.gldm.flag
            patchRadiusV = paramS.textureParamS.patchRadiusVox;
            [s,p] = calcNGTDM(quantizedM, patchRadiusV, ...
                numGrLevels);
            featureS.(outFieldName).ngtdmFeatS = ngtdmToScalarFeatures(s,p,numVoxels);
        end

        %d. GLDM
        if whichFeatS.gldm.flag
            patchRadiusV = paramS.textureParamS.patchRadiusVox;
            imgDiffThresh = paramS.textureParamS.imgDiffThresh;
            ngldM = calcNGLDM(quantizedM, patchRadiusV,numGrLevels,imgDiffThresh);
            featureS.(outFieldName).ngldmFeatS = ngldmToScalarFeatures(ngldM,numVoxels);
        end

        %e. GLSZM
        if whichFeatS.glszm.flag
            featC = whichFeatS.glszm.featureList;
            szmFlagS = getSizeZoneFlags(featC);
            szmType = dirctn; % 1: 3d, 2: 2d
            szmM = calcSZM(quantizedM, numGrLevels, szmType);
            numVoxels = sum(~isnan(quantizedM(:)));
            featureS.(outFieldName).szmFeatS = szmToScalarFeatures(szmM,numVoxels, szmFlagS);
        end

        %f. Peak-valley
        if whichFeatS.peakValley.flag
            radiusV = paramS.peakValleyParamS.peakRadius;
            units = paramS.peakValleyParamS.units; %'cm' or 'vox'
            featureS.(outFieldName).peakValleyFeatureS = getImPeakValley(maskBoundingBox3M,...
                volToEvalOrigM, radiusV, units);
        end

        %g. IVH
        if whichFeatS.ivh.flag
            IVHBinWidth = paramS.ivhParamS.binwidth; %IVH binwidth
            xForIxV = paramS.ivhParamS.xForIxPct; % percentage volume
            xAbsForIxV = paramS.ivhParamS.xForIxCc; % absolute volume [cc]
            xForVxV = paramS.ivhParamS.xForVxPct; % percent intensity cutoff
            xAbsForVxV = paramS.ivhParamS.xForVxAbs; % absolute intensity cutoff [HU]
            scanV = double(volToEval(maskBoundingBox3M));
            volV = repmat(VoxelVol,numel(scanV),1);
            featureS.(outFieldName).ivhFeaturesS = getIvhParams(scanV, volV, IVHBinWidth,...
                xForIxV, xAbsForIxV, xForVxV, xAbsForVxV);

        end
    end

end

%% -------------- Sub-functions ----------------------------
    function glcmFlagS = getHaralickFlags(varargin)

        %Default: all features
        glcmFlagS.energy = 1;
        glcmFlagS.jointEntropy = 1;
        glcmFlagS.jointMax = 1;
        glcmFlagS.jointAvg = 1;
        glcmFlagS.jointVar = 1;
        glcmFlagS.contrast = 1;
        glcmFlagS.invDiffMoment = 1;
        glcmFlagS.sumAvg = 1;
        glcmFlagS.corr = 1;
        glcmFlagS.clustShade = 1;
        glcmFlagS.clustProm = 1;
        glcmFlagS.haralickCorr = 1;
        glcmFlagS.invDiffMomNorm = 1;
        glcmFlagS.invDiff = 1;
        glcmFlagS.invDiffNorm = 1;
        glcmFlagS.invVar = 1;
        glcmFlagS.dissimilarity = 1;
        glcmFlagS.diffEntropy = 1;
        glcmFlagS.diffVar = 1;
        glcmFlagS.diffAvg = 0;  %Equivalent to dissimilarity
        glcmFlagS.sumVar = 1;
        glcmFlagS.sumEntropy = 1;
        glcmFlagS.clustTendency = 1;
        glcmFlagS.autoCorr = 1;
        glcmFlagS.invDiffMomNorm = 1;
        glcmFlagS.firstInfCorr = 1;
        glcmFlagS.secondInfCorr = 1;

        if nargin==1 && ~strcmpi(varargin{1},'all')
            featureC = varargin{1};
            allFeatC = fieldnames(glcmFlagS);
            idxV = find(~ismember(allFeatC,featureC));
            for n = 1:length(idxV)
                glcmFlagS.(allFeatC{idxV(n)})  = 0;
            end
        end

    end

    function rlmFlagS = getRunLengthFlags(varargin)

        rlmFlagS.shortRunEmphasis = 1;
        rlmFlagS.longRunEmphasis = 1;
        rlmFlagS.grayLevelNonUniformity = 1;
        rlmFlagS.grayLevelNonUniformityNorm = 1;
        rlmFlagS.runLengthNonUniformity = 1;
        rlmFlagS.runLengthNonUniformityNorm = 1;
        rlmFlagS.runPercentage = 1;
        rlmFlagS.lowGrayLevelRunEmphasis = 1;
        rlmFlagS.highGrayLevelRunEmphasis = 1;
        rlmFlagS.shortRunLowGrayLevelEmphasis = 1;
        rlmFlagS.shortRunHighGrayLevelEmphasis = 1;
        rlmFlagS.longRunLowGrayLevelEmphasis = 1;
        rlmFlagS.longRunHighGrayLevelEmphasis = 1;
        rlmFlagS.grayLevelVariance = 1;
        rlmFlagS.runLengthVariance = 1;
        rlmFlagS.runEntropy = 1;

        if nargin==1 && ~strcmpi(varargin{1},'all')
            featureC = lower(varargin{1});
            allFeatC = lower(fieldnames(rlmFlagS));
            idxV = find(~ismember(allFeatC,featureC));
            for n = 1:length(idxV)
                rlmFlagS.(allFeatC{idxV(n)})  = 0;
            end
        end

    end

function szmFlagS = getSizeZoneFlags(varargin)

        szmFlagS.grayLevelNonUniformity = 1;
        szmFlagS.grayLevelNonUniformityNorm = 1;
        szmFlagS.grayLevelVariance = 1;
        szmFlagS.highGrayLevelZoneEmphasis = 1;
        szmFlagS.lowGrayLevelZoneEmphasis = 1;
        szmFlagS.largeAreaEmphasis = 1;
        szmFlagS.largeAreaHighGrayLevelEmphasis = 1;
        szmFlagS.largeAreaLowGrayLevelEmphasis = 1;
        szmFlagS.sizeZoneNonUniformity = 1;
        szmFlagS.sizeZoneNonUniformityNorm = 1;
        szmFlagS.sizeZoneVariance = 1;
        szmFlagS.zonePercentage = 1;
        szmFlagS.smallAreaEmphasis = 1;
        szmFlagS.smallAreaLowGrayLevelEmphasis = 1;
        szmFlagS.smallAreaHighGrayLevelEmphasis = 1;
        szmFlagS.zoneEntropy = 1;

        if nargin==1 && ~strcmpi(varargin{1},'all')
            featureC = lower(varargin{1});
            allFeatC = fieldnames(szmFlagS);
            idxV = find(~ismember(allFeatC,featureC));
            for n = 1:length(idxV)
                szmFlagS.(allFeatC{idxV(n)})  = 0;
            end
        end

    end

end


function f = minus(f, g)
%-   Subtraction of two CHEBFUN3T objects.
%   F - G subtracts G from F, where F and G are CHEBFUN3T objects or
%   scalars.
%
% See also CHEBFUN3T/PLUS and CHEBFUN3T/UMINUS.

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

% f - g = f + (-g)
f = plus(f, uminus(g));

end


function test22(nmat)
%TEST22 test pos.def and indef. matrices
% Example:
% test22(nmat)
%
% if nmat <= 0, just test problematic matrices
% See also cholmod_test

% Copyright 2006-2007, Timothy A. Davis, University of Florida

fprintf ('=================================================================\n');
fprintf ('test22: test pos.def and indef. matrices\n') ;

index = UFget ;

[ignore f] = sort (index.nrows) ;

if (nargin > 0)
    problematic = (nmat <= 0) ;
    if (problematic)
	% Matrices for which MATLAB and CHOLMOD differ, for which the error
	% is high, or other issues arose during debugging
	fprintf ('testing matrices for which MATLAB and CHOLMOD differ\n') ;
	f = [
	    186 % HB/jgl011
	    109 % HB/curtis54
	    793 % Qaplib/lp_nug05
	    607 % LPnetlib/lp_brandy
	    707 % LPnetlib/lp_bore3d
	    231 % HB/plskz362
	    794 % Qaplib/lp_nug06
	    673 % LPnetlib/lp_scorpion
	   1156 % Sandia/oscil_dcop_45 (also 1157:1168)
	    795 % Qaplib/lp_nug07
	    796 % Qaplib/lp_nug08
	    260 % HB/well1033
	    261 % HB/well1850
	    230 % HB/plsk1919
	    649 % LPnetlib/lp_pds_02
	    660 % LPnetlib/lp_qap12
	    609 % LPnetlib/lp_cre_a
	    619 % LPnetlib/lp_dfl001
	    661 % LPnetlib/lp_qap15
	    650 % LPnetlib/lp_pds_06
	    379 % Cote/vibrobox
	    638 % LPnetlib/lp_ken_11
	    799 % Qaplib/lp_nug20
	    ]' ;
    else
	nmat = max (0,nmat) ;
	nmat = min (nmat, length (f)) ;
	f = f (1:nmat) ;
    end
end

skip = [
    811, ...		% Simon/appu, which is a random matrix
    937:939, ...	% large ND/ problems
    1157:1168 ...	% duplicates
    799			% rather large: Qaplib/lp_nug20
    ] ;

tlimit = 0.1 ;
fprintf ('test22: chol and chol2 are repeated so each take >= %g sec\n',tlimit);

klimit = 1 ;

% warmup, for more accurate timing
[R,p] = chol (sparse (1)) ;						    %#ok
[R,p] = chol2 (sparse (1)) ;						    %#ok
clear R p

for i = f

    if (any (i == skip))
	continue ;
    end

    Problem = UFget (i) ;
    A = Problem.A ;
    [m n] = size (A) ;
    fprintf ('\n================== %4d: Problem: %s  m: %d n: %d nnz: %d', ...
	i, Problem.name, m, n, nnz (A)) ;
    clear Problem ;

    try	% create a symmetric version of the matrix
	if (m == n)
	    if (nnz (A-A') > 0)
		A = A+A' ;
	    end
	else
	    A = A*A' ;
	end
    catch
	fprintf ('skip\n') ;
	continue
    end

    fprintf (' %d\n', nnz (A)) ;

    p = amd2 (A) ;
    A = A (p,p) ;
    anorm = norm (A,1) ;

    % Run each code for at least 'tlimit' seconds

    % MATLAB
    k = 0 ;
    t1 = 0 ;
    while (t1 < tlimit & k < klimit)					    %#ok
	tic ;
	[R1,p1] = chol (A) ;
	t = toc ;
	t1 = t1 + t ;
	k = k + 1 ;
    end
    t1 = t1 / k ;

    % CHOLMOD
    k = 0 ;
    t2 = 0 ;
    while (t2 < tlimit & k < klimit)					    %#ok
	tic ;
	[R2,p2] = chol2 (A) ;
	t = toc ;
	t2 = t2 + t ;
	k = k + 1 ;
    end
    t2 = t2 / k ;

    if (klimit == 1)
	rmin = full (min (abs (diag (R2)))) ;
	rmax = full (max (abs (diag (R2)))) ;
	if (p2 ~= 0 | isnan (rmin) | isnan (rmax) | rmax == 0)		    %#ok
	    rcond = 0 ;
	else
	    rcond = rmin / rmax ;
	end
	fprintf ('rcond: %30.20e\n', rcond) ;
    end

    if (p1 == 1)
	% MATLAB does not follow its own definitions.  If p is 1, then R is
	% supposed to be 0-by-n, not 0-by-0.  CHOLMOD fixes this bug.
	% Here, A is m-by-m
	R1 = sparse (0,m) ;
    end

    kerr = 0 ;
    if (p1 ~= p2)
	% MATLAB and CHOLMOD don't agree.  See if both are correct,
	% because differences in roundoff errors can make one go
	% a little farther than the other.

	% if p1 is zero, it means MATLAB was fully successful
	k1 = p1 ;
	if (k1 == 0)
	    k1 = n ;
	end

	% if p2 is zero, it means CHOLMOD was fully successful
	k2 = p2 ;
	if (k2 == 0)
	    k2 = n ;
	end

	if (k1 > k2)
	    % MATLAB went further than CHOLMOD. This is OK if MATLAB found
	    % a small entry where CHOLMOD stopped.
	    k = k2 ;
	    kerr = R1 (k,k) ;
	    % now reduce R1 in size, to compare with R2
	    R1 = R1 (1:k2-1,:) ;
	else
	    % CHOLMOD went further than MATLAB. This is OK if CHOLMOD found
	    % a small entry where MATLAB stopped.
	    k = k1 ;
	    kerr = R2 (k,k) ;
	    % now reduce R2 in size, to compare with R1
	    R2 = R2 (1:k1-1,:) ;
	end
    end

    err = norm (R1-R2,1) / max (anorm,1) ;
    fprintf ('p: %6d %6d MATLAB: %10.4f CHOLMOD: %10.4f speedup %6.2f err:', ...
	p1, p2, t1, t2, t1/t2) ;

    if (err == 0)
	fprintf ('      0') ;
    else
	fprintf (' %6.0e', err) ;
    end

    if (kerr == 0)
	fprintf ('      0\n') ;
    else
	fprintf (' %6.0e\n', kerr) ;
    end

%    if (err > 1e-6)
%	error ('!') ;
%    end

% pause
    clear R1 R2 p1 p2 p A

end

fprintf ('test22: all tests passed\n') ;


function M = spm_mesh_reduce(M,t)
% Reduce the number of triangles in a mesh
% FORMAT M = spm_mesh_reduce(M,f)
% M        - a patch structure
% t        - desired number of triangles
%
% M        - reduced patch structure
%__________________________________________________________________________
%
% References:
%
% M. Garland and P. Heckbert. Surface Simplification Using Quadric Error
% Metrics. In Proceedings of SIGGRAPH 97.
% http://mgarland.org/files/papers/quadrics.pdf
%
% M. Garland and P. Heckbert. Simplifying Surfaces with Color and Texture
% using Quadric Error Metrics. In Proceedings of IEEE Visualization 98.
% http://mgarland.org/files/papers/quadric2.pdf
%
% Wrapper around an implementation by Sven Forstmann, MIT licence:
% https://github.com/sp4cerat/Fast-Quadric-Mesh-Simplification
%__________________________________________________________________________
% Copyright (C) 2018 Wellcome Trust Centre for Neuroimaging

% Guillaume Flandin
% $Id: spm_mesh_reduce.m 7421 2018-09-20 10:58:01Z guillaume $

%-This is merely the help file for the compiled routine
% error('spm_mesh_reduce.cpp not compiled - see Makefile')

M = reducepatch(M,t);


filename='Gripping_quad_coarse';
 ptype = 'MACRO';
method = 'SIMPALL';
materialType = 'ISOTROPIC';
initial_case = 'full';
cost = {'nonadjoint_compliance'};
weights = [1, 0.1];
constraint = {'volumeConstraint'};
constraint_case = 'INEQUALITY';
optimizer = 'PROJECTED GRADIENT'; incrementFactor = 1;
filterType = 'P1';

nsteps = 1;
Vfrac_final = 0.75;
optimality_final = 1e-3;
constr_final =1e-3;

Vfrac_initial = 1;
optimality_initial = 1e-3;
constr_initial = 1e-3;
TOL.rho_plus = 1;
TOL.rho_minus = 0;
TOL.E_plus = 1;
TOL.E_minus = 1e-3;
TOL.nu_plus = 1/3;
TOL.nu_minus = 1/3;


% demo_ECG_sleep_staging_slpdb
% Note: Raw slpdb data should be converted to Matlab format by wfdb2mat function before analysis.

fid=fopen('RECORDS');
cc=textscan(fid,'%s');
cc=cc{1};
fclose(fid);

class=1;
% class = 1, four types classification
% class = 4, two types classification
%   sleep_stage:  = 1: Wake,
%                   2: REM sleep,
%                   3: NREM light sleep,
%                   4: NREM deep sleep, when class = 1
%   sleep_stage:  = 1: NREM sleep,
%                   2: Wake+REM, when class = 4

% par
for i=1:length(cc)
    do_ECG_sleep_staging_slpdb_sub(cc{i},class)
end

function do_ECG_sleep_staging_slpdb_sub(record,class)

record
[tm,signal,Fs,siginfo]=rdmat([record 'm']);
ECGdata=signal(:,1);
fs=250;
% class=1;

sleep_stage = ECG_sleep_staging_prediction(ECGdata,fs,class);

% read stage type from annotation
[ann,type,subtype,chan,num,comments]=read_ann(record,'st');
ann_epoch_len=30; % annotation epoch is 30s
seg_length=300; % segment length 300s (5 min)
stage=[];
stage(1:round(ann(end)/Fs/ann_epoch_len)+1)=NaN;

for j=1:length(ann)
    time_j=round(ann(j)/Fs/ann_epoch_len)+1;
    switch (comments{j,1}(1))
        case 'W'
            stage(time_j)=1;
        case 'R'
            stage(time_j)=2;
        case '1'
            stage(time_j)=3;
        case '2'
            stage(time_j)=3;
        case '3'
            stage(time_j)=4;
        case '4'
            stage(time_j)=4;
    end
end
if class==4
    stage(find(stage==1))=2;
    stage(find(stage>2))=1;
end
save(['ECG_sleep_staging_result_class_' num2str(class) '_' record],'sleep_stage','stage');
end


%% clear the workspace and select data
clear; clc; close all;

%% choose multiple datasets or just one
neuron = Sources2D();
nams = {'./data_2p.tif'};          % you can put all file names into a cell array; when it's empty, manually select files
nams = neuron.select_multiple_files(nams);  %if nam is [], then select data interactively

%% parameters
% -------------------------    COMPUTATION    -------------------------  %
pars_envs = struct('memory_size_to_use', 8, ...   % GB, memory space you allow to use in MATLAB
    'memory_size_per_patch', 0.3, ...   % GB, space for loading data within one patch
    'patch_dims', [30, 40],...  %GB, patch size
    'batch_frames', 1000);           % number of frames per batch

% -------------------------      SPATIAL      -------------------------  %
gSig = 0.5;           % pixel, gaussian width of a gaussian kernel for filtering the data. 0 means no filtering
gSiz = 10;          % pixel, neuron diameter
ssub = 1;           % spatial downsampling factor
with_dendrites = false;   % with dendrites or not
if with_dendrites
    % determine the search locations by dilating the current neuron shapes
    updateA_search_method = 'dilate';  %#ok<UNRCH>
    updateA_bSiz = 20;
    updateA_dist = neuron.options.dist;
else
    % determine the search locations by selecting a round area
    updateA_search_method = 'ellipse'; %#ok<UNRCH>
    updateA_dist = 5;
    updateA_bSiz = neuron.options.dist;
end
spatial_constraints = struct('connected', true, 'circular', false);  % you can include following constraints: 'circular'

% -------------------------      TEMPORAL     -------------------------  %
Fs = 5;             % frame rate
tsub = 1;           % temporal downsampling factor
deconv_options = struct('type', 'ar1', ... % model of the calcium traces. {'ar1', 'ar2'}
    'method', 'foopsi', ... % method for running deconvolution {'foopsi', 'constrained', 'thresholded'}
    'smin', -3, ...         % minimum spike size. When the value is negative, the actual threshold is abs(smin)*noise level
    'optimize_pars', true, ...  % optimize AR coefficients
    'optimize_b', true);% optimize the baseline);
nk = 1;             % detrending the slow fluctuation. usually 1 is fine (no detrending)
                    % when changed, try some integers smaller than total_frame/(Fs*30)
detrend_method = 'local_min';  % compute the local minimum as an estimation of trend.

% -------------------------     BACKGROUND    -------------------------  %
bg_model = 'svd';  % model of the background {'ring', 'svd'(default), 'nmf'}
nb = 1;             % number of background sources for each patch (only be used in SVD and NMF model)
bg_neuron_factor = 0.5;
ring_radius = round(bg_neuron_factor * gSiz);  % when the ring model used, it is the radius of the ring used in the background model.
                    %otherwise, it's just the width of the overlapping area

% -------------------------      MERGING      -------------------------  %
show_merge = false;  % if true, manually verify the merging step
merge_thr = 0.7;     % thresholds for merging neurons; [spatial overlap ratio, temporal correlation of calcium traces, spike correlation]
method_dist = 'max';   % method for computing neuron distances {'mean', 'max'}
dmin = 10;       % minimum distances between two neurons. it is used together with merge_thr
dmin_only = 2;  % merge neurons if their distances are smaller than dmin_only.

% -------------------------  INITIALIZATION   -------------------------  %
K = [];             % maximum number of neurons per patch. when K=[], take as many as possible
min_corr = 0.7;     % minimum local correlation for a seeding pixel
min_pnr =10;       % minimum peak-to-noise ratio for a seeding pixel
min_pixel = 2^2;      % minimum number of nonzero pixels for each neuron
bd = 0;             % number of rows/columns to be ignored in the boundary (mainly for motion corrected data)
frame_range = [];   % when [], uses all frames
save_initialization = false;    % save the initialization procedure as a video.
use_parallel = true;    % use parallel computation for parallel computing
show_init = true;   % show initialization results
choose_params = false; % manually choose parameters
center_psf = false;  % set the value as true when the background fluctuation is large (usually 1p data)
                    % set the value as false when the background fluctuation is small (2p)

% ----------------------   MANUAL INTERVENTION  --------------------  %
with_manual_intervention = false;

% -------------------------  FINAL RESULTS   -------------------------  %
save_demixed = true;    % save the demixed file or not
kt = 1;                 % frame intervals

% -------------------------    UPDATE ALL    -------------------------  %
neuron.updateParams('gSig', gSig, ...       % -------- spatial --------
    'gSiz', gSiz, ...
    'ring_radius', ring_radius, ...
    'ssub', ssub, ...
    'search_method', updateA_search_method, ...
    'bSiz', updateA_bSiz, ...
    'dist', updateA_bSiz, ...
    'spatial_constraints', spatial_constraints, ...
    'tsub', tsub, ...                       % -------- temporal --------
    'deconv_options', deconv_options, ...    '
    'nk', nk, ...
    'detrend_method', detrend_method, ...
    'background_model', bg_model, ...       % -------- background --------
    'nb', nb, ...
    'ring_radius', ring_radius, ...
    'merge_thr', merge_thr, ...             % -------- merging ---------
    'dmin', dmin, ...
    'method_dist', method_dist, ...
    'min_corr', min_corr, ...               % ----- initialization -----
    'min_pnr', min_pnr, ...
    'min_pixel', min_pixel, ...
    'bd', bd, ...
    'center_psf', center_psf);
neuron.Fs = Fs;

%% distribute data and be ready to run source extraction
neuron.getReady_batch(pars_envs);

%% initialize neurons in batch mode
neuron.initComponents_batch(K, save_initialization, use_parallel);

%% udpate spatial components for all batches
neuron.update_spatial_batch(use_parallel);

%% udpate temporal components for all bataches
neuron.update_temporal_batch(use_parallel);

%% update background
neuron.update_background_batch(use_parallel);

%% delete neurons

%% merge neurons

%% save workspace
neuron.save_workspace_batch();

%% get the correlation image and PNR image for all neurons
neuron.correlation_pnr_batch();

%% concatenate temporal components
neuron.concatenate_temporal_batch();
neuron.viewNeurons([], neuron.C_raw);


%% Etract IDE features
clear;clc;
addpath('../caffe/matlab/');
addpath(genpath('utils/'));
% load model and creat network
caffe.set_device(0);
caffe.set_mode_gpu();
netname = 'ResNet_50'; % network: CaffeNet  or ResNet_50

% set your path to the prototxt and model
model =  ['../models/duke/' netname '/' netname '_test.prototxt'];
weights = ['../output/duke_train/IDE_' netname '.caffemodel'];
net = caffe.Net(model, weights, 'test');

if strcmp(netname, 'CaffeNet')
    im_size = 227;
    feat_dim = 4096;
else
    im_size = 224;
    feat_dim = 2048;
end

% mean data
mean_data = importdata('../caffe/matlab/+caffe/imagenet/ilsvrc_2012_mean.mat');
image_mean = mean_data;
off = floor((size(image_mean,1) - im_size)/2)+1;
image_mean = image_mean(off:off+im_size-1, off:off+im_size-1, :);

ef_path = {'dataset/bounding_box_test/', 'dataset/query/'};
ef_name = {'test', 'query'};

if ~exist('feat')
    mkdir('feat')
end

% extract features
for i = 1:2
    img_path = ef_path{i};
    img_file = dir([img_path '*.jpg']);
    feat = single(zeros(feat_dim, length(img_file)));

    for n = 1:length(img_file)
        if mod(n, 1000) ==0
            fprintf('%s: %d/%d\n',ef_name{i}, n, length(img_file))
        end
        img_name = [img_path  img_file(n).name];
        im = imread(img_name);
        im = prepare_img( im, image_mean, im_size);
        feat_img = net.forward({im});
        feat(:, n) = single(feat_img{1}(:));
    end

    save(['feat/IDE_'  netname  '_' ef_name{i} '.mat'], 'feat');
    feat = [];
end

caffe.reset_all();