function mpc = apply_changes(label, mpc, chgtab)
%APPLY_CHANGES  Applies a set of changes to a MATPOWER case
%   mpc_modified = apply_changes(label, mpc_original, chgtab)
%
%   Applies the set of changes identified by LABEL to the case in MPC, where
%   the change sets are specified in CHGTAB.
%
%   LABEL is an integer which identifies the set of changes of interest
%
%   MPC is a MATPOWER case struct with at least fields bus, gen and branch
%
%   CHGTAB is the table of changes that lists the individual changes required
%   by each change set, one "change" per row (multiple rows or changes
%   allowed for each change set). Type "help idx_ct" for more complete
%   information about the format.
%   1st column: change set label, integer > 0
%   2nd column: change set probability
%   3rd column: table to be modified (1:bus, 2:gen, 3:branch) or
%               4: bus area changes, apply to all gens/buses/branches in
%               a given area; 5: gen table area changes apply to all
%               generators in a given area; or 6: branch area changes apply
%               to all branches connected to buses in a given area.
%   4th column: row of table to be modified (if 3rd col is 1-3),
%               or area number for overall changes (if 3rd column is 4-6).
%   5th column: column of table to be modified. It is best to use the
%               named column index defined in the corresponding idx_bus,
%               idx_gen, idx_branch or idx_cost M-files in MATPOWER.
%   6th column: type of change: 1: absolute (replace)
%                               2: relative (multiply by factor)
%                               3: additive (add to value)
%   7th column: new value or multiplicative or additive factor
%
%   Examples:
%
%   chgtab = [ ...
%       1   0.1   CT_TGEN       2  GEN_STATUS     CT_REP  0;
%       2   0.05  CT_TGEN       3  PMAX           CT_REP  100;
%       3   0.2   CT_TBRCH      2  BR_STATUS      CT_REP  0;
%       4   0.1   CT_TAREALOAD  2  CT_LOAD_ALL_P  CT_REL  1.1;
%    ];
%
%   Description of each change set:
%   1. Turn off generator 2, 10% probability.
%   2. Set generator 3's max output to 100 MW, 5% probability.
%   3. Take branch 2 out of service, 20% probability.
%   4. Scale all loads in area 2 (real & reactive, fixed and dispatchable)
%      by a factor of 1.1, 10% probability.
%
%   See IDX_CT.

%   To do:
%       - check for valid row number

%   MATPOWER
%   Copyright (c) 2000-2016, Power Systems Engineering Research Center (PSERC)
%   by Carlos E. Murillo-Sanchez, PSERC Cornell & Universidad Nacional de Colombia
%   and Ray Zimmerman, PSERC Cornell
%
%   This file is part of MATPOWER.
%   Covered by the 3-clause BSD License (see LICENSE file for details).
%   See https://matpower.org for more info.

%% define named indices into data matrices
[PQ, PV, REF, NONE, BUS_I, BUS_TYPE, PD, QD, GS, BS, BUS_AREA, VM, ...
    VA, BASE_KV, ZONE, VMAX, VMIN, LAM_P, LAM_Q, MU_VMAX, MU_VMIN] = idx_bus;
[GEN_BUS, PG, QG, QMAX, QMIN, VG, MBASE, GEN_STATUS, PMAX, PMIN, ...
    MU_PMAX, MU_PMIN, MU_QMAX, MU_QMIN, PC1, PC2, QC1MIN, QC1MAX, ...
    QC2MIN, QC2MAX, RAMP_AGC, RAMP_10, RAMP_30, RAMP_Q, APF] = idx_gen;
[F_BUS, T_BUS, BR_R, BR_X, BR_B, RATE_A, RATE_B, RATE_C, ...
    TAP, SHIFT, BR_STATUS, PF, QF, PT, QT, MU_SF, MU_ST, ...
    ANGMIN, ANGMAX, MU_ANGMIN, MU_ANGMAX] = idx_brch;
[PW_LINEAR, POLYNOMIAL, MODEL, STARTUP, SHUTDOWN, NCOST, COST] = idx_cost;
[CT_LABEL, CT_PROB, CT_TABLE, CT_TBUS, CT_TGEN, CT_TBRCH, CT_TAREABUS, ...
    CT_TAREAGEN, CT_TAREABRCH, CT_ROW, CT_COL, CT_CHGTYPE, CT_REP, ...
    CT_REL, CT_ADD, CT_NEWVAL, CT_TLOAD, CT_TAREALOAD, CT_LOAD_ALL_PQ, ...
    CT_LOAD_FIX_PQ, CT_LOAD_DIS_PQ, CT_LOAD_ALL_P, CT_LOAD_FIX_P, ...
    CT_LOAD_DIS_P, CT_TGENCOST, CT_TAREAGENCOST, CT_MODCOST_F, ...
    CT_MODCOST_X] = idx_ct;

%% find the change set to be applied
if numel(label) > 1
    error('apply_changes: LABEL must be a scalar');
end
kk = find(label == chgtab(:, CT_LABEL));
if isempty(kk)
    error('apply_changes: LABEL %d not found in CHGTAB', label);
end

%% create map of external bus numbers to bus indices
i2e = mpc.bus(:, BUS_I);
e2i = sparse(max(i2e), 1);
e2i(i2e) = (1:size(mpc.bus, 1))';

