function outcl = merge_clusters(c2m, subcl)
% Function for synchronizing the field list of cluster structures
% and merging them
%
% :Usage:
% ::
%
%    subclusters = merge_clusters(clusters_to_match, subclusters_to_change)
%

    if isempty(c2m)
        disp('Merge clusters: Clusters to match is empty.');
        outcl = subcl;
        return
    end

    if isempty(subcl)
        outcl = c2m;
        return
    end

    if ~iscell(subcl)
        s{1} = subcl;
        subcl = s;
    end
    outcl = c2m;

    for i = 1:length(subcl)
        for j = 1:length(subcl{i})
            % make all field names match.
            newcl{i}(j) = c2m(1);
            N = fieldnames(newcl{i}(j));
            for NN = 1:length(N)
                eval(['newcl{i}(j).' N{NN} ' = [];'])
            end

            %N = fieldnames(clusters(i));
            for NN = 1:length(N)
                str = ['newcl{i}(j).' N{NN} ' = subcl{i}(j).' N{NN} ';'];
                try
                    eval(str)
                catch
                    disp(['Making ' N{NN}])
                    str = ['newcl{i}(j).' N{NN} ' = [];'];
                    eval(str)
                end
            end
        end

        outcl = ([outcl, newcl{i}]);
    end
end


%SUBSREF Subscripted reference. Dataset overload
%
%
% $Id: subsref.m,v 1.20 2010/06/01 08:47:05 duin Exp $


clear;
netStruct = load('../data/res52_batch32_Rankloss_2:1:0.1_margin0.5_img0.75_shift_hard_256/net-epoch-60.mat');
net = dagnn.DagNN.loadobj(netStruct.net);
clear netStruct;
net.mode = 'test' ;
net.move('gpu') ;
net.removeLayer('RankLoss');  % If you test Stage I model, please comment it.
net.conserveMemory = true;
im_mean = reshape(net.meta.normalization.averageImage,1,1,3);
load('../dataset/Flickr30k-prepare/url_data.mat');

% val 2 test 3
p = imdb.images.data(imdb.images.set==3);
ff = [];
%%------------------------------
for i = 1:1000
    disp(i);
    str = [ p{i}];
    %im = imread(['./Flickr30k/flickr30k-images-300/',str(34:end)]);
    im = imread(['.',str]);
    oim = im;   % or oim = im;
    f = getFeature2(net,oim,im_mean,'data','fc1_1bn');
    f = sum(sum(f,1),2);
    f2 = getFeature2(net,fliplr(oim),im_mean,'data','fc1_1bn');
    f2 = sum(sum(f2,1),2);
    f = f+f2;
    size4 = size(f,4);
    f = reshape(f,[],size4)';
    f = norm_zzd(f);
    ff = cat(1,ff,f);
end

save('../test/resnet_flikr30k_pool5_img.mat','ff','-v7.3');
%}

ff = [];
load('../dataset/Flickr30k-prepare/dense_feature_word2.1.mat');
% val 2 test 3
test_set = find(imdb.images.set==3);

for i = 1:1000
    disp(i);
    for j=1:5
        index = 5*test_set(i)-5+j;
        %get vector
        content = wordcnn(:,index);
        len = sum(wordcnn(:,index)>0);
        txtinput = zeros(len,20074,'single');
        for k=1:len %32
            txtinput(k,content(k))=1;
        end
        %transfer it to different location
        win = 33-len;
        input = zeros(32,20074,win,'single');
        for kk = 1:win
            input(kk:kk+len-1,:,kk) = txtinput;
        end

        input = reshape(input,1,32,20074,[]);
        f = getFeature2(net,input,[],'data2','fc5_2bn');
        f = sum(f,4);
        size4 = size(f,4);
        f = reshape(f,[],size4)';
        f = norm_zzd(f);
        ff = cat(1,ff,f);
    end
end
save('../test/resnet_flikr30k_pool5_txt.mat','ff','-v7.3');
%}
evaluate;


function fbcEnabled = isFbcEnabled()
% Checks whether the version of libSBML has been built with
% the FBC package extension enabled

% Filename    : isFbcEnabled.m
% Description : check fbc status
% Author(s)   : SBML Team <sbml-team@googlegroups.com>
% Organization: EMBL-EBI, Caltech
% Created     : 2011-02-08
%
% This file is part of libSBML.  Please visit http://sbml.org for more
% information about SBML, and the latest version of libSBML.
%
% Copyright (C) 2013-2018 jointly by the following organizations:
%     1. California Institute of Technology, Pasadena, CA, USA
%     2. EMBL European Bioinformatics Institute (EMBL-EBI), Hinxton, UK
%     3. University of Heidelberg, Heidelberg, Germany
%
% Copyright (C) 2009-2011 jointly by the following organizations:
%     1. California Institute of Technology, Pasadena, CA, USA
%     2. EMBL European Bioinformatics Institute (EMBL-EBI), Hinxton, UK
%
% Copyright (C) 2006-2008 by the California Institute of Technology,
%     Pasadena, CA, USA
%
% Copyright (C) 2002-2005 jointly by the following organizations:
%     1. California Institute of Technology, Pasadena, CA, USA
%     2. Japan Science and Technology Agency, Japan
%
% This library 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.  A copy of the license
% agreement is provided in the file named "LICENSE.txt" included with
% this software distribution and also available online as
% http://sbml.org/software/libsbml/license.html

% assume not enabled
fbcEnabled = 0;

if (isoctave() == '0')
  filename = fullfile(tempdir, 'fbc.xml');
else
  filename = fullfile(pwd, 'fbc.xml');
end;
writeTempFile(filename);

try
  [m, e] = TranslateSBML(filename, 1, 0);

  if (length(e) == 0 && isfield(m, 'fbc_version') == 1 )
    fbcEnabled = 1;
  end;

  delete(filename);

catch

  delete(filename);

  return
end;

function writeTempFile(filename)

fout = fopen(filename, 'w');

fprintf(fout, '<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n');
fprintf(fout, '<sbml xmlns=\"http://www.sbml.org/sbml/level3/version1/core\" ');
fprintf(fout, 'xmlns:fbc=\"http://www.sbml.org/sbml/level3/version1/fbc/version1\" ');
fprintf(fout, 'level=\"3\" version=\"1\" fbc:required=\"false\">\n');
fprintf(fout, '  <model/>\n</sbml>\n');

fclose(fout);


function trxGraphicParams=cookTrxGraphicParams(trxGraphicParamsRaw)

trxGraphicParams=cookMarkerParams(trxGraphicParamsRaw);
if isfield(trxGraphicParamsRaw,'colormap') && ~isempty(trxGraphicParamsRaw.colormap)
  trxGraphicParams.colormap = trxGraphicParamsRaw.colormap;
else
  trxGraphicParams.colormap = 'jet';
end
if isfield(trxGraphicParamsRaw,'colormap_multiplier') && ~isempty(trxGraphicParamsRaw.colormap_multiplier)
  trxGraphicParams.colormap_multiplier = trxGraphicParamsRaw.colormap_multiplier;
else
  trxGraphicParams.colormap_multiplier = 0.7;
end

if isfield(trxGraphicParamsRaw,'assignment') && ~isempty(trxGraphicParamsRaw.assignment)
  trxGraphicParams.assignment = trxGraphicParamsRaw.assignment;
else
  trxGraphicParams.assignment = 'random';
end

end


if(exist('D:/Datasets/Bosphorus/', 'file'))
    Bosphorus_dir = 'D:\Datasets\Bosphorus/';
elseif(exist('E:/datasets/Bosphorus/', 'file'))
    Bosphorus_dir = 'E:\datasets\Bosphorus/';
else
    fprintf('Bosphorus dataset location not found (or not defined)\n');
end

hog_data_dir = ['E:\Datasets\face_datasets_processed\bosph'];

all_recs = dir([Bosphorus_dir, '/BosphorusDB/BosphorusDB/bs*']);
all_recs_mat = cat(1, all_recs.name);
all_recs = cell(numel(all_recs), 1);

for i=1:size(all_recs_mat,1)

    all_recs{i} = all_recs_mat(i,:);

end

devel_recs = all_recs(1:3:end);
train_recs = setdiff(all_recs, devel_recs);


classdef DualUpdaterFactory < handle

    methods (Access = public, Static)

        function d = create(cParams)
            switch cParams.type
                case 'AlternatingPrimalDual'
                    d = DualUpdater_AugmentedLagrangian(cParams);
                case 'LagrangeMultiplierEstimation'

                case 'NullSpace'
                    d = DualUpdater_NullSpace(cParams);
            end
        end

    end

end


classdef FeatureVocabularyForSelectFeatures < handle

  % instance variables
  properties
