function ImP_SQI_mimic_data_importer
% ImP_SQI_mimic_data_importer imports data from the MIMIC II database with
% which to assess the performance of the impedance pneumography SQI, and
% outputs the dataset in Matlab format.
%
%               ImP_SQI_mimic_data_importer
%
%	Inputs:
%       Just specify the relevant paths in the "universal_parameters"
%       function below. No other inputs are required as this script
%       downloads the required data files from PhysioNet automatically.
%
%	Outputs:
%       A Matlab file containing the collated data, called "mimic_imp_sqi_data.mat"
%
%   The WFDB Toolbox:
%       This requires the WFDB Toolbox, which can be downloaded from:
%           https://archive.physionet.org/physiotools/matlab/wfdb-app-matlab/
%       I installed it using the 'Quick Start' instructions at:
%           https://archive.physionet.org/physiotools/matlab/wfdb-app-matlab/#quick-start
%               (including modifying the download path to be https://archive.physionet.org ... )
%       The Toolbox is now available at:
%           https://physionet.org/content/wfdb-swig-matlab/1.0.0/
%
%   Further Information:
%       This version of the ImP_SQI_mimic_data_importer is provided to facilitate
%       reproduction of the MIMIC subset used in:
%           Charlton P. H. et al., "An impedance pneumography signal
%           quality index for respiratory rate monitoring: design,
%           assessment and application", [under review]
%       Further information on this study can be obtained at:
%           http://peterhcharlton.github.io/RRest/imp_sqi.html
%       In addition, further information on RRest, a toolbox of respiratory
%       algorithms which can be used with this dataset, can be obtained at:
%           http://peterhcharlton.github.io/RRest/index.html
%
%   Comments, Questions, Criticisms, Feedback, Contributions:
%       See: http://peterhcharlton.github.io/RRest/contributions.html
%
%   Version:
%       v.0.1 - accompanying peer review, 6th Aug 2020 by Peter H Charlton
%
%   Source:
%       This script has been adapted from 'MIMICII_data_importer.m', which
%       is one of the scripts in the RRest toolbox, available at:
%           https://github.com/peterhcharlton/RRest
%
%   Licence:
%       This program is available under the GNU public license. 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: <http://www.gnu.org/licenses/>.
%

%% Setup
% ~~~ This function needs adjusting by the individual user as it contains paths
% specific to their computer ~~~
up = universal_parameters;

%% Create ReadMe and License files
create_readme_and_license_files(up);

%% Download data
download_data(up);

%% Extract data from files on computer
extract_from_mimic_ii(up);

%% Add in manual annotations
add_manual_annotations(up);

% %% Save data in RRest format
% if up.output.rrest_format, save_data_in_rrest_format(up); end
%
% %% Save data in CSV format
% if up.output.csv_format, save_data_in_csv_format(up); end
%
% %% Save data in WFDB format
% if up.output.csv_format, save_data_in_wfdb_format(up); end

fprintf(['\n\n -- Finished importing the ImP SQI dataset. Data saved at:\n\n       ', up.paths.data_root, '\n\n'])

end

function up = universal_parameters

fprintf('\n -- Setting up Universal Parameters')

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%% PARAMETERS TO BE SPECIFIED %%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% Specify the root data directory (where the data will be stored)
up.paths.data_root = '/Users/petercharlton/Documents/Data/Imp_SQI_mimic_dataset_test/';
fprintf('\n   - The data will be stored at %s', up.paths.data_root);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%   OTHER PARAMETERS   %%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%   (don't need editing)   %%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

provide_details_of_script;

% Specify the web address of the data to be downloaded
rel_database = 'mimic3wdb/matched';
rel_database_part = 'p00';
up.paths.database_dir_root = ['https://archive.physionet.org/physiobank/database/', rel_database, '/'];
up.paths.database_dir = [up.paths.database_dir_root, rel_database_part];
up.paths.analysis_path = [up.paths.data_root, 'Analysis_files', filesep, 'Data_for_Analysis', filesep ];
up.paths.records_filepath = [up.paths.analysis_path, 'RECORDS'];
up.paths.download_details_file = [up.paths.analysis_path, 'download_details'];
up.no_pt_stays = 100;

% extraction definitions
up.extraction.rel_sigs = {'II', 'PLETH', 'RESP'};    % required signals
up.extraction.rel_nums = {'HR', 'PULSE', 'RESP', 'SpO2'};    % required numerics
up.fs = 125;  % Hz
up.req_data_time = 0*60;  % data taken from 0 minutes into record (secs)
up.req_data_duration = 60*60;  % segments of data extracted (secs)
up.dataset_name = 'imp_sqi_mimic';
up.csv.new_line = '\n';
up.units.signals = {'II', 'AVR', 'V',  'III', 'I',  'MCL1', 'MCL', 'PLETH', 'RESP', 'ABP',  'CVP',  'ART',   'P1',  'UAP',  'PAP'};
up.units.units   = {'mV', 'mV',  'mV', 'mV',  'mV', 'mV',   'mV',  'NU',    'pm'  , 'mmHg', 'mmHg', 'mmHg', 'mmHg', 'mmHg', 'mmHg'};
up.units.numerics    = {'HR',  'PULSE',  'SpO2', 'RESP', 'ABPSys', 'ABPDias', 'ABPMean', 'NBPSys', 'MBPDia', 'MBPMean'};
up.units.num_units   = {'bpm', 'bpm',    '%',    'pm',   'mmHg',   'mmHg',    'mmHg',    'mmHg',   'mmHg',   'mmHg'   };

% paths
up.paths.file_location = [up.paths.data_root, 'physionet.org', filesep, 'physiobank' ,filesep 'database', filesep];
up.paths.extracted_data_file = [up.paths.data_root, 'mimic_imp_sqi_data.mat'];

% url paths
up.paths.url.records = [up.paths.database_dir_root, 'RECORDS'];

% Add all functions within the directory
addpath(genpath(fileparts(mfilename('fullpath'))));

% check that all the required folders exist. If not, create them:
if ~exist(up.paths.data_root, 'dir')
    fprintf('\n The specified data folder does not exist \n - please either: \n 1) select a different location (speifying it in "universal_parameters"), or\n 2) create a folder at the specified location: %s', up.paths.data_root);
    error('Specified folder does not exist')
end
req_folders = {up.paths.analysis_path};
displayed_msg = 0;
for req_folder_no = 1 : length(req_folders)
    curr_folder = req_folders{req_folder_no};
    if ~exist(curr_folder, 'dir')
        if displayed_msg == 0
            fprintf('\n -- Creating folder in which to store data and analysis')
            displayed_msg = 1;
        end
        mkdir(curr_folder)
    end
end

% Type(s) of output (currently not used)
up.output.wfdb_format = true; % Save in PhysioNet's waveform database format
up.output.csv_format = true;  % Save as comma-separated-value format
up.output.rrest_format = true;% Save in a format suitable for use with the RRest Toolbox of algorithms

% Check that the WFDB Matlab Toolbox has been installed
if ~exist('getWfdbClass', 'file')
    error('Couldn''t find the WFDB Matlab Toolbox. Please install as described at the top of the file.')
end

end

function provide_details_of_script

licence_details = ['\n\n ImP_SQI_data_importer', ...
    '\n This program comes with ABSOLUTELY NO WARRANTY', ...
    '\n and is available under the GNU public license.', ...
    '\n For details see the accompanying LICENSE.txt file.\n\n'];

fprintf(licence_details)

end

function download_data(up)

fprintf('\n -- Downloading Data:')

%% set current directory to that of wget:
cd(up.paths.data_root)

%% Find out what records are available:
fprintf(' List of records, ');
% download list of records
if ~exist(up.paths.records_filepath, 'file')
    outfilename = websave(up.paths.records_filepath,up.paths.url.records);
end

% form list of patient stays from downloaded file
pt_stays = importdata(up.paths.records_filepath);

%% Download each record
file_counter = 0;

for pt_stay_no = 1 : length(pt_stays)

    % specify patient stay
    pt_stay = pt_stays{pt_stay_no};
    temp = strfind(pt_stay, '/');
    database_subset = pt_stay(1:temp(1)-1);
    pt_no = pt_stay(temp(1)+1:end-1); clear temp

    % set save location
    save_folder = [up.paths.file_location, pt_stay];
    if ~exist(save_folder, 'dir')
        mkdir(save_folder)
    end

    % download records for this pt stay
    pt_stay_records_url = [up.paths.database_dir_root, pt_stay, 'RECORDS'];
    save_path = [save_folder,'RECORDS'];
    if ~exist(save_path, 'file')
        outfilename = websave(save_path, pt_stay_records_url);
    end
    pt_stay_records = importdata(save_path);

    % download overall header file
    pt_stay_segment = pt_stay_records{1};
    curr_file_name = [pt_stay_segment, '.hea'];
    overall_header_file = [up.paths.database_dir_root, pt_stay, curr_file_name];
    overall_header_file_save_loc = [save_folder, curr_file_name];
    if ~exist(overall_header_file_save_loc, 'file')
        outfilename = websave(overall_header_file_save_loc,overall_header_file);
    end

    % find out what individual files are in this pt stay:
    fileID = fopen(overall_header_file_save_loc);
    individual_files.name = {};
    individual_files.duration = []; individual_files.cum_duration_before = []; individual_files.cum_duration_after = [];
    line_no = 0;
    while ~feof(fileID)
        curr_text_line = fgetl(fileID); line_no = line_no+1;
        if line_no == 1
            temp = strfind(curr_text_line, ':');
            individual_files.start_time = curr_text_line(temp(1)-2:temp(1)+9);
        elseif ~isempty(strfind(curr_text_line, '_')) & length(strfind(curr_text_line, ' '))==1 & isempty(strfind(curr_text_line, 'layout'))
            individual_files.name{end+1} = curr_text_line(1:strfind(curr_text_line, '_')+4);
            individual_files.duration(end+1) = str2double(curr_text_line(strfind(curr_text_line, '_')+6:end));
        end
        clear curr_text_line
    end
    clear line_no
    fclose(fileID); clear fileID

    % Find out which files contain sufficient data
    rel_file_els = find(individual_files.duration >= (up.req_data_duration*up.fs+1));
    rel_file_names = individual_files.name(rel_file_els);

    % cycle through each file containing sufficient data
    for file_no = 1 : length(rel_file_names)
        curr_rel_file = rel_file_names{file_no};

        % download data header file
        curr_file_name = [curr_rel_file, '.hea'];
        down_loc = [up.paths.database_dir_root, pt_stay, curr_file_name];
        save_loc = [save_folder, curr_file_name];
        if ~exist(save_loc, 'file')
            outfilename = websave(save_loc,down_loc);
        end
        data_header_file = save_loc;

        % identify the signals in this file
        fileID = fopen(save_loc);
        line_no = 0;
        signals = [];
        while ~feof(fileID)
            curr_text_line = fgetl(fileID); line_no = line_no+1;
            if line_no == 1
                temp = strfind(curr_text_line, ':');
                curr_file.start_time = curr_text_line(temp(1)-2:end);
            elseif contains(curr_text_line, '.dat')
                temp = strfind(curr_text_line, ' ');
                signals{end+1} = curr_text_line(temp(end)+1:end);
            end
            clear curr_text_line
        end
        clear line_no
        fclose(fileID); clear fileID

        % check to see whether this file contains the required signals
        if sum(strcmp(signals, 'II')) && sum(strcmp(signals, 'PLETH')) && sum(strcmp(signals, 'RESP'))

            % store details
            file_counter = file_counter+1;
            fprintf([' ' num2str(file_counter)]);
            download.source.waves_header_file{file_counter} = down_loc;
            download.source.overall_header_file{file_counter} = overall_header_file;
            download.saved.pt_stay{file_counter} = pt_stay;
            download.saved.pt_no{file_counter} = pt_no;
            download.saved.pt_stay_segment{file_counter} = pt_stay_segment;
            download.saved.waves_header_file{file_counter} = data_header_file;
            download.saved.root_folder{file_counter} = save_folder;
            download.saved.overall_header_file{file_counter} = overall_header_file_save_loc;

            % download data file
            curr_file_name = [curr_rel_file, '.dat'];
            download.saved.file_name{file_counter} = curr_file_name;
            down_loc = [up.paths.database_dir_root, pt_stay, curr_file_name];
            download.source.waves_data_file{file_counter} = down_loc;
            download.source.waves_data_filename{file_counter} = curr_file_name;
            save_loc = [save_folder, curr_file_name];
            if ~exist(save_loc, 'file')
                outfilename = websave(save_loc,down_loc);
            end
            download.saved.waves_data_file{file_counter} = save_loc;
            clear outfilename save_loc down_loc curr_file_name

            % download numerics header file
            curr_file_name = [pt_stay_segment, 'n.hea'];
            down_loc = [up.paths.database_dir_root, pt_stay, curr_file_name];
            save_loc = [save_folder, curr_file_name];
            if ~exist(save_loc, 'file')
                outfilename = websave(save_loc,down_loc);
                download.source.numerics_header_file{file_counter} = down_loc;
            end
            download.saved.numerics_header_file{file_counter} = save_loc;
            clear outfilename save_loc down_loc curr_file_name

            % download numerics data file
            temp = strfind(curr_rel_file, '_');
            curr_file_name = [curr_rel_file(1:temp-1), 'n.dat']; clear temp
            down_loc = [up.paths.database_dir_root, pt_stay, curr_file_name];
            download.source.numerics_data_file{file_counter} = down_loc;
            save_loc = [save_folder, curr_file_name];
            if ~exist(save_loc, 'file')
                outfilename = websave(save_loc,down_loc);
            end
            download.saved.numerics_data_file{file_counter} = save_loc;
            clear outfilename save_loc down_loc curr_file_name

            % skip any remaining files for this subject
            break

        end

        clear outfilename save_loc down_loc curr_file_name
    end

    % finish if reached the desired number of pt stays with data
    if file_counter >= up.no_pt_stays
        break
    end

end

% Save the download details
save(up.paths.download_details_file, 'download');

end

function extract_from_mimic_ii(up)
%
% Based on RDSAMP, part of the WFDB Toolbox. Further details are available
% at:
%       http://physionet.org/physiotools/matlab/wfdb-app-matlab/
%
% Reads MIMIC II data and outputs it as a structure of .t and .v values.

fprintf('\n -- Extracting Data from MIMIC files: ')

% Load download details
load(up.paths.download_details_file);

for pt_stay_no = 1 : length(download.saved.pt_stay)
    fprintf([' ' num2str(pt_stay_no)]);

    % set current dir
    dir_path = download.saved.root_folder{pt_stay_no};
    cd(dir_path)

    % setup java
    % persistent javaWfdbExec config
    [javaWfdbExec, config] = deal([]);
    if(isempty(javaWfdbExec))
        [javaWfdbExec,config]=getWfdbClass('rdsamp');
    end

    % Remove file extension
    record_str = download.saved.file_name{pt_stay_no}(1:end-4);

    % Extract file information (including which signals are present)
    siginfo = extract_file_info(record_str);

    % create wfdb argument
    N0 = (siginfo.fs * up.req_data_time);
    wfdb_argument={'-r',record_str,'-Ps','-f',['s' num2str(N0)]}; clear record_str

    % calculate start time of numerics data
    [numerics_start, waveforms_start] = calc_start_times(download, pt_stay_no);

    % Make command to only extract the required signals
    wfdb_argument{end+1}='-s ';
    %-1 is necessary because WFDB is 0 based indexed.
    for sInd=1:siginfo.no_sigs
        wfdb_argument{end+1} = num2str(sInd-1);
    end
    clear sInd

    % determine how many samples to extract from this file
    N= N0 +(siginfo.fs * up.req_data_duration);

    % additional bits of wfdb_argument
    wfdb_argument{end+1}='-t';
    wfdb_argument{end+1}=['s' num2str(N+1)];

    % extract waveform data
    extracted_data=javaWfdbExec.execToDoubleArray(wfdb_argument);
    clear wfdb_argument

    % Store waveform data
    waves.fs = siginfo.fs;
    for wave_no = 1 : length(siginfo.sig_names)
        eval(['waves.' siginfo.sig_names{wave_no} '.t = extracted_data(:, 1)-(N0/125);']);
        eval(['waves.' siginfo.sig_names{wave_no} '.v = extracted_data(:, wave_no+1);']);
    end
    clear wave_no N N0 current_waves extracted_data Fs ListCapacity signalList javaWfdbExec config

    %% Extract corresponding numerics

    % Extract file information (including which signals are present)
    header_filepath = download.saved.numerics_header_file{pt_stay_no};
    do_numerics = 1;
    if ~isempty(header_filepath) && do_numerics
        header_filepath = header_filepath(1:end-4);
        numinfo = extract_file_info(header_filepath);

        % setup java
        persistent javaWfdbExec_num config_num
        if(isempty(javaWfdbExec_num))
            [javaWfdbExec_num,config_num]=getWfdbClass('rdsamp');
        end

        % Identify relevant numerics
        num_inds = 1:numinfo.no_sigs;
        num_log = zeros(length(num_inds),1);
        for num_no = 1 : length(num_inds)
            if sum(strcmp(up.extraction.rel_nums, numinfo.sig_names(num_no)))
                num_log(num_no) = 1;
            end
        end

        numerics.fs = numinfo.fs;

        % create wfdb argument
        if length(numinfo.start_time) == 5, numinfo.start_time = ['00:', numinfo.start_time]; end
%         start_time_diff = (datenum(['00/01/0000 ' numinfo.start_time], 'dd/mm/yyyy HH:MM:SS') - datenum(['00/01/0000 ' siginfo.start_time], 'dd/mm/yyyy HH:MM:SS')) *24*60*60;  % in secs
        start_time_diff = round((waveforms_start - numerics_start) *24*60*60);  % in secs
        N0 = (numerics.fs * up.req_data_time) + (start_time_diff*numerics.fs);
        clear waveforms_start numerics_start start_time_diff
        N = N0 +(numerics.fs * up.req_data_duration);
        [~, header_filepath, ~] = fileparts(header_filepath);
        wfdb_argument={'-r',header_filepath,'-Ps','-f',['s' num2str(N0)]}; clear header_filepath

        % Make command to only extract the required signals
        numList = num_inds(logical(num_log)); clear num_log num_inds
        if(~isempty(numList))
            wfdb_argument{end+1}='-s ';
            %-1 is necessary because WFDB is 0 based indexed.
            for sInd=1:length(numList)
                wfdb_argument{end+1}=[num2str(numList(sInd)-1)];
            end
            clear sInd
        end

        % Additional wfdb_argument bits
        wfdb_argument{end+1}='-t';
        wfdb_argument{end+1}=['s' num2str(N+1)];

        % extract numerics
        extracted_data = javaWfdbExec_num.execToDoubleArray(wfdb_argument);
        clear wfdb_argument

        % store the numerics
        numNames = numinfo.sig_names(numList);
        for num_no = 1 : length(up.extraction.rel_nums)
            rel_num_ind = find(strcmp(numNames, up.extraction.rel_nums{num_no}));
            eval(['numerics.' up.extraction.rel_nums{num_no} '.t = round(extracted_data(:, 1)-N0);']);
            eval(['numerics.' up.extraction.rel_nums{num_no} '.v = extracted_data(:, rel_num_ind+1);']);
            clear rel_num_ind
        end
        data{pt_stay_no}.numerics = numerics;

        clear numerics num_no numinfo siginfo current_numerics extracted_data numNames ListCapacity numinfo numList javaWfdbExec_num config_num N N0

    end

    %% Extract fixed data

    fileID = fopen(download.saved.overall_header_file{pt_stay_no});
    while ~feof(fileID)
        curr_text_line = fgetl(fileID);
        if ~isempty(strfind(curr_text_line, 'Location'))
            fix.loc = curr_text_line(strfind(curr_text_line, ': ')+2:end);
        end
        if ~isempty(strfind(curr_text_line, 'age'))
            fix.age = curr_text_line(strfind(curr_text_line, 'age>: ')+6:strfind(curr_text_line, '<sex>: ')-2);
            if isempty(strfind(fix.age, '?')) && isempty(strfind(fix.age, '+'))
                fix.age = str2double(fix.age);
            elseif strcmp(fix.age, '??')
                fix.age = nan;
            end
        end
        if ~isempty(strfind(curr_text_line, 'sex'))
            fix.sex = curr_text_line(strfind(curr_text_line, 'sex>: ')+6:end);
        end
        clear curr_text_line
    end
    clear line_no
    fclose(fileID); clear fileID

    %% Store data for this patient stay in the output
    data{pt_stay_no}.loc = fix.loc;
    if sum(strcmp(fieldnames(fix), 'age'))
        data{pt_stay_no}.age = fix.age;
    end
    if sum(strcmp(fieldnames(fix), 'sex'))
        data{pt_stay_no}.sex = fix.sex;
    end
    data{pt_stay_no}.ID = download.saved.pt_stay{pt_stay_no};
    data{pt_stay_no}.data_segment = download.saved.pt_stay_segment{pt_stay_no};
    data{pt_stay_no}.waves = waves;
    clear waves numerics fix

end

%% Convert to required variable for analysis

old_data = data; clear data

for s = 1:length(old_data)
    data(s).ref.resp_sig.imp.v = old_data{s}.waves.RESP.v;
    data(s).ref.resp_sig.imp.fs = old_data{s}.waves.fs;
    data(s).ref.params.hr.t = old_data{s}.numerics.HR.t;
    data(s).ref.params.hr.v = old_data{s}.numerics.HR.v;
    data(s).ref.params.hr.units.t = 'secs since beginning of signal';
    data(s).ref.params.hr.units.v = 'beats per minute';
    data(s).ref.params.pr.t = old_data{s}.numerics.PULSE.t;
    data(s).ref.params.pr.v = old_data{s}.numerics.PULSE.v;
    data(s).ref.params.pr.units.t = 'secs since beginning of signal';
    data(s).ref.params.pr.units.v = 'beats per minute';
    data(s).ref.params.rr.t = old_data{s}.numerics.RESP.t;
    data(s).ref.params.rr.v = old_data{s}.numerics.RESP.v;
    data(s).ref.params.rr.units.t = 'secs since beginning of signal';
    data(s).ref.params.rr.units.v = 'breaths per minute';
    data(s).ref.params.spo2.t = old_data{s}.numerics.SpO2.t;
    data(s).ref.params.spo2.v = old_data{s}.numerics.SpO2.v;
    data(s).ref.params.spo2.units.t = 'secs since beginning of signal';
    data(s).ref.params.spo2.units.v = '%';
    data(s).ekg.v = old_data{s}.waves.II.v;
    data(s).ekg.fs = old_data{s}.waves.fs;
    data(s).ppg.v = old_data{s}.waves.PLETH.v;
    data(s).ppg.fs = old_data{s}.waves.fs;
    data(s).fix.id = old_data{s}.ID;
    data(s).fix.loc = old_data{s}.loc;
    data(s).fix.source.waves = download.source.waves_data_file{s};
    data(s).fix.source.numerics = download.source.numerics_data_file{s};

end

%% Save extracted data
save(up.paths.extracted_data_file, 'data')

end

function [numerics_start, waveforms_start] = calc_start_times(download, pt_stay_no)

% Identify relevant header files
all_waves_header_file = download.saved.overall_header_file{pt_stay_no};
specific_waves_header_file = download.saved.waves_header_file{pt_stay_no};
numerics_header_file = download.saved.numerics_header_file{pt_stay_no};

% Find start time of all waveform data
fid = fopen(all_waves_header_file);
lines = textscan(fid,'%s','Delimiter','\n'); header_line = lines{1,1}{1};
fclose(fid);
colons = strfind(header_line, ':');
timings.all_waves.start_time = header_line(colons(1)-2:colons(2)+6);
slashes = strfind(header_line, '/'); slashes = slashes(slashes>colons(2));
timings.all_waves.start_date = header_line(slashes(1)-2:slashes(2)+4);
timings.all_waves.deb = datenum([timings.all_waves.start_date, ' ', timings.all_waves.start_time], 'dd/mm/yyyy HH:MM:SS.FFF');
clear slashes colons fid lines

% Find start time of the waveform data in this file
fid = fopen(all_waves_header_file);
lines = textscan(fid,'%s','Delimiter','\n'); lines = lines{1,1};
fclose(fid);
% First line is a header line:
lines = lines(2:end);
% Exclude next line if it's a layout line
if contains(lines{1}, 'layout 0')
    lines = lines(2:end);
end
% Exclude last line if it's a location line
if strcmp(lines{end}(1), '#')
    lines = lines(1:end-1);
end
% Identify duration (and cumulative duration) of each waveform file
for line_no = 1 : length(lines)
    space_loc = strfind(lines{line_no}, ' ');
    line_data_duration(line_no,1) = str2double(lines{line_no}(space_loc+1:end));
    line_data_name{line_no,1} = lines{line_no}(1:space_loc-1);
end
clear fid lines line_no space_loc
line_cum_duration = [0;cumsum(line_data_duration)]; % in ms
% Find start time of the waveform file of interest
rel_line = find(strcmp(line_data_name, download.source.waves_data_filename{pt_stay_no}(1:end-4)));
timings.rel_waves.deb = timings.all_waves.deb + (line_cum_duration(rel_line)/(125*60*60*24));

% % Find start time of waveforms data
% fid = fopen(specific_waves_header_file);
% lines = textscan(fid,'%s','Delimiter','\n'); header_line = lines{1,1}{1};
% fclose(fid);
% colons = strfind(header_line, ':');
% timings.rel_waves.start_time = header_line(colons(1)-2:colons(2)+6);
% slashes = strfind(header_line, '/'); slashes = slashes(slashes>colons(2));
% timings.rel_waves.start_date = header_line(slashes(1)-2:slashes(2)+4);
% timings.rel_waves.deb = datenum([timings.rel_waves.start_date, ' ', timings.rel_waves.start_time], 'dd/mm/yyyy HH:MM:SS');
% clear slashes colons fid lines

% Find start time of all numerics data
fid = fopen(numerics_header_file);
lines = textscan(fid,'%s','Delimiter','\n'); header_line = lines{1,1}{1};
fclose(fid);
colons = strfind(header_line, ':');
timings.all_numerics.start_time = header_line(colons(1)-2:colons(2)+6);
slashes = strfind(header_line, '/'); slashes = slashes(slashes>colons(2));
timings.all_numerics.start_date = header_line(slashes(1)-2:slashes(2)+4);
timings.all_numerics.deb = datenum([timings.all_numerics.start_date, ' ', timings.all_numerics.start_time], 'dd/mm/yyyy HH:MM:SS');
clear slashes colons fid lines

numerics_start = timings.all_numerics.deb;
waveforms_start = timings.rel_waves.deb;

end

function headers = extract_file_info(filepath)

%% Obtain info on signals

fileID = fopen([filepath, '.hea']);
header_lines = textscan(fileID,'%s','Delimiter','\n'); header_lines = header_lines{1,1};
fclose all;

% Extract the relevant header information
temp.top_line = textscan(header_lines{1}, '%s', 'delimiter', sprintf(' ')); temp.top_line = temp.top_line{1,1};  % adapted from http://uk.mathworks.com/matlabcentral/newsreader/view_thread/249016
headers.no_sigs = str2double(temp.top_line{2});
headers.fs = str2double(temp.top_line{3});
headers.no_samps = str2double(temp.top_line{4});
rel_line = find(~cellfun(@isempty, strfind(temp.top_line, ':')));
headers.start_time = temp.top_line{rel_line};
clear rel_line

% extract signal names
headers.sig_names = cell(0);
for sig_no = 1 : headers.no_sigs
    temp.curr_line = textscan(header_lines{1+sig_no}, '%s', 'delimiter', sprintf(' ')); temp.curr_line = temp.curr_line{1,1};  % adapted from http://uk.mathworks.com/matlabcentral/newsreader/view_thread/249016
    headers.sig_names = [headers.sig_names; temp.curr_line{end}];
end

% extract location
loc_line = header_lines{end};
headers.loc = loc_line(13:end);

end

function save_data_in_rrest_format(up)

fprintf('\n -- Saving data in RRest toolbox format')

temp = dir([up.paths.individual_files_folder, '*.mat']);
created_files = extractfield(temp, 'name'); clear temp

for subj_el = 1:length(created_files)

    % Load this subject's data
    subj_data = load([up.paths.individual_files_folder, created_files{subj_el}]);

    % Insert fixed params
    data(1,subj_el).fix.id = created_files{subj_el}(10:15);
    data(1,subj_el).fix.data_segment = created_files{subj_el}(17:end);
    data(1,subj_el).fix.loc = subj_data.location;
    data(1,subj_el).fix.ventilation = 'unknown';
    data(1,subj_el).fix.recording_conditions = 'critical care';
    data(1,subj_el).fix.source = subj_data.download_source;

    % insert PPG signal
    rel_col = strcmp(subj_data.header, 'PLETH');
    data(1,subj_el).ppg.v = subj_data.data(:,rel_col);
    data(1,subj_el).ppg.fs = subj_data.fs(rel_col);
    data(1,subj_el).ppg.method = 'measured using clinical monitor';
    % insert EKG signal
    rel_col = strcmp(subj_data.header, 'II');
    data(1,subj_el).ekg.v = subj_data.data(:,rel_col);
    data(1,subj_el).ekg.fs = subj_data.fs(rel_col);
    data(1,subj_el).ekg.method = 'measured using clinical monitor';
    % insert RESP signal
    rel_col = strcmp(subj_data.header, 'RESP');
    data(1,subj_el).ref.resp_sig.imp.v = subj_data.data(:,rel_col);
    data(1,subj_el).ref.resp_sig.imp.fs = subj_data.fs(rel_col);
    data(1,subj_el).ref.resp_sig.imp.method = 'thoracic impedance measured using clinical monitor';
    % insert annotations
    data(1,subj_el).ref.breaths.ann1 = subj_data.ann.resp.breaths.x1;
    data(1,subj_el).ref.breaths.ann2 = subj_data.ann.resp.breaths.x2;
    data(1,subj_el).ref.breaths.method = 'two independent sets of breath annotations using the impedance respiratory signal';

    % insert numerics
    if sum(strcmp(fieldnames(subj_data), 'numerics'))
        data(1,subj_el).ref.params.rr.t = subj_data.numerics.RESP.t;
        data(1,subj_el).ref.params.rr.v = subj_data.numerics.RESP.v;
        data(1,subj_el).ref.params.rr.method = 'derived from thoracic impedance measured using clinical monitor';
        data(1,subj_el).ref.params.rr.units.t = 's';
        data(1,subj_el).ref.params.rr.units.v = 'breaths/min';
        data(1,subj_el).ref.params.hr.t = subj_data.numerics.HR.t;
        data(1,subj_el).ref.params.hr.v = subj_data.numerics.HR.v;
        data(1,subj_el).ref.params.hr.method = 'derived from ecg measured using clinical monitor';
        data(1,subj_el).ref.params.hr.units.t = 's';
        data(1,subj_el).ref.params.hr.units.v = 'beats/min';
        data(1,subj_el).ref.params.pr.t = subj_data.numerics.PULSE.t;
        data(1,subj_el).ref.params.pr.v = subj_data.numerics.PULSE.v;
        data(1,subj_el).ref.params.pr.method = 'derived from ppg measured using clinical monitor';
        data(1,subj_el).ref.params.pr.units.t = 's';
        data(1,subj_el).ref.params.pr.units.v = 'beats/min';
        data(1,subj_el).ref.params.spo2.t = subj_data.numerics.SpO2.t;
        data(1,subj_el).ref.params.spo2.v = subj_data.numerics.SpO2.v;
        data(1,subj_el).ref.params.spo2.method = 'measured using clinical monitor';
        data(1,subj_el).ref.params.spo2.units.t = 's';
        data(1,subj_el).ref.params.spo2.units.v = '%';
    end

end

% Save to file
savepath = [up.paths.data_root, up.dataset_name, '_data'];
save(savepath, 'data')

fprintf(['\n --- Dataset saved to: ' savepath])

end

function save_data_in_csv_format(up)

fprintf('\n -- Converting to CSV format');

% lower case of dataset name to keep to usual format of PhysioNet files:
dataset_name = up.dataset_name;

% Create folder to save data in
data_type = 'csv';
save_folder = [up.paths.data_root, dataset_name, '_', data_type, filesep];
if ~exist(save_folder, 'dir')
    mkdir(save_folder)
end
cd(save_folder)

% Load data
load(up.paths.extracted_data_file);

% cycle through each subject
no_subjs = length(data);
file_names = cell(no_subjs,1);
for subj_no = 1 : no_subjs

    % setup command
    file_names{subj_no} = [up.dataset_name, '_', num2str(subj_no, '%02.f')];
    fs = data{subj_no}.waves.fs;
    sig_names = fieldnames(data{subj_no}.waves); sig_names = sig_names(~strcmp(sig_names, 'fs'));
    num_names = fieldnames(data{subj_no}.numerics); num_names = num_names(~strcmp(num_names, 'fs'));

    % create a file of fixed variables for this subject
    curr_filename = [file_names{subj_no}, '_Fix.txt'];
    fid = fopen(curr_filename, 'wt');

    signals_line = 'Time [s],';
    data_mat = data{subj_no}.waves.II.t;
    for sig_no = 1 : length(sig_names)
        signals_line = [signals_line, ' ', sig_names{sig_no}];
        eval(['curr_sig = data{subj_no}.waves.' sig_names{sig_no} ';'])
        data_mat = [data_mat, curr_sig.v];
        % check whether time vector is consistent
        if ~isequal(curr_sig.t, data{subj_no}.waves.II.t)
            error('Check this')
        end

        if sig_no < length(sig_names)
            signals_line = [signals_line, ';'];
        end
    end
    clear sig_no curr_sig

    numerics_line = 'Time [s],';
    numerics_mat = data{subj_no}.numerics.HR.t;
    numerics_fs = data{subj_no}.numerics.fs;
    for num_no = 1 : length(num_names)
        numerics_line = [numerics_line, ' ', num_names{num_no}];
        eval(['curr_num = data{subj_no}.numerics.' num_names{num_no} ';'])
        numerics_mat = [numerics_mat, curr_num.v];
        % check whether time vector is consistent
        if ~isequal(curr_num.t, data{subj_no}.numerics.HR.t)
            error('Check this')
        end

        if num_no < length(num_names)
            numerics_line = [numerics_line, ';'];
        end
    end
    clear num_no curr_num

    % create a text file containing the fixed variables for this subject
    text_to_write = [file_names{subj_no}, up.csv.new_line, ...
        'Signals:' signals_line(10:end), up.csv.new_line, ...
        'Signals sampling frequency: ' num2str(fs) ' Hz', up.csv.new_line, ...
        'Numerics:' numerics_line(10:end), up.csv.new_line, ...
        'Numerics sampling frequency: ' num2str(numerics_fs) ' Hz', up.csv.new_line, ...
        'Age: ' num2str(data{subj_no}.age), up.csv.new_line, ...
        'Gender: ' data{subj_no}.sex, up.csv.new_line, ...
        'Location: ' data{subj_no}.loc, up.csv.new_line, ...
        'MIMIC II matched wdb ID: ' data{subj_no}.ID, up.csv.new_line, ...
        'MIMIC II data segment: ' data{subj_no}.data_segment, up.csv.new_line, ...
        'Original overall header file: ' data{subj_no}.download_source.overall_header_file, up.csv.new_line, ...
        'Original signals header file: ' data{subj_no}.download_source.waves_header_file, up.csv.new_line, ...
        'Original signals data file: ' data{subj_no}.download_source.waves_data_file, up.csv.new_line, ...
        'Original numerics header file: ' data{subj_no}.download_source.numerics_header_file, up.csv.new_line, ...
        'Original numerics data file: ' data{subj_no}.download_source.numerics_data_file, up.csv.new_line, ...
        ];
    fprintf(fid, text_to_write);
    fclose(fid);
    clear text_to_write fid

    % create a csv file of signals for this subject
    curr_filename = [file_names{subj_no}, '_Signals.csv'];
    fid = fopen(curr_filename,'w');
    signals_line = strrep(signals_line, ';', ',');
    fprintf(fid,[signals_line, up.csv.new_line]); clear signals_line
    fclose(fid); clear fid
    dlmwrite(curr_filename, data_mat, '-append'); clear data_mat curr_filename

    % create a csv file of numerics for this subject
    curr_filename = [file_names{subj_no}, '_Numerics.csv'];
    fid = fopen(curr_filename,'w');
    numerics_line = strrep(numerics_line, ';', ',');
    fprintf(fid,[numerics_line, up.csv.new_line]); clear numerics_line
    fclose(fid); clear fid
    dlmwrite(curr_filename, numerics_mat, '-append'); clear numerics_mat curr_filename

    % create a csv file of annotations for this subject
    curr_filename = [file_names{subj_no}, '_Breaths.csv'];
    fid = fopen(curr_filename,'w');
    header_line = 'breaths ann1 [signal sample no], breaths ann2 [signal sample no]';
    fprintf(fid,[header_line, up.csv.new_line]); clear header_line
    fclose(fid); clear fid
    length_diff = length(data{subj_no}.ann.resp.breaths.x1(:)) - length(data{subj_no}.ann.resp.breaths.x2(:));
    if length_diff > 0
        data{subj_no}.ann.resp.breaths.x2 = [data{subj_no}.ann.resp.breaths.x2, nan(1, length_diff)];
    elseif length_diff < 0
        data{subj_no}.ann.resp.breaths.x1 = [data{subj_no}.ann.resp.breaths.x1, nan(1, abs(length_diff))];
    end
    ann_mat = [data{subj_no}.ann.resp.breaths.x1(:), data{subj_no}.ann.resp.breaths.x2(:)];
    dlmwrite(curr_filename, ann_mat, '-append'); clear ann_mat curr_filename

    clear fs sig_names signals_line curr_filename numerics_line

end
clear subj_no

end

function save_data_in_wfdb_format(up)

fprintf('\n -- Converting to WFDB format');

% Create folder to save data in
data_type = 'wfdb';
save_folder = [up.paths.data_root, up.dataset_name, '_', data_type, filesep];
if ~exist(save_folder, 'dir')
    mkdir(save_folder)
end
cd(save_folder)

% Load data
load(up.paths.extracted_data_file);

% cycle through each subject
no_subjs = length(data);
file_names = cell(no_subjs,1);
for subj_no = 1 : no_subjs

    % setup command
    file_names{subj_no} = [up.dataset_name, sprintf('%.2d', subj_no)];
    fs = data{subj_no}.waves.fs;
    sig_names = fieldnames(data{subj_no}.waves); sig_names = sig_names(~strcmp(sig_names, 'fs'));
    num_names = fieldnames(data{subj_no}.numerics); num_names = num_names(~strcmp(num_names, 'fs'));

    % Extract signals data
    data_mat = [];
    for sig_no = 1 : length(sig_names)
        eval(['curr_sig = data{subj_no}.waves.' sig_names{sig_no} ';'])
        data_mat = [data_mat, curr_sig.v];
        % check whether time vector is consistent
        if ~isequal(curr_sig.t, data{subj_no}.waves.II.t)
            error('Check this')
        end
    end
    clear sig_no curr_sig

    % Create signals file
    descrip = ['<age>: ' num2str(data{subj_no}.age) ' <sex>: ' data{subj_no}.sex ' <location>: ' data{subj_no}.loc ' <source>: https://physionet.org/physiobank/database/mimic2wdb/matched/' data{subj_no}.ID '/ <data segment>: ' data{subj_no}.data_segment '<modifications>: this is not a verbatim copy of an original file. Please see the accompanying LICENSE.txt file for further details.'];
    units = '';
    for sig_no = 1 : length(sig_names)
        signal_names(sig_no,1) = {[sig_names{sig_no} ', ' ]};
        rel_unit = up.units.units{strcmp(up.units.signals, sig_names{sig_no})};
        units = [units, rel_unit, '/']; clear rel_unit
    end
    clear sig_no
    units = units(1:end-1);
    % convert this subject's data into WFDB format
    mat2wfdb(data_mat, file_names{subj_no}, fs, [], units, descrip, [], signal_names);
    clear data_mat fs units descrip

    % Extract numerics data
    numerics_mat = [];
    for num_no = 1 : length(num_names)
        eval(['curr_num = data{subj_no}.numerics.' num_names{num_no} ';'])
        numerics_mat = [numerics_mat, curr_num.v];
        % check whether time vector is consistent
        if ~isequal(curr_num.t, data{subj_no}.numerics.HR.t)
            error('Check this')
        end
    end
    clear num_no curr_num

    % Create numerics file
    descrip = ['<age>: ' num2str(data{subj_no}.age) ' <sex>: ' data{subj_no}.sex ' <location>: ' data{subj_no}.loc ' <source>: ' data{subj_no}.download_source.numerics_data_file ' <modifications>: this is not a verbatim copy of an original file. Please see the accompanying LICENSE.txt file for further details.'];
    units = '';
    for num_no = 1 : length(num_names)
        numeric_names(num_no,1) = {[num_names{num_no} ', ' ]};
        rel_unit = up.units.num_units{strcmp(up.units.numerics, num_names{num_no})};
        units = [units, rel_unit, '/']; clear rel_unit
    end
    clear sig_no
    units = units(1:end-1);
    % convert this subject's data into WFDB format
    fs = data{subj_no}.numerics.fs;
    mat2wfdb(numerics_mat, [file_names{subj_no}, 'n'], fs, [], units, descrip, [], numeric_names);
    clear numerics_mat fs units descrip

    % create annotation files
    ann1 = data{subj_no}.ann.resp.breaths.x1;
    ann2 = data{subj_no}.ann.resp.breaths.x2;
    ann = [ann1(:); ann2(:)]; % col vector of samples of anns
    chan = find(~cellfun(@isempty, strfind(sig_names, 'RESP')))*ones(size(ann))-1;
    num = zeros(size(ann));
    comments = [repmat({'ann1'}, [length(ann1),1]); repmat({'ann2'}, [length(ann2),1])]; % ann1 or ann2 as char
    type = repmat('"', size(ann));
    subtype = zeros(size(num));
    wrann(file_names{subj_no},'breath',ann,type,subtype,chan,num,comments);

    clear sig_names ann1 ann2 ann chan num type subtype

end
clear no_subjs subj_no

% create a RECORDS file
file_name = 'RECORDS';
file_path = [save_folder, file_name];
fid = fopen(file_path, 'w');
formatSpec = '%s\r\n';
for line_no = 1 : length(file_names)
    fprintf(fid, formatSpec, file_names{line_no});
end

% create an ANNOTATORS file
file_name = 'ANNOTATORS';
file_path = [save_folder, file_name];
fid = fopen(file_path, 'w');
fprintf(fid, 'ann1\tbreath annotations by annotator 1\n');
fprintf(fid, 'ann2\tbreath annotations by annotator 2');

% create a DBS file
file_name = 'DBS';
file_path = [save_folder, file_name];
fid = fopen(file_path, 'w');
fprintf(fid, [up.dataset_name, '\t', 'BIDMC ECG, PPG and Respiration Dataset']);

fclose all;

end

function create_readme_and_license_files(up)

fprintf('\n -- Saving ReadMe and License files')

%% License file
licence_file_path = [up.paths.data_root, 'LICENSE'];
fid = fopen(licence_file_path, 'wt');
fprintf(fid, ['The mimic_imp_sqi_data file produced by this script contains modified versions of PhysioNet files. The modified files are distributed under the terms for distribution of modified data files listed at: https://archive.physionet.org/copying.shtml#data .',...
    '\n\n The following modifications have been made:', ...
    '\n\n - Waveform, numerics and fixed data (such as age) were downloaded from the PhysioNet MIMIC III matched waveform database. 1-hour segments of data were extracted for each of the 100 ICU stays included in the dataset.',...
    '\n\n - The data were imported into Matlab. In addition, manually annotated breaths are provided for some 32 second windows of most subjects'' impedance pneumography signals, and estimated respiratory rates (calculated from these manual annotations) are provided.', ...
    '\n\n Further details of the dataset are provided in the accompanying ReadMe file.']);
fclose all;

readme_file_path = [up.paths.data_root, 'README'];
fid = fopen(readme_file_path, 'wt');

%% ReadMe file

readme_text = [...
'Impedance Pneumography Signal Quality Index Dataset',...
'\n=============',...
'\n\nTable of Contents',...
'\n=================',...
'\n\n  * [The Dataset](#the-dataset)',...
'\n  * [Revisions](#revisions)',...
'\n  * [Description](#description)',...
'\n\n# The Dataset',...
'\n\nThe Impedance Pneumography Signal Quality Index Dataset was first reported in the following publication:',...
'\n\nCharlton, P.H. et al. An impedance pneumography signal quality index: design, assessment and application to respiratory rate monitoring, [under review], 2020',...
'\n\nIn this publication it was used to assess the performance of a signal quality index for the impedance pneumography signal. The dataset was extracted from the much larger [MIMIC III matched waveform Database](https://archive.physionet.org/physiobank/database/mimic3wdb/matched/), which was acquired from critically-ill patients during hospital care at the Beth Israel Deaconess Medical Centre (Boston, MA, USA). The 100 recordings within the dataset, each of 1-hour duration, each contain:',...
'\n  * Physiological signals: the impedance pneumography respiratory signal (ImP), photoplethysmogram (PPG) and electrocardiogram (ECG). These are sampled at 125 Hz.',...
'\n  * Physiological parameters, such as the heart rate (HR), pulse rate (PR), respiratory rate (RR), and blood oxygen saturation level (SpO2). These are sampled at 1 Hz.',...
'\n  * Fixed parameters, such as age, sex, ward and patient ID',...
'\n  * Manual annotations of breaths, and RRs calculated from these: A single annotator manually annotated individual breaths in approximately five 32 sec segments of each recording using the impedance respiratory signal.',...
'\n\nThe dataset is distributed in Matlab (r) format.',...
'\n\nFor more information about the dataset, please contact author at:',...
'\npc657@medschl.cam.ac.uk .',...
'\n\n# Revisions',...
'\n\nR1: 	2017-Aug-06 		initial release',...
'\n\n## Description',...
'\n### Matlab (r) Format',...
'\nThe *mimic_imp_sqi_data.mat* file contains the following subset of the dataset in a single Matlab (r) variable named *data*. The following are provided for each of the 100 recordings:',...
'\n* *ekg*:   Lead II ECG signal. Each signal is provided in a structure, where the *v* field denotes the signal values, and *fs* is the sampling frequency.',...
'\n* *ppg*:   Photoplethysmogram signal',...
'\n* *ref.resp_sig.imp*:  Impedance respiratory signal',...
'\n* *ref.ann*:  Manual annotations of breaths provided by a single annotator. A vector of sample numbers is provided (*breath_els*), which correspond to the signal sample numbers. Breaths were only annotated in certain segments of the signal, rather than the whole signal: the start and end samples of each segment in which breaths were annotated are provided in *win_start_els* and *win_end_els*. RRs calculated from the manual annotations are provided (*rr*, one per window).',...
'\n* *ref.params*:  Physiological parameters: *rr* (respiratory rate, derived by the monitor from the impedance signal, breaths per minute), *hr* (heart rate, derived from the ECG, beats per minute), *pr* (pulse rate, derived from the PPG, beats per minute), *spo2* (blood oxygen saturation level, %%).',...
'\n* *fix*: A structure of fixed variables: *id* (the MIMIC III matched waveform database subject ID and recording identifier), *loc* (the ward location), and download source (specified for both the waves and numerics).',...
'\n\nFurther details on the contents of each file are provided [here](https://archive.physionet.org/tutorials/creating-records.shtml).'];

fprintf(fid, readme_text);
fclose all;

end

function add_manual_annotations(up)

add_manual_annotations = 1;

if ~add_manual_annotations, return, else clear add_manual_annotations, end

fprintf('\n -- Adding in Manual Annotations')

% load extracted data file
load(up.paths.extracted_data_file)

% specify folder containing manual annotations files
ann_folder = '/Users/petercharlton/Documents/Data/Imp_SQI_mimic_dataset_test/2019_annotations/';
manual_annotator = 'PC';
temp = dir([ann_folder, '*', manual_annotator, '_an.mat']);
ann_files = extractfield(temp, 'name'); clear temp

do_check = 0;
% import annotations for each manually annotated subject
for subj_no = 1 : length(data)

    % create fields in which to store annotations
    data(subj_no).ref.ann.win_start_els = [];
    data(subj_no).ref.ann.win_end_els = [];
    data(subj_no).ref.ann.breath_els = [];
    data(subj_no).ref.ann.rr = [];

    % identify candidate annotations file for this subject
    annotations_filename = ['ImP_SQI' num2str(subj_no) '_' manual_annotator '_an.mat'];
    annotations_filepath = [ann_folder, annotations_filename];

    % skip if no annotations for this subject
    if ~exist(annotations_filepath)
        continue
    end

    % load annotations (which are stored in 'pk_anns' and 'qual')
    temp = load(annotations_filepath);

    % identify windows
    imp.v = data(subj_no).ref.resp_sig.imp.v; imp.fs = 125;
    imp.t = [0:(length(imp.v)-1)]/imp.fs;
    up.paramSet.winLeng = 32;     % the duration of each window in secs (no overlap)
    win_starts = imp.t(1):up.paramSet.winLeng:imp.t(end);
    win_ends = win_starts+up.paramSet.winLeng;

    for win_no = 1 : length(win_starts)

        % Identify annotations within this window
        if sum(strcmp(fieldnames(temp), 'pk_anns'))
            win_start = win_starts(win_no);
            win_end = win_ends(win_no);
            rel_ann_els = temp.pk_anns.t >= win_start & temp.pk_anns.t <= win_end;
            if sum(rel_ann_els) > 1
                first_breath = min(temp.pk_anns.t(rel_ann_els));
                last_breath = max(temp.pk_anns.t(rel_ann_els));
                no_breaths = sum(rel_ann_els)-1;
                rr_ann = 60/((last_breath-first_breath)/no_breaths);

                data(subj_no).ref.ann.win_start_els = [data(subj_no).ref.ann.win_start_els; win_start*125+1];
                data(subj_no).ref.ann.win_end_els = [data(subj_no).ref.ann.win_end_els; win_end*125+1];
                data(subj_no).ref.ann.breath_els = [data(subj_no).ref.ann.breath_els; round(temp.pk_anns.t(rel_ann_els)*125+1)];
                data(subj_no).ref.ann.rr = [data(subj_no).ref.ann.rr; rr_ann];

                % to check:
                if do_check
                    sig_els = (win_starts(win_no)*125+1):(win_ends(win_no)*125+1); plot(imp.t(sig_els), imp.v(sig_els)), hold on, rel_breath_els = data(subj_no).ref.ann.breath_els(data(subj_no).ref.ann.breath_els >= sig_els(1) & data(subj_no).ref.ann.breath_els <= sig_els(end)); plot(imp.t(rel_breath_els), imp.v(rel_breath_els), '*r')
                    close all
                end

                clear rr_ann no_breaths first_breath last_breath
            end
            clear win_start win_end rel_ann_els
        end

    end
    % to check
    if do_check
        plot(imp.t,imp.v), hold on, plot(imp.t(data(subj_no).ref.ann.breath_els), imp.v(data(subj_no).ref.ann.breath_els), '*r')
        close all
    end

    clear win_no temp imp win_starts win_ends

end

% Save new data file with annotations added
save(up.paths.extracted_data_file, 'data')

end


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

%%
clear; close all; clc;

%% Syntax
% |k=shear_poisson2bulk(mu,v);|

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


function pth = ffpath(fname)
%   FFPATH    Find file path
%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% The function browses very fast current directory and directories known in
% 'matlabpath' and the system variable 'path'. It searches for the file,
% name of which is in the input argument 'fname'. If a directory is found,
% the output argument pth is filled by path to the file name from the input
% argument, otherwise pth is empty.
% File names should have their extensions, but MATLAB m-files.
%
% Arguments:
%   fname   file name
%   pth     path to the fname
%
% Examples:
%   pth = ffpath('gswin32c.exe')
%   pth =
%   c:\Program Files\gs\gs8.60\bin\
%
%   pth = ffpath('hgrc')
%   pth =
%   C:\PROGRA~1\MATLAB\R2006b\toolbox\local

% Miroslav Balda
% miroslav AT balda DOT cz
% 2008-12-15    v 0.1   only for system variable 'path'
% 2008-12-20    v 1.0   for both 'path' and 'matlabpath'

if nargin<1
    error('The function requires one input argument (file name)')
end
pth = filesepStandard(pwd);
if exist([pth '/' fname],'file')
    return
end % fname found in current dir

tp = matlabpath;
t  = 0;
if isunix() | ismac()
    I = [t, findstr(tp,':'), length(tp)+1];
elseif ispc()
    I = [t, findstr(tp,';'), length(tp)+1];
end
for k = 1:length(I)-1               %   search in path's directories
    pth = tp(I(k)+1:I(k+1)-1);
    % fprintf('%s\n', [pth '/' fname]);
    if exist([pth '/' fname],'file')
        return;
    end
end
[status,tp] = system('path');
t = 5;
pth = '';


function [source] = ft_datatype_source(source, varargin)

% FT_DATATYPE_SOURCE describes the FieldTrip MATLAB structure for data that is
% represented at the source level. This is typically obtained with a beamformer of
% minimum-norm source reconstruction using FT_SOURCEANALYSIS.
%
% An example of a source structure obtained after performing DICS (a frequency domain
% beamformer scan) is shown here
%
%           pos: [6732x3 double]       positions at which the source activity could have been estimated
%        inside: [6732x1 logical]      boolean vector that indicates at which positions the source activity was estimated
%           dim: [xdim ydim zdim]      if the positions can be described as a 3D regular grid, this contains the
%                                       dimensionality of the 3D volume
%     cumtapcnt: [120x1 double]        information about the number of tapers per original trial
%          time: 0.100                 the latency at which the activity is estimated (in seconds)
%          freq: 30                    the frequency at which the activity is estimated (in Hz)
%           pow: [6732x120 double]     the estimated power at each source position
%     powdimord: 'pos_rpt'             defines how the numeric data has to be interpreted,
%                                       in this case 6732 dipole positions x 120 repetitions (i.e. trials)
%           cfg: [1x1 struct]          the configuration used by the function that generated this data structure
%
% Required fields:
%   - pos
%
% Optional fields:
%   - inside, pow, coh, eta, mom, ori, leadfield, filter, or any other field with dimensions that are consistent with pos or dim
%   - dim, transform, unit, coordsys, time, freq, cumtapcnt, dimord
%
% Deprecated fields:
%   - method, outside
%
% Obsoleted fields:
%   - xgrid, ygrid, zgrid, transform, latency, frequency
%
% Revision history:
%
% (2014) The subfields in the avg and trial fields are now present in the
% main structure, e.g. source.avg.pow is now source.pow. Furthermore, the
% inside is always represented as logical vector.
%
% (2011) The source representation should always be irregular, i.e. not
% a 3-D volume, contain a "pos" field and not contain a "transform".
%
% (2010) The source structure should contain a general "dimord" or specific
% dimords for each of the fields. The source reconstruction in the avg and
% trial substructures has been moved to the toplevel.
%
% (2007) The xgrid/ygrid/zgrid fields have been removed, because they are
% redundant.
%
% (2003) The initial version was defined
%
% See also FT_DATATYPE, FT_DATATYPE_COMP, FT_DATATYPE_DIP, FT_DATATYPE_FREQ,
% FT_DATATYPE_MVAR, FT_DATATYPE_RAW, FT_DATATYPE_SOURCE, FT_DATATYPE_SPIKE,
% FT_DATATYPE_TIMELOCK, FT_DATATYPE_VOLUME

% Copyright (C) 2013-2014, 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$

% FIXME: I am not sure whether the removal of the xgrid/ygrid/zgrid fields
% was really in 2007

% get the optional input arguments, which should be specified as key-value pairs
version = ft_getopt(varargin, 'version', 'latest');

if strcmp(version, 'latest') || strcmp(version, 'upcoming')
  version = '2014';
end

if isempty(source)
  return;
end

% old data structures may use latency/frequency instead of time/freq. It is
% unclear when these were introduced and removed again, but they were never
% used by any FieldTrip function itself.
if isfield(source, 'frequency')
  source.freq = source.frequency;
  source      = rmfield(source, 'frequency');
end
if isfield(source, 'latency')
  source.time = source.latency;
  source      = rmfield(source, 'latency');
end

switch version
  case '2014'
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    % ensure that it has individual source positions
    source = fixpos(source);

    if isfield(source, 'inside')
      % ensure that it is always logical
      source = fixinside(source, 'logical');
    end

    if isfield(source, 'coordsys')
      % ensure that it is in lower case
      source.coordsys = lower(source.coordsys);
    end

    if isfield(source, 'unit')
      % ensure that it is in lower case
      source.unit = lower(source.unit);
    end

    % remove obsolete fields
    if isfield(source, 'method')
      source = rmfield(source, 'method');
    end
    if isfield(source, 'transform')
      source = rmfield(source, 'transform');
    end
    if isfield(source, 'xgrid')
      source = rmfield(source, 'xgrid');
    end
    if isfield(source, 'ygrid')
      source = rmfield(source, 'ygrid');
    end
    if isfield(source, 'zgrid')
      source = rmfield(source, 'zgrid');
    end

    if isfield(source, 'avg') && isstruct(source.avg) && isfield(source, 'trial') && isstruct(source.trial) && ~isempty(intersect(fieldnames(source.avg), fieldnames(source.trial)))
      % it is not possible to convert both since they have the same field names
      ft_warning('removing ''avg'', keeping ''trial''');
      source = rmfield(source, 'avg');
    end

    if isfield(source, 'avg') && isstruct(source.avg)
      % move the average fields to the main structure
      fn = fieldnames(source.avg);
      for i=1:length(fn)
        dat = source.avg.(fn{i});
        if isequal(size(dat), [1 size(source.pos,1)])
          source.(fn{i}) = dat';
        else
          source.(fn{i}) = dat;
        end
        clear dat
      end % j
      source = rmfield(source, 'avg');
    end

    if isfield(source, 'trial') && isstruct(source.trial)
      npos = size(source.pos,1);

      % concatenate the fields for each trial and move them to the main structure
      fn = fieldnames(source.trial);

      for i=1:length(fn)
        % some fields are descriptive and hence identical over trials
        if any(strcmp(fn{i}, {'csdlabel' 'label' 'filterdimord' 'leadfielddimord'}))
          source.(fn{i}) = source.trial(1).(fn{i});
          continue
        end

        % start with the first trial
        dat    = source.trial(1).(fn{i});
        datsiz = getdimsiz(source, fn{i});
        nrpt   = datsiz(1);
        datsiz = datsiz(2:end);

        if iscell(dat)
          datsiz(1) = nrpt; % swap the size of pos with the size of rpt
          val  = cell(npos,1);
          if isfield(source, 'inside')
            indx = find(source.inside);
          else
            indx = 1:npos;
          end
          for k=1:length(indx)
            val{indx(k)}          = nan(datsiz);
            val{indx(k)}(1,:,:,:) = dat{indx(k)};
          end
          % concatenate all data as {pos}_rpt_etc
          for j=2:nrpt
            dat = source.trial(j).(fn{i});
            for k=1:length(indx)
              val{indx(k)}(j,:,:,:) = dat{indx(k)};
            end

          end % for all trials
          source.(fn{i}) = val;

        else
          % concatenate all data as pos_rpt_etc
          val = nan([datsiz(1) nrpt datsiz(2:end)]);
          val(:,1,:,:,:) = dat(:,:,:,:);
          for j=2:length(source.trial)
            dat = source.trial(j).(fn{i});
            val(:,j,:,:,:) = dat(:,:,:,:);
          end % for all trials
          source.(fn{i}) = val;

          %         else
          %           siz = size(dat);
          %           if prod(siz)==npos
          %             siz = [npos nrpt];
          %           elseif siz(1)==npos
          %             siz = [npos nrpt siz(2:end)];
          %           end
          %           val = nan(siz);
          %           % concatenate all data as pos_rpt_etc
          %           val(:,1,:,:,:) = dat(:);
          %           for j=2:length(source.trial)
          %             dat = source.trial(j).(fn{i});
          %             val(:,j,:,:,:) = dat(:);
          %           end % for all trials
          %           source.(fn{i}) = val;

        end
      end % for each field

      source = rmfield(source, 'trial');

    end % if trial

    % ensure that it has a dimord (or multiple for the different fields)
    source = fixdimord(source);

    if isfield(source, 'inside')
      % ensure that for positions outside the brain it is [], not nan
      if isfield(source, 'leadfield')
        source.leadfield(~source.inside) = {[]};
      end
      if isfield(source, 'filter')
        source.filter(~source.inside) = {[]};
      end
    end

    if isfield(source, 'leadfield') && ~isfield(source, 'label') && isfield(source, 'cfg')
      % try to determine the channel labels from the cfg
      label = ft_findcfg(source.cfg, 'channel');
      if ~isempty(label)
        source.label = label;
      end
    end

    if isfield(source, 'filter') && ~isfield(source, 'label') && isfield(source, 'cfg')
      % try to determine the channel labels from the cfg
      label = ft_findcfg(source.cfg, 'channel');
      if ~isempty(label)
        source.label = label;
      end
    end

    % ensure that all data fields have the correct dimensions
    fn = getdatfield(source);
    for i=1:numel(fn)
      dimord = getdimord(source, fn{i});
      dimtok = tokenize(dimord, '_');
      dimsiz = getdimsiz(source, fn{i}, numel(dimtok));
      if numel(dimsiz)>=3 && strcmp(dimtok{1}, 'dim1') && strcmp(dimtok{2}, 'dim2') && strcmp(dimtok{3}, 'dim3')
        % convert it from voxel-based representation to position-based representation
        try
          source.(fn{i}) = reshape(source.(fn{i}), [prod(dimsiz(1:3)) dimsiz(4:end) 1]);
        catch
          ft_warning('could not reshape %s to the expected dimensions', fn{i});
        end
      end
    end

    % ensure that the structure has all required fields
    for required={'pos'}
      assert(isfield(source, required), 'required field "%s" is missing', required{:});
    end

  case '2011'
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    % ensure that it has individual source positions
    source = fixpos(source);

    % remove obsolete fields
    if isfield(source, 'xgrid')
      source = rmfield(source, 'xgrid');
    end
    if isfield(source, 'ygrid')
      source = rmfield(source, 'ygrid');
    end
    if isfield(source, 'zgrid')
      source = rmfield(source, 'zgrid');
    end
    if isfield(source, 'transform')
      source = rmfield(source, 'transform');
    end

    % ensure that it has a dimord (or multiple for the different fields)
    source = fixdimord(source);

  case '2010'
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    % ensure that it has individual source positions
    source = fixpos(source);

    % remove obsolete fields
    if isfield(source, 'xgrid')
      source = rmfield(source, 'xgrid');
    end
    if isfield(source, 'ygrid')
      source = rmfield(source, 'ygrid');
    end
    if isfield(source, 'zgrid')
      source = rmfield(source, 'zgrid');
    end

    % ensure that it has a dimord (or multiple for the different fields)
    source = fixdimord(source);

  case '2007'
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    % ensure that it has individual source positions
    source = fixpos(source);

    % remove obsolete fields
    if isfield(source, 'dimord')
      source = rmfield(source, 'dimord');
    end
    if isfield(source, 'xgrid')
      source = rmfield(source, 'xgrid');
    end
    if isfield(source, 'ygrid')
      source = rmfield(source, 'ygrid');
    end
    if isfield(source, 'zgrid')
      source = rmfield(source, 'zgrid');
    end

  case '2003'
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    if isfield(source, 'dimord')
      source = rmfield(source, 'dimord');
    end

    if ~isfield(source, 'xgrid') || ~isfield(source, 'ygrid') || ~isfield(source, 'zgrid')
      if isfield(source, 'dim')
        minx = min(source.pos(:,1));
        maxx = max(source.pos(:,1));
        miny = min(source.pos(:,2));
        maxy = max(source.pos(:,2));
        minz = min(source.pos(:,3));
        maxz = max(source.pos(:,3));
        source.xgrid = linspace(minx, maxx, source.dim(1));
        source.ygrid = linspace(miny, maxy, source.dim(2));
        source.zgrid = linspace(minz, maxz, source.dim(3));
      end
    end

  otherwise
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    ft_error('unsupported version "%s" for source datatype', version);
end

function pos = grid2pos(xgrid, ygrid, zgrid)
[X, Y, Z] = ndgrid(xgrid, ygrid, zgrid);
pos = [X(:) Y(:) Z(:)];

function pos = dim2pos(dim, transform)
[X, Y, Z] = ndgrid(1:dim(1), 1:dim(2), 1:dim(3));
pos = [X(:) Y(:) Z(:)];
pos = ft_warp_apply(transform, pos, 'homogenous');


function obj = saveExpression(obj, export_path, varargin)
    % save the symbolic expression of system dynamical equations to a MX
    % binary files
    %
    % Parameters:
    %  export_path: the path to export the file @type char
    %  varargin: variable input parameters @type varargin
    %   ForceExport: force the export @type logical

    input_categories = {'Control','ConstraintWrench','External'};
    for k=1:3
        category = input_categories{k};
        % export the input map
        gmap_funcs = fieldnames(obj.Gmap.(category));
        if ~isempty(gmap_funcs)
            for i=1:length(gmap_funcs)
                fun = gmap_funcs{i};
                if ~isempty(obj.Gmap.(category).(fun))
                    save(obj.Gmap.(category).(fun),export_path,varargin{:});
                end
            end
        end

        % export the input vector fields
        gvec_funcs = fieldnames(obj.Gvec.(category));
        if ~isempty(gvec_funcs)
            for i=1:length(gvec_funcs)
                fun = gvec_funcs{i};
                if ~isempty(obj.Gvec.(category).(fun))
                    save(obj.Gvec.(category).(fun),export_path,varargin{:});
                end
            end
        end
    end

end


data_path = tracker_param.seq_path;
location = tracker_param.init_rect;
s1 = tracker_param.s1;
s2 = tracker_param.s2;
roi_size = tracker_param.roi_size;
pf_param = tracker_param.pf_param ;
gnet_solver_def_file = tracker_param.gnet_solver_def_file;
snet_solver_def_file = tracker_param.snet_solver_def_file;
im1_id = tracker_param.startFrame;
ch_num = tracker_param.ch_num;
fnum = tracker_param.endFrame;
check_num = im1_id + 19;


function hdr = read_deymed_ini(filename)

% READ_DEYMED_INI reads EEG data from the Deymed Truescan file format
%
% Use as
%   hdr = read_deymed_ini(filename)
%
% See also READ_DEYMED_DAT

% Copyright (C) 2013, 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$

[p, f, x] = fileparts(filename);
headerfile = fullfile(p, [f '.ini']);
if ~exist(headerfile, 'file')
  headerfile = fullfile(p, [f '.Ini']);
end
datafile = fullfile(p, [f '.154576dat']);
if ~exist(datafile, 'file')
  datafile = fullfile(p, [f '.Dat']);
end

fid = fopen_or_error(datafile, 'rb');
hdr.id        = fread(fid, [1, 12], 'uint8=>char');
hdr.lastname  = fread(fid, [1, 12], 'uint8=>char');
hdr.firstname = fread(fid, [1, 12], 'uint8=>char');
hdr.date      = fread(fid, [1, 12], 'uint8=>char');
hdr.time      = fread(fid, [1, 12], 'uint8=>char');
hdr.Fs        = fread(fid, [1, 1],  'int16');
hdr.nChans    = fread(fid, [1, 1],  'int8');

dummy         = fread(fid, [1, 1],  'int8'); % this is 4 in the example file
if dummy~=4
  ft_warning('deviant header format detected');
end

% A numerical comparison between a deymed dat file and a BESA-exported edf version of the
% same file showed a calibration difference of 1/4. So this might be a calibration value.
hdr.calib = 1/dummy;

hdr.label = {};
for i=1:hdr.nChans
  str = fread(fid, [1, 6], 'uint8=>char');
  str(str==0)  = ' ';
  hdr.label{i} = strtrim(str);
end
fclose(fid);

% determine the number of samples
d = dir(datafile);
hdr.nSamples = (d.bytes-512-1024*hdr.nChans)/(2*hdr.nChans);

% also store some details from the ini file
try
  hdr.ini.patient.id        = inifile(headerfile, 'read', {'Pacient', '', 'ID'});
  hdr.ini.patient.lastname  = inifile(headerfile, 'read', {'Pacient', '', 'Prijmeni'});
  hdr.ini.patient.firstname = inifile(headerfile, 'read', {'Pacient', '', 'Jmeno'});
  hdr.ini.record.user       = inifile(headerfile, 'read', {'Record',  '', 'AcqUser'});
  hdr.ini.record.type       = inifile(headerfile, 'read', {'Record',  '', 'Type'});
end


function marker = extractScatterMarker(patch_data)

% EXTRACTS THE MARKER STYLE USED FOR MATLAB OBJECTS
% OF TYPE "PATCH". THESE OBJECTS ARE USED IN AREASERIES
% BARSERIES, CONTOURGROUP, SCATTERGROUP.

%-------------------------------------------------------------------------%

%-AXIS STRUCTURE-%
axis_data = get(ancestor(patch_data.Parent,'axes'));

%-FIGURE STRUCTURE-%
figure_data = get(ancestor(patch_data.Parent,'figure'));

%-INITIALIZE OUTPUT-%
marker = struct();

%-------------------------------------------------------------------------%

%-MARKER SIZEREF-%
marker.sizeref = 1;

%-------------------------------------------------------------------------%

%-MARKER SIZEMODE-%
marker.sizemode = 'area';

%-------------------------------------------------------------------------%

%-MARKER SIZE (STYLE)-%
marker.size = patch_data.SizeData;

%-------------------------------------------------------------------------%

%-MARKER SYMBOL (STYLE)-%
if ~strcmp(patch_data.Marker,'none')

    switch patch_data.Marker
        case '.'
            marksymbol = 'circle';
        case 'o'
            marksymbol = 'circle';
        case 'x'
            marksymbol = 'x-thin-open';
        case '+'
            marksymbol = 'cross-thin-open';
        case '*'
            marksymbol = 'asterisk-open';
        case {'s','square'}
            marksymbol = 'square';
        case {'d','diamond'}
            marksymbol = 'diamond';
        case 'v'
            marksymbol = 'triangle-down';
        case '^'
            marksymbol = 'triangle-up';
        case '<'
            marksymbol = 'triangle-left';
        case '>'
            marksymbol = 'triangle-right';
        case {'p','pentagram'}
            marksymbol = 'star';
        case {'h','hexagram'}
            marksymbol = 'hexagram';
    end

    marker.symbol = marksymbol;
end

%-------------------------------------------------------------------------%

%-MARKER LINE WIDTH (STYLE)-%
marker.line.width = patch_data.LineWidth;

%-------------------------------------------------------------------------%

%--MARKER FILL COLOR--%

%-figure colormap-%
colormap = figure_data.Colormap;

% marker face color
MarkerColor = patch_data.MarkerFaceColor;

filledMarkerSet = {'o','square','s','diamond','d',...
    'v','^', '<','>','hexagram','pentagram'};

filledMarker = ismember(patch_data.Marker,filledMarkerSet);

if filledMarker

    if isnumeric(MarkerColor)
        col = 255*MarkerColor;
        markercolor = ['rgb(' num2str(col(1)) ',' num2str(col(2)) ',' num2str(col(3)) ')'];
    else
        switch MarkerColor

            case 'none'

                markercolor = 'rgba(0,0,0,0)';

            case 'auto'

                if ~strcmp(axis_data.Color,'none')
                    col = 255*axis_data.Color;
                else
                    col = 255*figure_data.Color;
                end

                markercolor  = ['rgb(' num2str(col(1)) ',' num2str(col(2)) ',' num2str(col(3)) ')'];

            case 'flat'

                for n = 1:length(patch_data.CData)

                    capCD = max(min(patch_data.CData(1,n),axis_data.CLim(2)),axis_data.CLim(1));
                    scalefactor = (capCD - axis_data.CLim(1))/diff(axis_data.CLim);
                    col =  255*(colormap(1 + floor(scalefactor*(length(colormap)-1)),:));

                    markercolor{n} = ['rgb(' num2str(col(1)) ',' num2str(col(2)) ',' num2str(col(3)) ')'];

                end
        end
    end

    marker.color = markercolor;

end

%-------------------------------------------------------------------------%

%-MARKER LINE COLOR-%

% marker edge color
MarkerLineColor = patch_data.MarkerEdgeColor;

filledMarker = ismember(patch_data.Marker,filledMarkerSet);

if isnumeric(MarkerLineColor)
    col = 255*MarkerLineColor;
    markerlinecolor = ['rgb(' num2str(col(1)) ',' num2str(col(2)) ',' num2str(col(3)) ')'];
else
    switch MarkerLineColor

        case 'none'

            markerlinecolor = 'rgba(0,0,0,0)';

        case 'auto'

            EdgeColor = patch_data.EdgeColor;

            if isnumeric(EdgeColor)
                col = 255*EdgeColor;
                markerlinecolor = ['rgb(' num2str(col(1)) ',' num2str(col(2)) ',' num2str(col(3)) ')'];
            else

                switch EdgeColor

                    case 'none'

                        markerlinecolor = 'rgba(0,0,0,0)';

                    case {'flat', 'interp'}

                        for n = 1:length(patch_data.CData)

                            capCD = max(min(patch_data.CData(1,n),axis_data.CLim(2)),axis_data.CLim(1));
                            scalefactor = (capCD - axis_data.CLim(1))/diff(axis_data.CLim);
                            col =  255*(colormap(1 + floor(scalefactor*(length(colormap)-1)),:));

                            markerlinecolor{n} = ['rgb(' num2str(col(1)) ',' num2str(col(2)) ',' num2str(col(3)) ')'];

                        end

                end
            end

        case 'flat'

            for n = 1:length(patch_data.CData)

                capCD = max(min(patch_data.CData(1,n),axis_data.CLim(2)),axis_data.CLim(1));
                scalefactor = (capCD - axis_data.CLim(1))/diff(axis_data.CLim);
                col =  255*(colormap(1+floor(scalefactor*(length(colormap)-1)),:));

                markerlinecolor{n} = ['rgb(' num2str(col(1)) ',' num2str(col(2)) ',' num2str(col(3)) ')'];

            end
    end
end

if filledMarker
    marker.line.color = markerlinecolor;
else
    marker.color = markerlinecolor;
end

%-------------------------------------------------------------------------%

end


% Init Script
% Remarks:
%   1.  If there is a predefined variable called 'subStreamNumberDefault'
%       it will be used to set the SubStream of the Random Number
%       Generator.
% TODO:
% 	1.  ds
% Release Notes
% - 1.0.005     02/08/2019  Royi Avital
%   *   Added Octave Comptability.
% - 1.0.004     05/07/2018  Royi Avital
%   *   Added case for 'subStreamNumberDefault = 0'.
% - 1.0.003     29/06/2018  Royi Avital
%   *   Clearing all variables but 'subStreamNumberDefault'.
%   *   Setting 'subStreamNumber' only if 'subStreamNumberDefault' not
%       defined.
% - 1.0.002     19/04/2018  Royi Avital
%   *   Added 'markerSizeMedium' (Matches 'markerSizeNormal').
% - 1.0.001     24/11/2016  Royi Avital
%   *   Added figure positions options.
% - 1.0.000     08/11/2016  Royi Avital
%   *   First release.
%
%
%% General Parameters

close('all');
clearvars('-except', 'subStreamNumberDefault');
clc();

FALSE   = 0;
TRUE    = 1;

OFF     = 0;
ON      = 1;

% See https://stackoverflow.com/questions/2246579
isOctave = (exist('OCTAVE_VERSION', 'builtin') ~= 0);

% set(0, 'DefaultFigureWindowStyle', 'docked');
% defaultLoosInset = get(0, 'DefaultAxesLooseInset');
% set(0, 'DefaultAxesLooseInset', [0.05, 0.05, 0.05, 0.05]);

figPosSmall     = [100, 100, 0400, 0300];
figPosMedium    = [100, 100, 0800, 0600];
figPosLarge     = [100, 100, 0960, 0720];
figPosXLarge    = [100, 100, 1100, 0825];
figPosX2Large   = [100, 100, 1200, 0900];
figPosX3Large   = [100, 100, 1400, 1050];
figPosDefault   = [100, 100, 0560, 0420];

fontSizeTitle   = 14;
fontSizeAxis    = 12;
fontSizeString  = 12;

lineWidthThin   = 1;
lineWidthNormal = 3;
lineWidthThick  = 4;

markerSizeSmall     = 4;
markerSizeMedium    = 8;
markerSizeNormal    = 8;
markerSizeLarge     = 10;

% https://www.mathworks.com/help/matlab/graphics_transition/why-are-plot-lines-different-colors.html
% https://www.mathworks.com/matlabcentral/answers/160332
mColorOrder = get(groot, 'DefaultAxesColorOrder');

if(isOctave == FALSE)
    randomNumberStream  = RandStream('mlfg6331_64', 'NormalTransform', 'Ziggurat');

    if(exist('subStreamNumberDefault', 'var') && (subStreamNumberDefault ~= 0))
        subStreamNumber = subStreamNumberDefault;
    else
        subStreamNumber = round(sum(clock()));
    end
    set(randomNumberStream, 'Substream', subStreamNumber);
    RandStream.setGlobalStream(randomNumberStream);
end


function varargout = geotiffread_lcog(filename, varargin)
%GEOTIFFREAD Read GeoTIFF file
%
%       GEOTIFFREAD is not recommended. Use READGEORASTER instead.
%
%   [A, R] = GEOTIFFREAD(FILENAME) reads a georeferenced grayscale, RGB, or
%   multispectral image or data grid from the GeoTIFF file specified by
%   FILENAME into A and constructs a spatial referencing object, R.
%
%   [X, CMAP, R] = GEOTIFFREAD(FILENAME) reads an indexed image into X and
%   the associated colormap into CMAP, and constructs a spatial referencing
%   object, R. Colormap values in the image file are rescaled into the
%   range [0,1].
%
%   FILENAME is a string scalar or character vector that specifies the name
%   of the GeoTIFF file. FILENAME can include the folder name. Otherwise,
%   the file must be in the current folder or in a folder on the MATLAB
%   path. If the named file includes the extension '.TIF' or '.TIFF'
%   (either upper or lower case), you can omit the extension from FILENAME.
%
%   A is a two-dimensional array if the file contains a grayscale image or
%   data grid. A is an M-by-N-by-P array if the file contains a color
%   image, multispectral image, hyperspectral image, or data grid. The
%   class of A depends on the storage class of the pixel data in the file
%   which is related to the BitsPerSample property as returned by the
%   IMFINFO function.
%
%   R is a geographic raster reference object if the image or data grid is
%   referenced to a geographic coordinate system, or a map raster reference
%   object if it is referenced to a projected coordinate system.
%
%   [A, REFMAT, BBOX] = GEOTIFFREAD(FILENAME) reads a georeferenced
%   grayscale, RGB, or multispectral image or data grid into A; the
%   corresponding referencing matrix into REFMAT; and the bounding box into
%   BBOX.
%
%   [X, CMAP, REFMAT, BBOX] = GEOTIFFREAD(FILENAME) reads an indexed image
%   into X, the associated colormap into CMAP, the referencing matrix into
%   REFMAT, and the bounding box into BBOX.  The referencing  matrix must
%   be unambiguously defined by the GeoTIFF file, otherwise  it and the
%   bounding box are returned empty.
%
%   [___] = GEOTIFFREAD(URL) reads the GeoTIFF image from a URL. The
%   URL must include the protocol type (e.g., "https://").
%
%   [___] = GEOTIFFREAD(___, IDX) reads one image from a multi-image
%   GeoTIFF file (or URL). IDX is an integer value that specifies the order
%   that the image appears in the file. For example, if IDX is 3,
%   GEOTIFFREAD reads the third image in the file. If you omit this
%   argument, GEOTIFFREAD reads the first image.
%
%   Note
%   ----
%   GEOTIFFREAD imports pixel data using the TIFF-reading capabilities of
%   the MATLAB function IMREAD and likewise shares any limitations of
%   IMREAD.  Consult the IMREAD documentation for specific information on
%   TIFF image support.
%
%   Example
%   -------
%   % Read and display the Boston GeoTIFF image.
%   % Includes material (c) GeoEye, all rights reserved.
%   [boston, R] = geotiffread('boston.tif');
%   figure
%   mapshow(boston, R)
%   axis image off
%
%   See also GEOSHOW, GEOTIFFINFO, GEOTIFFWRITE, IMREAD, MAPSHOW, READGEORASTER

% Copyright 1996-2020 The MathWorks, Inc.

% Verify the input and output argument count.
narginchk(1,2);
nargoutchk(0,4);

% Parse the inputs.
filename = convertStringsToChars(filename);
[filename, url, idx] = parseInputs(filename, varargin);

% Read the info fields from the filename.
info = geotiffinfo_lcog(filename);

% Read the image from the filename.
[A, cmap] = imread(filename,idx);

% If the RefMatrix is empty, try to obtain the spatial information from a
% corresponding worldfile.
if isempty(info.RefMatrix)
    worldfilename = getworldfilename(filename);
    info = getSpatialInfoFromWorldfile(worldfilename, info, size(A));
end

% Delete temporary file from Internet download.
if (url)
    deleteDownload(filename);
end

% Assign output arguments.
varargout = assignOutputArguments(A, cmap, info, nargout);

%--------------------------------------------------------------------------

function [filename, url, idx] = parseInputs(filename, inputs)
% Parse the inputs from the cell array, INPUTS.

% Verify the filename and obtain the full pathname.
extensions = {'tif', 'tiff'};
[filename, url] = internal.map.checkfilename(filename, extensions, mfilename, 1, true);

% Check and set the image index number.
if ~isempty(inputs)
  idx = inputs{1};
  attributes = {'real' 'scalar' 'positive'};
  validateattributes(idx, {'numeric'}, attributes, mfilename, 'IDX', 2);
else
  idx = 1;
end

%--------------------------------------------------------------------------

function info = getSpatialInfoFromWorldfile(worldFileName, info, rasterSize)
% Obtain the referencing matrix from a world file, if it exits. If so,
% compute the bounding box and construct a spatial referencing object from
% the referencing matrix and update the fields of the INFO structure.
% WORLDFILENAME is a string denoting the name of the world file.

if exist(worldFileName,'file')
    % Check that worldFileName is a file and that it can be opened.
    worldFileName = internal.map.checkfilename(worldFileName, {}, mfilename, 1, false);

    % Obtain the referencing matrix from the world file.
    W = load(worldFileName);
    map.internal.assert(isnumeric(W) && numel(W) == 6, 'map:fileio:expectedSixNumbers');
    W = reshape(W,[2 3]);
    refmat = map.internal.referencingMatrix(W);

    % Calculate the spatial referencing object and bounding box from the
    % referencing matrix if it is not empty.
    if ~isempty(refmat)
        if strcmp(info.ModelType, 'ModelTypeGeographic')
            R = refmatToGeoRasterReference(refmat, rasterSize);
        elseif strcmp(info.ModelType, 'ModelTypeProjected')
            R = refmatToMapRasterReference(refmat, rasterSize);
        else
            R = [];
        end
        h = rasterSize(1);
        w = rasterSize(2);
        outline = [(0.5 + [0 0; 0 w; h w; h 0]), ones(4,1)] * refmat;
        info.BoundingBox = [min(outline); max(outline)];
        info.RefMatrix = refmat;
        info.SpatialRef = R;
    end
end

%--------------------------------------------------------------------------

function outputs = assignOutputArguments(A, cmap, info, numOutputs)
% Assign the output arguments based on the number of arguments requested.

outputs{1} = A;
switch numOutputs
    case 2
        if strcmp(info.ColorType, 'indexed')
            % [X, CMAP] = GEOTIFFREAD(...)
            outputs{2} = cmap;
        else
            % [A, R] = GEOTIFFREAD(...)
            outputs{2} = info.SpatialRef;
        end

    case 3
        if strcmp(info.ColorType, 'indexed')
            % [X, CMAP, R] = GEOTIFFREAD(...)
            outputs{2} = cmap;
            outputs{3} = info.SpatialRef;
        else
            % [A, REFMAT, BBOX] = GEOTIFFREAD(...)
            outputs{2} = info.RefMatrix;
            outputs{3} = info.BoundingBox;
        end

    case 4
        % [X, CMAP, REFMAT, BBOX] = GEOTIFFREAD(...)
        outputs{2} = cmap;
        outputs{3} = info.RefMatrix;
        outputs{4} = info.BoundingBox;
end


  function arg = remove_spaces(arg)
%|function arg = remove_spaces(arg)
%| replace extra spaces at ends of matrix string array with zeros.
%| also add extra '0' at end to be sure to null terminate string
%| Copyright May 2000, Jeff Fessler, University of Michigan

if nargin < 1, ir_usage, end

if streq(arg, 'test'), remove_spaces_test, clear arg, return, end

for ii=1:size(arg,1)
	jj = size(arg,2);
	while (arg(ii,jj) == ' ')
		arg(ii,jj) = 0;
		jj = jj - 1;
		if jj < 1
			warn('bug in %s', mfilename)
			keyboard
		end
	end
end
arg(:,end+1) = 0;

arg(arg(:,1) == 10, :) = []; % remove blank lines too

function remove_spaces_test
str = strvcat('a', 'bb ', 'c c ');
out = remove_spaces(str);
tmp = char([97 0 0 0 0; 98 98 0 0 0; 99 32 99 0 0]);
jf_equal(out, tmp)


%-----------------------------------------------------------------------
% Job configuration created by cfg_util (rev $Rev: 4899 $)
%-----------------------------------------------------------------------
matlabbatch{1}.menu_cfg{1}.menu_entry{1}.conf_entry.type = 'cfg_entry';
matlabbatch{1}.menu_cfg{1}.menu_entry{1}.conf_entry.name = 'Output Variable Name';
matlabbatch{1}.menu_cfg{1}.menu_entry{1}.conf_entry.tag = 'name';
matlabbatch{1}.menu_cfg{1}.menu_entry{1}.conf_entry.strtype = 's';
matlabbatch{1}.menu_cfg{1}.menu_entry{1}.conf_entry.extras = [];
matlabbatch{1}.menu_cfg{1}.menu_entry{1}.conf_entry.num = [1 Inf];
matlabbatch{1}.menu_cfg{1}.menu_entry{1}.conf_entry.check = @cfg_check_assignin;
matlabbatch{1}.menu_cfg{1}.menu_entry{1}.conf_entry.help = {'Enter a valid MATLAB variable name. The contents of the input to "Output Item" will be assigned to a variable of this name in MATLAB workspace.'};
matlabbatch{1}.menu_cfg{1}.menu_entry{1}.conf_entry.def = [];
matlabbatch{2}.menu_cfg{1}.menu_entry{1}.conf_entry.type = 'cfg_entry';
matlabbatch{2}.menu_cfg{1}.menu_entry{1}.conf_entry.name = 'Output Item';
matlabbatch{2}.menu_cfg{1}.menu_entry{1}.conf_entry.tag = 'output';
matlabbatch{2}.menu_cfg{1}.menu_entry{1}.conf_entry.strtype = 'e';
matlabbatch{2}.menu_cfg{1}.menu_entry{1}.conf_entry.extras = [];
matlabbatch{2}.menu_cfg{1}.menu_entry{1}.conf_entry.num = [];
matlabbatch{2}.menu_cfg{1}.menu_entry{1}.conf_entry.check = [];
matlabbatch{2}.menu_cfg{1}.menu_entry{1}.conf_entry.help = {'The contents that is passed to this input that will be assigned to the workspace variable whose name is given in "Output Variable Name".'};
matlabbatch{2}.menu_cfg{1}.menu_entry{1}.conf_entry.def = [];
matlabbatch{3}.menu_cfg{1}.menu_struct{1}.conf_exbranch.type = 'cfg_exbranch';
matlabbatch{3}.menu_cfg{1}.menu_struct{1}.conf_exbranch.name = 'Pass Output to Workspace';
matlabbatch{3}.menu_cfg{1}.menu_struct{1}.conf_exbranch.tag = 'cfg_assignin';
matlabbatch{3}.menu_cfg{1}.menu_struct{1}.conf_exbranch.val{1}(1) = cfg_dep;
matlabbatch{3}.menu_cfg{1}.menu_struct{1}.conf_exbranch.val{1}(1).tname = 'Val Item';
matlabbatch{3}.menu_cfg{1}.menu_struct{1}.conf_exbranch.val{1}(1).tgt_spec = {};
matlabbatch{3}.menu_cfg{1}.menu_struct{1}.conf_exbranch.val{1}(1).sname = 'Output Variable Name (cfg_entry)';
matlabbatch{3}.menu_cfg{1}.menu_struct{1}.conf_exbranch.val{1}(1).src_exbranch = substruct('.','val', '{}',{1}, '.','val', '{}',{1}, '.','val', '{}',{1});
matlabbatch{3}.menu_cfg{1}.menu_struct{1}.conf_exbranch.val{1}(1).src_output = substruct('()',{1});
matlabbatch{3}.menu_cfg{1}.menu_struct{1}.conf_exbranch.val{2}(1) = cfg_dep;
matlabbatch{3}.menu_cfg{1}.menu_struct{1}.conf_exbranch.val{2}(1).tname = 'Val Item';
matlabbatch{3}.menu_cfg{1}.menu_struct{1}.conf_exbranch.val{2}(1).tgt_spec = {};
matlabbatch{3}.menu_cfg{1}.menu_struct{1}.conf_exbranch.val{2}(1).sname = 'Output Item (cfg_entry)';
matlabbatch{3}.menu_cfg{1}.menu_struct{1}.conf_exbranch.val{2}(1).src_exbranch = substruct('.','val', '{}',{2}, '.','val', '{}',{1}, '.','val', '{}',{1});
matlabbatch{3}.menu_cfg{1}.menu_struct{1}.conf_exbranch.val{2}(1).src_output = substruct('()',{1});
matlabbatch{3}.menu_cfg{1}.menu_struct{1}.conf_exbranch.prog = @cfg_run_assignin;
matlabbatch{3}.menu_cfg{1}.menu_struct{1}.conf_exbranch.vout = [];
matlabbatch{3}.menu_cfg{1}.menu_struct{1}.conf_exbranch.check = [];
matlabbatch{3}.menu_cfg{1}.menu_struct{1}.conf_exbranch.help = {
                                                                'Assign a computation result to a workspace variable.'
                                                                'The value entered into "Output Item" will be assigned to a MATLAB workspace variable whose name is specified in "Output Variable Name". If this variable already exists, a new variable name will be generated. This can be useful to assess the results of computations with other MATLAB routines or for debugging.'
                                                                }';


function this = MrDataNd_constructor(this, testVariantsDataNd)
% Unit test for MrDataNd constructor with different input variants
%
%   Y = MrUnitTest()
%   run(Y, 'MrDataNd_constructor');
%
% This is a method of class MrUnitTest.
%
% IN
%
% OUT
%
% EXAMPLE
%   MrDataNd_constructor
%
%   See also MrUnitTest

% Author:   Saskia Bollmann
% Created:  2018-02-08
% Copyright (C) 2018 Institute for Biomedical Engineering
%                    University of Zurich and ETH Zurich
%
% This file is part of the TAPAS UniQC Toolbox, which is released
% under the terms of the GNU General Public License (GPL), version 3.
% You can redistribute it and/or modify it under the terms of the GPL
% (either version 3 or, at your option, any later version).
% For further details, see the file COPYING or
%  <http://www.gnu.org/licenses/>.
%

% get the dimInfo reference
dimInfo = this.make_dimInfo_reference(0);
% change some parameters to check defaults are overwritten
dimInfo.dimLabels = {'dL1', 'dL2', 'dL3', 'dL4', 'dL5'};
dimInfo.units = {'u1', 'u2', 'u3', 'u4', 'u5'};
% remove samples to make data matrix smaller
dimInfo.dL4.nSamples = 10;
rng(1);
dataMatrix = rand(dimInfo.nSamples);

switch testVariantsDataNd
    case 'matrix'
        % load matrix from workspace
        actSolution = MrDataNd(dataMatrix);
        expSolution = actSolution;

    case 'matrixWithDimInfo'
        % load matrix with dimInfo object
        actSolution = MrDataNd(dataMatrix, 'dimInfo', dimInfo);
        expSolution = actSolution;
    case 'matrixWithPropVal'
        % load matrix with prop/val pairs describing dimInfo
        actSolution = MrDataNd(dataMatrix, 'dimLabels', dimInfo.dimLabels, ...
            'units', dimInfo.units, 'samplingPoints', dimInfo.samplingPoints);
        expSolution = actSolution;
end
% verify whether expected and actual solution are identical
% Note: convert dimInfo to struct, since the PublicPropertyComparator (to allow
% nans to be treated as equal) does not compare properties of objects that
% overload subsref

warning('off', 'MATLAB:structOnObject');
actSolution.dimInfo = struct(actSolution.dimInfo);
warning('on', 'MATLAB:structOnObject');
% verify equality of expected and actual solution
% import matlab.unittests to apply tolerances for objects
import matlab.unittest.TestCase
import matlab.unittest.constraints.IsEqualTo
import matlab.unittest.constraints.AbsoluteTolerance
import matlab.unittest.constraints.PublicPropertyComparator

this.verifyThat(actSolution, IsEqualTo(expSolution,...
    'Within', AbsoluteTolerance(10e-7),...
    'Using', PublicPropertyComparator.supportingAllValues));
end


classdef prtPreProcClass < prtPreProc
    % prtPreProcClass
    %
    % prtPreProcClass is an abstract class and cannot be instantiated.
    %
    %   See Also: prtPreProc, prtPreProcPca, prtPreProcPls,
    %   prtPreProcHistEq, prtPreProcZeroMeanColumns, prtPreProcLda,
    %   prtPreProcZeroMeanRows, prtPreProcLogDisc, prtPreProcZmuv,
    %   prtPreProcMinMaxRows

    methods

        function obj = prtPreProcClass()
            obj.classTrain = 'prtDataSetClass';

            obj.isSupervised = true; % Overload this here.
        end
    end

    methods (Access = protected, Hidden = true)
        function ClassObj = preTrainProcessing(ClassObj, DataSet)
            % Overload preTrainProcessing() so that we can determine mary
            % output status
            assert(DataSet.isLabeled & DataSet.nClasses > 1,'The prtDataSetClass input to the train() method of a prtPreProcClass must have non-empty targets and have more than one class.');

            ClassObj = preTrainProcessing@prtAction(ClassObj,DataSet);
        end
    end
end


function [paramsOut] = globalTrendGUI(hObject, callbackdata, inputData)%#ok<INUSL>
    theTitle ='Global trend parameters';
    defaultStruct = inputData.userData.globalTrend;

    while(true)
        mainFigure = findobj('Type', 'Figure', '-and', 'Name', inputData.name);
        userdata = get(mainFigure, 'UserData');
        if isempty(userdata) || ~isfield(userdata, 'globalTrend')
            paramsOut = struct();
        else
            paramsOut = userdata.globalTrend;
        end
        [defaultStruct, errors] = checkStructureDefaults(paramsOut, defaultStruct);

        if ~isempty(errors)
            warning('globalTrendGUI:bad parameters', getMessageString(errors)); %#ok<CTPCT>
        end

        % Creates structure for
        fNamesDefault = fieldnames(defaultStruct);
        for k = 1:length(fNamesDefault)
            textColorStruct.(fNamesDefault{k}) = 'k';
        end
        if defaultStruct.doLocal.value
            checkValue = 1;
        else
            checkValue = 0;
        end

        %% starts the while loop, sets up the uilist and creates the GUI
        closeOpenWindows(theTitle);
        geometry = {[1,4], 1, [3,1,3,1],[3,1,4]};
        geomvert = [];
        uilist={{'style', 'text', 'string', 'Global trend channels', ...
            'TooltipString', defaultStruct.globalTrendChannels.description}...
            {'style', 'edit', 'string', ...
            num2str(defaultStruct.globalTrendChannels.value), ...
            'tag', 'globalTrendChannels', 'ForegroundColor', ...
            textColorStruct.globalTrendChannels}...
            {'style', 'text', 'string', ''}...
            {'style', 'text', 'string', 'Do local', 'TooltipString', ...
            defaultStruct.doLocal.description}...
            {'style', 'checkbox', 'Value', checkValue, ...
            'tag', 'doLocal', 'ForegroundColor', textColorStruct.doLocal}...
            {'style', 'text', 'string', 'Local cutoff', ...
            'TooltipString', defaultStruct.localCutoff.description}...
            {'style', 'edit', 'string', ...
            num2str(defaultStruct.localCutoff.value), ...
            'tag', 'localCutoff', 'ForegroundColor', textColorStruct.localCutoff}...
            {'style', 'text', 'string', 'Local step size', ...
            'TooltipString', defaultStruct.localStepSize.description}...
            {'style', 'edit', 'string', ...
            num2str(defaultStruct.localStepSize.value), ...
            'tag', 'localStepSize', 'ForegroundColor', ...
            textColorStruct.localStepSize}...
            {'style', 'text', 'string', ''}};
        [~, ~, ~, paramsOut] = inputgui('geometry', geometry, ...
            'geomvert', geomvert, 'uilist', uilist, 'title', theTitle, ...
            'helpcom', 'pophelp(''pop_prepPipeline'')');
        if(isempty(paramsOut))
            break;
        end

        [paramsOut, typeErrors, fNamesErrors] = ...
            changeType(paramsOut, defaultStruct);
        %paramsOut.detrendType = typeMenuString(paramsOut.detrendType);
        mainFigure = findobj('Type', 'Figure', '-and', 'Name', inputData.name);
        userdata = get(mainFigure, 'UserData');
        userdata.globalTrend = paramsOut;
        set(mainFigure, 'UserData', userdata);
        if isempty(typeErrors)
            break;
        end
        textColorStruct = highlightErrors(fNamesErrors, ...
            fNamesDefault, textColorStruct);

        displayErrors(typeErrors); % Displays the errors and restarts GUI
    end
end


function btf_make
%BTF_MAKE compile BTF for use in MATLAB
% Your current working directory must be BTF/MATLAB for this function to work.
%
% Example:
%   btf_make
%
% See also btf, maxtrans, stroncomp, dmperm.

% Copyright 2004-2007, Tim Davis, University of Florida

details = 0 ;       % if 1, print details of each command

mexcmd = 'mex -O -DDLONG -I../Include -I../../UFconfig ' ;
if (~isempty (strfind (computer, '64')))
    mexcmd = [mexcmd '-largeArrayDims '] ;
end

s = [mexcmd 'maxtrans.c ../Source/btf_maxtrans.c'] ;
if (details)
    fprintf ('%s\n', s) ;
end
eval (s) ;

s = [mexcmd 'strongcomp.c ../Source/btf_strongcomp.c'] ;
if (details)
    fprintf ('%s\n', s) ;
end
eval (s) ;

s = [mexcmd 'btf.c ../Source/btf_maxtrans.c ../Source/btf_strongcomp.c ../Source/btf_order.c'] ;
if (details)
    fprintf ('%s\n', s) ;
end
eval (s) ;

fprintf ('BTF successfully compiled.\n') ;