for c = 1:length(kk)
    %% extract data for individual change to be applied
    %% (c-th change in specified change set)
    change = num2cell(chgtab(kk(c), :));
    [tbl, row, col, typ, val] = ...
        deal(change{[CT_TABLE, CT_ROW, CT_COL, CT_CHGTYPE, CT_NEWVAL]});

    %% apply the change
    if tbl == CT_TBUS                               %% modify bus table
      if ~any(col == [PD QD GS BS VMAX VMIN])
        error('apply_changes: modification to column %d of bus table not supported', col);
      end
      if row == 0                                       %% modify all rows
        if typ == CT_REP                                    %% replace
          mpc.bus(:, col) = val * ones(size(mpc.bus, 1), 1);
        elseif typ == CT_REL                                %% scale
          mpc.bus(:, col) = val * mpc.bus(:, col);
        elseif typ == CT_ADD                                %% shift
          mpc.bus(:, col) = val + mpc.bus(:, col);
        else
          error('apply_changes: unsupported modification type %d for bus table', typ);
        end
      else                                              %% modify single row
        if typ == CT_REP                                    %% replace
          mpc.bus(row, col) = val;
        elseif typ == CT_REL                                %% scale
          mpc.bus(row, col) = val * mpc.bus(row, col);
        elseif typ == CT_ADD                                %% shift
          mpc.bus(row, col) = val + mpc.bus(row, col);
        else
          error('apply_changes: unsupported modification type %d for bus table', typ);
        end
      end
    elseif tbl == CT_TBRCH                          %% modify branch table
      if ~any(col == ...
            [BR_R BR_X BR_B RATE_A RATE_B RATE_C TAP SHIFT BR_STATUS ANGMIN ANGMAX])
        error('apply_changes: modification to column %d of branch table not supported', col);
      end
      if row == 0                                       %% modify all rows
        if typ == CT_REP                                    %% replace
          mpc.branch(:, col) = val * ones(size(mpc.branch, 1), 1);
        elseif typ == CT_REL                                %% scale
          mpc.branch(:, col) = val * mpc.branch(:, col);
        elseif typ == CT_ADD                                %% shift
          mpc.branch(:, col) = val + mpc.branch(:, col);
        else
          error('apply_changes: unsupported modification type %d for branch table', typ);
        end
      else                                              %% modify single row
        if typ == CT_REP                                    %% replace
          mpc.branch(row, col) = val;
        elseif typ == CT_REL                                %% scale
          mpc.branch(row, col) = val * mpc.branch(row, col);
        elseif typ == CT_ADD                                %% shift
          mpc.branch(row, col) = val + mpc.branch(row, col);
        else
          error('apply_changes: unsupported modification type %d for branch table', typ);
        end
      end
    elseif tbl == CT_TGEN                           %% modify gen table
      if ~any(col == ...
            [QMAX QMIN GEN_STATUS PMAX PMIN PC1 PC2 QC1MIN QC1MAX QC2MIN  QC2MAX ...
            RAMP_AGC RAMP_10 RAMP_30 RAMP_Q APF])
        error('apply_changes: modification to column %d of gen table not supported', col);
      end
      if row == 0                                       %% modify all rows
        if typ == CT_REP                                    %% replace
          mpc.gen(:, col) = val * ones(size(mpc.gen, 1), 1);
        elseif typ == CT_REL                                %% scale
          mpc.gen(:, col) = val * mpc.gen(:, col);
        elseif typ == CT_ADD                                %% shift
          mpc.gen(:, col) = val + mpc.gen(:, col);
        else
          error('apply_changes: unsupported modification type %d for gen table', typ);
        end
      else                                              %% modify single row
        if typ == CT_REP                                    %% replace
          mpc.gen(row, col) = val;
        elseif typ == CT_REL                                %% scale
          mpc.gen(row, col) = val * mpc.gen(row, col);
        elseif typ == CT_ADD                                %% shift
          mpc.gen(row, col) = val + mpc.gen(row, col);
        else
          error('apply_changes: unsupported modification type %d for gen table', typ);
        end
      end
    elseif tbl == CT_TGENCOST                       %% modify gencost table
      if col == CT_MODCOST_F || col == CT_MODCOST_X     %% use modcost to scale/shift cost
        if typ == CT_REL
          if col == CT_MODCOST_F
            modcost_type = 'SCALE_F';
          else  %% col == CT_MODCOST_X
            modcost_type = 'SCALE_X';
          end
        elseif typ == CT_ADD
          if col == CT_MODCOST_F
            modcost_type = 'SHIFT_F';
          else  %% col == CT_MODCOST_X
            modcost_type = 'SHIFT_X';
          end
        else
            error('apply_changes: unsupported modification type %d for gencost table CT_MODCOST_F/X modification', typ);
        end
        if row == 0
          mpc.gencost = modcost(mpc.gencost, val, modcost_type);
        else
          mpc.gencost(row, :) = modcost(mpc.gencost(row, :), val, modcost_type);
        end
      else                                              %% normal individual column mod
        if col < 1 || fix(col) ~= col   %% needs to be positive integer
          error('apply_changes: modification to column %d of gencost table not supported', col);
        end
        if row == 0                                       %% modify all rows
          if typ == CT_REP                                    %% replace
            mpc.gencost(:, col) = val * ones(size(mpc.gencost, 1), 1);
          elseif typ == CT_REL                                %% scale
            mpc.gencost(:, col) = val * mpc.gencost(:, col);
          elseif typ == CT_ADD                                %% shift
            mpc.gencost(:, col) = val + mpc.gencost(:, col);
          else
            error('apply_changes: unsupported modification type %d for gencost table', typ);
          end
        else                                              %% modify single row
          if typ == CT_REP                                    %% replace
            mpc.gencost(row, col) = val;
          elseif typ == CT_REL                                %% scale
            mpc.gencost(row, col) = val * mpc.gencost(row, col);
          elseif typ == CT_ADD                                %% shift
            mpc.gencost(row, col) = val + mpc.gencost(row, col);
          else
            error('apply_changes: unsupported modification type %d for gencost table', typ);
          end
        end
      end
    elseif tbl == CT_TAREABUS                   %% area-wide mod to bus table
      if ~any(col == [PD QD GS BS VMAX VMIN])
        error('apply_changes: area-wide modification to column %d of bus table not supported', col);
      end
      jj = find(mpc.bus(:, BUS_AREA) == row);
      if typ == CT_REP                                      %% replace
        mpc.bus(jj, col) = val * ones(size(jj));
      elseif typ == CT_REL                                  %% scale
        mpc.bus(jj, col) = val * mpc.bus(jj, col);
      elseif typ == CT_ADD                                  %% shift
        mpc.bus(jj, col) = val + mpc.bus(jj, col);
      else
        error('apply_changes: unsupported area-wide modification type %d for bus table', typ);
      end
    elseif tbl == CT_TAREABRCH                  %% area-wide mod to branch table
      if ~any(col == ...
            [BR_R BR_X BR_B RATE_A RATE_B RATE_C TAP SHIFT BR_STATUS ANGMIN ANGMAX])
        error('apply_changes: area-wide modification to column %d of branch table not supported', col);
      end
      jj = find((row == mpc.bus(e2i(mpc.branch(:, F_BUS)), BUS_AREA)) | ...
                (row == mpc.bus(e2i(mpc.branch(:, T_BUS)), BUS_AREA)) );
      if typ == CT_REP                                      %% replace
        mpc.branch(jj, col) = val * ones(size(jj));
      elseif typ == CT_REL                                  %% scale
        mpc.branch(jj, col) = val * mpc.branch(jj, col);
      elseif typ == CT_ADD                                  %% shift
        mpc.branch(jj, col) = val + mpc.branch(jj, col);
      else
        error('apply_changes: unsupported area-wide modification type %d for branch table', typ);
      end
    elseif tbl == CT_TAREAGEN                   %% area-wide mod to gen table
      if ~any(col == ...
            [QMAX QMIN GEN_STATUS PMAX PMIN PC1 PC2 QC1MIN QC1MAX QC2MIN  QC2MAX ...
            RAMP_AGC RAMP_10 RAMP_30 RAMP_Q APF])
        error('apply_changes: area-wide modification to column %d of gen table not supported', col);
      end
      jj = find(row == mpc.bus(e2i(mpc.gen(:, GEN_BUS)), BUS_AREA));
      if typ == CT_REP                                      %% replace
        mpc.gen(jj, col) = val * ones(size(jj));
      elseif typ == CT_REL                                  %% scale
        mpc.gen(jj, col) = val * mpc.gen(jj, col);
      elseif typ == CT_ADD                                  %% shift
        mpc.gen(jj, col) = val + mpc.gen(jj, col);
      else
        error('apply_changes: unsupported area-wide modification type %d for gen table', typ);
      end
    elseif tbl == CT_TAREAGENCOST               %% area-wide mod to gencost table
      jj = find(row == mpc.bus(e2i(mpc.gen(:, GEN_BUS)), BUS_AREA));
      if col == CT_MODCOST_F || col == CT_MODCOST_X
        if typ == CT_REL
          if col == CT_MODCOST_F
            modcost_type = 'SCALE_F';
          else  %% col == CT_MODCOST_X
            modcost_type = 'SCALE_X';
          end
        elseif typ == CT_ADD
          if col == CT_MODCOST_F
            modcost_type = 'SHIFT_F';
          else  %% col == CT_MODCOST_X
            modcost_type = 'SHIFT_X';
          end
        else
            error('apply_changes: unsupported area-wide modification type %d for gencost table CT_MODCOST_F/X modification', typ);
        end
        mpc.gencost(jj, :) = modcost(mpc.gencost(jj, :), val, modcost_type);
      else
        if col < 1 || fix(col) ~= col   %% needs to be positive integer
          error('apply_changes: area-wide modification to column %d of gencost table not supported', col);
        end
        if typ == CT_REP                                      %% replace
          mpc.gencost(jj, col) = val * ones(size(jj));
        elseif typ == CT_REL                                  %% scale
          mpc.gencost(jj, col) = val * mpc.gencost(jj, col);
        elseif typ == CT_ADD                                  %% shift
          mpc.gencost(jj, col) = val + mpc.gencost(jj, col);
        else
          error('apply_changes: unsupported area-wide modification type %d for gencost table', typ);
        end
      end
    elseif tbl == CT_TLOAD || tbl == CT_TAREALOAD           %% modify loads
      if ~any(abs(col) == (CT_LOAD_ALL_PQ:CT_LOAD_DIS_P))
        error('apply_changes: column=%d for load modifications is not supported', col);
      end
      switch abs(col)
        case {CT_LOAD_ALL_PQ, CT_LOAD_FIX_PQ, CT_LOAD_DIS_PQ}
            opt.pq = 'PQ';
        case {CT_LOAD_ALL_P, CT_LOAD_FIX_P, CT_LOAD_DIS_P}
            opt.pq = 'P';
      end
      switch abs(col)
        case {CT_LOAD_ALL_PQ, CT_LOAD_ALL_P}
            opt.which = 'BOTH';
        case {CT_LOAD_FIX_PQ, CT_LOAD_FIX_P}
            opt.which = 'FIXED';
        case {CT_LOAD_DIS_PQ, CT_LOAD_DIS_P}
            opt.which = 'DISPATCHABLE';
      end

      %% define load_zone
      if tbl == CT_TLOAD
        nb = size(mpc.bus, 1);
        if row == 0                                     %% modify load at all buses
          load_zone = ones(nb, 1);
        else                                            %% modify load at single bus
          load_zone = zeros(nb, 1);
          load_zone(row) = 1;
        end
      elseif tbl == CT_TAREALOAD
        load_zone = double(mpc.bus(:, BUS_AREA) == row);  %% modify load in single area
      end

      switch typ
        case CT_REP                                     %% replace
            opt.scale = 'QUANTITY';
            dmd = val;
        case CT_REL                                     %% scale
            opt.scale = 'FACTOR';
            dmd = val;
        case CT_ADD                                     %% shift
            opt.scale = 'QUANTITY';
            old_val = total_load(mpc, load_zone);
            dmd = old_val + val;
        otherwise
          error('apply_changes: unsupported modification type %d for loads', typ);
      end
      %% scale the loads ...
      if col < 0    %% ... including dispatchable load costs
          [mpc.bus, mpc.gen, mpc.gencost] = scale_load(dmd, mpc.bus, mpc.gen, load_zone, opt, mpc.gencost);
      else          %% ... not including dispatchable load costs
          [mpc.bus, mpc.gen] = scale_load(dmd, mpc.bus, mpc.gen, load_zone, opt);
      end
    else
      error('apply_changes: CHGTAB attempts to modify unsupported table type')
    end
end


function ME = myfunc(x)
% Break Test

for i = 1:100
    try
        x = x - 1;
        if x < 0
            error('x has become negative')
        end
    catch ME
        fprintf('x become negative')
        break
    end
end