%     jld  % instance of JLabelData, a ref
%     scoreFeatures  % the scores-as-input structure, detailing the files that
%                    % hold classifiers to be used as inputs, and their
%                    % per-frame feature names
%     windowFeatureParams
%    featureLexicon  % the dictionary of possible per-frame features
    wfParamsFromAmount  % scalar structure holding the window feature parameters
                        % that go with each of the possible "amount" levels.
                        % e.g. 'normal', 'more', 'less'.
    pfCategoriesFromName  % scalar structure.  Each field is the name of a
                          % per-frame feature, and the value is a cell
                          % array of strings, telling what categories that
                          % per-frame feature is in.  Most PFs are in only
                          % one category, but some are in more than one.
    pfCategoryNames  % a cell array of strings, holding the names of all the
                     % possible per-frame feature categories
    subdialectPFNames
      % A cell array of strings, containing the names of all the
      % to-be-calculated per-frame features.  This includes the
      % scores-as-input PFs.  I like to call this the "subdialect".
    defaultPFTransTypesFromName
      % A structure, each field is a per-frame feature, and each value is a cell
      % array of strings holding the default translation types
      % for that per-frame feature  (formerly transType)
    vocabulary
      % a structure where each field is a per-frame feauture, and the value
      % holds the window feature parameters for that PF.
      % Should have as many fields as there are per-frame features in the
      % to-be-calculated vocabulary (the subdialect).  vocabulary.(perframeFeatureName).enabled is a boolean
      % indicating whether that per-frame feature is in the vocabulary.
      % (formerly data)
  end  % properties

  % things that look like instance vars from the outside, but
  % which are calculated on-the-fly
  properties (Dependent=true)
    wfAmounts
  end

  % class constants
  properties (Access=public,Constant=true)
    % Window features come in different types.  Below is a list of the
    % different types.  (formerly windowComp)
    wfTypes = {'default','mean','min','max','hist','prctile',...
               'change','std','harmonic','diff_neighbor_mean',...
               'diff_neighbor_min','diff_neighbor_max','zscore_neighbors'};
    % Window features have parameters.  Below are the names of those parameters.
    % These parameters exist for each WF type.  (They should arguably be
    % called the "standard" parameters, or the "common" parameters,
    % because they exist for all WF types.  Whereas the extra parameters,
    % below, are each particular to one WF type.
    % (Formerly winParams.)
    wfParamNames = {'max_window_radius','min_window_radius','nwindow_radii', ...
                    'trans_types','window_offsets'};
%     % These are the default values for the window feature parameters.  (Formerly defaultWinParams.)
%     % They are only used if the lexicon fails to specify pre-set
%     % window-feature amounts (e.g. normal, more, less)
%     defaultWFParams = {10,1,3,{'none'},0};
    % A Window feature type can sometimes have an extra parameter.  Below are the names
    % of those parameters, one for each WF type.  WF types without an extra param have the
    % empty string for their entry.  (Formerly winextraParams.)
    wfExtraParamNames = {'','','','','hist_edges','prctiles','change_window_radii', ...
                         '','num_harmonic','','','',''};
%     % Below are the default values for the extra window feature parameters.  (Formerly winextraDefaultParams.)
%     % They are only used if the lexicon fails to specify pre-set
%     % window-feature amounts (e.g. normal, more, less)
%     defaultWFExtraParams = {[],[],[],[],[-400000 0 40000],[5 10 30 50 70 90 95],[1 3],...
%                             [],2,[],[],[],[]};
  end  % class constants

  % class methods
  methods (Access=public,Static=true)
    % ---------------------------------------------------------------------
    function wfParamsFromAmount=computeWFParamsFromAmount(featureLexicon)
      % Specify some constants we need
      % Below are the default values for the window feature parameters.  (Formerly defaultWinParams.)
      % They are only used if the lexicon fails to specify pre-set
      % window-feature amounts (e.g. normal, more, less)
      defaultWFParams = {10,1,3,{'none'},0};
      % Below are the default values for the extra window feature parameters.  (Formerly winextraDefaultParams.)
      % They are only used if the lexicon fails to specify pre-set
      % window-feature amounts (e.g. normal, more, less)
      defaultWFExtraParams = {[],[],[],[],[-400000 0 40000],[5 10 30 50 70 90 95],[1 3],...
                              [],2,[],[],[],[]};

      % Get some class constants we'll need
      wfTypes = ...
        FeatureVocabularyForSelectFeatures.wfTypes;
      wfParamNames = ...
        FeatureVocabularyForSelectFeatures.wfParamNames;
      wfExtraParamNames = ...
        FeatureVocabularyForSelectFeatures.wfExtraParamNames;
      % Read the default parameters for different categories.
      wfAmounts = fieldnames(featureLexicon.defaults);
      if isempty(wfAmounts)
        % If no preset amounts have been specified in the lexicon file,
        % fall back on the built-in defaults.
        wfAmount='default';
        wfParams = struct;
        % Set the parameters for the WFs of type 'default'
        wfParams.default.enabled = true;
        for ndx = 1:numel(wfParamNames)
          wfParamName = wfParamNames{ndx};
          wfParams.default.values.(wfParamName) = defaultWFParams{ndx};
        end
        wfParams.default.values.sanitycheck = false;
        % Set the parameters for the other WF types
        for ndx = 2:numel(wfTypes)
          wfType = wfTypes{ndx};
          wfParams.(wfType).enabled = false;
          for paramIndex = 1:numel(wfParamNames)
            wfParamName = wfParamNames{paramIndex};
            wfParams.(wfType).values.(wfParamName) = defaultWFParams{paramIndex};
          end
          if ~isempty(wfExtraParamNames{ndx})
            extraParamName = wfExtraParamNames{ndx};
            wfParams.(wfType).values.(extraParamName) = defaultWFExtraParams{ndx};
          end
        end
        wfParamsFromAmount.(wfAmount) = wfParams;
      else
        % The usual case --- preset amounts are defined in the lexicon.
        for wfAmountIndex = 1:numel(wfAmounts)
          wfAmount=wfAmounts{wfAmountIndex};
          wfParams = struct;
          wfParamsThisAmountRaw = featureLexicon.defaults.(wfAmount);
          % Set the parameters for the WFs of type 'default'
          wfParams.default.enabled = true;
          for ndx = 1:numel(wfParamNames)
            wfParamName = wfParamNames{ndx};
            wfParams.default.values.(wfParamName) = wfParamsThisAmountRaw.(wfParamName);
          end
          wfParams.default.values.sanitycheck = false;
          % Set the parameters for the other WF types
          for ndx = 2:numel(wfTypes)
            % Copy the standard WF params for this WF type
            wfType = wfTypes{ndx};
            for paramIndex = 1:numel(wfParamNames)
              wfParamName = wfParamNames{paramIndex};
              if isfield(wfParamsThisAmountRaw,wfType) && isfield(wfParamsThisAmountRaw.(wfType),wfParamName)
                wfParams.(wfType).values.(wfParamName) = wfParamsThisAmountRaw.(wfType).(wfParamName);
              else
                % Use the common value
                wfParams.(wfType).values.(wfParamName) = wfParamsThisAmountRaw.(wfParamName);
              end
            end
            % Copy the extra WF parameter, if present for this WF type
            if ~isempty(wfExtraParamNames{ndx})
              extraParamName = wfExtraParamNames{ndx};
              if isfield(wfParamsThisAmountRaw,wfType) && isfield(wfParamsThisAmountRaw.(wfType),extraParamName)
                % Use a value from the preset, if present
                wfParams.(wfType).values.(extraParamName) = wfParamsThisAmountRaw.(wfType).(extraParamName);
              else
                % If not present, fall back to the default default
                wfParams.(wfType).values.(extraParamName) = defaultWFExtraParams{ndx};
              end
            end
            % If this WF type is included in the lexicon preset, enable the WF type
            wfParams.(wfType).enabled=isfield(wfParamsThisAmountRaw,wfType);
            % If this WF type is included in the lexicon preset, check for any WF
            % parameter names besides the usual standard and extra ones,
            % and include them in the processed preset parameters.
            % This seems weird.  Maybe we can leave it out?
            % if isfield(wfParamsThisAmount,wfType)
            %   wfParamNamesThisAmount = fieldnames(wfParamsThisAmount.(wfType));
            %   for dndx = 1:numel(wfParamNamesThisAmount)
            %     if ismember(wfParamNamesThisAmount{dndx},wfParamNames) ,
            %       wfParamName = wfParamNamesThisAmount{dndx};
            %       wfParams.(wfType).values.(wfParamName) = wfParamsThisAmount.(wfType).(wfParamName);
            %     end
            %   end
            % end
          end
          wfParamsFromAmount.(wfAmount) = wfParams;
        end
      end
    end  % method

    % ---------------------------------------------------------------------
    function defaultPFTransTypesFromName= ...
        defaultPFTransTypesFromNameFromFeatureLexicon(featureLexicon,scoreFeatures)
      % Calculate the structure that maps PF names to transformation types.

      % Get the list of all the per-frame features in the lexicon,
      % including the scores-as-inputs ones
      lexiconPFNames=fieldnames(featureLexicon.perframe);
      scorePFNames = {scoreFeatures(:).scorefilename};
      subdialectPFNames=[lexiconPFNames;scorePFNames'];

      % Make one entry in the result structure per PF
      defaultPFTransTypesFromName = struct;
      for i = 1:length(subdialectPFNames)
        pfName = subdialectPFNames{i};
        if ismember(pfName,scorePFNames) ,
          transTypesThis = {'none'};
        else
          transTypesThisRaw = featureLexicon.perframe.(pfName).trans_types;
          if ischar(transTypesThisRaw)
            transTypesThis = {transTypesThisRaw};
          else
            transTypesThis = transTypesThisRaw;
          end
        end
        defaultPFTransTypesFromName.(pfName)=transTypesThis;
      end  % for
    end  % method

    % ---------------------------------------------------------------------
    function pfCategoriesFromName= ...
        pfCategoriesFromNameFromFeatureLexicon(featureLexicon,scoreFeatures)
      % Calculate the structure that maps a PF name to the PF categories
      % that it belongs to.

      % Get the list of all the per-frame features in the lexicon,
      % including the scores-as-inputs ones
      lexiconPFNames=fieldnames(featureLexicon.perframe);
      scorePFNames = {scoreFeatures(:).scorefilename};
      subdialectPFNames=[lexiconPFNames;scorePFNames'];

      % Make one entry in the result structure per PF
      pfCategoriesFromName = struct;
      for i = 1:length(subdialectPFNames)
        pfName = subdialectPFNames{i};
        if ismember(pfName,scorePFNames) ,
          pfCategoriesThis = {'scores'};
        else
          pfCategoriesThisRaw = featureLexicon.perframe.(pfName).type;
          if ischar(pfCategoriesThisRaw)
            pfCategoriesThis = {pfCategoriesThisRaw};
          else
            pfCategoriesThis = pfCategoriesThisRaw;
          end
        end
        pfCategoriesFromName.(pfName)=pfCategoriesThis;
      end  % for
    end  % method

    % ---------------------------------------------------------------------
    function pfCategoryNames=pfCategoryNamesFromFeatureLexicon(featureLexicon)
      fallpf = fieldnames(featureLexicon.perframe);
      pfCategoryNames = {};
      for pfndx = 1:numel(fallpf)
        curpf = fallpf{pfndx};
        curtypes  = featureLexicon.perframe.(curpf).type;
        if ischar(curtypes)
          curT = curtypes;
          if ~any(strcmp(pfCategoryNames,curT))
            pfCategoryNames{end+1} = curT;  %#ok
          end
        else
          for tndx = 1:numel(curtypes)
            curT = curtypes{tndx};
            if ~any(strcmp(pfCategoryNames,curT))
              pfCategoryNames{end+1} = curT;  %#ok
            end
          end
        end
      end
      if ~any(strcmp(pfCategoryNames,'scores')),
        pfCategoryNames{end+1} = 'scores';
      end
    end  % method

  end  % class methods

  % -----------------------------------------------------------------------
  % public instance methods
  methods
    % ---------------------------------------------------------------------
    function self=FeatureVocabularyForSelectFeatures(featureLexicon, ...
                                                     scoreFeatures, ...
                                                     subdialectPFNames, ...
                                                     windowFeatureParams, ...
                                                     maxWindowRadiusCommon)
      % Constructor.  jld an instance of JLabelData, maxWindowRadiusCommon
      % a value to be set for max_window_radius for all PFs, all WF types,
      % and all preset WF amounts.

      % Set a bunch of things that are derived from the constructor input,
      % but stay constant for the life of the object
      self.wfParamsFromAmount= ...
        FeatureVocabularyForSelectFeatures.computeWFParamsFromAmount(featureLexicon);
      self.defaultPFTransTypesFromName= ...
        FeatureVocabularyForSelectFeatures.defaultPFTransTypesFromNameFromFeatureLexicon(featureLexicon, ...
                                                                                scoreFeatures);
      self.pfCategoriesFromName= ...
        FeatureVocabularyForSelectFeatures.pfCategoriesFromNameFromFeatureLexicon(featureLexicon, ...
                                                                                scoreFeatures);
      self.pfCategoryNames= ...
        FeatureVocabularyForSelectFeatures.pfCategoryNamesFromFeatureLexicon(featureLexicon);

      % Populate the list of per-frame feature names
      self.subdialectPFNames = subdialectPFNames;

      % Populate the feature vocabulary proper
      enabledPFs = fieldnames(windowFeatureParams);
      wfParamNames = self.wfParamNames;
      wfAmounts=self.wfAmounts;
      wfAmount=wfAmounts{1};  % by convention, the first one is the default setting (e.g. 'normal')
      wfParamsAmount=self.wfParamsFromAmount.(wfAmount);
      nSubdialectPFNames=length(subdialectPFNames);
      vocabulary = cell(1,nSubdialectPFNames);
      for pfIndex = 1:nSubdialectPFNames
        pfName = subdialectPFNames{pfIndex};
        vocabulary{pfIndex}.name = pfName;
        if ismember(pfName,enabledPFs) ,
          % if this PF is enabled, copy its params out of
          % windowFeatureParams
          vocabulary{pfIndex}.enabled = true;
          vocabulary{pfIndex}.sanitycheck = windowFeatureParams.(pfName).sanitycheck;
          % Fill for the window feature type called 'default'
          for wfParamNdx = 1:numel(wfParamNames)
            wfParamName = wfParamNames{wfParamNdx};
            if isfield(windowFeatureParams.(pfName),wfParamName)
              vocabulary{pfIndex}.default.values.(wfParamName) = windowFeatureParams.(pfName).(wfParamName);
            else % Fill in the value from the default preset amount
              vocabulary{pfNdx}.default.values.(wfParamName) = ...
                wfParamsAmount.default.values.(wfParamName);
                %self.defaultWFParams{wfParamNdx};
            end
            vocabulary{pfIndex}.default.enabled = true;
          end
          % Fill for the rest of the window feature types.
          enabledWFTypes = fieldnames(windowFeatureParams.(pfName));
          for wfTypeNdx = 2:numel(self.wfTypes)
            wfType = self.wfTypes{wfTypeNdx};
            if ismember(wfType,enabledWFTypes) ,
              vocabulary{pfIndex}.(wfType).enabled = true;
              wfParamsThisType = windowFeatureParams.(pfName).(wfType);
              for wfParamNdx = 1:numel(wfParamNames)
                wfParamName = wfParamNames{wfParamNdx};
                if isfield(wfParamsThisType,wfParamName)
                  vocabulary{pfIndex}.(wfType).values.(wfParamName) = wfParamsThisType.(wfParamName);
                else  % fill in the default values
                  vocabulary{pfIndex}.(wfType).values.(wfParamName) = windowFeatureParams.(pfName).(wfParamName);
                end
              end
              if ~isempty(self.wfExtraParamNames{wfTypeNdx})
                extraParamName = self.wfExtraParamNames{wfTypeNdx};
                vocabulary{pfIndex}.(wfType).values.(extraParamName) = wfParamsThisType.(extraParamName);
              end
            else
              % If the window type is disabled, use default values for its
              % parameters
              vocabulary{pfIndex}.(wfType).enabled = false;
              for wfParamNdx = 1:numel(wfParamNames)
                wfParamName = wfParamNames{wfParamNdx};
                vocabulary{pfIndex}.(wfType).values.(wfParamName) = windowFeatureParams.(pfName).(wfParamName);
              end
              if ~isempty(self.wfExtraParamNames{wfTypeNdx})
                extraParamName = self.wfExtraParamNames{wfTypeNdx};
                %vocabulary{pfIndex}.(wfType).values.(extraParamName) = self.defaultWFExtraParams{wfTypeNdx};
                vocabulary{pfIndex}.(wfType).values.(extraParamName) = wfParamsAmount.(wfType).values.(extraParamName);
              end
            end
          end
        else
          % If PF is disabled, use values from the default preset amount
          vocabulary{pfIndex}=wfParamsAmount;
          vocabulary{pfIndex}.enabled = false;
          vocabulary{pfIndex}.sanitycheck = false;
          % Tack on the default translation types, which depend on which
          % per-frame feature this is
          for i=1:length(self.wfTypes)
            wfType=self.wfTypes{i};
            vocabulary{pfIndex}.(wfType).values.trans_types=self.defaultPFTransTypesFromName.(pfName);
          end
%           vocabulary{pfIndex}.default.enabled = true;
%           for wfParamNamesNdx = 1:numel(self.wfParamNames)
%             wfParamName = self.wfParamNames{wfParamNamesNdx};
%             vocabulary{pfIndex}.default.values.(wfParamName) = ...
%               self.defaultWFParams{wfParamNamesNdx};
%           end
%           % Copy the default values into the other window params.
%           for winfnNdx = 2:numel(self.wfTypes)
%             wfType = self.wfTypes{winfnNdx};
%             vocabulary{pfIndex}.(wfType).enabled = false;
%             for wfParamNamesNdx = 1:numel(self.wfParamNames)
%               wfParamName = self.wfParamNames{wfParamNamesNdx};
%               vocabulary{pfIndex}.(wfType).values.(wfParamName) = ...
%                 vocabulary{pfIndex}.default.values.(wfParamName);
%             end
%             if ~isempty(self.wfExtraParamNames{winfnNdx})
%               extraParamName = self.wfExtraParamNames{winfnNdx};
%               vocabulary{pfIndex}.(wfType).values.(extraParamName) = self.defaultWFExtraParams{winfnNdx};
%             end
%           end
        end
      end
      self.vocabulary = vocabulary;

      % If a common max window radius is set, use it
      if exist('maxWindowRadiusCommon','var') && ~isempty(maxWindowRadiusCommon)
        % set the global maxWindowRadius to the given value
        self.setMaxWindowRadiusForAllWFs(maxWindowRadiusCommon);
        self.setMaxWindowRadiusForAllWFAmounts(maxWindowRadiusCommon);
      end
    end  % constructor method

    % ---------------------------------------------------------------------
    function enableAllPFsInCategory(self,pfCategoryIndex)
      %pfCategory=self.pfCategoryNames{pfCategoryIndex};
      %subdialectPFNames=self.subdialectPFNames;
      %pfCategoriesFromName=self.pfCategoriesFromName;
      pfNamesThisCategory=self.getPFNamesInCategory(pfCategoryIndex);
      % Iterate over the per-frame features, looking for ones that are
      % within the selected category.
      for i = 1:length(pfNamesThisCategory)
        pfName=pfNamesThisCategory{i};
        self.enablePerframeFeature(pfName)
      end
    end  % method

    % ---------------------------------------------------------------------
    function setAllPFsInCategoryToWFAmount(self,pfCategoryIndex,wfAmount)
      % This does what it says, but note that it doesn't add or subtract
      % any PFs from the vocabulary.
      %pfCategory=self.pfCategoryNames{pfCategoryIndex};
      %subdialectPFNames=self.subdialectPFNames;
      %pfCategoriesFromName=self.pfCategoriesFromName;
      pfNamesThisCategory=self.getPFNamesInCategory(pfCategoryIndex);
      % Iterate over the per-frame features, looking for ones that are
      % within the selected category.
      for i = 1:length(pfNamesThisCategory)
        pfName=pfNamesThisCategory{i};
        self.setPFToWFAmount(pfName,wfAmount)
      end
    end  % method

    % ---------------------------------------------------------------------
    function setPFEnablement(self,pfSpecifier,enabled)
      % Turn on/off the given per-frame feature, but leave the window feature
      % parameters alone.
      % pfIndicator can be a name or an index.
      if ischar(pfSpecifier)
        % this means pfIndex is really a name
        pfName=pfSpecifier;
        pfIndex=find(strcmp(pfName,self.subdialectPFNames));
      else
        pfIndex=pfSpecifier;
      end
      self.vocabulary{pfIndex}.enabled = enabled;
    end  % method

    % ---------------------------------------------------------------------
    function enablePerframeFeature(self,pfSpecifier)
      % Turn on the given per-frame feature, but leave the window feature
      % parameters alone.
      % This method is deprecated.
      setPFEnablement(self,pfSpecifier,true);
    end  % method

    % ---------------------------------------------------------------------
    function disablePerframeFeature(self,pfSpecifier)
      % Turn off the given per-frame feature.  (I.e. remove all it's window
      % features from the vocabulary)
      % This method is deprecated.
      setPFEnablement(self,pfSpecifier,false);
    end  % method

    % ---------------------------------------------------------------------
    function setPFToWFAmount(self,pfSpecifier,wfAmount)
      % Set the window feature parameters for the named PF to those
      % specified by the named wfAmount (normal, more, less).  This is
      % orthogonal to whether that PF is enabled or not.  Note that this
      % also sets the transformation types for the PF to the default
      % set.
      if isequal(wfAmount,'custom')
        return
      end
      if ischar(pfSpecifier)
        pfName=pfSpecifier;
        pfIndex=find(strcmp(pfName,self.subdialectPFNames));
      else
        pfIndex=pfSpecifier;
        pfName=self.subdialectPFNames{pfIndex};
      end
      defaultPFTransTypes=self.defaultPFTransTypesFromName.(pfName);
      %self.vocabulary{pfIndex}.enabled = true;
      %pfCategories=self.pfCategoriesFromName.(pfName);
      %pfCategory = pfCategories{1};  % why the first one?
      %categoryNdx = find(strcmp(pfCategory,self.pfCategoryNames));
      wfParamTemplate=self.wfParamsFromAmount.(wfAmount);
      %self.vocabulary{pfIndex}.enabled = true;
      for wfTypeIndex = 1:numel(self.wfTypes)
        wfType = self.wfTypes{wfTypeIndex};
        %wfParamsThisType=wfParamTemplate.(wfType);
        %if ~wfParamsThisType.enabled
        %  self.vocabulary{pfIndex}.(wfType).enabled = false;
        %  continue;
        %end
        self.setWindowFeaturesOfSingleType(pfIndex,wfTypeIndex,wfParamTemplate);
        self.vocabulary{pfIndex}.(wfType).values.trans_types = defaultPFTransTypes;
      end
    end  % method

    % ---------------------------------------------------------------------
    function setWFTypeEnablement(self,pfName,wfType,enable)
      % For the given per-frame feature name, enable/disable the given
      % window-feature type
      pfIndex=find(strcmp(pfName,self.subdialectPFNames));
      self.vocabulary{pfIndex}.(wfType).enabled = enable;  %#ok
    end

    % ---------------------------------------------------------------------
    function setWFParam(self,pfName,wfType,wfParamName,newValue)
      % For the given per-frame feature name, window-feature type, and
      % window-feature parameter name, set the parameter to newValue
      if ischar(pfName)
        % the usual case
        pfIndex=find(strcmp(pfName,self.subdialectPFNames));
      else
        % this means pfName is really an index
        pfIndex=pfName;
        %pfName=self.subdialectPFNames{pfIndex};
      end
      if ~ischar(wfType)
        % this means wfType is really an index
        wfIndex=wfType;
        wfType=FeatureVocabulary.wfTypes{wfIndex};
      end
      self.vocabulary{pfIndex}.(wfType).values.(wfParamName) = newValue;
    end

    % ---------------------------------------------------------------------
    function addWFTransformation(self,pfName,wfType,newTransformation)
      % For per-frame feature pfName, window feature type wfType, add a new
      % transformation, newTransformation.  (A transformation is one of 'none',
      % 'flip', 'abs', 'relative'.)
      pfIndex=find(strcmp(pfName,self.subdialectPFNames));
      transformations = self.vocabulary{pfIndex}.(wfType).values.trans_types;
      if ~any(strcmp(newTransformation,transformations))
        % if it's not in the list, add it to the end
        transformations{end+1}=newTransformation;
        self.vocabulary{pfIndex}.(wfType).values.trans_types=transformations;
      end
    end  % method

    % ---------------------------------------------------------------------
    function removeWFTransformation(self,pfName,wfType,transformation)
      % For per-frame feature pfName, window feature type wfType, remove a
      % transformation, transformation.  (A transformation is one of 'none',
      % 'flip', 'abs', 'relative'.)  If transformation is not in the
      % transformation list for that window feature, nothing changes.  If
      % removing the given transformation would leave the transformation
      % list empty, nothing changes.
      pfIndex=find(strcmp(pfName,self.subdialectPFNames));
      transformations = self.vocabulary{pfIndex}.(wfType).values.trans_types;
      toBeRemoved = strcmp(transformation,transformations);
      transformations(toBeRemoved) = [];
      % only commit the change if there is still at least one transformation
      % left
      if ~isempty(transformations)
        self.vocabulary{pfIndex}.(wfType).values.trans_types=transformations;
      end
    end  % method

    % ---------------------------------------------------------------------
    function result=isWFTransformation(self,pfName,wfType,transformation)
      % For per-frame feature pfName, window feature type wfType, returns
      % true iff the given transformation is in the transformation list.
      pfIndex=find(strcmp(pfName,self.subdialectPFNames));
      transformations = self.vocabulary{pfIndex}.(wfType).values.trans_types;  %#ok
      result=ismember(transformation,transformations);
    end  % method

    % ---------------------------------------------------------------------
    function setMaxWindowRadiusForAllWFAmounts(self,newValue)
      % This sets the maximum window radius for all of the preset window
      % feature amounts, for all window feature types.  Note, however, that
      % it does not change the window feature parameters
      % themselves.  So WF params that were previously set using one of the
      % WF amount pre-sets will retain the old values.
      wfAmounts = self.wfAmounts;
      wfTypes = self.wfTypes;
      for i = 1:numel(wfAmounts)
        wfAmount = wfAmounts{i};
        for j = 1:numel(wfTypes)
          wfType=wfTypes{j};
          wfParams=self.wfParamsFromAmount.(wfAmount);
          if isfield(wfParams,wfType),
            self.wfParamsFromAmount.(wfAmount).(wfType).values.max_window_radius = newValue;
          end
        end
      end
    end  % method

    % ---------------------------------------------------------------------
    function setMaxWindowRadiusForAllWFs(self,newValue)
      % This sets the maximum window radius for all per-frame features, for
      % all window-feature types.  Thus for all window features.
      pfNames=self.subdialectPFNames;
      wfTypes = self.wfTypes;
      for i = 1:length(pfNames)
        for j = 1:length(wfTypes)
          wfType=wfTypes{j};
          if isfield(self.vocabulary{i},wfType),
            self.vocabulary{i}.(wfType).values.max_window_radius=newValue;
          end
        end
      end
    end  % method

    % ---------------------------------------------------------------------
    function [thereIsConsensus,consensusValue]=getConsensusMaxWindowRadiusForAllWFs(self)
      % Determines the common value of max_window_radius across all WFs, in
      % all PFs.  thereIsConsensus will be true iff there is a common
      % value.  The common value is returned in consensusValue.  If there
      % is no common value, consensusValue is unspecified.  If there are no
      % WFs at all in the vocabulary, thereIsConsensus will be true, but
      % consensusValue will be empty.
      thereIsConsensus=true;
      consensusValue=[];
      pfNames=self.subdialectPFNames;
      wfTypes = self.wfTypes;
      for i = 1:length(pfNames)
        for j = 1:length(wfTypes)
          wfType=wfTypes{j};
          if isfield(self.vocabulary{i},wfType),
            thisValue=self.vocabulary{i}.(wfType).values.max_window_radius;
            if isempty(consensusValue)
              % If this is the first value we've examined, it becomes the
              % consensus value
              consensusValue=thisValue;
            else
              % if this is not the first value we've examined, compare it
              % to the consensusValue.
              if thisValue~=consensusValue
                % No consensus value---return empty matrix
                thereIsConsensus=false;
                return
              end
            end
          end
        end
      end
    end  % method

    % ---------------------------------------------------------------------
    function copyWFParams(self,pfNdxFrom,wfTypeNdxFrom,pfNdxTo,wfTypeNdxTo)
      wfTypeFrom = self.wfTypes{wfTypeNdxFrom};
      % something to copy from?
      if ~isfield(self.vocabulary{pfNdxFrom},wfTypeFrom),
        return;
      end
      wfTypeTo = self.wfTypes{wfTypeNdxTo};
      self.vocabulary{pfNdxTo}.(wfTypeTo).enabled = self.vocabulary{pfNdxFrom}.(wfTypeFrom).enabled;
      for i = 1:numel(self.wfParamNames),
        wfParamName = self.wfParamNames{i};
        self.vocabulary{pfNdxTo}.(wfTypeTo).values.(wfParamName) = ...
          self.vocabulary{pfNdxFrom}.(wfTypeFrom).values.(wfParamName);
      end
      if ~isempty(self.wfExtraParamNames{wfTypeNdxTo})
        extraParam = self.wfExtraParamNames{wfTypeNdxTo};
        if isfield(self.vocabulary{pfNdxFrom}.(wfTypeFrom).values,extraParam)
          self.vocabulary{pfNdxTo}.(wfTypeTo).values.(extraParam) = ...
            self.vocabulary{pfNdxFrom}.(wfTypeFrom).values.(extraParam);
        else
          self.vocabulary{pfNdxTo}.(wfTypeTo).values.(extraParam) = '';
        end
      end
    end  % method

    % ---------------------------------------------------------------------
    function result=get.wfAmounts(self)
      result=fieldnames(self.wfParamsFromAmount);
    end  % method

    % ---------------------------------------------------------------------
    function result=pfIsInVocabulary(self,pfIndex)
      if ischar(pfIndex)
        % means pfIndex is really a per-frame feature name
        pfName=pfIndex;
        pfIndex=find(strcmp(pfName,self.subdialectPFNames));
      end
      result=self.vocabulary{pfIndex}.enabled;
    end  % method

    % ---------------------------------------------------------------------
    function result=wfTypeIsInVocabulary(self,pfIndex,wfType)
      if ischar(pfIndex)
        % means pfIndex is really a per-frame feature name
        pfName=pfIndex;
        pfIndex=find(strcmp(pfName,self.subdialectPFNames));
      end
      if isnumeric(wfType)
        % means wfType is really a window feature index
        wfIndex=wfType;
        wfType=self.wfTypes{wfIndex};
      end
      result=self.vocabulary{pfIndex}.(wfType).enabled;
    end  % method

    % ---------------------------------------------------------------------
    function windowFeatureParams = getInJLabelDataFormat(self)
      % Converts the feature vocabulary into the format used by JLabelData,
      % returns this.
      % Note that none of the information about window-feature amount
      % presets (e.g. normal, more, less) gets used in computing the
      % output.
      windowFeatureParams = struct;
      for pfIndex = 1:numel(self.subdialectPFNames)
        pfName = self.subdialectPFNames{pfIndex};
        if ~self.vocabulary{pfIndex}.enabled; continue;end
        pfParams = self.vocabulary{pfIndex};
        windowFeatureParams.(pfName).sanitycheck = pfParams.sanitycheck;
        % the default window feature type is always included
        for wfParamsIndex = 1:numel(self.wfParamNames)
          wfParamName = self.wfParamNames{wfParamsIndex};
          windowFeatureParams.(pfName).(wfParamName) = pfParams.default.values.(wfParamName);
        end
        for wfTypeIndex = 2:numel(self.wfTypes)
          wfType = self.wfTypes{wfTypeIndex};
          if pfParams.(wfType).enabled,
            for wfParamsIndex = 1:numel(self.wfParamNames)
              wfParamName = self.wfParamNames{wfParamsIndex};
              windowFeatureParams.(pfName).(wfType).(wfParamName) =...
                  pfParams.(wfType).values.(wfParamName);
            end
            if ~isempty(self.wfExtraParamNames{wfTypeIndex})
              extraParam = self.wfExtraParamNames{wfTypeIndex};
              extraParamVal = pfParams.(wfType).values.(extraParam);
              windowFeatureParams.(pfName).(wfType).(extraParam) = extraParamVal;
            end
          end
        end
      end
    end  % method

    % ---------------------------------------------------------------------
    function pfNames=getPFNamesInCategory(self,pfCategoryName)
      % For the given per-frame feature category, return a cell array of
      % strings containing the names of all per-frame features in that
      % category.
      if isnumeric(pfCategoryName)
        % this means the category name is really a category index
        pfCategoryIndex=pfCategoryName;
        pfCategoryName=self.pfCategoryNames{pfCategoryIndex};
      end
      pfNames=cell(1,0);
      for iPF = 1:numel(self.subdialectPFNames)
        pfName=self.subdialectPFNames{iPF};
        categoriesThisPF=self.pfCategoriesFromName.(pfName);
        if ismember(pfCategoryName,categoriesThisPF)
          % if the selected category contains this per-frame feature,
          % do stuff
          pfNames{1,end+1}=pfName;  %#ok
        end
      end
    end  % method

    % ---------------------------------------------------------------------
    function level=getPFCategoryLevel(self,pfCategoryName)
      % For the given per-frame feature category, calculate what 'level' of
      % per-frame features are in the vocabulary.  Returns one of 'none',
      % 'custom', 'all'.
      pfNamesInCategory=self.getPFNamesInCategory(pfCategoryName);
      nPFsInCategory=length(pfNamesInCategory);
      nPFsInCategoryAndVocab=0;
      for i = 1:nPFsInCategory
        pfName=pfNamesInCategory{i};
        if self.pfIsInVocabulary(pfName),
          nPFsInCategoryAndVocab=nPFsInCategoryAndVocab+1;
        end
      end
      if nPFsInCategoryAndVocab==0
        if nPFsInCategoryAndVocab==nPFsInCategory
          level='n/a';
        else
          level='none';
        end
      elseif nPFsInCategoryAndVocab==nPFsInCategory
        level='all';
      else
        level='custom';
      end
    end  % method

    % ---------------------------------------------------------------------
    function wfAmount=getWFAmountForPFCategory(self,pfCategoryName)
      % Get the current amount for the given per-frame feature category.  Returns
      % one of 'normal', 'more', 'less', 'custom', and 'n/a' (if there are
      % no PFs in the category.
      pfNamesInCategory=self.getPFNamesInCategory(pfCategoryName);
      nPFsInCategory=length(pfNamesInCategory);
      if (nPFsInCategory==0)
        wfAmount='n/a';
        return
      end
      % Get the amount for the first PF
      pfName=pfNamesInCategory{1};
      wfAmountPutative=self.getWFAmountForPF(pfName);
      % If the first one is 'custom', no need to check the rest
      if isequal(wfAmountPutative,'custom')
        wfAmount='custom';
        return
      end
      % If we get here, wfAmountThisPutative is something other than
      % 'custom'
      % The rest have to match wfAmountPutative, or else the amount is
      % 'custom'
      for i = 2:nPFsInCategory
        pfName=pfNamesInCategory{i};
        wfAmountThis=self.getWFAmountForPF(pfName);
        if ~isequal(wfAmountThis,wfAmountPutative)
          wfAmount='custom';
          return
        end
      end
      % If we get here, they all match wfAmountPutative
      wfAmount=wfAmountPutative;
    end

    % ---------------------------------------------------------------------
    function wfAmount=getWFAmountForPF(self,pfIndex)
      % get the current amount for the given per-frame feature.  Returns
      % one of 'normal', 'more', 'less', and 'custom'.
      if ischar(pfIndex)
        % means it's really a name
        pfName=pfIndex;
        pfIndex=find(strcmp(pfName,self.subdialectPFNames));
      else
        % pfIndex is really an index
        pfName=self.subdialectPFNames{pfIndex};
      end
      wfAmount='custom';  % if no match, we default to custom
      defaultPFTransTypes=self.defaultPFTransTypesFromName.(pfName);
      pfParams=self.vocabulary{pfIndex};  % all the WF params for the given PF
      nAmounts=length(self.wfAmounts);
      for j=1:nAmounts
        wfAmountTest=self.wfAmounts{j};
        isMatch=true;
        pfParamsTemplate=self.wfParamsFromAmount.(wfAmountTest);
          % all the WF params for a single PF, from the pre-set amount
        for wfTypeIndex = 1:length(self.wfTypes)
          wfType = self.wfTypes{wfTypeIndex};
          if isfield(pfParams,wfType) && ~isfield(pfParamsTemplate,wfType),
            % If this window-feature type is present in the vocabulary, but not
            % in the template, then skip to next wfType
            continue
          end
          % check that the enablement in the vocab matches the template
          if pfParams.(wfType).enabled ~= pfParamsTemplate.(wfType).enabled ,
            isMatch=false;
            break
          end
          % For all the regular WF parameter names, check that they're
          % equal
          for i = 1:numel(self.wfParamNames),
            wfParamName = self.wfParamNames{i};
            if isequal(wfParamName,'trans_types')
              % Skip transformation types, we do those below
              continue
            end
            if pfParams.(wfType).values.(wfParamName) ~= ...
               pfParamsTemplate.(wfType).values.(wfParamName) ,
              isMatch=false;
              break
            end
          end
          if ~isMatch, break, end
          % If there is an extra WF parameter for this WF type, copy it over
          % also.
          extraWFParamName = self.wfExtraParamNames{wfTypeIndex};
          if ~isempty(extraWFParamName) && ...
             isfield(pfParamsTemplate.(wfType).values,extraWFParamName) && ...
             ~isequal(pfParams.(wfType).values.(extraWFParamName), ...
                      pfParamsTemplate.(wfType).values.(extraWFParamName)) ,
            isMatch=false;
            break
          end
          % Check the transformation types also
          if ~isequal(unique(pfParams.(wfType).values.trans_types) , ...
                      unique(defaultPFTransTypes) ) ,
            isMatch=false;
            break
          end
        end  % for wfTypeIndex = 1:length(self.wfTypes)
        if isMatch,
          wfAmount=wfAmountTest;
          break  % if match, we're done
        else
          continue  % if no match, try next wfAmount
        end
      end  % for wfAmountTest=self.wfAmounts
    end  % method

  end  % public instance methods

  % -----------------------------------------------------------------------
  % private instance methods
  methods (Access=private)
    % ---------------------------------------------------------------------
    function setWindowFeaturesOfSingleType(self,pfNdx,wfTypeNdx,wfParamTemplate)
      % For the per-frame feature with index pfNdx, sets the window features
      % with type given by index wfTypeNdx to the amount given in wfParamTemplate.

      wfType = self.wfTypes{wfTypeNdx};
      %wfParamTemplate=self.wfParamsFromAmount.(wfAmount);
      % Want to do something like:
      %   self.vocabulary{pfNdx}.(wfType)=pfParamTemplate.(wfType)  ,
      % but adapted to the particular PF, I guess ---ALT, Mar 31, 2013
      % something to copy from?
      if isfield(self.vocabulary{pfNdx},wfType) && ~isfield(wfParamTemplate,wfType),
        % If this window-feature type is present in the vocabulary, but not
        % in the template, then disable it in the vocab, and return.
        % (If this ever happens, doesn't it mean the template is broken?
        % And since we control the template, this need never happen.
        % --ALT, Mar 31, 2013)
        self.vocabulary{pfNdx}.(wfType).enabled = false;
        return
      end
      % Set the enablement in the vocab to match the template
      self.vocabulary{pfNdx}.(wfType).enabled = wfParamTemplate.(wfType).enabled;
      % For all the regular WF parameter names, copy them over
      for i = 1:numel(self.wfParamNames),
        wfParamName = self.wfParamNames{i};
        self.vocabulary{pfNdx}.(wfType).values.(wfParamName) = ...
          wfParamTemplate.(wfType).values.(wfParamName);
      end
      % If there is an extra WF parameter for this WF type, copy it over
      % also.
      extraWFParamName = self.wfExtraParamNames{wfTypeNdx};
      if ~isempty(extraWFParamName)
        if isfield(wfParamTemplate.(wfType).values,extraWFParamName) ,
          value=wfParamTemplate.(wfType).values.(extraWFParamName);
        else
          % If no value is specified, set to empty matrix
          value=[];
        end
        self.vocabulary{pfNdx}.(wfType).values.(extraWFParamName) = ...
          value;
      end
    end  % method

  end  % private methods
end  % classdef


function incorrectGeneRules = testGeneRules(model)
% Finds gene rules that have an incorrect nomenclature.
%
% INPUT
% model             COBRA model structure
%
% OUTPUT
% incorrectGeneRules       Cell array listing entries in model.rules that
%                          have incorrect nomenclature.
%
% Almut Heinken, Oct 2019

cnt=1;
incorrectGeneRules={};

for i=1:length(model.rules)
   tf = verifyRuleSyntax(model.rules{i});
   if ~tf
       incorrectGeneRules{cnt}=i;
       cnt=cnt+1;
   end
end

end


classdef GemCinePanel < GemVirtualPanel
    % GemCinePanel  Image axes with scrollbar for displaing a slice of a 3D volume
    %
    %     GemCinePanel contains a 2D image and a slider, which allows the user to
    %     cine through slices of a 3D images
    %
    %
    %     Licence
    %     -------
    %     Part of the TD Pulmonary Toolkit. https://github.com/tomdoel/pulmonarytoolkit
    %     Author: Tom Doel, 2014.  www.tomdoel.com
    %     Distributed under the GNU GPL v3 licence. Please see website for details.
    %

    properties (Access = protected)
        Axes
        ImageSource
        ImageParameters
        Slider
    end

    events
        MousePositionChanged
    end

    methods
        function obj = GemCinePanel(parent, image_source, image_parameters, image_overlay_axes)
            obj = obj@GemVirtualPanel(parent);

            obj.ImageSource = image_source;
            obj.ImageParameters = image_parameters;

            obj.Slider = GemSlider(obj);
            obj.AddChild(obj.Slider);

            obj.Axes = image_overlay_axes;
            obj.AddChild(obj.Axes);

            obj.AddEventListener(obj.Slider, 'SliderValueChanged', @obj.SliderValueChanged);
        end

        function Resize(obj, position)
            Resize@GemUserInterfaceObject(obj, position);

            % Resize axes
            axis_width = max(1, position(3) - obj.Slider.SliderWidth);
            axes_position = [position(1), position(2), axis_width, position(4)];
            obj.Axes.Resize(axes_position);

            % Resize slider
            slider_x = position(1) + axis_width;
            slider_position = [slider_x, position(2) - 1, obj.Slider.SliderWidth, position(4)];
            obj.Slider.Resize(slider_position);
        end

        function SetSliceNumber(obj, slice_number)
            current_slice_value = obj.Slider.SliderValue;
            if (current_slice_value ~= slice_number)
                obj.Slider.SetSliderValue(slice_number);
            end
        end

        function SetSliderLimits(obj, min, max)
            obj.Slider.SetSliderLimits(min, max);
        end

        function SetSliderSteps(obj, steps)
            obj.Slider.SetSliderSteps(steps);
        end

        function EnableSlider(obj, enabled)
            if enabled
                obj.Slider.Enable;
            else
                obj.Slider.Disable;
            end
        end

        function frame = Capture(obj, image_size, orientation)
            drawnow;

            origin = [0.5, 0.5, 0.5];
            image_limit = origin + image_size;
            switch orientation
                case GemImageOrientation.XZ
                    xlim_image = [origin(2), image_limit(2)];
                    ylim_image = [origin(3), image_limit(3)];
                case GemImageOrientation.YZ
                    xlim_image = [origin(1), image_limit(1)];
                    ylim_image = [origin(3), image_limit(3)];
                case GemImageOrientation.XY
                    xlim_image = [origin(2), image_limit(2)];
                    ylim_image = [origin(1), image_limit(1)];
            end

            frame = obj.Axes.Capture(xlim_image, ylim_image);
        end

        function ZoomTo(obj, i_limits, j_limits, k_limits)
            % Changes the current axis limits to the specified global coordinates
            % i_limits = [minimum_i, maximum_i] and the same for j, k.

            obj.Axes.ZoomTo(obj, i_limits, j_limits, k_limits);

        end

        function ClearAxesCache(obj)
            obj.Axes.ClearAxesCache;
        end

        function UpdateAxes(obj)
            obj.Axes.UpdateAxes;
        end

        function screen_coords = GetScreenCoordinates(obj)
            coords = obj.Axes.GetCurrentPoint;
            if (~isempty(coords))
                i_screen = coords(2,1);
                j_screen = coords(2,2);
                screen_coords = [i_screen, j_screen];
            else
                screen_coords = [0, 0];
            end
        end

        function current_point = GetCurrentPoint(obj)
            current_point = obj.Axes.GetCurrentPoint;
        end

        function axes_object = GetAxes(obj)
            if isempty(obj.Axes)
                obj.Reporting.Error('GemCinePanel:AxesDoNotExist', 'Axes have not been created');
            end
            axes_object = obj.Axes;
        end

        function global_coords = GetImageCoordinates(obj)
            coords = round(obj.GetCurrentPoint);
            if (~isempty(coords))
                orientation = obj.ImageParameters.Orientation;
                i_screen = coords(2,1);
                j_screen = coords(2,2);
                k_screen = obj.ImageParameters.SliceNumber(orientation);

                switch orientation
                    case GemImageOrientation.XZ
                        i = k_screen;
                        j = i_screen;
                        k = j_screen;
                    case GemImageOrientation.YZ
                        i = i_screen;
                        j = k_screen;
                        k = j_screen;
                    case GemImageOrientation.XY
                        i = j_screen;
                        j = i_screen;
                        k = k_screen;
                end
            else
                i = 1;
                j = 1;
                k = 1;
            end
            global_coords = obj.ImageSource.Image.LocalToGlobalCoordinates([i, j, k]);
        end
    end

    methods (Access = protected)
        function input_has_been_processed = MouseDown(obj, click_point, selection_type, src, eventdata)
            % This method is called when the mouse is clicked inside the control

            obj.UpdateStatus(true);
            input_has_been_processed = true;
        end

        function input_has_been_processed = MouseUp(obj, click_point, selection_type, src, eventdata)
            % This method is called when the mouse is released inside the control

            obj.UpdateStatus(true);
            input_has_been_processed = true;
        end

        function input_has_been_processed = MouseHasMoved(obj, click_point, selection_type, src, eventdata)
            % Mouse has moved over the figure

            obj.UpdateStatus(true);
            input_has_been_processed = true;
        end

        function input_has_been_processed = MouseDragged(obj, click_point, selection_type, src, eventdata)
            % Mouse dragged over the figure

            obj.UpdateStatus(true);
            input_has_been_processed = true;
        end

        function input_has_been_processed = MouseExit(obj, click_point, selection_type, src, eventdata)
            % This method is called when the mouse exits a control which previously
            % processed a MouseHasMoved event

            obj.UpdateStatus(false);
            input_has_been_processed = true;
        end

        function UpdateStatus(obj, in_image)
            image_coordinates = obj.GetImageCoordinates;
            notify(obj, 'MousePositionChanged', CoreEventData(GemCoordsInImage(image_coordinates, in_image)));
        end

        function SliderValueChanged(obj, ~, ~)
            obj.ImageParameters.SliceNumber(obj.ImageParameters.Orientation) = round(obj.Slider.SliderValue);
        end
    end
end


function X= readeAviFile(name)
xyloObj = mmreader(name);

nFrames = xyloObj.NumberOfFrames;
vidHeight = xyloObj.Height;
vidWidth = xyloObj.Width;

% Preallocate movie structure.
X= zeros(vidHeight,vidWidth,nFrames);

% Read one frame at a time.
for k = 1 : nFrames
    X(:,:,k)= rgb2gray( read(xyloObj, k));
end

% Size a figure based on the video's width and height.


function [nodes] = idtf_model_nodes(n_meshes, n_lines, n_pointsets)
% File:      idtf_model_nodes.m
% Author:    Ioannis Filippidis, jfilippidis@gmail.com
% Date:      2012.06.10 - 2012.06.24
% Language:  MATLAB R2012a
% Purpose:   create IDTF model nodes
% Copyright: Ioannis Filippidis, 2012-

% depends
%   verbatim

%% mesh nodes
mesh_node = mesh_node_str;
temp_nodes = cell(1, n_meshes);
for i=1:n_meshes
    cur_mesh_node = sprintf(mesh_node, i, i);
    temp_nodes{1, i} = cur_mesh_node;
end
mesh_nodes = [temp_nodes{:} ];
%disp(mesh_nodes)

%% line nodes
line_node = line_node_str;
temp_nodes = cell(1, n_lines);
for i=1:n_lines
    cur_line_node = sprintf(line_node, i, i);
    temp_nodes{1, i} = cur_line_node;
end
line_nodes = [temp_nodes{:} ];
%disp(line_nodes)

%% pointset nodes
point_node = point_node_str;
temp_nodes = cell(1, n_pointsets);
for i=1:n_pointsets
    cur_point_node = sprintf(point_node, i, i);
    temp_nodes{1, i} = cur_point_node;
end
point_nodes = [temp_nodes{:} ];
%disp(point_nodes)

%% output
nodes = [mesh_nodes, line_nodes, point_nodes];

function [str] = mesh_node_str
%mesh_id_number, mesh_id_number
str = verbatim;
%{

NODE "MODEL" {
     NODE_NAME "Mesh%d"
     PARENT_LIST {
          PARENT_COUNT 1
          PARENT 0 {
               PARENT_NAME "<NULL>"
               PARENT_TM {
                    1.000000 0.000000 0.000000 0.000000
                    0.000000 1.000000 0.000000 0.000000
                    0.000000 0.000000 1.000000 0.000000
                    0.000000 0.000000 0.000000 1.000000
               }
          }
     }
     RESOURCE_NAME "MyMesh%d"
     MODEL_VISIBILITY "BOTH"
}

%}

function [str] = line_node_str
% lineset_id_number, lineset_id_number
str = verbatim;
%{

NODE "MODEL" {
     NODE_NAME "Line%d"
     PARENT_LIST {
          PARENT_COUNT 1
          PARENT 0 {
               PARENT_NAME "<NULL>"
               PARENT_TM {
                    1.000000 0.000000 0.000000 0.000000
                    0.000000 1.000000 0.000000 0.000000
                    0.000000 0.000000 1.000000 0.000000
                    0.000000 0.000000 0.000000 1.000000
               }
          }
     }
     RESOURCE_NAME "MyLine%d"
}

%}

function [str] = point_node_str
% pointset_id_number, pointset_id_number
str = verbatim;
%{

NODE "MODEL" {
     NODE_NAME "Points%d"
     PARENT_LIST {
          PARENT_COUNT 1
          PARENT 0 {
               PARENT_NAME "<NULL>"
               PARENT_TM {
                    1.000000 0.000000 0.000000 0.000000
                    0.000000 1.000000 0.000000 0.000000
                    0.000000 0.000000 1.000000 0.000000
                    0.000000 0.000000 0.000000 1.000000
               }
          }
     }
     RESOURCE_NAME "MyPoints%d"
}

%}


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


function test_ft_analysispipeline

% MEM 2gb
% WALLTIME 02:53:11
% DEPENDENCY ft_analysispipeline

% the style of this test script is also used in test_ft_datatype and test_bug2185

dirlist = {
  dccnpath('/home/common/matlab/fieldtrip/data/test/latest')
  dccnpath('/home/common/matlab/fieldtrip/data/test/20131231')
  dccnpath('/home/common/matlab/fieldtrip/data/test/20130630')
  dccnpath('/home/common/matlab/fieldtrip/data/test/20121231')
  dccnpath('/home/common/matlab/fieldtrip/data/test/20120630')
  dccnpath('/home/common/matlab/fieldtrip/data/test/20111231')
  dccnpath('/home/common/matlab/fieldtrip/data/test/20110630')
  dccnpath('/home/common/matlab/fieldtrip/data/test/20101231')
  dccnpath('/home/common/matlab/fieldtrip/data/test/20100630')
  dccnpath('/home/common/matlab/fieldtrip/data/test/20091231')
  dccnpath('/home/common/matlab/fieldtrip/data/test/20090630')
  dccnpath('/home/common/matlab/fieldtrip/data/test/20081231')
  dccnpath('/home/common/matlab/fieldtrip/data/test/20080630')
  dccnpath('/home/common/matlab/fieldtrip/data/test/20071231')
  dccnpath('/home/common/matlab/fieldtrip/data/test/20070630')
  dccnpath('/home/common/matlab/fieldtrip/data/test/20061231')
  dccnpath('/home/common/matlab/fieldtrip/data/test/20060630')
  dccnpath('/home/common/matlab/fieldtrip/data/test/20051231')
  dccnpath('/home/common/matlab/fieldtrip/data/test/20050630')
  dccnpath('/home/common/matlab/fieldtrip/data/test/20040623')
  dccnpath('/home/common/matlab/fieldtrip/data/test/20031128')
  };

for j=1:length(dirlist)
  filelist = hcp_filelist(dirlist{j});

  [dummy, dummy, x] = cellfun(@fileparts, filelist, 'uniformoutput', false);
  sel = strcmp(x, '.mat');
  filelist = filelist(sel);
  clear p f x

  for i=1:length(filelist)

    % skip the large files
    d = dir(filelist{i});
    if d.bytes>50000000
      continue
    end

    try
      fprintf('processing data structure from %s\n', filelist{i});
      var = loadvar(filelist{i});
      disp(var)
    catch
      % some of the mat files are corrupt, this should not spoil the test
      disp(lasterr);
      continue
    end

    cfg = [];
    cfg.showcallinfo = 'no';
    ft_analysispipeline(cfg, var);
    set(gcf, 'Name', shortname(filelist{i}));
    drawnow
    close all

  end % for filelist

end % for dirlist
end % main function

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% SUBFUNCTION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function str = shortname(str)
len = 50;
if length(str)>len
  begchar = length(str)-len+4;
  endchar = length(str);
  str = ['...' str(begchar:endchar)];
end
end % function shortname


%% Synthetic video demo
% Demonstrates using |VideoSynth| classes as stub for |cv.VideoCapture|.
% Capture object is created using the helper function |createVideoCapture|.
%
% Sources:
%
% * <https://github.com/opencv/opencv/blob/3.3.1/samples/python/video.py>
%

%%
% create synth video capture object
if true
    %cap = createVideoCapture(0, 'aruco');
    cap = createVideoCapture([], 'chess');
else
    %cap = createVideoCapture('synth|class=scene|bg=../test/lena.jpg|fg=../test/mask.png|noise=0.1|size=400x400|speed=1');
    %cap = createVideoCapture('synth|class=scene|bg=../test/fruits.jpg|noise=0.1|size=512x480|speed=1|deformation=true');
    cap = createVideoCapture('synth|class=chess|framecount=500|bg=../test/lena.jpg|size=320x320|noise=0.1|gridsize=9x6');
end
assert(cap.isOpened());

%%
% set a limit on iterations when we're publising demo
if false && isa(cap, 'VideoSynthBase')
    cap.set('FrameCount', 500);
end

%%
% prepare plot
img = cap.read();
assert(~isempty(img));
hImg = imshow(img, 'InitialMagnification',100, 'Border','tight');
hTxt = text(10, 15, 'FPS = 0', 'FontSize',8, 'Color','y');

%%
% main loop
counter = 0;
tID = tic();
while ishghandle(hImg)
    % grab new frame
    img = cap.read();
    if isempty(img); break; end

    % frames-per-second
    counter = counter + 1;
    fps = counter / toc(tID);

    % display
    set(hTxt, 'String',sprintf('FPS = %.2f', fps))
    set(hImg, 'CData',img)
    if ~mexopencv.isOctave()
        drawnow limitrate
    else
        drawnow
    end
end
cap.release();


function display(obj)
% Display a NIFTI-1 object
% _______________________________________________________________________
% Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging

%
% $Id$

disp(' ');
disp([inputname(1),' = '])
disp(' ');
disp(obj)
disp(' ')


function y = cvx_value( x )
y = apply( @cvx_value, x );

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


% This file is part of the Matlab Toolbox for Dimensionality Reduction v0.7.2b.
% The toolbox can be obtained from http://homepage.tudelft.nl/19j49
% You are free to use, change, or redistribute this code in any way you
% want for non-commercial purposes. However, it is appreciated if you
% maintain the name of the original author.
%
% (C) Laurens van der Maaten, 2010
% University California, San Diego / Delft University of Technology

function lnst(hs,he)
ls=get(he,'value');

switch ls
    case 1
        set(hs,'LineStyle','-','marker','x','color','b');
    case 2
        set(hs,'LineStyle','-','marker','o','color','r');
    case 3
        set(hs,'LineStyle','none','marker','o','color','b');
    case 4
        set(hs,'LineStyle','-','marker','none','color','g');
    case 5
        set(hs,'LineStyle','--','marker','d','color','b');

end
drawnow;


function sigm = nonlinearity_nil(x)
    sigm = x;
end


function obj = trim_mask(obj)
% Exclude empty voxels from mask information in obj.volInfo structure, and re-make obj.volInfo
%
% ..
%    Tor Wager, 2013
% ..

obj = replace_empty(obj);
whomit = all(obj.dat == 0 | isnan(obj.dat), 2);

obj.dat(whomit, :) = [];
if ~isempty(obj.removed_voxels) && length(obj.removed_voxels) == obj.volInfo.n_inmask
    obj.removed_voxels(whomit) = [];
end
obj.volInfo.image_indx(obj.volInfo.wh_inmask(whomit)) = 0;
obj.volInfo.wh_inmask(whomit) = [];
obj.volInfo.xyzlist(whomit, :) = [];
obj.volInfo.cluster(whomit) = [];
obj.volInfo.n_inmask = length(obj.volInfo.wh_inmask);

if isa(obj, 'fmri_data')
    obj.mask.dat(whomit, :) = [];
    if ~isempty(obj.mask.removed_voxels) && length(obj.mask.removed_voxels) == obj.mask.volInfo.n_inmask
        obj.mask.removed_voxels(whomit) = [];
    end
    obj.mask.volInfo.image_indx(obj.mask.volInfo.wh_inmask(whomit)) = 0;
    obj.mask.volInfo.wh_inmask(whomit) = [];
    obj.mask.volInfo.xyzlist(whomit, :) = [];
    obj.mask.volInfo.cluster(whomit) = [];
    obj.mask.volInfo.n_inmask = length(obj.mask.volInfo.wh_inmask);
end

end % function


function [cfg, artifact] = ft_artifact_nan(cfg, data)

% FT_ARTIFACT_NAN identifies artifacts that are indicated in the data as NaN (not a
% number) values.
%
% Use as
%   [cfg, artifact] = ft_artifact_nan(cfg, data)
% where the input data is a structure as obtained from FT_REJECTARTIFACT with the
% option cfg.artfctdef.reject='nan', or from FT_REJECTVISUAL with cfg.keeptrial='nan'
% or cfg.keepchannel='nan'.
%
% The configuration can contain
%   cfg.artfctdef.nan.channel = Nx1 cell-array with selection of channels, see FT_CHANNELSELECTION for details
%
% The output argument "artifact" is a Nx2 matrix comparable to the "trl" matrix of
% FT_DEFINETRIAL. The first column of which specifying the beginsamples of an
% artifact period, the second column contains the endsamples of the artifactperiods.
%
% To facilitate data-handling and distributed computing, you can use
%   cfg.inputfile   =  ...
% to read the input data from a *.mat file on disk. This mat files should contain
% only a single variable named 'data', corresponding to the input structure.
%
% See also FT_REJECTARTIFACT, FT_ARTIFACT_CLIP, FT_ARTIFACT_ECG, FT_ARTIFACT_EOG,
% FT_ARTIFACT_JUMP, FT_ARTIFACT_MUSCLE, FT_ARTIFACT_THRESHOLD, FT_ARTIFACT_ZVALUE

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

% these are used by the ft_preamble/ft_postamble function and scripts
ft_revision = '$Id$';
ft_nargin   = nargin;
ft_nargout  = nargout;

% do the general setup of the function
ft_defaults
ft_preamble init
ft_preamble debug
ft_preamble loadvar    data
ft_preamble provenance data

% the ft_abort variable is set to true or false in ft_preamble_init
if ft_abort
  return
end

% set the default options
cfg.feedback              = ft_getopt(cfg, 'feedback', 'text');
cfg.artfctdef             = ft_getopt(cfg, 'artfctdef');
cfg.artfctdef.nan         = ft_getopt(cfg.artfctdef, 'nan');
cfg.artfctdef.nan.channel = ft_getopt(cfg.artfctdef.nan, 'channel', {'all'});

% the data is either passed into the function by the user or read from file with cfg.inputfile
hasdata = exist('data', 'var');

% read the header, or get it from the input data
if ~hasdata
  cfg = ft_checkconfig(cfg, 'dataset2files', 'yes');
  cfg = ft_checkconfig(cfg, 'required', {'headerfile', 'datafile'});
  hdr = ft_read_header(cfg.headerfile, 'headerformat', cfg.headerformat);
else
  data = ft_checkdata(data, 'datatype', 'raw', 'hassampleinfo', 'yes');
  cfg  = ft_checkconfig(cfg, 'forbidden', {'dataset', 'headerfile', 'datafile'});
  hdr  = ft_fetch_header(data);
end

% set default cfg.continuous
if ~isfield(cfg, 'continuous')
  if hdr.nTrials==1
    cfg.continuous = 'yes';
  else
    cfg.continuous = 'no';
  end
end

% get the specification of the data segments that should be scanned for artifacts
if ~isfield(cfg, 'trl') && hasdata
  trl = data.sampleinfo;
  for k = 1:numel(data.trial)
    trl(k,3) = time2offset(data.time{k}, data.fsample);
  end
elseif isfield(cfg, 'trl') && ischar(cfg.trl)
  trl = loadvar(cfg.trl, 'trl');
elseif isfield(cfg, 'trl') && isnumeric(cfg.trl)
  trl = cfg.trl;
else
  ft_error('cannot determine which segments of data to scan for artifacts');
end

% get the remaining settings
artfctdef     = cfg.artfctdef.nan;
artfctdef.trl = trl;
numtrl        = size(trl,1);
channel       = ft_channelselection(artfctdef.channel, hdr.label);
chanindx      = match_str(hdr.label, channel);
nchan         = length(chanindx);
artifact      = zeros(0,2);

ft_progress('init', cfg.feedback, ['searching for artifacts in ' num2str(nchan) ' channels']);
for trlop=1:numtrl
  ft_progress(trlop/numtrl, 'searching in trial %d from %d\n', trlop, numtrl);
  if hasdata
    dat = ft_fetch_data(data,        'header', hdr, 'begsample', trl(trlop,1), 'endsample', trl(trlop,2), 'chanindx', chanindx, 'checkboundary', strcmp(cfg.continuous, 'no'));
  else
    dat = ft_read_data(cfg.datafile, 'header', hdr, 'begsample', trl(trlop,1), 'endsample', trl(trlop,2), 'chanindx', chanindx, 'checkboundary', strcmp(cfg.continuous, 'no'), 'dataformat', cfg.dataformat);
  end

  tmp = any(isnan(dat),1);
  if any(tmp)
    % there can be multiple segments with nans
    begsample = find(diff([0 tmp])>0);
    endsample = find(diff([tmp 0])<0);
    for j=1:numel(begsample)
      artifact(end+1,:) = [begsample(j) endsample(j)] + data.sampleinfo(trlop,1) - 1;
    end
  end
end % for trlop
ft_progress('close');

% remember the details that were used here
cfg.artfctdef.nan          = artfctdef;
cfg.artfctdef.nan.artifact = artifact;

% do the general cleanup and bookkeeping at the end of the function
ft_postamble debug
ft_postamble previous   data
ft_postamble savevar


function str = strCell2EnumStr(str_cell, separator)
% SYNTAX:
%   [str] = strcell2enumstr(str_cell, <separator == ' '>);
%
% INPUT:
%   str_cell  = cell array of strings
%   separator = <optional> contains the separator string (white space as default)
%
% OUTPUT:
%   str = single string of the element list separated by the "separator"
%
% DESCRIPTION:
%   Convert to a character array the input cell of strings

%--- * --. --- --. .--. ... * ---------------------------------------------
%               ___ ___ ___
%     __ _ ___ / __| _ | __|
%    / _` / _ \ (_ |  _|__ \
%    \__, \___/\___|_| |___/
%    |___/                    v 1.0RC1
%
%--------------------------------------------------------------------------
%  Copyright (C) 2021 Geomatics Research & Development srl (GReD)
%  Written by:       Andrea Gatti
%  Contributors:     Andrea Gatti
%  A list of all the historical goGPS contributors is in CREDITS.nfo
%--------------------------------------------------------------------------
%
%   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/>.
%
%--------------------------------------------------------------------------
% 01100111 01101111 01000111 01010000 01010011
%--------------------------------------------------------------------------
    if nargin == 1
        separator = ' ';
    end
    if ischar(str_cell)
        str_cell = {str_cell};
    end
    if ~isempty(str_cell)
        str = sprintf('%d: %s', 0, str_cell{1});
        for i = 2 : numel(str_cell)
            str = sprintf('%s%s%d: %s', str, separator, i-1, str_cell{i});
        end
    else
        str = '';
    end
end


function [ algorithmJob ] = jobInit()
% Construct an instance of an algorithm analyser job

    % Read configuration
    load ../GUI/config/config.mat;
    algorithmJob.algorithmId    = 1;
    algorithmJob.datasetId      = 1;
    AL                          = char(algorithmList);
    algorithmJob.algorithm      = AL(algorithmJob.algorithmId,:);
    DL                          = char(dataList);
    algorithmJob.dataset        = DL(algorithmJob.datasetId,:);
    algorithmJob.parameters     = cell2mat(defaultParameters(algorithmJob.algorithmId,:));
end


function UFcollection_install (nlargefile)
%UFCOLLECTION_INSTALL install the UFcollection toolbox
%
% Example:
%   UFcollection_install
%
% See also UFget.

% Copyright 2006-2007, Timothy A. Davis

if (nargin < 1)
    % try with large-file I/O
    nlargefile = 0 ;
end

if (nlargefile)
    fprintf ('Trying to compile without large file support...\n') ;
    mex -I../UFconfig -DNLARGEFILE UFfull_write.c
else
    try
	mex -I../UFconfig UFfull_write.c
    catch
	fprintf ('Trying to compile without large file support...\n') ;
	mex -I../UFconfig -DNLARGEFILE UFfull_write.c
    end
end

addpath (pwd) ;
fprintf ('UFcollection toolbox successfully compiled.\n') ;


function annotate(obj,varargin)
% annotate to a existing figure

% is only applicable for MTEX figures
if isempty(gcm), return; end

aS = getMTEXpref('annotationStyle');
plot(obj,aS{:},'add2all',varargin{:});
pause(0.01);
drawNow(gcm);

end


classdef testPrintingDescriptor < handle

    properties (Access = protected, Abstract)
        fileOutputName
        iter
    end

    methods (Access = protected)

        function hasPassed = hasPassed(obj)
            hasMshChanged = obj.compareFile('.msh');
            hasResChanged = obj.compareFile('.res');
            filesHaveChanged = hasMshChanged || hasResChanged;
            hasPassed = ~filesHaveChanged;
        end

        function selectComputedVar(obj)
        end

    end

    methods (Access = private)

       function sF = obtainSavedPrintedFile(obj,ext)
           out   = obj.fileOutputName;
           fullOutSavedName = [out,'.flavia',ext];
           sF = fullfile('tests','PrintingTests','PrintedFiles',fullOutSavedName);
        end

        function oF = obtainOutPutFile(obj,ext)
           out   = obj.fileOutputName;
           fullOutName = [out,num2str(obj.iter),'.flavia',ext];
           oF = fullfile('Output',out,fullOutName);
        end

        function hasChanged = compareFile(obj,ext)
            sF = obj.obtainSavedPrintedFile(ext);
            oF = obj.obtainOutPutFile(ext);
            hasChanged = FileComperator().areFilesDifferent(sF,oF);
        end

    end

end


function print2eps(name, fig, bb_padding, varargin)
%PRINT2EPS  Prints figures to eps with improved line styles
%
% Examples:
%   print2eps filename
%   print2eps(filename, fig_handle)
%   print2eps(filename, fig_handle, bb_padding)
%   print2eps(filename, fig_handle, bb_padding, options)
%
% This function saves a figure as an eps file, with two improvements over
% MATLAB's print command. First, it improves the line style, making dashed
% lines more like those on screen and giving grid lines their own dotted
% style. Secondly, it substitutes original font names back into the eps
% file, where these have been changed by MATLAB, for up to 11 different
% fonts.
%
%IN:
%   filename - string containing the name (optionally including full or
%              relative path) of the file the figure is to be saved as. A
%              ".eps" extension is added if not there already. If a path is
%              not specified, the figure is saved in the current directory.
%   fig_handle - The handle of the figure to be saved. Default: gcf().
%   bb_padding - Scalar value of amount of padding to add to border around
%                the cropped image, in points (if >1) or percent (if <1).
%                Can be negative as well as positive; Default: 0.
%                May be a 2-element vector of padding and crop amount.
%   options - Additional parameter strings to be passed to print.

%{
% Copyright (C) Oliver Woodford 2008-2014, Yair Altman 2015-

% The idea of editing the EPS file to change line styles comes from Jiro
% Doke's FIXPSLINESTYLE (fex id: 17928)
% The idea of changing dash length with line width came from comments on
% fex id: 5743, but the implementation is mine :)
%}
%{
% 14/11/11: Fix a MATLAB bug rendering black or white text incorrectly.
%           Thanks to Mathieu Morlighem for reporting the issue and
%           obtaining a fix from TMW.
% 08/12/11: Added ability to correct fonts. Several people have requested
%           this at one time or another, and also pointed me to printeps
%           (fex id: 7501), so thank you to them. My implementation (which
%           was not inspired by printeps - I'd already had the idea for my
%           approach) goes slightly further in that it allows multiple
%           fonts to be swapped.
% 14/12/11: Fix bug affecting font names containing spaces. Thanks to David
%           Szwer for reporting the issue.
% 25/01/12: Add a font not to be swapped. Thanks to Anna Rafferty and Adam
%           Jackson for reporting the issue. Also fix a bug whereby using a
%           font alias can lead to another font being swapped in.
% 10/04/12: Make the font swapping case insensitive.
% 26/10/12: Set PaperOrientation to portrait. Thanks to Michael Watts for
%           reporting the issue.
% 26/10/12: Fix issue to do with swapping fonts changing other fonts and
%           sizes we don't want, due to listeners. Thanks to Malcolm Hudson
%           for reporting the issue.
% 22/03/13: Extend font swapping to axes labels. Thanks to Rasmus Ischebeck
%           for reporting the issue.
% 23/07/13: Bug fix to font swapping. Thanks to George for reporting the
%           issue.
% 13/08/13: Fix MATLAB feature of not exporting white lines correctly.
%           Thanks to Sebastian Heßlinger for reporting it.
% 24/02/15: Fix for Matlab R2014b bug (issue #31): LineWidths<0.75 are not
%           set in the EPS (default line width is used)
% 25/02/15: Fixed issue #32: BoundingBox problem caused uncropped EPS/PDF files
% 05/03/15: Fixed issue #43: Inability to perform EPS file post-processing
% 06/03/15: Improved image padding & cropping thanks to Oscar Hartogensis
% 21/03/15: Fixed edge-case of missing handles having a 'FontName' property
% 26/03/15: Attempt to fix issue #45: white lines in subplots do not print correctly
% 27/03/15: Attempt to fix issue #44: white artifact lines appearing in patch exports
% 30/03/15: Fixed issue #52: improved performance on HG2 (R2014b+)
% 09/04/15: Comment blocks consolidation and minor code cleanup (no real code change)
% 12/04/15: Fixed issue #56: bad cropping
% 14/04/15: Workaround for issue #45: lines in image subplots are exported in invalid color
%}

    options = {'-loose'};
    if nargin > 3
        options = [options varargin];
    elseif nargin < 3
        bb_padding = 0;
        if nargin < 2
            fig = gcf();
        end
    end

    % Retrieve crop value (2nd element of bb_padding vector, or default=0)
    try
        bb_crop = bb_padding(2);
        bb_padding = bb_padding(1);  % reached this point, so it's a vector
    catch
        bb_crop = 0;  % scalar value, so use default bb_crop value of 0
    end

    % Construct the filename
    if numel(name) < 5 || ~strcmpi(name(end-3:end), '.eps')
        name = [name '.eps']; % Add the missing extension
    end

    % Set paper size
    old_pos_mode = get(fig, 'PaperPositionMode');
    old_orientation = get(fig, 'PaperOrientation');
    set(fig, 'PaperPositionMode', 'auto', 'PaperOrientation', 'portrait');

    % Find all the used fonts in the figure
    font_handles = findall(fig, '-property', 'FontName');
    fonts = get(font_handles, 'FontName');
    if isempty(fonts)
        fonts = {};
    elseif ~iscell(fonts)
        fonts = {fonts};
    end

    % Map supported font aliases onto the correct name
    fontsl = lower(fonts);
    for a = 1:numel(fonts)
        f = fontsl{a};
        f(f==' ') = [];
        switch f
            case {'times', 'timesnewroman', 'times-roman'}
                fontsl{a} = 'times-roman';
            case {'arial', 'helvetica'}
                fontsl{a} = 'helvetica';
            case {'newcenturyschoolbook', 'newcenturyschlbk'}
                fontsl{a} = 'newcenturyschlbk';
            otherwise
        end
    end
    fontslu = unique(fontsl);

    % Determine the font swap table
    matlab_fonts = {'Helvetica', 'Times-Roman', 'Palatino', 'Bookman', 'Helvetica-Narrow', 'Symbol', ...
        'AvantGarde', 'NewCenturySchlbk', 'Courier', 'ZapfChancery', 'ZapfDingbats'};
    matlab_fontsl = lower(matlab_fonts);
    require_swap = find(~ismember(fontslu, matlab_fontsl));
    unused_fonts = find(~ismember(matlab_fontsl, fontslu));
    font_swap = cell(3, min(numel(require_swap), numel(unused_fonts)));
    fonts_new = fonts;
    for a = 1:size(font_swap, 2)
        font_swap{1,a} = find(strcmp(fontslu{require_swap(a)}, fontsl));
        font_swap{2,a} = matlab_fonts{unused_fonts(a)};
        font_swap{3,a} = fonts{font_swap{1,a}(1)};
        fonts_new(font_swap{1,a}) = font_swap(2,a);
    end

    % Swap the fonts
    if ~isempty(font_swap)
        fonts_size = get(font_handles, 'FontSize');
        if iscell(fonts_size)
            fonts_size = cell2mat(fonts_size);
        end
        M = false(size(font_handles));

        % Loop because some changes may not stick first time, due to listeners
        c = 0;
        update = zeros(1000, 1);
        for b = 1:10 % Limit number of loops to avoid infinite loop case
            for a = 1:numel(M)
                M(a) = ~isequal(get(font_handles(a), 'FontName'), fonts_new{a}) || ~isequal(get(font_handles(a), 'FontSize'), fonts_size(a));
                if M(a)
                    set(font_handles(a), 'FontName', fonts_new{a}, 'FontSize', fonts_size(a));
                    c = c + 1;
                    update(c) = a;
                end
            end
            if ~any(M)
                break;
            end
        end

        % Compute the order to revert fonts later, without the need of a loop
        [update, M] = unique(update(1:c));
        [M, M] = sort(M);
        update = reshape(update(M), 1, []);
    end

    % MATLAB bug fix - black and white text can come out inverted sometimes
    % Find the white and black text
    black_text_handles = findall(fig, 'Type', 'text', 'Color', [0 0 0]);
    white_text_handles = findall(fig, 'Type', 'text', 'Color', [1 1 1]);
    % Set the font colors slightly off their correct values
    set(black_text_handles, 'Color', [0 0 0] + eps);
    set(white_text_handles, 'Color', [1 1 1] - eps);

    % MATLAB bug fix - white lines can come out funny sometimes
    % Find the white lines
    white_line_handles = findall(fig, 'Type', 'line', 'Color', [1 1 1]);
    % Set the line color slightly off white
    set(white_line_handles, 'Color', [1 1 1] - 0.00001);

    % Workaround for issue #45: lines in image subplots are exported in invalid color
    % In this case the -depsc driver solves the problem, but then all the other workarounds
    % below (for all the other issues) will fail, so it's better to let the user decide by
    % just issuing a warning and accepting the '-depsc' input parameter
    epsLevel2 = ~any(strcmpi(options,'-depsc'));
    if epsLevel2
        % Use -depsc2 (EPS color level-2) if -depsc (EPS color level-3) was not specifically requested
        options{end+1} = '-depsc2';
        % Issue a warning if multiple images & lines were found in the figure, and HG1 with painters renderer is used
        isPainters = any(strcmpi(options,'-painters'));
        if isPainters && ~using_hg2 && numel(findall(fig,'Type','image'))>1 && ~isempty(findall(fig,'Type','line'))
            warning('YMA:export_fig:issue45', ...
                    ['Multiple images & lines detected. In such cases, the lines might \n' ...
                     'appear with an invalid color due to an internal MATLAB bug (fixed in R2014b). \n' ...
                     'Possible workaround: add a ''-depsc'' or ''-opengl'' parameter to the export_fig command.']);
        end
    end

    % Print to eps file
    print(fig, options{:}, name);

    % Do post-processing on the eps file
    try
        % Read the EPS file into memory
        fstrm = read_write_entire_textfile(name);
    catch
        fstrm = '';
    end

    % Fix for Matlab R2014b bug (issue #31): LineWidths<0.75 are not set in the EPS (default line width is used)
    try
        if ~isempty(fstrm) && using_hg2(fig)
            % Convert miter joins to line joins
            %fstrm = regexprep(fstrm, '\n10.0 ML\n', '\n1 LJ\n');
            % This is faster (the original regexprep could take many seconds when the axes contains many lines):
            fstrm = strrep(fstrm, sprintf('\n10.0 ML\n'), sprintf('\n1 LJ\n'));

            % In HG2, grid lines and axes Ruler Axles have a default LineWidth of 0.5 => replace en-bulk (assume that 1.0 LineWidth = 1.333 LW)
            %   hAxes=gca; hAxes.YGridHandle.LineWidth, hAxes.YRuler.Axle.LineWidth
            %fstrm = regexprep(fstrm, '(GC\n2 setlinecap\n1 LJ)\nN', '$1\n0.667 LW\nN');
            % This is faster:
            fstrm = strrep(fstrm, sprintf('GC\n2 setlinecap\n1 LJ\nN'), sprintf('GC\n2 setlinecap\n1 LJ\n0.667 LW\nN'));

            % This is more accurate but *MUCH* slower (issue #52)
            %{
            % Modify all thin lines in the figure to have 10x LineWidths
            hLines = findall(fig,'Type','line');
            hThinLines = [];
            for lineIdx = 1 : numel(hLines)
                thisLine = hLines(lineIdx);
                if thisLine.LineWidth < 0.75 && strcmpi(thisLine.Visible,'on')
                    hThinLines(end+1) = thisLine; %#ok<AGROW>
                    thisLine.LineWidth = thisLine.LineWidth * 10;
                end
            end

            % If any thin lines were found
            if ~isempty(hThinLines)
                % Prepare an EPS with large-enough line widths
                print(fig, options{:}, name);
                % Restore the original LineWidths in the figure
                for lineIdx = 1 : numel(hThinLines)
                    thisLine = handle(hThinLines(lineIdx));
                    thisLine.LineWidth = thisLine.LineWidth / 10;
                end

                % Compare the original and the new EPS files and correct the original stream's LineWidths
                fstrm_new = read_write_entire_textfile(name);
                idx = 500;  % skip heading with its possibly-different timestamp
                markerStr = sprintf('10.0 ML\nN');
                markerLen = length(markerStr);
                while ~isempty(idx) && idx < length(fstrm)
                    lastIdx = min(length(fstrm), length(fstrm_new));
                    delta = fstrm(idx+1:lastIdx) - fstrm_new(idx+1:lastIdx);
                    idx = idx + find(delta,1);
                    if ~isempty(idx) && ...
                            isequal(fstrm(idx-markerLen+1:idx), markerStr) && ...
                            ~isempty(regexp(fstrm_new(idx-markerLen+1:idx+12),'10.0 ML\n[\d\.]+ LW\nN')) %#ok<RGXP1>
                        value = str2double(regexprep(fstrm_new(idx:idx+12),' .*',''));
                        if isnan(value), break; end  % something's wrong... - bail out
                        newStr = sprintf('%0.3f LW\n',value/10);
                        fstrm = [fstrm(1:idx-1) newStr fstrm(idx:end)];
                        idx = idx + 12;
                    else
                        break;
                    end
                end
            end
            %}

            % This is much faster although less accurate: fix all non-gray lines to have a LineWidth of 0.75 (=1 LW)
            % Note: This will give incorrect LineWidth of 075 for lines having LineWidth<0.75, as well as for non-gray grid-lines (if present)
            %       However, in practice these edge-cases are very rare indeed, and the difference in LineWidth should not be noticeable
            %fstrm = regexprep(fstrm, '([CR]C\n2 setlinecap\n1 LJ)\nN', '$1\n1 LW\nN');
            % This is faster (the original regexprep could take many seconds when the axes contains many lines):
            fstrm = strrep(fstrm, sprintf('\n2 setlinecap\n1 LJ\nN'), sprintf('\n2 setlinecap\n1 LJ\n1 LW\nN'));
        end
    catch err
        fprintf(2, 'Error fixing LineWidths in EPS file: %s\n at %s:%d\n', err.message, err.stack(1).file, err.stack(1).line);
    end

    % Reset the font and line colors
    set(black_text_handles, 'Color', [0 0 0]);
    set(white_text_handles, 'Color', [1 1 1]);
    set(white_line_handles, 'Color', [1 1 1]);

    % Reset paper size
    set(fig, 'PaperPositionMode', old_pos_mode, 'PaperOrientation', old_orientation);

    % Reset the font names in the figure
    if ~isempty(font_swap)
        for a = update
            set(font_handles(a), 'FontName', fonts{a}, 'FontSize', fonts_size(a));
        end
    end

    % Bail out if EPS post-processing is not possible
    if isempty(fstrm)
        warning('Loading EPS file failed, so unable to perform post-processing. This is usually because the figure contains a large number of patch objects. Consider exporting to a bitmap format in this case.');
        return
    end

    % Replace the font names
    if ~isempty(font_swap)
        for a = 1:size(font_swap, 2)
            %fstrm = regexprep(fstrm, [font_swap{1,a} '-?[a-zA-Z]*\>'], font_swap{3,a}(~isspace(font_swap{3,a})));
            fstrm = regexprep(fstrm, font_swap{2,a}, font_swap{3,a}(~isspace(font_swap{3,a})));
        end
    end

    % Move the bounding box to the top of the file (HG2 only), or fix the line styles (HG1 only)
    if using_hg2(fig)
        % Move the bounding box to the top of the file (HG2 only)
        [s, e] = regexp(fstrm, '%%BoundingBox: [^%]*%%');
        if numel(s) == 2
            fstrm = fstrm([1:s(1)-1 s(2):e(2)-2 e(1)-1:s(2)-1 e(2)-1:end]);
        end
    else
        % Fix the line styles (HG1 only)
        fstrm = fix_lines(fstrm);
    end

    % Apply the bounding box padding & cropping, replacing Matlab's print()'s bounding box
    if bb_crop
        % Calculate a new bounding box based on a bitmap print using crop_border.m
        % 1. Determine the Matlab BoundingBox and PageBoundingBox
        [s,e] = regexp(fstrm, '%%BoundingBox: [^%]*%%'); % location BB in eps file
        if numel(s)==2, s=s(2); e=e(2); end
        aa = fstrm(s+15:e-3); % dimensions bb - STEP1
        bb_matlab = cell2mat(textscan(aa,'%f32%f32%f32%f32'));  % dimensions bb - STEP2

        [s,e] = regexp(fstrm, '%%PageBoundingBox: [^%]*%%'); % location bb in eps file
        if numel(s)==2, s=s(2); e=e(2); end
        aa = fstrm(s+19:e-3); % dimensions bb - STEP1
        pagebb_matlab = cell2mat(textscan(aa,'%f32%f32%f32%f32'));  % dimensions bb - STEP2

        % 2. Create a bitmap image and use crop_borders to create the relative
        %    bb with respect to the PageBoundingBox
        [A, bcol] = print2array(fig, 1, '-opengl');
        [aa, aa, aa, bb_rel] = crop_borders(A, bcol, bb_padding);

        % 3. Calculate the new Bounding Box
        pagew = pagebb_matlab(3)-pagebb_matlab(1);
        pageh = pagebb_matlab(4)-pagebb_matlab(2);
        %bb_new = [pagebb_matlab(1)+pagew*bb_rel(1) pagebb_matlab(2)+pageh*bb_rel(2) ...
        %          pagebb_matlab(1)+pagew*bb_rel(3) pagebb_matlab(2)+pageh*bb_rel(4)];
        bb_new = pagebb_matlab([1,2,1,2]) + [pagew,pageh,pagew,pageh].*bb_rel;  % clearer
        bb_offset = (bb_new-bb_matlab) + [-1,-1,1,1];  % 1px margin so that cropping is not TOO tight

        % Apply the bounding box padding
        if bb_padding
            if abs(bb_padding)<1
                bb_padding = round((mean([bb_new(3)-bb_new(1) bb_new(4)-bb_new(2)])*bb_padding)/0.5)*0.5; % ADJUST BB_PADDING
            end
            add_padding = @(n1, n2, n3, n4) sprintf(' %d', str2double({n1, n2, n3, n4}) + [-bb_padding -bb_padding bb_padding bb_padding] + bb_offset);
        else
            add_padding = @(n1, n2, n3, n4) sprintf(' %d', str2double({n1, n2, n3, n4}) + bb_offset); % fix small but noticeable bounding box shift
        end
        fstrm = regexprep(fstrm, '%%BoundingBox:[ ]+([-]?\d+)[ ]+([-]?\d+)[ ]+([-]?\d+)[ ]+([-]?\d+)', '%%BoundingBox:${add_padding($1, $2, $3, $4)}');
    end

    % Fix issue #44: white artifact lines appearing in patch exports
    % Note: the problem is due to the fact that Matlab's print() function exports patches
    %       as a combination of filled triangles, and a white line appears where the triangles touch
    % In the workaround below, we will modify such dual-triangles into a filled rectangle.
    % We are careful to only modify regexps that exactly match specific patterns - it's better to not
    % correct some white-line artifacts than to change the geometry of a patch, or to corrupt the EPS.
    %   e.g.: '0 -450 937 0 0 450 3 MP PP 937 0 0 -450 0 450 3 MP PP' => '0 -450 937 0 0 450 0 0 4 MP'
    fstrm = regexprep(fstrm, '\n([-\d.]+ [-\d.]+) ([-\d.]+ [-\d.]+) ([-\d.]+ [-\d.]+) 3 MP\nPP\n\2 \1 \3 3 MP\nPP\n','\n$1 $2 $3 0 0 4 MP\nPP\n');
    fstrm = regexprep(fstrm, '\n([-\d.]+ [-\d.]+) ([-\d.]+ [-\d.]+) ([-\d.]+ [-\d.]+) 3 MP\nPP\n\2 \3 \1 3 MP\nPP\n','\n$1 $2 $3 0 0 4 MP\nPP\n');
    fstrm = regexprep(fstrm, '\n([-\d.]+ [-\d.]+) ([-\d.]+ [-\d.]+) ([-\d.]+ [-\d.]+) 3 MP\nPP\n\3 \1 \2 3 MP\nPP\n','\n$1 $2 $3 0 0 4 MP\nPP\n');
    fstrm = regexprep(fstrm, '\n([-\d.]+ [-\d.]+) ([-\d.]+ [-\d.]+) ([-\d.]+ [-\d.]+) 3 MP\nPP\n\3 \2 \1 3 MP\nPP\n','\n$1 $2 $3 0 0 4 MP\nPP\n');

    % Write out the fixed eps file
    read_write_entire_textfile(name, fstrm);
end


function res = isequal(this, that)
% Method to check if 2 MEEG objects are the same
% FORMAT res = isequal(this, that)
% _______________________________________________________________________
% Copyright (C) 2011 Wellcome Trust Centre for Neuroimaging

% Christophe Phillips
% $Id: isequal.m 5025 2012-10-31 14:44:13Z vladimir $

res = isequal(struct(this), struct(that));