function [MASKSTATS] = canlab_glm_maskstats(DIRS,MASK,varargin)
% Returns a MASKSTATS structure containing data from robfitdir's input subject level
%    SPM analyses.
%
% :Usage:
% ::
%
%     MASKSTATS = canlab_glm_maskstats(robfitdir, mask, [options])
%
% output structure:
% MASKSTATS
%
%   **COV:**
%        subject x covariate matrix (EXPT.cov from robfit design)
%
%   **COVNAME:**
%        names of covariates in COV
%
%   **MASK:**
%        array of structs (1 per mask)
%
%   **MASKFILE:**
%        the filename of the mask used
%
%   **SUB:**
%        struct containing data from subject level images
%
%   **CON:**
%        struct contains data from contrast images
%
%   **NAME:**
%        cell array (1 cell per contrast) of contrast names
%
%   **IMGFILES:**
%        cell array (1 cell per contrast) of character arrays of
%        contrast image filenames
%
%   **(MEASURE):**
%        subject X contrast matrix of measures (see canlab_maskstats)
%
%   **COVxCON.(MEASURE):**
%        arrays of covariate matrix X contrast means correlation
%        results (RHO and P, see help corr())
%
%   **GRP:**
%        struct containing data from group level images
%
%   **BETA:**
%        struct array (1 struct per group level regressor) of data
%        from beta images
%
%   **NAME:**
%        name of group level regressor
%
%   **IMGFILES:**
%        cell array (1 cell per robust directory) of beta image files
%
%   **(MEASURE):**
%        row vector (1 value per robust directory) of measures
%        (see canlab_maskstats)
%
% The following plots are saved in each mask's directory in the plots directory:
%   - contrast means by contrast (means are lines across subjects on x axis)
%   - contrast means by subject (means are dots, lined up along the x axis by contrast)
%   - group level betas (bar plot with group level regressors grouped by
%     subject level contrasts)
%
% If there's more than one regressor in the group level model, for each regressor:
%   - scatter plot of subject level contrast means against group level regressor
%
%
% :Arguments:
%
%   **robfitdir:**
%        a directory in which robfit was run
%        contains robfit directories (e.g., robust0001)
%        preferably contains EXPT.mat or EXPTm.mat
%
%   **mask:**
%        a filename or cell array of filenames of masks to apply to data
%
% :Options:
%
%   **MEASURE OPTIONS:**
%        (see canlab_maskstats) (DEFAULT: mean (within mask's non-zero voxels))
%
%   **'cons', connums:**
%        (vector of contrast numbers)
%        only include data from contrasts as specified by numbers
%
%   **'cons', conname(s):**
%        (string or cell array of strings)
%        only include data from contrasts as specified by name
%
%   **'plots':**
%        make plots
%
%   **'od', dir:**
%        will save plots in dir (DEFAULT: robfitdir/stats_scatterplots)
%
% ..
%    Programmers' notes:
%    to write:
%    save data option
%    save as csv option
%    option to include subject-level beta data
%    option to break input mask into regions?
%    within subjects error bars
%    grouped bar plots?
%    save volInfo (for mask? betas? cons? from fmri_data or spm?)
% ..

DO_PLOTS = false; % SETUP
% set defaults
OP = {};
ALLOPS = false;

applymaskopts = '';

% modify arguments as needed
if ~iscell(MASK), MASK = {MASK}; end

% error checking
if ~exist('DIRS','var') || isempty(DIRS)
    error('No robftidir specified.')
end
if ~exist('MASK','var') || isempty(MASK)
    error('No MASK(s) specified.')
end

% this gets checked/errored in canlab_maskstats
% for i=1:numel(MASK)
%     if ~exist(MASK{i},'file') && ~any(~cellfun('isempty',regexp({'nps','nps_thresh','nps_thresh_smooth'},['^' MASK{i} '$'])))
%         error('No such file: %s',MASK{i})
%     end
% end

% parse varargin
i=1;
while i<=numel(varargin)
    if ischar(varargin{i})
        switch varargin{i}
            case {'c' 'cons'}
                i=i+1;
                if ischar(varargin{i})
                    included_connames = varargin(i);
                elseif iscellstr(varargin{i})
                    included_connames = varargin{i};
                elseif isnumeric(varargin{i})
                    included_connums = varargin{i};
                else
                    error('argument to ''c'' must be number, string, or cell string')
                end
            case 'od'
                i=i+1;
                plotdir = varargin{i};
            case 'plots'
                DO_PLOTS = true;
            case {'binmean' 'mean' 'std' 'nonbinmean' 'nonbinstd' 'norm' ...
                  'dot_product' 'centered_dot_product' 'cosine_similarity' 'correlation'}
                OP{end+1} = varargin{i}; %#ok
            case 'all'
                ALLOPS = true;
            case 'l1norm'
                applymaskopts = [applymaskopts ', ''l1norm''']; %#ok
            case 'l2norm'
                applymaskopts = [applymaskopts ', ''l2norm''']; %#ok
            otherwise
                error(['UNRECOGNIZED OPTION: ' varargin{i}])
        end
    elseif iscellstr(varargin{i})
        for j=1:numel(varargin{i})
            switch varargin{i}{j}
                case {'binmean' 'mean' 'std' 'nonbinmean' 'nonbinstd' 'norm' ...
                        'dot_product' 'centered_dot_product' 'cosine_similarity' 'correlation'}
                    OP{end+1} = varargin{i}{j}; %#ok
                case 'all'
                    ALLOPS = true;
                otherwise
                    error('Unrecognized argument %s',varargin{i}{j})
            end
        end
    else
        disp(varargin{i})
        error('Above option UNRECOGNIZED')
    end
    i=i+1;
end

if ALLOPS, OP = {'cosine_similarity' 'dot_product' 'correlation' 'mean' 'std' 'centered_dot_product'}; end

if isempty(OP), OP = {'mean'}; end

%% PREP
if ischar(DIRS)
    DO_GRPLEV = true;
    BASEDOUT = DIRS;

    robdirs = filenames(fullfile(DIRS,'robust[0-9][0-9][0-9][0-9]'),'absolute');
    if isempty(robdirs)
        error('No such directories: %s/robust[0-9][0-9][0-9][0-9]',DIRS)
    end
    fprintf('... LOADING SETUP files from %d robust* directories in %s\n',numel(robdirs),DIRS)
    SETUPS = cell(numel(robdirs),1); allconnames = SETUPS; allconfiles = SETUPS;
    for r = 1:numel(robdirs)
        load(fullfile(robdirs{r},'SETUP'));
        SETUPS{r} = SETUP;
        allconnames{r} = SETUP.name;
        allconfiles{r} = SETUP.files;
        clear SETUP
    end

    % load EXPT
    if exist(fullfile(DIRS,'EXPTm.mat'),'file')
        load(fullfile(DIRS,'EXPTm'));
        EXPT = EXPTm;
        clear EXPTm;
    elseif exist(fullfile(DIRS,'EXPT.mat'),'file')
        load(fullfile(DIRS,'EXPT'));
    end

     % get covariates, if any
    if size(SETUPS{1}.X,2)>1
        fprintf('... GETTING between-subjects covariate(s)\n')
        cov = SETUPS{1}.X(:,2:end);

        covname = {};
        if isfield(EXPT,'covnames')
            covname = EXPT.covnames(2:end);
        end
    else
        cov = {};
        covname = '';
    end
    MASKSTATS.cov = cov;
    MASKSTATS.covname = covname;
elseif iscellstr(DIRS)
    DO_GRPLEV = false;
    BASEDOUT = pwd;

    for i = 1:numel(DIRS)
        clear SPM spmmat
        spmmat = fullfile(DIRS{i},'SPM.mat');
        if isempty(spmmat)
            error('No such file: %s',spmmat)
        end
        load(spmmat);
        tmpconnames{i} = cellstr(char(SPM.xCon.name)); %#ok
        ntmpconnames(i) = size(tmpconnames{i},1); %#ok
        allconfiles{i} = filenames(fullfile(DIRS{i}),'con_*.img'); %#ok
    end

    if size(unique(ntmpconnames))~=1
        error('Not all subject levels have same contrast file names.')
    end
    allconnames = tmpconnames{1};
    clear SPM spmmat tmpconnames
end

if DO_PLOTS && ~exist('plotdir','var')
    plotdir = fullfile(BASEDOUT,'maskstats_plots');
end

%% MAIN
% for r=1:numel(MASK)
%     clear mask
%
%     mask = MASK{r};
%     [ignore maskname] = fileparts(mask);
%     fprintf('... GETTING data for mask: %s\n',mask)

%% SUBJECT LEVEL STAT MAPS (cons)
fprintf('SUBJECT LEVELS\n')

clear lconname lconfiles lcondata

% get filenames
fprintf('Subject level contrasts:\n')
n=0;
lconfiles = {};
if exist('included_connums','var')
    connums = included_connums;
else
    connums = 1:numel(allconfiles);
end
for i = connums
    thisconname = deblank(allconnames{i});
    if exist('included_connames','var') && ~any(~cellfun('isempty',(regexp(thisconname,included_connames))))
        fprintf('EXCLUDED: %s\n',thisconname)
        continue
    else
        n=n+1;
        included_robustdirs(n) = i; %#ok
    end
    % name
    lconname{n} = thisconname; %#ok
    fprintf('%s\n',lconname{n})
    % files
    lconfiles = [lconfiles cellstr(SETUPS{i}.files)]; %#ok
end

% get data
lmaskstats = canlab_maskstats(MASK,lconfiles,OP);

% pack output struct
for m=1:numel(MASK)
    MASKSTATS.mask(m).mask = lmaskstats(m).maskfile;
    MASKSTATS.mask(m).sub.con = lmaskstats(m).stats;
    MASKSTATS.mask(m).sub.con.imgfiles = lmaskstats(m).imgfiles;
    MASKSTATS.mask(m).sub.con.name = lconname;
    if ~isempty(cov)
        for i=1:numel(OP)
            [rho pval] = corr(cov,MASKSTATS.mask(m).sub.con.(OP{i}));
            MASKSTATS.mask(m).sub.covXcon.(OP{i}).rho = rho;
            MASKSTATS.mask(m).sub.covXcon.(OP{i}).pval = pval;
        end
    end
end

% make plots
if DO_PLOTS
    fprintf('... MAKING plots\n')
    for m = 1:numel(MASK)
        [ignore maskname] = fileparts(MASK{m}); %#ok

        dout = fullfile(plotdir,maskname);
        if ~exist(dout,'dir'), mkdir(dout); end

        subticklabels = {};
        for i = 1:size(char(MASKSTATS.mask(m).sub.con.imgfiles{:,1}),1)
            subticklabels{i} = regexprep(deblank(MASKSTATS.mask(m).sub.con.imgfiles{i,1}),'.*/([^/]*)/[^/]*','$1'); %#ok
            subticklabels{i} = sprintf('%s (%d)',subticklabels{i},i); %#ok
        end

        for o=1:numel(OP)
            switch OP{o}
                case {'mean' 'dot_product' 'centered_dot_product' 'cosine_similarity' 'correlation'}
                    % line plot (one line for each contrast, across subjects)

                    figure; plot(MASKSTATS.mask(m).sub.con.(OP{o}));
                    if strcmp(OP{o},'mean')
                        title(sprintf('subject level contrasts mean within %s',maskname),'interpreter','none');
                    else
                        title(sprintf('subject level contrasts %ss with %s',OP{o},maskname),'interpreter','none');
                    end
                    ylabel(sprintf('beta contrast %s',OP{o}),'interpreter','none');
                    xaxis_labeling('subjects',subticklabels)
                    fout = fullfile(dout,sprintf('contrasts_by_subject__%ss.png',OP{o}));
                    fprintf('saving: %s\n',fout);
                    saveas(gcf,fout);
                    close gcf

                    % bar plot (mean across subjects per contrast)
                    evalc(['barplot_columns2(MASKSTATS.mask(m).sub.con.(OP{o}),maskname,'...
                        '''labels'',MASKSTATS.mask(m).sub.con.name,' ...
                        '''ylabel'',OP{o},''plabels'',''robust'',''ind'');']);
                    fout = fullfile(dout,sprintf('group_mean_%s.png',OP{o}));
                    fprintf('saving: %s\n',fout);
                    scn_export_papersetup(500);
                    saveas(gcf,fout);
                    close gcf

                    %%% covariate plots
                    if ~isempty(cov)
                        subdout = fullfile(dout,'scatterplots');
                        if ~exist(subdout,'dir'), mkdir(subdout); end
                        for i = 1:size(cov,2)
                            for j = 1:size(MASKSTATS.mask(m).sub.con.(OP{o}),2)
                                figure;
                                plot(cov(:,i),MASKSTATS.mask(m).sub.con.(OP{o})(:,j),'o');
                                lsline;
                                xlabel(covname{i},'interpreter','none');
                                ylabel(sprintf('beta (meant within %s)',maskname),'interpreter','none');
                                title(sprintf('%s correlation with %s (r=%.4f, p<%.4f)',...
                                    MASKSTATS.mask(m).sub.con.name{j},covname{i},...
                                    MASKSTATS.mask(m).sub.covXcon.(OP{o}).rho(i,j),...
                                    MASKSTATS.mask(m).sub.covXcon.(OP{o}).pval(i,j)),...
                                    'interpreter','none');

                                scon = regexprep(lconname{j},'[()]','');
                                scon = regexprep(scon,'[^A-Za-z0-9_+.-]','');

                                gcov = regexprep(covname{i},'[()]','');
                                gcov = regexprep(gcov,'[^A-Za-z0-9_+.-]','');

                                fout = fullfile(subdout,sprintf('%s_%ss__by__%s.png',scon,OP{o},gcov));
                                fprintf('saving: %s\n',fout)
                                saveas(gcf,fout);
                                close gcf
                            end
                        end
                    end
            end
        end
    end
end

%% GROUP LEVEL STAT MAPS (beta)
if DO_GRPLEV
    fprintf('GROUP LEVELS\n')
    nhcons = size(SETUPS{1}.X,2);
    hbetafiles = {};
    hbetaname = cell(nhcons,1);
    for b = 1:nhcons
        % get name
        if exist('EXPT','var') && isfield(EXPT,'covnames')
            hbetaname{b} = EXPT.covnames{b};
        else
            hbetaname{b} = sprintf('hbeta%04d',b);
        end

        % get list of files
        temphbetafiles = {};
        for i = included_robustdirs
            temphbetafiles{end+1,1} = fullfile(robdirs{i},sprintf('rob_beta_%04d.img',b)); %#ok
        end
        hbetafiles{b} = temphbetafiles; %#ok
    end
    hbetafiles = horzcat(hbetafiles{:});

    % extract data
    hmaskstats = canlab_maskstats(MASK,hbetafiles,OP);

    % pack output struct
    for b = 1:nhcons
        for m=1:numel(MASK)
            for o=1:numel(OP)
                MASKSTATS.mask(m).grp.beta(b).(OP{o}) = hmaskstats(m).stats.(OP{o})(:,b);
            end
            MASKSTATS.mask(m).grp.beta(b).imgfiles = hmaskstats(m).imgfiles;
            MASKSTATS.mask(m).grp.beta(b).name = hbetaname{b};
        end
    end

    if DO_PLOTS
        dout = fullfile(plotdir,maskname);
        if ~exist(dout,'dir'), mkdir(dout); end

        fprintf('... MAKING plots\n')
        for m = 1:numel(MASK)
            conticklabels = {};
            for i = 1:numel(MASKSTATS.mask(m).sub.con.name)
                conticklabels{i} = MASKSTATS.mask(m).sub.con.name{i}; %#ok
            end

            for o = 1:numel(OP)
                X = [];
                for b = 1:nhcons
                    X = [X MASKSTATS.mask(m).grp.beta(b).(OP{o})]; %#ok
                end

                figure; bar(X,'group');
                title(sprintf('group level betas %s with %s',OP{o},maskname),'Interpreter','none');
                ylabel(sprintf('beta %s',OP{o}),'Interpreter','none');
                legend(strvcat(MASKSTATS.mask(m).grp.beta.name)); %#ok
                xaxis_labeling('subject level contrasts',conticklabels);
                fout = fullfile(dout,sprintf('group_level_betas_%s.png',OP{o}));
                fprintf('saving: %s\n',fout);
                saveas(gcf,fout);
                close gcf
            end
        end
    end
end

%% clean up
close all

if DO_PLOTS
    % save MASKSTATS
    for m = 1:numel(MASK)
        [ignore maskname] = fileparts(MASK{m}); %#ok
        dout = fullfile(plotdir,maskname);
        maskstats = MASKSTATS.mask(m); %#ok
        save(fullfile(dout,'raw_data'),'maskstats');
    end
end

end

%% slightly edited code from mathworks.com (search "matlab angled tick labels")
function xaxis_labeling(xaxislabel,ticklabels)

% reduce size of axis to fit labels
pos = get(gca,'Position');
set(gca,'Position',[pos(1), .2, pos(3) .65])

% set tick locations
Xt = 1:numel(ticklabels);
Xl = [0 numel(ticklabels)+1];
set(gca,'XTick',Xt,'Xlim',Xl);

ax = axis; % Current axis limits
axis(axis); % Set the axis limit modes to manual
Yl = ax(3:4); % Y-axis limits

% Place the text labels
t = text(Xt,Yl(1)*ones(1,numel(Xt)),ticklabels,'Interpreter','none');
set(t,'HorizontalAlignment','right','VerticalAlignment','top', ...
    'Rotation',45);

% Remove the default labels
set(gca,'XTickLabel','')

% get the Extent of each text object
for i = 1:length(ticklabels)
    ext(i,:) = get(t(i),'Extent'); %#ok
end

% Determine lower point for alignment of text
LowYPoint = min(ext(:,2));

% Place the axis label
XMidPoint = Xl(1) + abs(diff(Xl))/2;
text(XMidPoint,LowYPoint,xaxislabel,'VerticalAlignment','top','HorizontalAlignment','center');

% adjust width
OP = get(gca,'OuterPosition');
set(gcf,'OuterPosition',[OP(1) OP(2) numel(ticklabels)*50 OP(3)])

end


% EEG_INSERTBOUND - insert boundary event in an EEG event structure.
%
% Usage:
%       >> [eventout newind] = eeg_insertbound( eventin, pnts, ...
%                                                abslatency, duration);
% Required Inputs:
%   eventin    - EEGLAB event structure (EEG.event)
%   pnts       - data points in EEG dataset (EEG.pnts * EEG.trials)
%   abslatency - absolute latency of regions in original dataset. Can
%                also be an array of [beg end] latencies with one row
%                per region removed. Then 'lengths' argument is ignored.
% Optional Inputs:
%   lengths    - lengths of removed regions
%
% Outputs:
%   eventout   - EEGLAB event output structure with added boundaries
%   newind     - Indices of the new events
%
% Notes:
%   This function performs the following:
%   1) add boundary events to the 'event' structure;
%        remove nested boundaries;
%        recompute the latencies of all events.
%   2) all latencies are given in (float) data points.
%        e.g., a latency of 2000.3 means 0.3 samples (at EEG.srate)
%              after the 2001st data frame (since first frame has latency 0).
%
% Author: Arnaud Delorme and Hilit Serby, SCCN, INC, UCSD, April, 19, 2004
%
% See also: EEG_EEGREJ, POP_MERGESET

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

function [eventin, newind] = eeg_insertbound( eventin, pnts, regions, lengths)

    if nargin < 3
        help eeg_insertbound;
        return;
    end

    eeglab_options;
    regions = round(regions);
    regions(regions < 1) = 1;
    regions(regions > pnts) = pnts;
    for i=2:size(regions,1)
        if regions(i-1,2) >= regions(i,1)
            regions(i,1) = regions(i-1,2)+1;
        end
    end

    if ~isempty(regions)
        fprintf('eeg_insertbound(): %d boundary (break) events added.\n', size(regions, 1));
    else
        return;
    end

    % recompute latencies of boundevents (in new dataset)
    % ---------------------------------------------------
    [~, tmpsort] = sort(regions(:,1));
    regions        = regions(tmpsort,:);
    lengths = regions(:,2)-regions(:,1)+1;

    if ~isempty(eventin) && isfield(eventin, 'latency')
         eventLatencies = [ eventin.latency ];
         if length(eventLatencies) ~= length(eventin)
             error('Some events do not have latencies')
         end
    else
        eventLatencies = [];
    end
    newEventLatencies = eventLatencies;
    oriLen            = length(eventin);
    rmEvent           = [];
	for iRegion = 1:size(regions,1) % sorted in decreasing order

        % find event succeeding boundary to insert event
        % at the correct location in the event structure
        % ----------------------------------------------
        tmpind    = find( eventLatencies - regions(iRegion,1) > 0 );
        if ~isempty(newEventLatencies)
            newEventLatencies(tmpind) = newEventLatencies(tmpind)-lengths(iRegion);
        end

        % insert event
        % ------------
        [tmpnest, addlength ]  = findnested(eventin, eventLatencies, regions(iRegion,:));
        rmEvent = [ rmEvent tmpnest ];
        %if regions(iRegion,1) % do not remove first event

            eventin(end+1).type   = eeg_boundarytype(eventin);
            eventin(end).latency  = regions(iRegion,1)-sum(lengths(1:iRegion-1))-0.5;
            eventin(end).duration = lengths(iRegion,1)+addlength;
        %end
    end

    % copy latencies
    % --------------
    if ~isempty(newEventLatencies)
        for iEvent = 1:oriLen
            eventin(iEvent).latency = newEventLatencies(iEvent);
        end
    end
    eventin(rmEvent) = [];

    % resort events
    % -------------
    if ~isempty(eventin) && isfield(eventin, 'latency')
%       eventin([ eventin.latency ] < 1) = [];
        eventin([ eventin.latency ] < 0) = [];
        alllatencies = [ eventin.latency ];
        [~, sortind] = sort(alllatencies);
        eventin = eventin(sortind);
        newind = sortind(oriLen+1:end);
    end

    if ~isempty(rmEvent)
        fprintf('eeg_insertbound(): boundary events inserted and %d events removed.\n', length(rmEvent));
    end

% look for nested events
% return indices of nested events and
% their total length
% -----------------------------------
function [ indEvents, addlen ] = findnested(event, eventlat, region)
    indEvents = find( eventlat > region(1) & eventlat < region(2));

    if ~isempty(event) && isfield(event,'type') && isfield(event, 'duration')
        boundaryInd = eeg_findboundaries( event(indEvents) );
        addlen      = sum( [ event(indEvents(boundaryInd)).duration ] );
    else
        addlen = 0;
    end


function [AGC_atten_dB] = load_AGC_block( file_pre, first_rg_line, ...
                                        Nrg_lines_blk, block, UseMATfiles )
%
% -------------------------------------------------------------------------
% This program returns the RADARSAT-1 receiver attenuation values for
% one block.  They are integers from 2 to 17, in 1 dB steps.
% -------------------------------------------------------------------------
% file_pre        : File prefix ( path + prefix + block number)
% first_rg_line   : First range line for the extracted data
% Nrg_lines_blk   : Number of range lines in a block
% block           : the current block number
% UseMATfiles     : (0) using extract_AGC and the '_aux.dat' file
%                   (1) load 'AGC_attenuation_values.mat',
%                          which has the AGC values for every range line.
% -------------------------------------------------------------------------
% Created:  Nov 01, 2004  by Kaan Ersahin
% Modified: Nov 29, 2004  by Kaan Ersahin
% Modified: Dec 13, 2004  by Ian Cumming
%            - added UseMATfiles option
% -------------------------------------------------------------------------

if     UseMATfiles == 0  % extract AGC values from binary aux data file

    file_in = strcat(file_pre, '_aux.dat');
    tic
    AGC_atten_dB = extract_AGC( file_in, Nrg_lines_blk );
    toc

elseif UseMATfiles == 1   % load all AGC values from .mat file
    tic
    file_in = 'AGC_attenuation_values.mat'
    load( file_in )

    first = first_rg_line + (Nrg_lines_blk * (block-1));
    last  = first + Nrg_lines_blk - 1;
    AGC_atten_dB = nom_attenuation( first : last );
    toc
end

% Use check sum to verify read operation
% lenAGC = length(AGC_atten_dB);
% multfac = ([1:lenAGC] - lenAGC/2)';
% checksumAGC = sum( multfac .* AGC_atten_dB );
% fprintf('\nCheck sum of AGC values:%10.0f\n', checksumAGC )

%{
figure(201),   clf      %  Plot AGC values
plot( AGC_atten_dB ),   grid
axis([-9 length(AGC_atten_dB)+10  0.5 17.5])
xlabel('Azimuth (sample no. within this block)  \rightarrow', 'FontS', 12 )
ylabel('Magnitude  (dB)  \rightarrow', 'FontS', 12 )
title('AGC attenuation values for this block of data', 'FontS', 13 )
% text( lenAGC/2, 1.6, sprintf(...
%     '\nCheck sum of AGC values:%10.0f\n', checksumAGC ), 'Hor', 'c' )
%}
pause(0.1)


function netOutputs = fromDagNN(dag, customFn)
%FROMDAGNN Converts a DagNN/SimpleNN object to the AutoNN framework
%   OUTPUTS = Layer.fromDagNN(DAG) converts a MatConvNet DagNN object, DAG,
%   into the AutoNN framework (i.e., a set of recursively nested Layer
%   objects). SimpleNN is also supported.
%
%   Returns a cell array of Layer objects, each corresponding to an output
%   of the network. These can be composed with other layers, or compiled
%   into a Net object for training/evaluation.
%
%   To convert a SimpleNN, first use DagNN.fromSimpleNN and then use this
%   function.
%
%   Example:
%     layers = Layer.fromDagNN(myDag) ;
%     net = Net(layers{:}) ;
%     net.eval({'images', randn(5,5,1,3), 'labels', 1:3}) ;
%
%   OUTPUTS = Layer.fromDagNN(DAG, @CUSTOMFN) adds support for custom DagNN
%   blocks, by calling function CUSTOMFN to convert unknown DagNN blocks.
%
%   The function's signature is customFn(block, inputs, params), where
%   block is the DagNN layer object, inputs is a cell array with the AutoNN
%   input layers, and likewise for params.
%
%   The following example would convert a DagNN block of class myClass,
%   with 1 input and 1 parameter, to a AutoNN layer that calls function
%   myfunc:
%     function obj = customFn(block, inputs, params)
%       if isa(block, 'myClass')
%         f = Layer.fromFunction(@myfunc) ;
%         obj = f(inputs{1}, params{1}, 'alpha', block.alpha) ;
%       end
%     end

% Copyright (C) 2016-2017 Joao F. Henriques.
% All rights reserved.
%
% This file is part of the VLFeat library and is made available under
% the terms of the BSD license (see the COPYING file).

  if nargin < 2
    customFn = [] ;
  end

  % handle struct inputs (SimpleNN or serialized DagNN)
  if isstruct(dag)
    assert(isfield(dag, 'layers'), 'Invalid struct (must be DagNN or SimpleNN).') ;
    if iscell(dag.layers)  % SimpleNN
      dag = dagnn.DagNN.fromSimpleNN(dag, 'CanonicalNames', true) ;
    else
      % serialized DagNN
      % compatibility: first remove 'exBackprop' property, which does not
      % exist in recent versions
      for l = 1:numel(dag.layers)
        if isfield(dag.layers(l).block, 'exBackprop')
          dag.layers(l).block = rmfield(dag.layers(l).block, 'exBackprop') ;
        end
      end
      % from struct to DagNN object
      dag = dagnn.DagNN.loadobj(dag) ;
    end
  end

  assert(isa(dag, 'dagnn.DagNN'), 'Input must be a DagNN or SimpleNN.') ;

  % update DagNN variable indexes
  dag.rebuild() ;

  % like initParams, but does not overwrite them if already defined
  initUninitializedParams(dag) ;

  assert(all(cellfun(@isscalar, {dag.layers.outputIndexes})), ...
    'Layers with 0 or multiple outputs are not supported yet.') ;

  % one-to-out relationship between each layer and its output var
  idx = [dag.layers.outputIndexes] ;
  var2layer = zeros(size(dag.vars)) ;
  var2layer(idx) = 1:numel(idx) ;

  % Layer handle corresponding to each DAG layer, empty if not processed
  layers = cell(1, numel(dag.layers)) ;

  % create all Param objects
  allParams = cell(size(dag.params)) ;
  for k = 1:numel(dag.params)
    p = dag.params(k) ;
    allParams{k} = Param('name', p.name, 'value', p.value, ...
      'learningRate', p.learningRate, 'weightDecay', p.weightDecay, ...
      'trainMethod', p.trainMethod) ;
  end

  % create Input objects for network inputs, corresponding to sources in
  % the graph
  netInputsIdx = find([dag.vars.fanin] == 0) ;
  netInputs = cell(1, numel(netInputsIdx));
  for k = 1:numel(netInputsIdx)
    netInputs{k} = Input('name', dag.vars(netInputsIdx(k)).name) ;
    var2layer(netInputsIdx(k)) = numel(layers) + k ;  % Inputs are at the end of the layers list
  end
  layers = [layers, netInputs] ;

  % find sinks in the graph, these are the network outputs
  netOutputsIdx = find([dag.vars.fanout] == 0) ;

  % recursively process the layers, starting with the network outputs
  netOutputs = cell(1, numel(netOutputsIdx)) ;
  for o = 1:numel(netOutputsIdx)
    netOutputs{o} = convertLayer(dag, var2layer(netOutputsIdx(o))) ;
  end

  % copy meta properties to one of the Layers
  netOutputs{1}.meta = dag.meta ;

  % process a single layer, and recurse on its inputs
  function obj = convertLayer(dag, layerIdx)
    % if this layer has already been processed, return its handle
    if ~isempty(layers{layerIdx})
      obj = layers{layerIdx} ;
      return
    end

    layer = dag.layers(layerIdx) ;
    params = allParams(layer.paramIndexes) ;
    block = layer.block ;

    % recurse on inputs; they must be defined before this Layer
    inputVars = layer.inputIndexes ;
    inputs = cell(size(inputVars)) ;
    for i = 1:numel(inputVars)
      inputs{i} = convertLayer(dag, var2layer(inputVars(i))) ;
    end

    % now create a Layer with those inputs and parameters

    switch class(block)
    case 'dagnn.Conv'
      if isscalar(params), params{2} = [] ; end  % no bias

      % ommit arguments with default values, for conciseness
      args = {} ;
      if ~all(block.stride == 1)
        args(end+1:end+2) = {'stride', block.stride} ;
      end
      if ~all(block.pad == 0)
        args(end+1:end+2) = {'pad', block.pad} ;
      end
      if ~all(block.dilate == 1)
        args(end+1:end+2) = {'dilate', block.dilate} ;
      end

      obj = vl_nnconv(inputs{1}, params{1}, params{2}, args{:}, block.opts{:}) ;

    case 'dagnn.ConvTranspose'
      if isscalar(params), params{2} = [] ; end  % no bias

      obj = vl_nnconvt(inputs{1}, params{1}, params{2}, ...
        'upsample', block.upsample, 'crop', block.crop, ...
        'numGroups', block.numGroups, block.opts{:}) ;

    case 'dagnn.BatchNorm'
      % make sure the Params are not empty, but scalar
      defaults = single([0, 0, 1]) ;
      for i = 1:3
        if isempty(params{i}.value)
          params{i}.value = defaults(i) ;
        end
      end

      obj = vl_nnbnorm(inputs{1}, params{1}, params{2}, ...
        'moments', params{3}, 'epsilon', block.epsilon) ;

    case 'dagnn.Pooling'
      % ommit arguments with default values, for conciseness
      args = {} ;
      if ~all(block.stride == 1)
        args(end+1:end+2) = {'stride', block.stride} ;
      end
      if ~all(block.pad == 0)
        args(end+1:end+2) = {'pad', block.pad} ;
      end

      obj = vl_nnpool(inputs{1}, block.poolSize, ...
       'method', block.method, args{:}, block.opts{:}) ;

    case 'dagnn.ReLU'
      % ommit arguments with default values, for conciseness
      args = {} ;
      if block.leak ~= 0
        args(end+1:end+2) = {'leak', block.leak} ;
      end

      obj = vl_nnrelu(inputs{1}, args{:}, block.opts{:}) ;

    case 'dagnn.DropOut'
      obj = vl_nndropout(inputs{1}) ;

    case 'dagnn.Loss'
      obj = vl_nnloss(inputs{1}, inputs{2}, 'loss', block.loss, block.opts{:}) ;

    case 'dagnn.LRN'
      obj = vl_nnnormalize(inputs{1}, block.param) ;

    case 'dagnn.NormOffset'
      obj = vl_nnnoffset(inputs{1}, block.param) ;

    case 'dagnn.SpatialNorm'
      obj = vl_nnspnorm(inputs{1}, block.param) ;

    case 'dagnn.Sigmoid'
      obj = vl_nnsigmoid(inputs{1}) ;

    case 'dagnn.SoftMax'
      obj = vl_nnsoftmax(inputs{1}) ;
    case 'dagnn.GlobalPooling'
      obj = vl_nnglobalpool(inputs{1});
    case 'dagnn.Axpy'
      obj = vl_nnaxpy(inputs{1},inputs{2});
    case 'dagnn.Concat'
      obj = cat(block.dim, inputs{:}) ;

    case 'dagnn.Sum'
      obj = inputs{1} ;
      for i = 2:numel(inputs)
        obj = obj + inputs{i} ;
      end

    case 'dagnn.Crop'
      % replicate Crop block's functionality using simple operations
      A = inputs{1} ;
      B = inputs{2} ;

      sizeA1 = size(A,1);  % reuse this layer
      sizeA2 = size(A,2);
      cropv = sizeA1 - size(B,1) ;
      cropu = sizeA2 - size(B,2) ;
      cropv1 = max(0, cropv - block.crop(1)) ;
      cropu1 = max(0, cropu - block.crop(2)) ;

      crop = [cropv - cropv1, cropv1, cropu - cropu1, cropu1] ;
      obj = vl_nncrop(A, crop) ;

    otherwise
      if ~isempty(customFn)
        % handle custom blocks
        obj = customFn(block, inputs, params) ;
      else
        error(['Unknown block type ''' class(block) '''.']) ;
      end
    end

    obj.name = layer.name ;
    layers{layerIdx} = obj ;
  end
end

function initUninitializedParams(obj)
  % Same as INITPARAMS, but does not replace parameters that were already
  % initialized

  for l = 1:numel(obj.layers)
    p = obj.getParamIndex(obj.layers(l).params) ;
    params = obj.layers(l).block.initParams() ;
    switch obj.device
      case 'cpu'
        params = cellfun(@gather, params, 'UniformOutput', false) ;
      case 'gpu'
        params = cellfun(@gpuArray, params, 'UniformOutput', false) ;
    end

    for i = 1:numel(params)
      if isempty(obj.params(p(i)).value)
        obj.params(p(i)).value = params{i} ;
      end
    end
  end
end


function X = value(X)
% VALUE (overloaded)

X = reshape(value(sdpvar(X)),X.dim);


function visual_odo_main(visualDataFile,groundTruthFile)
%     NeuroSLAM System Copyright (C) 2018-2019
%     NeuroSLAM: A Brain inspired SLAM System for 3D Environments
%
%     Fangwen Yu (www.yufangwen.com), Jianga Shang, Youjian Hu, Michael Milford(www.michaelmilford.com)
%
%     The NeuroSLAM V1.0 (MATLAB) was developed based on the OpenRatSLAM (David et al. 2013).
%     The RatSLAM V0.3 (MATLAB) developed by David Ball, Michael Milford and Gordon Wyeth in 2008.
%
%     Reference:
%     Ball, David, Scott Heath, Janet Wiles, Gordon Wyeth, Peter Corke, and Michael Milford.
%     "OpenRatSLAM: an open source brain-based SLAM system." Autonomous Robots 34, no. 3 (2013): 149-176.
%
%     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/>.

    %% vo
    % for drawing images for estimating pitch and yaw
    global SUB_TRANS_IMG;
    global SUB_YAW_ROT_IMG;
    global SUB_HEIGHT_V_IMG;

    global ODO_IMG_TRANS_RESIZE_RANGE;
    global ODO_IMG_YAW_ROT_RESIZE_RANGE;
    global ODO_IMG_HEIGHT_V_RESIZE_RANGE;

    global PREV_TRANS_V_IMG_X_SUMS;
    global PREV_YAW_ROT_V_IMG_X_SUMS;
    global PREV_HEIGHT_V_IMG_Y_SUMS;

    global KEY_POINT_SET;
    global ODO_STEP;
    %% read imgs as VT input

    % get the visual data info
    [subFoldersPathSet, numSubFolders] = get_images_data_info(visualDataFile);

    % load ground truth data
    if nargin > 1
        [frameId, gt_x, gt_y, gt_z, gt_rx, gt_ry, gt_rz] = load_ground_truth_data(groundTruthFile);
        groundTruthData = [gt_x gt_y gt_z];
    end

    % define temp variables of previous profiles
    preProfilesTransImg = zeros(1,ODO_IMG_TRANS_RESIZE_RANGE(2));
    preProfilesYawRotImg = zeros(1,ODO_IMG_YAW_ROT_RESIZE_RANGE(2));
    preProfilesPitchRotImg = zeros(ODO_IMG_HEIGHT_V_RESIZE_RANGE(2), 1);

    % For drawing the current rot direction
    curRotDir = [0 0 0];
    curRotDir(1,3) = 0;

    % The total rotational angle
    sumRotAngle = 0;

    % for drawing the direction theta (rotational angle)
    theta = [0 0 0 0 0 0];
    rho = [0 0 0 0 0 0];
    startPoint =[0 0];
    endPoint = [0.8 0.8];

    % For drawing the velocity
    subRotVel = [];
    transVelVector = [];
    heightVelVector = [];
    sumHeight = [];
    transVelVector(1) = 0;
    heightVelVector(1) = 0;
    subRotVel(1) = 0;
    sumHeight = 0;

     % Transform the angle from degree to radian
    global DEGREE_TO_RADIAN;
    DEGREE_TO_RADIAN = pi / 180;

    % Transform the angle from radian to degree
    global RADIAN_TO_DEGREE;
    RADIAN_TO_DEGREE = 180 / pi;

    RENDER_RATE = 2;

    IMG_TYPE = '*.png';

    global OFFSET_YAW_ROT;
    offset_yaw_rot_vector = [];
    offset_yaw_rot_vector(1) = 0;

    global OFFSET_HEIGHT_V;
    offset_height_V_vector = [];
    offset_height_V_vector(1) = 0;

    odoMapTrajectory(1,1) = 0;
    odoMapTrajectory(1,2) = 0;
    odoMapTrajectory(1,3) = 0;
    odoMapTrajectory(1,4) = 0;

    ODO_MAP_X_SCALING = -1;
    ODO_MAP_Y_SCALING = 1;
    ODO_MAP_Z_SCALING = 1;
    GT_ODO_X_SCALING = 1;
    GT_ODO_Y_SCALING = 1;
    GT_ODO_Z_SCALING = 1;

    curFrame = 0;
    startFrame = 1;

    %% processing visual odometry
    for iSubFolder = 1 : numSubFolders

        [curFolderPath, imgFilesPathList, numImgs] = get_cur_img_files_path_list(subFoldersPathSet, IMG_TYPE, iSubFolder);

        if numImgs > 0
            for indFrame = startFrame :ODO_STEP: numImgs-1
                curFrame = curFrame + 1;
                [curImg] = read_current_image(curFolderPath, imgFilesPathList, indFrame);

                % visual templates and visual odo uses intensity so convert to grayscale
                curGrayImg = rgb2gray(curImg);
                curGrayImg = im2double(curGrayImg);

                % for drawing previous intensity
                preProfilesTransImg = PREV_TRANS_V_IMG_X_SUMS;
                preProfilesYawRotImg = PREV_YAW_ROT_V_IMG_X_SUMS;
                preProfilesHeightVImg = PREV_HEIGHT_V_IMG_Y_SUMS;

                %% step2: processing vo
                % get the odometry from the video

               % for common use
               %  [transV, yawRotV, heightV] = visual_odometry(curGrayImg);

                % for special use
                if length(KEY_POINT_SET) == 2
                    if indFrame < KEY_POINT_SET(1)
                        [transV, yawRotV, heightV] = visual_odometry(curGrayImg);
                    elseif indFrame < KEY_POINT_SET(2)
                        [transV, yawRotV, heightV] = visual_odometry_up(curGrayImg);
                    else
                        [transV, yawRotV, heightV] = visual_odometry(curGrayImg);
                    end
                    transV = 2;
                else
                    if indFrame < KEY_POINT_SET(1)
                        [transV, yawRotV, heightV] = visual_odometry(curGrayImg);
                    elseif indFrame < KEY_POINT_SET(2)
                        [transV, yawRotV, heightV] = visual_odometry_up(curGrayImg);
                    elseif indFrame < KEY_POINT_SET(3)
                        [transV, yawRotV, heightV] = visual_odometry(curGrayImg);
                    elseif indFrame < KEY_POINT_SET(4)
                        [transV, yawRotV, heightV] = visual_odometry_down(curGrayImg);
                    else
                        [transV, yawRotV, heightV] = visual_odometry(curGrayImg);
                    end
                end

                subRotVel(curFrame) = yawRotV;
                transVelVector(curFrame) = transV;
                heightVelVector(curFrame) = heightV;

                offset_yaw_rot_vector(curFrame) = OFFSET_YAW_ROT;
                offset_height_V_vector(curFrame) = OFFSET_HEIGHT_V;

                sumHeight(curFrame + 1) = sumHeight(curFrame) + heightV;

                % For drawing the total rotational angle
                if  sumRotAngle + yawRotV >= 360
                    sumRotAngle = mod(sumRotAngle + yawRotV, 360);
                elseif sumRotAngle + yawRotV <= -360
                    sumRotAngle = sumRotAngle + yawRotV + 360;
                else
                    sumRotAngle = sumRotAngle + yawRotV;
                end

                % Transform the vrot from degree to radian
                yawRotV = yawRotV * DEGREE_TO_RADIAN;

                % Direction by raw odometry
                curRotDir(curFrame + 1, 1) = cos(curRotDir(curFrame, 3) + yawRotV );
                curRotDir(curFrame + 1, 2) = sin(curRotDir(curFrame, 3) + yawRotV );
                curRotDir(curFrame + 1, 3) = curRotDir(curFrame, 3) + yawRotV;

                odoMapTrajectory(curFrame + 1,1) = odoMapTrajectory(curFrame,1) + transV * cos(sym(curRotDir(curFrame + 1, 3)));
                odoMapTrajectory(curFrame + 1,2) = odoMapTrajectory(curFrame,2) + transV * sin(sym(curRotDir(curFrame + 1, 3)));
                odoMapTrajectory(curFrame + 1,3) = odoMapTrajectory(curFrame,3) + heightV;
                odoMapTrajectory(curFrame + 1,4) = curRotDir(curFrame + 1, 3);

                % render results information
                if (mod(curFrame, RENDER_RATE) == 0)

                    % computing the normalized profile of horizontal translational image
                    profilesTransImg = sum(SUB_TRANS_IMG);
                    avgIntensity = sum(profilesTransImg) / size(profilesTransImg, 2);
                    profilesTransImg = profilesTransImg / avgIntensity;

                    % computing the normalized profile of rotational image
                    profilesYawRotImg = sum(SUB_YAW_ROT_IMG);
                    avgIntensity = sum(profilesYawRotImg) / size(profilesYawRotImg, 2);
                    profilesYawRotImg = profilesYawRotImg / avgIntensity;

%                     diffYawRotImgs = profilesYawRotImg - preProfilesYawRotImg;
                    % computing the normalized profile of vertical transV image
                    profilesHeightVImg = sum(SUB_HEIGHT_V_IMG,2);
                    avgIntensity = sum(profilesHeightVImg) / size(profilesHeightVImg, 1);
                    profilesHeightVImg = profilesHeightVImg / avgIntensity;

                    diffHeightVImgs = profilesHeightVImg - preProfilesHeightVImg;

                    figure(1)

                    % render the sub image for estimating horizontal translational velocity
                    subplot(3, 4, 1, 'replace');
                    imshow(SUB_TRANS_IMG, []);
                    title('Sub image (vHoriTrans)');
                    xlabel('Width');
                    ylabel('Height');
                    axis on

                    % draw the the normalized profile of horizontal translational image
                    subplot(3,4,5, 'replace');
                    hold on
                    plot(1: size(profilesTransImg,2),profilesTransImg, 'r');
                    plot(1: size(preProfilesTransImg,2),preProfilesTransImg, 'g');
                    hold off
%                     legend('curr', 'g: prev');
                    title('The intensity profiles');
                    xlabel('Width');
                    ylabel('Normalized profile');
                    axis on

                    % render the horizontal translational velocity
                    subplot(3, 4, 9, 'replace');
                    plot(transVelVector);
                    title('Translational velocity');
                    xlabel('Time');
                    ylabel('vTrans');
                    axis on

                    % render the sub image for estimating rotational velocity
                    subplot(3, 4, 2, 'replace');
                    imshow(SUB_YAW_ROT_IMG, []);
                    title('Sub image (vYawRot)');
                    xlabel('Width');
                    ylabel('Height');
                    axis on

                    % draw the the normalized profile of rotational image
                    subplot(3,4,6, 'replace');
                    hold on
                    plot(1: size(profilesYawRotImg,2),profilesYawRotImg, 'r');
                    plot(1: size(preProfilesYawRotImg,2),preProfilesYawRotImg, 'g');
                    hold off
%                     legend('r: curr', 'g: prev');
                    title('The intensity profiles');
                    xlabel('Width');
                    ylabel('Normalized profile');
                    axis on

                    % render the horizontal translational velocity
                    subplot(3, 4, 10, 'replace');
                    plot(subRotVel);
                    title('Yaw rotational velocity');
                    xlabel('Time');
                    ylabel('vYawRot');
                    axis on

                    % render the sub image for estimating pitch velocity
                    subplot(3, 4, 3, 'replace');
                    imshow(SUB_HEIGHT_V_IMG, []);
                    title('Sub image (vPitchRot)');
                    xlabel('Width');
                    ylabel('Height');
                    axis on

                    % draw the the normalized profile of height v image
                    subplot(3,4,7, 'replace');
                    hold on
                    plot(profilesHeightVImg, 1: size(profilesHeightVImg,1), 'r');
                    plot(preProfilesHeightVImg, 1: size(preProfilesHeightVImg,1), 'g');
                    hold off
%                     legend('r: curr', 'g: prev');
                    title('The intensity profiles');
                    xlabel('Width');
                    ylabel('Normalized profile');
                    axis on

                    % render the horizontal translational velocity
                    subplot(3, 4, 11, 'replace');
                    plot(heightVelVector);
                    title('Vertical translational velocity');
                    xlabel('Time');
                    ylabel('vVertTrans');
                    axis on

                    % render current rot direction
                    subplot(3, 4, 4, 'replace');
                    polar(theta,rho);
                    hold on
                    plot([startPoint(1) curRotDir(curFrame,1)],[startPoint(2) curRotDir(curFrame,2)],'linewidth',1,'color',[0.9 0 0]);
                    title('Current rotational direction');

                    % render the horizontal translational velocity
                    subplot(3, 4, 8, 'replace');
                    plot(sumHeight);
                    title('Height Change');
                    xlabel('Time');
                    ylabel('Height');
                    axis on

                    % render the horizontal translational velocity
                    subplot(3, 4, 12, 'replace');
                    hold on
                    plot(offset_yaw_rot_vector, 'r');
                    plot(offset_height_V_vector, 'g');
                    hold off
                    title('Offset yaw rotation');
                    xlabel('Time');
                    ylabel('Offset');
                    axis on

                    figure(2)

                    % draw odo map
                    subplot(1, 2, 1, 'replace');
                    hold on
                    plot3((odoMapTrajectory(:,2))* ODO_MAP_X_SCALING , ...
                        (odoMapTrajectory(:,1)) * ODO_MAP_Y_SCALING, ...
                        (odoMapTrajectory(:,3) ) * ODO_MAP_Z_SCALING, '.b');

%                     plot3((gt_x(1:curFrame) - gt_x(1)) * GT_ODO_X_SCALING, ...
%                         (gt_y(1:curFrame) - gt_y(1)) * GT_ODO_Y_SCALING, ...
%                         (gt_z(1:curFrame) - gt_z(1)) * GT_ODO_Z_SCALING, '.r');
                    hold off;
                    view(3);
                    grid on;
%                     view(0, 90);
                    xl = xlabel('odo-map-x');
                    yl = ylabel('odo-map-y');
                    zlabel('odo-map-z');
                    set(xl,'Rotation',15);
                    set(yl,'Rotation',-30);
                    title('Multilayered Odometry Map');
%                     legend('Result','Truth' ,'1');
%                     axis([-5 110 -5 100 -2 5]);
                    axis on
                    rotate3d on

                    % draw odo map
                    subplot(1, 2, 2, 'replace');
                    hold on
                    plot3((odoMapTrajectory(:,2))* ODO_MAP_X_SCALING , ...
                        (odoMapTrajectory(:,1)) * ODO_MAP_Y_SCALING, ...
                        (odoMapTrajectory(:,3) ) * ODO_MAP_Z_SCALING, '.b');

%                     plot3((gt_x(1:curFrame) - gt_x(1)) * GT_ODO_X_SCALING, ...
%                         (gt_y(1:curFrame) - gt_y(1)) * GT_ODO_Y_SCALING, ...
%                         (gt_z(1:curFrame) - gt_z(1)) * GT_ODO_Z_SCALING, '.r');
                    hold off;
                    grid on;
                    view(0, 90);
                    xlabel('odo-map-x');
                    ylabel('odo-map-y');
                    zlabel('odo-map-z');
                    title('Multilayered Odometry Map (top view)');
%                     legend('Result','Truth' ,'1');
%                      axis([-5 110 -5 100]);
                    axis on

%                     figure(3)
%
%                      subplot(1,1,1, 'replace');
%                     hold on
%                     plot(1: size(diffHeightVImgs,1),diffHeightVImgs, 'r');
%                     hold off
% %                     legend('r: curr', 'g: prev');
%                     title('The diff between two intensity profiles');
%                     xlabel('Width');
%                     ylabel('Diff');
%                     axis on

                    drawnow
                end
            end
        end
    end
end


function options = setoptions(varargin)

start = 1;
if (~isempty(varargin) && isstruct(varargin{1}))
    options = varargin{1};
    start = start+1;
else
    %defaults
    options.feature_select = 10000;
    options.subsample = 0.2;
    options.out_bag_estimate = false;
    options.lasso_alpha = 1.0;
end

for iter=start:2:length(varargin)

    if (strcmpi(varargin{iter},'SelectFeatures'))
        options.feature_select = varargin{iter+1};
    elseif (strcmpi(varargin{iter},'Subsample'))
        options.subsample = varargin{iter+1};
    elseif (strcmpi(varargin{iter},'LassoAlpha'))
        options.lasso_alpha = varargin{iter+1};
    elseif (strcmpi(varargin{iter},'OutBagEstimate'))
        options.subsample = varargin{iter+1};
    else
        error(['Unknown option ',varargin{iter}]);
    end

end;


% Textual representation of any MatLab value.

% Copyright 2009 Levente Hunyadi
function s = var2str(value)

if islogical(value) || isnumeric(value)
    s = num2str(value);
elseif ischar(value) && isvector(value)
    s = reshape(value, 1, numel(value));
elseif isjava(value)
    s = char(value);  % calls java.lang.Object.toString()
else
    try
        s = char(value);
    catch %#ok<CTCH>
        s = '[no preview available]';
    end
end


classdef ClassifierStuff < handle
  % Class that includes the classifier plus classifier metadata, like the
  % classifier training parameters

  properties
    type
    params
    timeStamp
    confThresholds % 1x2 vec
    scoreNorm % AL does windowdata.scoreNorm dup .scoreNorm?
    postProcessParams
    trainingParams
    %featureNames
    windowdata
    savewindowdata
    predictOnlyCurrentFly
    selFeatures
  end

  % store featureCodes instead
  properties (Dependent)

    featureNames

  end

  % use featureCode instead of featureNames to save space
  properties (Access = public)

    featureCodes
    perframeFeatureNames
    statNames
    transNames

  end

  methods

    function self = ClassifierStuff(varargin)

      self.type = 'boosting';
      self.params = struct('dim',cell(0,1), ...
                           'error',[], ...
                           'dir',[], ....
                           'tr',[], ...
                           'alpha',[]);
      self.timeStamp = 0;  % default time stamp (bad idea?)
      self.confThresholds = [0 0];
      self.scoreNorm = nan;  % default time stamp, number of days since Jan 0, 0000 (typically non-integer)
      self.postProcessParams.method = 'Hysteresis';
      self.postProcessParams.hystopts(1) = struct('name','High Threshold','tag','hthres','value',0);
      self.postProcessParams.hystopts(2) = struct('name','Low Threshold','tag','lthres','value',0);
      self.postProcessParams.filtopts(1) = struct('name','Size','tag','size','value',1);
      self.postProcessParams.blen = 1;
      self.trainingParams = ...
        struct('iter',100, ...
               'iter_updates',10, ...
               'numSample',2500, ...
               'numBins',30, ...
               'CVfolds',7, ...
               'baseClassifierTypes','Decision Stumps', ...
               'baseClassifierSelected',1,...
               'nselfeatures',1000);

      % KB 20201208
      % replace featureNames with a code, make featureNames be dependent
      % variable
      self.featureCodes = nan(0,5);
      self.perframeFeatureNames = {};
      self.statNames = {};
      self.transNames = {};
      self.windowdata = WindowData.windowdata(0);
      self.savewindowdata = false;
      self.predictOnlyCurrentFly = false;
      self.selFeatures =  SelFeatures.createEmpty();

      % varargin should be key-value pairs
      % Keys should be property names, values should be initialization
      % values for the corresponding property
      keys = varargin(1:2:end);
      values = varargin(2:2:end);

      % Process the keys, assigning to any that match properties, and
      % throwing an error if a key matches no property
      propertyNames = properties(self);
      for iKey=1:length(keys)
        key = keys{iKey};
        iProperty = whichstr(key,propertyNames);
        if isempty(iProperty)
          error('Classifier:noPropertyMatchingKey', ...
                'Classifier: No property matching key %s',key);
        else
          propertyName = propertyNames{iProperty};
          value = values{iKey};
          self.(propertyName) = value;
        end
      end
    end

    function tfmodified = modernize(self)
      tfmodified = false;
      for i = 1:numel(self)
        if isempty(self(i).savewindowdata)
          self(i).savewindowdata = false;
          tfmodified = true;
        end
      end

      % MK: May 09 2016
      for i = 1:numel(self)
        % AL: prob don't need to check isprop(...,'selFeatures') b/c the
        % property is in the class now -- any/all ClassifierStuffs running
        % with this version should get the prop
        if ~isprop(self(i),'selFeatures') || isempty(self(i).selFeatures),
          tfmodified = true;
          self(i).selFeatures = SelFeatures.createEmpty();
        else
          [self(i).selFeatures,sfmod] = SelFeatures.modernize(self(i).selFeatures);
          tfmodified = tfmodified || sfmod;
        end
      end

      % MK: May 09 2016
      for i = 1:numel(self)
        if ~isfield(self(i).trainingParams,'nselfeatures'),
          tfmodified = true;
          self(i).trainingParams.nselfeatures = 1000;
        end
      end

      % MK: May 09 2016
      for i = 1:numel(self)
        % AL: isprop etc
        if ~isprop(self(i),'predictOnlyCurrentFly') || isempty(self(i).predictOnlyCurrentFly),
          tfmodified = true;
          self(i).predictOnlyCurrentFly = false;
        end
      end

%       % MK: modernize window data.
%       wdmodified = false;
%       for i = 1:numel(self)
%           [self(i).windowdata,wdm] =  WindowData.modernize(self(i).windowdata);
%           if wdm,
%               wdmodified = true;
%           end
%       end
%       if wdmodified,
%           tfmodified = true;
%       end

      % MK: modernize window data.
      % AL: fixed for classifiers without windowdata
      wdmodified = false;
      for i = 1:numel(self)
          if ~isequal(self(i).windowdata,[])
              [self(i).windowdata,wdm] =  WindowData.modernize(self(i).windowdata);
              if wdm,
                  wdmodified = true;
              end
          end
      end
      if wdmodified,
          tfmodified = true;
      end

    end

    function set.featureNames(obj,featureNames)

      [obj.featureCodes,obj.perframeFeatureNames,obj.statNames,obj.transNames] = ...
        windowFeatureNames2Codes(featureNames);

    end

    function featureNames = get.featureNames(obj)
      featureNames = windowFeatureCodes2Names(obj.featureCodes,obj.perframeFeatureNames,obj.statNames,obj.transNames);
    end

  end

end


    sched = findResource('scheduler','configuration','local');
    pjob  = createParallelJob('configuration', 'local',...
                    'PathDependencies',{'/Users/ecliff/Desktop/lyrics/'});

    % Set the number of workers
    set(pjob, 'MinimumNumberOfWorkers', 3);
    set(pjob, 'MaximumNumberOfWorkers', 3);
    hndl=@systolicLyricsFilter;
    task= createTask(pjob, hndl, 1, {'mjam.wav'});

    submit(pjob);
    waitForState(pjob, 'finished');
    disp('Parallel Job is finished')
    results = getAllOutputArguments(pjob);
%
%  Clean up the job
%
  destroy(pjob);

    [tune, fs] = wavread('mjam.wav');
    disp('Play the original version')
    sound(tune, fs)

    disp('Play the filtered version')
    sound(results{3}, fs)


function class=fixTopology(classFile,datFile,outFile)
%
% class=fixTopology(classFile,datFile,outFile)
%
% Function: Given a mrGray .Class file and the corresponding unsegmented
% data from a .dat file, fixes the topology and writes the result to a new
% .Class file.
%
%
% Requires:
% removeBridgesMex (in Matlab path)
%
% HISTORY:
%   110202 ISS wrote it.
%   2002.11.13 RFD (bob@white.stanford.edu) cleaned up and updated to use
%   the new bridgeRemovalMex file.
%
%

warndlg('fixTopology has been replaced by mrgRemoveBridges.  Calling that program for you.')
mrgRemoveBridges(classFile,datFile,outFile);
return;

if ~exist('classFile','var')
    [f,p]=uigetfile('*.class;*.Class', 'Select a class file...');
    if(isnumeric(f) & f==0) return; end
    classFile = fullfile(p,f);
    disp(['Class file: ',classFile]);
end
if ~exist('datFile','var')
    [f,p]=uigetfile('*.dat;*.DAT', 'Select corresponding vAnatomy...');
    if(isnumeric(f) & f==0) return; end
    datFile = fullfile(p,f);
    disp(['Anatomy file: ',datFile]);
end
if(~exist('outFile','var') | isempty(outFile))
    [f,p]=uiputfile('*.class;*.Class', 'When finished, save Class file as...');
    if(isnumeric(f) & f==0) return; end
    outFile = fullfile(p,f);
    disp(['Output Class file: ',outFile]);
end

%-----------------------Read in presegmented data----------------------------
% Data is coming in permuted!
preSegClass = readClassFile(classFile,0,0);
preSegWhite = preSegClass.data;
preSegWhite(preSegClass.data~=16)=0;
% The handle removal function sets all non-zero values to white matter, so
% we can leave the 16's in there. We just need to set all non-white matter
% to zero.

%-----------------------Read in unsegmented data-----------------------------

[imageData,mmPerVox,img_dim]=readVolAnat(datFile);
% [ysize,xsize,zsize]=size(imageData);
% imageData=reshape(imageData,[ysize,xsize,zsize]);

% Permuting to match preseg data
imageData=permute(imageData,[2 1 3]);
scale = 225/256;
imageData = uint8(double(imageData)*scale);

%-----------------------Call removeBridgesMex----------------------------

if(isfield(preSegClass.header,'params') & length(preSegClass.header.params)>=6)
    % the 6 classification params are csf mean, gray mean, white mean,
    % noise stdev, confidence and smoothness.
    avgWhite = preSegClass.header.params(3)*scale;
    avgGray = preSegClass.header.params(2)*scale;
    threshold = mean([avgWhite,avgGray]);
else
    warning('Classification parameters not found in class file header- making some guesses...');
    avgWhite = mean(imageData(preSegWhite==16));
    threshold = 0.9*avgWhite;
    avgGray = 0.8*avgWhite;
end
tic;
corrected = removeBridgesMex(preSegWhite,imageData,avgWhite,threshold,avgGray);
toc;
% Voxels marked with 236 should be removed (make a cut).
% Voxels marked with 245 should be added.
% Voxels marked with 226 are 'alternative' voxels (what are they?).
% Voxels marked with 225 are unchanged voxels.
%preSegClass.data(corrected==236)=0;
%preSegClass.data(corrected==245 | corrected==225) = 16;

% For some reason, this works better than the previous, even though they
% *should* be equivalent. I suspect that the bridge removal code doesn't
% properly mark all the removed voxels. However, we need to put the CSF
% back in.
preSegWhite = uint8(corrected==225 | corrected==245);
preSegWhite(preSegWhite>0) = preSegClass.type.white;
preSegWhite(preSegClass.data==preSegClass.type.csf) = preSegClass.type.csf;
preSegClass.data = preSegWhite;

class = writeClassFile(preSegClass, outFile);

% class = writeClassFileFromRaw(preSegClass.data, outFile, [0,0; 16,16; 32,32; 48,48]);


function varargout=lpcdb2pf(varargin)
%
% For calling details please see v_lpcdb2pf.m
%
% This dummy routine is included for backward compatibility only
% and will be removed in a future release of voicebox. Please use
% v_lpcdb2pf.m in future and/or update with v_voicebox_update.m
%
%      Copyright (C) Mike Brookes 2018
%      Version: $Id: lpcdb2pf.m 10863 2018-09-21 15:39:23Z dmb $
%
if nargout
    varargout=cell(1,nargout);
    [varargout{:}]=v_lpcdb2pf(varargin{:});
else
    v_lpcdb2pf(varargin{:});
end


function formulas = printRxnFormula(model, varargin)
% Prints the reaction formulas for a list of reactions
%
% Reactions that have an upperbound <= 0 and lowerbound < 0 will have
% its directionality reversed unless directionFlag = false.
%
% USAGE:
%
%    formulas = printRxnFormula(model, varargin)
%
% INPUTS:
%    model:             COBRA model structure
%
% OPTIONAL INPUTS:
%
%    varargin:           Optional Inputs provided as 'ParameterName', Value
%                        pairs. the following parameternames are available:
%
%                       * rxnAbbrList:       Cell array of rxnIDs to be printed (Default = print all reactions)
%                       * printFlag:        Print formulas or just return them (Default = true)
%                       * lineChangeFlag:    Append a line change at the end of each line
%                                            (Default = true)
%                       * metNameFlag:       Print full met names instead of abbreviations
%                                            (Default = false)
%                       * fid:               Optional file identifier for printing in files
%                                            (default 1, i.e. stdout)
%                       * directionFlag:     Checks directionality of reaction. See Note.
%                                            (Default = false)
%                       * gprFlag:           Print gene protein reaction association
%                                            (Default = false)
%                       * proteinFlag:       Print the protein names associated with the genes in the
%                                            GPRs associated with the reactions. (Default = false)
%                       * printBounds:       Print the upper and lower Bounds of the reaction (Default = false)
%
%
% OUTPUT:
%    formulas:          Cell array containing formulas of specified reactions
%
% EXAMPLES:
%    1) print only ATPM and TKT1:
%       printRxnFormula(model,'rxnAbbrList',{'ATPM','TKT1'});
%    2) print the reactions of the model with the metabolite Names instead of ids.
%       printRxnFormula(model, 'metNameFlag',true);
%    3) print all reactions with the metabolite names to a given fileID
%       printRxnFormula(model, 'metNameFlag',true, 'fid', fileID);
%
% .. Authors:
%       - Markus Herrgard 11/17/05
%       - Ronan Fleming 04/30/08 (altered code since findRxnIDs used
%                                abbreviations not names of reactions)
%       - Jeff Ortn 10/11/09 (added metNameFlag option)
%       - Richard Que 03/10/10 (added lb < 0 requirement for reversing
%                              directionality)
%       - Ronan Fleming 21/11/14 (printing gpr optional)
%       - Thomas Pfau 15/12/14 (corrected line end)
%       - Ronan Fleming 16/07/16 (directionality not flipped by default
%                                anymore)
%       - Thomas Pfau May 2017 - Changed to Parameter value pair input

optionalParameters = {'rxnAbbrList','printFlag', 'lineChangeFlag', 'metNameFlag', 'fid', 'directionFlag', 'gprFlag', 'proteinFlag','printBounds'};
if (numel(varargin) > 0 && (~ischar(varargin{1}) || ~any(ismember(varargin{1},optionalParameters))))
    %We have an old style thing....
    %Now, we need to check, whether this is a formula, or a complex setup
        tempargin = cell(1,2*(numel(varargin)));

        for i = 1:numel(varargin)

                tempargin{2*(i-1)+1} = optionalParameters{i};
                tempargin{2*(i-1)+2} = varargin{i};
        end
        varargin = tempargin;

end

if ~isfield(model,'lb')
    model.lb=ones(size(model.S,2),1)*-inf;
end
if ~isfield(model,'ub')
    model.lb=ones(size(model.S,2),1)*inf;
end

parser = inputParser();
parser.addRequired('model',@isstruct) % we only check, whether its a struct, no details for speed
parser.addParamValue('rxnAbbrList',model.rxns,@(x) iscell(x) || ischar(x))
parser.addParamValue('printFlag',true,@(x) isnumeric(x) || islogical(x))
parser.addParamValue('lineChangeFlag',true,@(x) isnumeric(x) || islogical(x));
parser.addParamValue('metNameFlag',false,@(x) isnumeric(x) || islogical(x));
parser.addParamValue('fid',1, @isnumeric);
parser.addParamValue('directionFlag',false,@(x) isnumeric(x) || islogical(x));
parser.addParamValue('gprFlag',false,@(x) isnumeric(x) || islogical(x));
parser.addParamValue('proteinFlag',false,@(x) isnumeric(x) || islogical(x));
parser.addParamValue('printBounds',false,@(x) isnumeric(x) || islogical(x));

parser.parse(model,varargin{:})

model = parser.Results.model;
rxnAbbrList = parser.Results.rxnAbbrList;
printFlag = parser.Results.printFlag;
lineChangeFlag = parser.Results.lineChangeFlag;
metNameFlag = parser.Results.metNameFlag;
fid = parser.Results.fid;
directionFlag = parser.Results.directionFlag;
gprFlag = parser.Results.gprFlag;
proteinFlag = parser.Results.proteinFlag;
printBounds = parser.Results.printBounds;

if proteinFlag && ~isfield(model,'proteins')
    %If no proteins field is present, we will use the genes field.
    fprintf('Proteins requested, but no proteins Field exists in the model, using genes instead!\n');
    model.proteins = model.genes;
end

if metNameFlag && ~isfield(model,'metNames')
    %if we want to print the metNames, but they don't exist, just use the mets instead.
    fprintf('metNames requested, but no metNames Field exists in the model, using mets instead!\n');
    model.metNames = model.mets;
end

if (~iscell(rxnAbbrList))
    if (strcmp(rxnAbbrList, 'all'))
        rxnAbbrList = model.rxns;
    else
        rxnAbbrTmp = rxnAbbrList;
        clear rxnAbbrList;
        rxnAbbrList{1} = rxnAbbrTmp;
    end
end

formulas = cell(size(rxnAbbrList));

for i = 1:length(rxnAbbrList)

    rxnAbbr = rxnAbbrList{i};

    rxnID = findRxnIDs(model, rxnAbbr);

    if (rxnID > 0)

        Srxn = full(model.S(:, rxnID));

        if directionFlag && (isfield(model, 'ub') && model.ub(rxnID) <= 0) && (isfield(model, 'lb') && model.lb(rxnID) < 0)
            Srxn = -Srxn;
        end

        Sprod = (Srxn(Srxn > 0));
        if metNameFlag
            prodMets = model.metNames(Srxn > 0);
        else
            prodMets = model.mets(Srxn > 0);
        end

        Sreact = (Srxn(Srxn < 0));
        if metNameFlag
            reactMets = model.metNames(Srxn < 0);
        else
            reactMets = model.mets(Srxn < 0);
        end

        formulaStr = '';
        for j = 1:length(reactMets)
            if (j > 1)
                formulaStr = sprintf('%s+ ', formulaStr);
            end
            if (abs(Sreact(j)) ~= 1)
                formulaStr = sprintf('%s%g %s ', formulaStr, abs(Sreact(j)), reactMets{j});
            else
                formulaStr = sprintf('%s%s ', formulaStr, reactMets{j});
            end
        end

        if (model.lb(rxnID) < 0)
            formulaStr = sprintf('%s <=> ', formulaStr);
        else
            formulaStr = sprintf('%s -> ', formulaStr);
        end

        for j = 1:length(prodMets)
            if (j > 1)
                formulaStr = sprintf('%s+ ', formulaStr);
            end
            if (Sprod(j) ~= 1)
                formulaStr = sprintf('%s%g %s ', formulaStr, abs(Sprod(j)), prodMets{j});
            else
                formulaStr = sprintf('%s%s ', formulaStr, prodMets{j});
            end
        end
        formulas{i} = formulaStr;
        if (printFlag)
            formulaStr = regexprep(formulaStr, ' <=> ', '\t<=>\t');
            formulaStr = regexprep(formulaStr, ' -> ', '\t->\t');
            fprintf(fid, '%s\t%s', rxnAbbr, formulaStr);
            if gprFlag
                if (isfield(model, 'grRules'))
                    fprintf(fid, '\t%s', model.grRules{rxnID});
                elseif (isfield(model,'rules'))
                    rule = regexprep(model.rules{rxnID},'|','or');
                    rule = regexprep(rule,'&','and');
                    rule = regexprep(rule,'x\((?<id>[0-9]+)\)','${model.genes{num2str($1)}}');
                    fprintf(fid, '\t%s', rule);
                end
            end
            if proteinFlag
                if (isfield(model, 'rules'))
                    rule = regexprep(model.rules{rxnID},'|','or');
                    rule = regexprep(rule,'&','and');
                    rule = regexprep(rule,'x\((?<id>[0-9]+)\)','${model.proteins{num2str($1)}}');
                    fprintf(fid, '\t%s', rule);
                end
            end
            if printBounds
                fprintf('\tlb:%f\tub:%f',model.lb(rxnID),model.ub(rxnID));
            end
        end
    else
        if (printFlag)
            fprintf(fid, 'not in model');
        end
        formulaStr = 'NA';
        formulas{i} = formulaStr;
    end
    if (lineChangeFlag && printFlag)
        fprintf(fid, '\n');
    end
end

% %pass out a character string if only one reaction in the abbreviation list
% if length(rxnAbbrList)==1
%     formulas=formulas{1};
% end