function [sFile, ChannelMat, ImportOptions] = in_fopen_edf(DataFile, ImportOptions)
% IN_FOPEN_EDF: Open a BDF/EDF file (continuous recordings)
%
% USAGE:  [sFile, ChannelMat, ImportOptions] = in_fopen_edf(DataFile, ImportOptions)

% @=============================================================================
% This function is part of the Brainstorm software:
% https://neuroimage.usc.edu/brainstorm
%
% Copyright (c) University of Southern California & McGill University
% This software is distributed under the terms of the GNU General Public License
% as published by the Free Software Foundation. Further details on the GPLv3
% license can be found at http://www.gnu.org/copyleft/gpl.html.
%
% FOR RESEARCH PURPOSES ONLY. THE SOFTWARE IS PROVIDED "AS IS," AND THE
% UNIVERSITY OF SOUTHERN CALIFORNIA AND ITS COLLABORATORS DO NOT MAKE ANY
% WARRANTY, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO WARRANTIES OF
% MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, NOR DO THEY ASSUME ANY
% LIABILITY OR RESPONSIBILITY FOR THE USE OF THIS SOFTWARE.
%
% For more information type "brainstorm license" at command prompt.
% =============================================================================@
%
% Authors: Francois Tadel, 2012-2021

% Parse inputs
if (nargin < 2) || isempty(ImportOptions)
    ImportOptions = db_template('ImportOptions');
end

%% ===== READ HEADER =====
% Open file
fid = fopen(DataFile, 'r', 'ieee-le');
if (fid == -1)
    error('Could not open file');
end
% Read all fields
hdr.version    = fread(fid, [1  8], 'uint8=>char');  % Version of this data format ('0       ' for EDF, [255 'BIOSEMI'] for BDF)
hdr.patient_id = fread(fid, [1 80], '*char');  % Local patient identification
hdr.rec_id     = fread(fid, [1 80], '*char');  % Local recording identification
hdr.startdate  = fread(fid, [1  8], '*char');  % Startdate of recording (dd.mm.yy)
hdr.starttime  = fread(fid, [1  8], '*char');  % Starttime of recording (hh.mm.ss)
hdr.hdrlen     = str2double(fread(fid, [1 8], '*char'));  % Number of bytes in header record
hdr.unknown1   = fread(fid, [1 44], '*char');             % Reserved ('24BIT' for BDF)
hdr.nrec       = str2double(fread(fid, [1 8], '*char'));  % Number of data records (-1 if unknown)
hdr.reclen     = str2double(fread(fid, [1 8], '*char'));  % Duration of a data record, in seconds
hdr.nsignal    = str2double(fread(fid, [1 4], '*char'));  % Number of signals in data record
% Check file integrity
if isnan(hdr.nsignal) || isempty(hdr.nsignal) || (hdr.nsignal ~= round(hdr.nsignal)) || (hdr.nsignal < 0)
    error('File header is corrupted.');
end
% Read values for each nsignal
for i = 1:hdr.nsignal
    hdr.signal(i).label = strtrim(fread(fid, [1 16], '*char'));
end
for i = 1:hdr.nsignal
    hdr.signal(i).type = strtrim(fread(fid, [1 80], '*char'));
end
for i = 1:hdr.nsignal
    hdr.signal(i).unit = strtrim(fread(fid, [1 8], '*char'));
end
for i = 1:hdr.nsignal
    hdr.signal(i).physical_min = str2double(fread(fid, [1 8], '*char'));
end
for i = 1:hdr.nsignal
    hdr.signal(i).physical_max = str2double(fread(fid, [1 8], '*char'));
end
for i = 1:hdr.nsignal
    hdr.signal(i).digital_min = str2double(fread(fid, [1 8], '*char'));
end
for i = 1:hdr.nsignal
    hdr.signal(i).digital_max = str2double(fread(fid, [1 8], '*char'));
end
for i = 1:hdr.nsignal
    hdr.signal(i).filters = strtrim(fread(fid, [1 80], '*char'));
end
for i = 1:hdr.nsignal
    hdr.signal(i).nsamples = str2num(fread(fid, [1 8], '*char'));
end
for i = 1:hdr.nsignal
    hdr.signal(i).unknown2 = fread(fid, [1 32], '*char');
end
% Unknown record size, determine correct nrec
if (hdr.nrec == -1)
    datapos = ftell(fid);
    fseek(fid, 0, 'eof');
    endpos = ftell(fid);
    hdr.nrec = floor((endpos - datapos) / (sum([hdr.signal.nsamples]) * 2));
end
% Close file
fclose(fid);

%% ===== RECONSTRUCT INFO =====
% Individual signal gain
for i = 1:hdr.nsignal
    % Interpet units
    switch (hdr.signal(i).unit)
        case 'mV',                        unit_gain = 1e3;
        case {'uV', char([166 204 86])},  unit_gain = 1e6;
        otherwise,                        unit_gain = 1;
    end
    % Check min/max values
    if isempty(hdr.signal(i).digital_min) || isnan(hdr.signal(i).digital_min)
        disp(['EDF> Warning: The digitial minimum is not set for channel "' hdr.signal(i).label '".']);
        hdr.signal(i).digital_min = -2^15;
    end
    if isempty(hdr.signal(i).digital_max) || isnan(hdr.signal(i).digital_max)
        disp(['EDF> Warning: The digitial maximum is not set for channel "' hdr.signal(i).label '".']);
        hdr.signal(i).digital_max = -2^15;
    end
    if isempty(hdr.signal(i).physical_min) || isnan(hdr.signal(i).physical_min)
        disp(['EDF> Warning: The physical minimum is not set for channel "' hdr.signal(i).label '".']);
        hdr.signal(i).physical_min = hdr.signal(i).digital_min;
    end
    if isempty(hdr.signal(i).physical_max) || isnan(hdr.signal(i).physical_max)
        disp(['EDF> Warning: The physical maximum is not set for channel "' hdr.signal(i).label '".']);
        hdr.signal(i).physical_max = hdr.signal(i).digital_max;
    end
    if (hdr.signal(i).physical_min >= hdr.signal(i).physical_max)
        disp(['EDF> Warning: Physical maximum larger than minimum for channel "' hdr.signal(i).label '".']);
        hdr.signal(i).physical_min = hdr.signal(i).digital_min;
        hdr.signal(i).physical_max = hdr.signal(i).digital_max;
    end
    % Calculate and save channel gain
    hdr.signal(i).gain   = unit_gain ./ (hdr.signal(i).physical_max - hdr.signal(i).physical_min) .* (hdr.signal(i).digital_max - hdr.signal(i).digital_min);
    hdr.signal(i).offset = hdr.signal(i).physical_min ./ unit_gain - hdr.signal(i).digital_min ./ hdr.signal(i).gain;
    % Error: The number of samples is not specified
    if isempty(hdr.signal(i).nsamples)
        % If it is not the first electrode: try to use the previous one
        if (i > 1)
            disp(['EDF> Warning: The number of samples is not specified for channel "' hdr.signal(i).label '".']);
            hdr.signal(i).nsamples = hdr.signal(i-1).nsamples;
        else
            error(['The number of samples is not specified for channel "' hdr.signal(i).label '".']);
        end
    end
    hdr.signal(i).sfreq = hdr.signal(i).nsamples ./ hdr.reclen;
end
% Find annotations channel
iAnnotChans = find(strcmpi({hdr.signal.label}, 'EDF Annotations'));  % Mutliple "EDF Annotation" channels allowed in EDF+
iStatusChan = find(strcmpi({hdr.signal.label}, 'Status'), 1);        % Only one "Status" channel allowed in BDF
iOtherChan = setdiff(1:hdr.nsignal, [iAnnotChans iStatusChan]);
% % Remove channels with lower sampling rates
% iIgnoreChan = find([hdr.signal(iOtherChan).sfreq] < max([hdr.signal(iOtherChan).sfreq]));    % Ignore all the channels with lower sampling rate
% if ~isempty(iIgnoreChan)
%     iOtherChan = setdiff(iOtherChan, iIgnoreChan);
% end
% Get all the other channels
if isempty(iOtherChan)
    error('This file does not contain any data channel.');
end
% Read events preferencially from the EDF Annotations track
if ~isempty(iAnnotChans)
    iEvtChans = iAnnotChans;
elseif ~isempty(iStatusChan)
    iEvtChans = iStatusChan;
else
    iEvtChans = [];
end
% % Detect channels with inconsistent sampling frenquency
% iErrChan = find([hdr.signal(iOtherChan).sfreq] ~= hdr.signal(iOtherChan(1)).sfreq);
% iErrChan = setdiff(iErrChan, iAnnotChans);
% if ~isempty(iErrChan)
%     error('Files with mixed sampling rates are not supported yet.');
% end
% Detect interrupted signals (time non-linear)
hdr.interrupted = ischar(hdr.unknown1) && (length(hdr.unknown1) >= 5) && isequal(hdr.unknown1(1:5), 'EDF+D');
if hdr.interrupted
    if ImportOptions.DisplayMessages
        [res, isCancel] = java_dialog('question', ...
            ['Interrupted EDF file ("EDF+D") detected. It is recommended to convert it' 10 ...
            'to a continuous ("EDF+C") file first. Do you want to continue reading this' 10 ...
            'file as continuous and attempt to fix the timing of event markers?' 10 ...
            'NOTE: This may not work as intended, use at your own risk!']);
        hdr.fixinterrupted = ~isCancel && strcmpi(res, 'yes');
    else
        hdr.fixinterrupted = 1;
    end
    if ~hdr.fixinterrupted
        warning(['Interrupted EDF file ("EDF+D"): requires conversion to "EDF+C". ' 10 ...
             'Brainstorm will read this file as a continuous file ("EDF+C"), the timing of the samples after the first discontinuity will be wrong.' 10 ...
             'This may not cause any major problem unless there are time markers in the file, they might be inaccurate in all the segments >= 2.']);
    end
else
    hdr.fixinterrupted = 0;
end

%% ===== CREATE BRAINSTORM SFILE STRUCTURE =====
% Initialize returned file structure
sFile = db_template('sfile');
% Add information read from header
sFile.byteorder  = 'l';
sFile.filename   = DataFile;
if (uint8(hdr.version(1)) == uint8(255))
    sFile.format = 'EEG-BDF';
    sFile.device = 'BDF';
else
    sFile.format = 'EEG-EDF';
    sFile.device = 'EDF';
end
sFile.header = hdr;
% Comment: short filename
[tmp__, sFile.comment, tmp__] = bst_fileparts(DataFile);
% No info on bad channels
sFile.channelflag = ones(hdr.nsignal,1);
% Acquisition date
sFile.acq_date = str_date(hdr.startdate);

%% ===== PROCESS CHANNEL NAMES/TYPES =====
% Try to split the channel names in "TYPE NAME"
SplitType = repmat({''}, 1, hdr.nsignal);
SplitName = repmat({''}, 1, hdr.nsignal);
for i = 1:hdr.nsignal
    if ~isempty(hdr.signal(i).label)
        % Removing trailing dots (eg. "Fc5." instead of "FC5", as in: https://www.physionet.org/pn4/eegmmidb/)
        if (hdr.signal(i).label(end) == '.') && (length(hdr.signal(i).label) > 1)
            hdr.signal(i).label(end) = [];
            if (hdr.signal(i).label(end) == '.') && (length(hdr.signal(i).label) > 1)
                hdr.signal(i).label(end) = [];
                if (hdr.signal(i).label(end) == '.') && (length(hdr.signal(i).label) > 1)
                    hdr.signal(i).label(end) = [];
                end
            end
        end
        % Remove extra spaces
        signalLabel = strrep(hdr.signal(i).label, ' - ', '-');
        % Find space chars (label format "Type Name")
        iSpace = find(signalLabel == ' ');
        % Only if there is one space only, and what is after the space is not only made of numbers
        if (length(iSpace) == 1) && (iSpace >= 3) && ~all(ismember(signalLabel(iSpace+1:end), '0123456789'))
            SplitName{i} = signalLabel(iSpace+1:end);
            SplitType{i} = signalLabel(1:iSpace-1);
        % Accept also 2 spaces
        elseif (length(iSpace) == 2) && (iSpace(1) >= 3) && ~all(ismember(signalLabel(iSpace(1)+1:end), ' 0123456789'))
            SplitName{i} = strrep(signalLabel(iSpace(1)+1:end), ' ', '_');
            SplitType{i} = signalLabel(1:iSpace(1)-1);
        end
    else
        hdr.signal(i).label = sprintf('E%d', i);
    end
end
% Remove the classification if it makes some names non unique
uniqueNames = unique(SplitName);
for i = 1:length(uniqueNames)
    if ~isempty(uniqueNames{i})
        iName = find(strcmpi(SplitName, uniqueNames{i}));
        if (length(iName) > 1)
            [SplitName{iName}] = deal('');
            [SplitType{iName}] = deal('');
        end
    end
end

%% ===== CREATE EMPTY CHANNEL FILE =====
ChannelMat = db_template('channelmat');
ChannelMat.Comment = [sFile.device ' channels'];
ChannelMat.Channel = repmat(db_template('channeldesc'), [1, hdr.nsignal]);
chRef = {};
% For each channel
for i = 1:hdr.nsignal
    % If is the annotation channel
    if ~isempty(iAnnotChans) && ismember(i, iAnnotChans)
        ChannelMat.Channel(i).Type = 'EDF';
        ChannelMat.Channel(i).Name = 'Annotations';
    elseif ~isempty(iStatusChan) && (i == iStatusChan)
        ChannelMat.Channel(i).Type = 'BDF';
        ChannelMat.Channel(i).Name = 'Status';
    % Regular channels
    else
        % If there is a pair name/type already detected
        if ~isempty(SplitName{i}) && ~isempty(SplitType{i})
            ChannelMat.Channel(i).Name = SplitName{i};
            ChannelMat.Channel(i).Type = SplitType{i};
        else
            % Channel name
            ChannelMat.Channel(i).Name = hdr.signal(i).label(hdr.signal(i).label ~= ' ');
            % Channel type
            if ~isempty(hdr.signal(i).type)
                if (length(hdr.signal(i).type) == 3)
                    ChannelMat.Channel(i).Type = hdr.signal(i).type(hdr.signal(i).type ~= ' ');
                elseif isequal(hdr.signal(i).type, 'Active Electrode') || isequal(hdr.signal(i).type, 'AgAgCl electrode')
                    ChannelMat.Channel(i).Type = 'EEG';
                else
                    ChannelMat.Channel(i).Type = 'Misc';
                end
            else
                ChannelMat.Channel(i).Type = 'EEG';
            end
        end
        % Extract reference name (at the end of the channel name, separated with a "-", eg. "-REF")
        iDash = find(ChannelMat.Channel(i).Name == '-');
        if ~isempty(iDash) && (iDash(end) < length(ChannelMat.Channel(i).Name))
            chRef{end+1} = ChannelMat.Channel(i).Name(iDash(end):end);
        end
    end
    ChannelMat.Channel(i).Loc     = [0; 0; 0];
    ChannelMat.Channel(i).Orient  = [];
    ChannelMat.Channel(i).Weight  = 1;
    % ChannelMat.Channel(i).Comment = hdr.signal(i).type;
end

% If the same reference is indicated for all the channels: remove it
if (length(chRef) >= 2)
    % Get the shortest reference tag
    lenRef = cellfun(@length, chRef);
    minLen = min(lenRef);
    % Check if all the ref names are equal (up to the max length - some might be cut because the channel name is too long)
    if all(cellfun(@(c)strcmpi(c(1:minLen), chRef{1}(1:minLen)), chRef))
        % Remove the reference tag from all the channel names
        for i = 1:length(ChannelMat.Channel)
            ChannelMat.Channel(i).Name = strrep(ChannelMat.Channel(i).Name, chRef{1}, '');
            ChannelMat.Channel(i).Name = strrep(ChannelMat.Channel(i).Name, chRef{1}(1:minLen), '');
        end
    end
end

% If there are only "Misc" and no "EEG" channels: rename to "EEG"
iMisc = find(strcmpi({ChannelMat.Channel.Type}, 'Misc'));
iEeg  = find(strcmpi({ChannelMat.Channel.Type}, 'EEG'));
if ~isempty(iMisc) && isempty(iEeg)
    [ChannelMat.Channel(iMisc).Type] = deal('EEG');
    iEeg = iMisc;
end

%% ===== DETECT MULTIPLE SAMPLING RATES =====
% Use the first "EEG" channel as the reference sampling rate (or the first channel if no "EEG" channels available)
if ~isempty(iEeg) && ismember(iEeg(1), iOtherChan)
    iChanFreqRef = iEeg(1);
else
    iChanFreqRef = iOtherChan(1);
end
% Mark as bad channels with sampling rates different from EEG
iChanWrongRate = find([sFile.header.signal.sfreq] ~= sFile.header.signal(iChanFreqRef).sfreq);
iChanWrongRate = intersect(iChanWrongRate, iOtherChan);
if ~isempty(iChanWrongRate)
    sFile.channelflag(iChanWrongRate) = -1;
    disp([sprintf('EDF> WARNING: Excluding channels with sampling rates other than %.3f Hz : ', hdr.signal(iChanFreqRef).sfreq), sprintf('%s ', ChannelMat.Channel(iChanWrongRate).Name)]);
end

% Consider that the sampling rate of the file is the sampling rate of the first signal
sFile.prop.sfreq = hdr.signal(iChanFreqRef).sfreq;
sFile.prop.times = [0, hdr.signal(iChanFreqRef).nsamples * hdr.nrec - 1] ./ sFile.prop.sfreq;
sFile.prop.nAvg  = 1;

%% ===== READ EDF ANNOTATION CHANNEL =====
if ~isempty(iEvtChans) % && ~isequal(ImportOptions.EventsMode, 'ignore')
    % Set reading options
    ImportOptions.ImportMode = 'Time';
    ImportOptions.UseSsp     = 0;
    ImportOptions.UseCtfComp = 0;
    % Read EDF annotations
    if strcmpi(sFile.format, 'EEG-EDF')
        evtList = {};
        % By default: the file starts at 0s (definitions useful in case the first records are missing)
        t0_file = 0;
        prev_rec = 0;
        prev_irec = 1;
        % In EDF+, the first annotation channel has epoch time stamps (EDF
        % calls epochs records).  So read all annotation channels per epoch.
        for irec = 1:hdr.nrec
            for ichan = 1:length(iEvtChans)
                bst_progress('text', sprintf('Reading annotations... [%d%%]', round((ichan + (irec-1)*length(iEvtChans))/length(iEvtChans)/hdr.nrec*100)));
                % Sample indices for the current epoch (=record)
                SampleBounds = [irec-1,irec] * sFile.header.signal(iEvtChans(ichan)).nsamples - [0,1];
                % Read record
                F = in_fread(sFile, ChannelMat, 1, SampleBounds, iEvtChans(ichan), ImportOptions);
                % Find all the TALs (Time-stamped Annotations Lists): separated with [20][0]
                % Possible configurations:
                %    Onset[21]Duration[20]Annot1[20]Annot2[20]..AnnotN[20][0]
                %    Onset[20]Annot1[20]Annot2[20]..AnnotN[20][0]
                %    Onset[20]Annot1[20][0]
                %    Onset[20][20][0]                       : First TAL of a record, with an empty annotation, indicating the offset from the beginning of the file
                %    Onset[20][20]Annot2[20]..AnnotN[20][0] : First TAL + extra annotations
                iSeparator = [-1, find((F(1:end-1) == 20) & (F(2:end) == 0))];
                % Loop on all the TALs
                for iAnnot = 1:length(iSeparator)-1
                    % Get annotation
                    strAnnot = char(F(iSeparator(iAnnot)+2:iSeparator(iAnnot+1)-1));
                    if isempty(strAnnot) || (strAnnot(1) == 20)
                        continue;
                    end
                    % Split in blocks with [20]
                    splitAnnot = str_split(strAnnot, 20, 0);
                    % The first TAL in a record should be indicating the timing of the first sample of the record (empty annotation)
                    if (iAnnot == 1) && ((length(splitAnnot) == 1) || isempty(splitAnnot{2})) && ~any(splitAnnot{1} == 21)
                        % Ignore if this is not the first channel
                        if (ichan > 1)
                            continue;
                        end
                        % Get record time stamp
                        t0_rec = str2double(splitAnnot{1});
                        if isempty(t0_rec) || isnan(t0_rec)
                            continue;
                        end
                        if (irec == 1)
                            t0_file = t0_rec;
                        % Find discontinuities equal or larger than 2 samples (1 is expected)
                        elseif abs(t0_rec - prev_rec - (irec - prev_irec) * hdr.reclen) >= (2 / sFile.prop.sfreq)
                            % Brainstorm fills partial/interrupted records with zeros
                            bstTime = prev_rec + hdr.reclen;
                            timeDiff = bstTime - t0_rec;
                            % If we want to fix timing, apply skip to initial timestamp
                            if hdr.fixinterrupted
                                t0_file = t0_file - timeDiff;
                            end
                            % Warn user of discontinuity
                            if timeDiff > 0
                                expectMsg = 'blank data';
                            else
                                expectMsg = 'skipped data';
                            end
                            startTime = min(t0_rec - t0_file - [0, timeDiff]); % before and after t0_file adjustment
                            endTime  = max(t0_rec - t0_file - [0, timeDiff]);
                            fprintf('WARNING: Found discontinuity between %.3fs and %.3fs (%d samples), expect %s in between.\n', startTime, endTime, round(abs(timeDiff).*sFile.prop.sfreq), expectMsg);
                            % Create event for users information
                            if timeDiff < 0
                                endTime = startTime; % no extent in this case, there is skipped time.
                            end
                            evtList(end+1,:) = {'EDF+D Discontinuity', [startTime; endTime]};
                        end
                        prev_rec = t0_rec;
                        prev_irec = irec;
                        % If there are extra annotations for this TAL: Create one event for each label in the TAL
                        for iLabel = 3:length(splitAnnot)
                            evtList(end+1,:) = {splitAnnot{iLabel}, (t0_rec-t0_file) + [0;0]};
                        end
                    % Regular TAL: indicating a marker
                    else
                        % Split time in onset/duration
                        splitTime = str_split(splitAnnot{1}, 21);
                        % Get time
                        t = str2double(splitTime{1});
                        if isempty(t) || isnan(t)
                            continue;
                        end
                        % Get duration
                        if (length(splitTime) > 1)
                            duration = str2double(splitTime{2});
                            % Exclude 1-sample long events
                            if isempty(duration) || isnan(duration) || (round(duration .* sFile.prop.sfreq) <= 1)
                                duration = 0;
                            end
                        else
                            duration = 0;
                        end
                        % Unnamed event
                        if (length(splitAnnot) == 1) || isempty(splitAnnot{2})
                            splitAnnot{2} = 'Unnamed';
                        end
                        % Create one event for each label in the TAL
                        for iLabel = 2:length(splitAnnot)
                            evtList(end+1,:) = {splitAnnot{iLabel}, (t-t0_file) + [0;duration]};
                        end
                    end
                end
            end
        end

        % If there are events: create a create an events structure
        if ~isempty(evtList)
            % Initialize events list
            sFile.events = repmat(db_template('event'), 0);
            % Events list
            [uniqueEvt, iUnique] = unique(evtList(:,1));
            uniqueEvt = evtList(sort(iUnique),1);
            % Build events list
            for iEvt = 1:length(uniqueEvt)
                % Find all the occurrences of this event
                iOcc = find(strcmpi(uniqueEvt{iEvt}, evtList(:,1)));
                % Concatenate all times
                t = [evtList{iOcc,2}];
                % If second row is equal to the first one (no extended events): delete it
                if all(t(1,:) == t(2,:))
                    t = t(1,:);
                end
                % Set event
                sFile.events(iEvt).label    = strtrim(uniqueEvt{iEvt});
                sFile.events(iEvt).times    = round(t .* sFile.prop.sfreq) ./ sFile.prop.sfreq;
                sFile.events(iEvt).epochs   = 1 + 0*t(1,:);
                sFile.events(iEvt).select   = 1;
                sFile.events(iEvt).channels = [];
                sFile.events(iEvt).notes    = [];
            end
        end

    % BDF Status line
    elseif strcmpi(sFile.format, 'EEG-BDF') && ~strcmpi(ImportOptions.EventsTrackMode, 'ignore')
        % Ask how to read the events
        [events, ImportOptions.EventsTrackMode] = process_evt_read('Compute', sFile, ChannelMat, ChannelMat.Channel(iEvtChans).Name, ImportOptions.EventsTrackMode);
        if isequal(events, -1)
            sFile = [];
            ChannelMat = [];
            return;
        end
        % Report the events in the file structure
        sFile.events = events;
        % Remove the 'Status: ' string in front of the events
        for i = 1:length(sFile.events)
            sFile.events(i).label = strrep(sFile.events(i).label, 'Status: ', '');
        end
        % Group events by time
        % sFile.events = process_evt_grouptime('Compute', sFile.events);
    end
end


function [engine, loglik, niter] = enter_evidence(engine, evidence, varargin)
% ENTER_EVIDENCE Add the specified evidence to the network (pearl)
% [engine, loglik, num_iter] = enter_evidence(engine, evidence, ...)
% evidence{i} = [] if if X(i) is hidden, and otherwise contains its observed value (scalar or column vector)
%
% The following optional arguments can be specified in the form of name/value pa irs:
% [default value in brackets]
%
% maximize - if 1, does max-product instead of sum-product [0]
% 'filename' -  msgs will be printed to this file, so you can assess convergence while it runs [engine.filename]
%
% e.g., engine = enter_evidence(engine, ev, 'maximize', 1)
%
% For discrete nodes, loglik is the negative Bethe free energy evaluated at the final beliefs.
% For Gaussian nodes, loglik is currently always 0.
%
% 'num_iter' returns the number of iterations used.

maximize = 0;
filename = engine.filename;

% parse optional params
args = varargin;
nargs = length(args);
if nargs > 0
  for i=1:2:nargs
    switch args{i},
     case 'maximize', maximize = args{i+1};
     case 'filename', filename = args{i+1};
     otherwise,
      error(['invalid argument name ' args{i}]);
    end
  end
end

if maximize
  error('can''t handle max-prop yet')
end

engine.maximize = maximize;
engine.filename = filename;
engine.bel = []; % reset if necessary

bnet = bnet_from_engine(engine);
N = length(bnet.dag);
ns = bnet.node_sizes(:);

observed_bitv = ~isemptycell(evidence);
disconnected = find(engine.disconnected_nodes_bitv);
if ~all(observed_bitv(disconnected))
  error(['The following discrete nodes must be observed: ' num2str(disconnected)])
end
msg = init_pearl_msgs(engine.msg_type, engine.msg_dag, ns, evidence);

niter = 1;
switch engine.protocol
 case 'parallel', [msg, niter] = parallel_protocol(engine, evidence, msg);
 case 'tree', msg = tree_protocol(engine, evidence, msg);
 otherwise,
  error(['unrecognized protocol ' engine.protocol])
end
engine.niter = niter;

engine.marginal = cell(1,N);
nodes = find(~engine.disconnected_nodes_bitv);
for n=nodes(:)'
  engine.marginal{n} = compute_bel(engine.msg_type, msg{n}.pi, msg{n}.lambda);
end

engine.evidence = evidence; % needed by marginal_nodes and marginal_family
engine.msg = msg;  % needed by marginal_family

if (nargout >= 2)
  if (engine.msg_type == 'd')
    loglik = bethe_free_energy(engine, evidence);
  else
    loglik = 0;
  end
end

%%%%%%%%%%%

function msg =  init_pearl_msgs(msg_type, dag, ns, evidence)
% INIT_MSGS Initialize the lambda/pi message and state vectors
% msg =  init_msgs(dag, ns, evidence)
%

N = length(dag);
msg = cell(1,N);
observed = ~isemptycell(evidence);
lam_msg = 1;

for n=1:N
  ps = parents(dag, n);
  msg{n}.pi_from_parent = cell(1, length(ps));
  for i=1:length(ps)
    p = ps(i);
    msg{n}.pi_from_parent{i} = mk_msg(msg_type, ns(p));
  end

  cs = children(dag, n);
  msg{n}.lambda_from_child = cell(1, length(cs));
  for i=1:length(cs)
    c = cs(i);
    msg{n}.lambda_from_child{i} = mk_msg(msg_type, ns(n), lam_msg);
  end

  msg{n}.lambda = mk_msg(msg_type, ns(n), lam_msg);
  msg{n}.pi = mk_msg(msg_type, ns(n));

  if observed(n)
    msg{n}.lambda_from_self = mk_msg_with_evidence(msg_type, ns(n), evidence{n});
  else
    msg{n}.lambda_from_self = mk_msg(msg_type, ns(n), lam_msg);
  end
end

%%%%%%%%%

function msg =  mk_msg(msg_type, sz, is_lambda_msg)

if nargin < 3, is_lambda_msg = 0; end

switch msg_type
 case 'd', msg = ones(sz, 1);
 case 'g',
  if is_lambda_msg
    msg.precision = zeros(sz, sz);
    msg.info_state = zeros(sz, 1);
  else
    msg.Sigma = zeros(sz, sz);
    msg.mu = zeros(sz,1);
  end
end

%%%%%%%%%%%%

function msg = mk_msg_with_evidence(msg_type, sz, val)

switch msg_type
 case 'd',
  msg = zeros(sz, 1);
  msg(val) = 1;
 case 'g',
  %msg.observed_val = val(:);
  msg.precision = inf;
  msg.mu = val(:);
end


function f = abs(f, varargin)
%ABS   Absolute value of a DELTAFUN object.
%   ABS(F) returns the absolute value of F, where F is a DELTAFUN object such
%   that the fun part of F has no roots in the domain of F. If
%   ~isempty(roots(F)), then ABS(F) will return garbage with no warning.

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

% Take the absolute values:
f.funPart = abs(f.funPart, varargin{:});
f.deltaMag = abs(f.deltaMag);

end


function [output,timing] = global_solve_upper(p,p_original,x,options,uppersolver,timing)

if ~p.solver.uppersolver.constraint.binary
    if ~isempty(p.binary_variables)
        local_gave_good = find(abs(x(p_original.binary_variables)-fix(x(p_original.binary_variables)))< options.bnb.inttol);
        p.lb(p.binary_variables(local_gave_good)) = fix(x(p.binary_variables(local_gave_good)));
        p.ub(p.binary_variables(local_gave_good)) = fix(x(p.binary_variables(local_gave_good)));
        for i = 1:3
            p = propagate_bounds_from_evaluations(p);
            p = propagate_bounds_from_equalities(p);
            p = update_monomial_bounds(p);
        end
    end

    if ~isempty(p_original.integer_variables)
        local_gave_good = find(abs(x(p.integer_variables)-fix(x(p.integer_variables)))< options.bnb.inttol);
        p.lb((p.integer_variables(local_gave_good))) = fix(x(p.integer_variables(local_gave_good)));
        p.ub((p.integer_variables(local_gave_good))) = fix(x(p.integer_variables(local_gave_good)));
        for i = 1:3
            p = propagate_bounds_from_evaluations(p);
            p = propagate_bounds_from_equalities(p);
            p = update_monomial_bounds(p);
        end
    end
end

% The bounds and relaxed solutions have been computed w.r.t to the relaxed
% bilinear model. We only need the original bounds and variables.
p.lb = p.lb(1:length(p_original.c));
p.ub = p.ub(1:length(p_original.c));

x = x(1:length(p_original.c));
%
p_upper = p_original;

p_upper = restoreQ(p_upper);

p_upper.lb = p.lb;
p_upper.ub = p.ub;

% KNITRO does not like fixed binary/integer variables, so we remove them
fixed_binary = find(p_upper.lb(p_upper.binary_variables) == p_upper.ub(p_upper.binary_variables));
fixed_integer = find(p_upper.lb(p_upper.integer_variables) == p_upper.ub(p_upper.integer_variables));
p_upper.binary_variables(fixed_binary) = [];if length(p_upper.binary_variables)==0;p_upper.binary_variables = [];end
p_upper.integer_variables(fixed_integer) = [];if length(p_upper.integer_variables)==0;p_upper.integer_variables = [];end

% Pick an initial point (this can be a bit tricky...)
lbinfbounds = find(isinf(p.lb));
ubinfbounds = find(isinf(p.ub));
switch p.options.bmibnb.localstart
    case 'relaxed'
        p_upper.x0 = x;
    case 'boxcenter'
        p_upper.x0 = x;
        p_upper.x0(lbinfbounds)=0;
        p_upper.x0(ubinfbounds)=0;
    case 'zero'
        p_upper.x0 = zeros(length(p.lb),1);
    otherwise
        error('Unknown local starting point option');
end

% Find x(a)*x(b) where x(a) or x(b) is fixed, and remove from Q
[a,b,kk] = find(triu(2*p_upper.Q));
for i = 1:length(a)
    if p.lb(a(i)) == p.ub(a(i))
        p_upper.c(b(i)) = p_upper.c(b(i)) + p.lb(a(i))*kk(i);
        p_upper.Q(a(i),b(i))=0;
        p_upper.Q(b(i),a(i))=0;
    elseif p.lb(b(i)) == p.ub(b(i))
        p_upper.c(a(i)) = p_upper.c(a(i)) + p.lb(b(i))*kk(i);
        p_upper.Q(a(i),b(i))=0;
        p_upper.Q(b(i),a(i))=0;
    end
end

% Save time
p_upper.options.saveduals = 0;

% Solve upper bounding problem
p_upper.options.warmstart = 1;
tstart = tic;
try
    output = feval(uppersolver,p_upper);
catch
    output.Primal = zeros(length(p_upper.lb),1);
    output.problem = -1;
end
if isempty(output.Primal)
     output.Primal = zeros(length(p_upper.lb),1);
end

timing.uppersolve = timing.uppersolve + toc(tstart);

% Project into the box (numerical issue)
output.Primal(output.Primal<p_upper.lb) = p_upper.lb(output.Primal<p_upper.lb);
output.Primal(output.Primal>p_upper.ub) = p_upper.ub(output.Primal>p_upper.ub);

function p = restoreQ(p)
% Sometimes the Q-term has been lifted and relaxed, but it might be the
% case that we are using a pure Q solver, and then we should really solve
% it as such
if nnz(p.Q)==0 && any(p.variabletype > 0)
    if p.solver.uppersolver.objective.quadratic.convex

        nonlinear = find(p.variabletype > 0 & p.variabletype <= 2);
        linear = find(p.variabletype == 0);
        % Find quadratic and linear terms
        used_in_c = find(p.c);
        quadraticterms = used_in_c(find(ismember(used_in_c,nonlinear)));
        Q = zeros(length(p.c),length(p.c));
        if ~isempty(quadraticterms)
            usedinquadratic = zeros(1,length(p.c));
            for i = 1:length(quadraticterms)
                Qij = p.c(quadraticterms(i));
                power_index = find(p.monomtable(quadraticterms(i),:));
                if length(power_index) == 1
                    Q(power_index,power_index) = Qij;
                else
                    Q(power_index(1),power_index(2)) = Qij/2;
                    Q(power_index(2),power_index(1)) = Qij/2;
                end
            end
        end
        p.Q = Q;
        p.c(nonlinear) = 0;
    end
end


x = 2 * ( 1 - 2*i )^3;
str = ['disp:    x = ' num2str(x)];
disp(str);
fprintf('fprintf: x = %8.4f\n',x);


function fiff_write_int(fid,kind,data)
%
% fiff_write_int(fid,kind,data)
%
% Writes a 32-bit integer tag to a fif file
%
%     fid           An open fif file descriptor
%     kind          Tag kind
%     data          The integers to use as data
%

%
%   Author : Matti Hamalainen, MGH Martinos Center
%   License : BSD 3-clause
%
%   Revision 1.3  2006/04/27 22:38:37  msh
%   Splitting an empty list now results in an empty output.
%   Added fiff_write_double and fiff_write_short
%   Write an array of floats, ints, and doubles instead of just one value.
%   Fixed help text of fiff_pick_channels.
%
%   Revision 1.2  2006/04/23 15:29:40  msh
%   Added MGH to the copyright
%
%   Revision 1.1  2006/04/10 23:26:54  msh
%   Added fiff reading routines
%
%

me='MNE:fiff_write_int';

if nargin ~= 3
        error(me,'Incorrect number of arguments');
end

FIFFT_INT=3;
FIFFV_NEXT_SEQ=0;
nel=numel(data);
datasize=nel*4;
count = fwrite(fid,int32(kind),'int32');
if count ~= 1
    error(me,'write failed');
end
count = fwrite(fid,int32(FIFFT_INT),'int32');
if count ~= 1
    error(me,'write failed');
end
count = fwrite(fid,int32(datasize),'int32');
if count ~= 1
    error(me,'write failed');
end
count = fwrite(fid,int32(FIFFV_NEXT_SEQ),'int32');
if count ~= 1
    error(me,'write failed');
end
count = fwrite(fid,int32(data),'int32');
if count ~= nel
    error(me,'write failed');
end

return;


function output = call_xpressfico_miqp(interfacedata)

% Retrieve needed data
options = interfacedata.options;
model = yalmip2xpress(interfacedata);

if options.savedebug
    save xpressdebug model
end

solvertime = tic;
if isempty(model.extra.integer_variables) & isempty(model.extra.binary_variables) & isempty(model.extra.semicont_variables) & isempty(model.sos)
    if options.verbose
        [x,fval,exitflag,output,lambda] = xprsqp(model.H,model.f,model.A,model.b,model.rtype,model.lb,model.ub,model.ops);
    else
        evalc('[x,fval,exitflag,output,lambda] = xprsqp(model.H,model.f,model.A,model.b,model.rtype,model.lb,model.ub,model.ops);');
    end
else
    if options.verbose
        [x,fval,exitflag,output] = xprsmiqp(model.H,model.f,model.A,model.b,model.rtype,model.ctype,model.clim,model.sos,model.lb,model.ub,[],model.ops);
    else
        evalc('[x,fval,exitflag,output] = xprsmiqp(model.H,model.f,model.A,model.b,model.rtype,model.ctype,model.clim,model.sos,model.lb,model.ub,[],model.ops);        ');
    end
    lambda = [];
end
solvertime = toc(solvertime);

if ~isempty(lambda)
    D_struc = [lambda.lin];
else
    D_struc = [];
end

if length(x) == length(model.f)
    if ~isempty(model.extra.NegatedSemiVar)
        x(model.extra.NegatedSemiVar) = -x(model.extra.NegatedSemiVar);
    end
end

% Check, currently not exhaustive...
switch exitflag
    case {1}
        problem = 0;
    case {-2}
        problem = 1; % Infeasible
    case {0,-4,-5}
        problem = 3;
    case -8
        problem = -11;
    otherwise
        problem = -1;
end

% Save all data sent to solver?
if options.savesolverinput
    solverinput.f = f;
    solverinput.A = A;
    solverinput.b = b;
    solverinput.ctype = ctype;
    solverinput.rtype = rtype;
    solverinput.lb = lb;
    solverinput.ub = ub;
    solverinput.x0 = [];
else
    solverinput = [];
end

% Save all data from the solver?
if options.savesolveroutput
    solveroutput.x = x;
    solveroutput.fval = fval;
    solveroutput.exitflag = exitflag;
    solveroutput.output = output;
    solveroutput.lambda = lambda;
else
    solveroutput = [];
end

if isempty(x)
    x = zeros(length(model.f),1);
end

% Standard interface
output = createOutputStructure(x(:),D_struc,[],problem,interfacedata.solver.tag,solverinput,solveroutput,solvertime);


function P = spm_mesh_project(M, dat, method, varargin)
% Project volumetric data onto a mesh
% FORMAT P = spm_mesh_project(M, dat, method)
% M        - a patch structure, a handle to a patch
%            or a [nx3] vertices array
% dat      - a structure array [1xm] with fields dim, mat, XYZ and t
%            (see spm_render.m)
%            or a structure array [1xm] with fields mat and dat
%            or a structure array [1xm] from spm_vol.m
%            or a char array/cellstr of image filenames
% method   - interpolation method {'nn'}
% varargin - other parameters required by the interpolation method
%
% P        - a [mxn] projected data array
%__________________________________________________________________________
% Copyright (C) 2009-2019 Wellcome Trust Centre for Neuroimaging

% Guillaume Flandin
% $Id: spm_mesh_project.m 7543 2019-03-15 12:56:04Z guillaume $

if ishandle(M)
    V = get(M,'Vertices');
elseif isstruct(M) || isa(M,'gifti')
    V = M.vertices;
else
    V = M;
end

if nargin < 3, method = 'nn'; end
if ~strcmpi(method,'nn')
    error('Only Nearest Neighbours interpolation is available.');
end

if ischar(dat), dat = cellstr(dat); end
P = zeros(length(dat),size(V,1));
for i=1:numel(dat)
    if iscellstr(dat) || isfield(dat,'dt')
        if iscellstr(dat)
            v  = spm_vol(dat{i});
        else
            v  = dat(i);
        end
        Y      = spm_read_vols(v);
        mat    = v.mat;
    elseif isfield(dat,'dat')
        Y      = dat(i).dat;
        mat    = dat(i).mat;
    else
        if isfield(dat,'Z') %-xSPM structure
           dat = struct('dim',dat.DIM,'XYZ',dat.XYZ,'t',dat.Z,'mat',dat.M);
        end
        dim    = dat(i).dim(1:3);
        Y      = zeros(dim(:)');
        OFF    = dat(i).XYZ(1,:) + ...
                 dat(i).dim(1)*(dat(i).XYZ(2,:)-1 + ...
                 dat(i).dim(2)*(dat(i).XYZ(3,:)-1));
        Y(OFF) = dat(i).t .* (dat(i).t > 0);
        mat    = dat(i).mat;
    end
    XYZ        = double(inv(mat)*[V';ones(1,size(V,1))]);
    P(i,:)     = spm_sample_vol(Y,XYZ(1,:),XYZ(2,:),XYZ(3,:),0);
end


function config=createConfig()

config.path.input='/home/gneelima/work/data/datasets/PASCAL2007/testImages/';
config.path.output='/home/gneelima/work/data/output/objectProposals/randomizedPrims/PASCAL2007/proposals/';

config.opts.imageExt='.jpg';

config.params.rSeedForRun=-1;
config.params.approxFinalNBoxes=10000;
config.params.q=10;

config.params.segmentations{1}.colorspace='LAB';

config.params.segmentations{1}.superpixels.sigma=0.8;
config.params.segmentations{1}.superpixels.c=100;
config.params.segmentations{1}.superpixels.min_size=100;

config.params.segmentations{1}.simWeights.wBias=3.0017;
config.params.segmentations{1}.simWeights.wCommonBorder=-1.0029;
config.params.segmentations{1}.simWeights.wLABColorHist=-2.6864;
config.params.segmentations{1}.simWeights.wSizePer=-2.3655;

config.params.segmentations{1}.alpha=ones(1,65536);
config.params.segmentations{1}.verbose=1;


function [contact, fric_coef, geometry] = RightSole(robot)

    param = sys.GetExtraParams();

    r_foot_frame = robot.Joints(getJointIndices(robot, 'r_leg_akx'));
    contact = CoordinateFrame(...
        'Name','RightSole',...
        'Reference',r_foot_frame,...
        'Offset',[0, 0, param.hf],...
        'R',[0,0,0]... % z-axis is the normal axis, so no rotation required
        );

    fric_coef.mu = param.mu;
    fric_coef.gamma = param.gamma;

    geometry.la = param.wf/2;
    geometry.lb = param.wf/2;
    geometry.La = param.lt;
    geometry.Lb = param.lh;
end


% std_topomovie - make movie in the frequency domain
%
% Usage:
%  >> [STUDY] = std_movie(STUDY, ALLEEG, key1, val1, key2, val2, ...);
%
% Inputs:
%   STUDY      - STUDY structure comprising some or all of the EEG datasets in ALLEEG.
%   ALLEEG     - vector of EEG dataset structures for the dataset(s) in the STUDY,
%                typically created using load_ALLEEG().
%   'channels' - [numeric vector]  specific channel group to plot. By
%                default, the grand mean channel spectrum is plotted (using the
%                same format as for the cluster component means described above)
%   'moviemode' - ['erp'|'spec'|'ersptime'] movie mode. Currently only
%                 'spec' is implemented.
%   'erspfreq'  - [min max] frequency range when making movie of ERSP. The
%                 ERSP values are averaged over the selected frequency
%                 range. Not implemented
%   'limitbeg'  - [min max] limits at the beginning of the movie
%   'limitend'  - [min max] limits at the end of the movie
%   'freqslim'  - [freqs] array of landmark frequencies to set color
%                 limits.
%   'movieparams' - [low inc high] lower limit, higher limit and increment. If
%                increment is omited, movie is generate at every possible
%                increment (max freq resolution or max time resolution)
%
% Authors: Arnaud Delorme, CERCO, August, 2006
%
% See also: std_specplot(), std_erppplot(), std_erspplot()

% Copyright (C) Arnaud Delorme, arno@salk.edu
%
% 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 2 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, write to the Free Software
% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

function [STUDY, M] = std_movie(STUDY, ALLEEG, varargin);

if nargin < 2
    help std_specplot;
    return;
end;

[opt addparams ] = finputcheck( varargin, ...
                             { 'erspfreq'    'real'    [] [];
                               'movieparams' 'real'    [] [];
                               'channels'    'cell'    [] {};
                               'freqslim'    'real'    [] [];
                               'limitbeg'    'real'    [] [];
                               'limitend'    'real'    [] [];
                               'moviemode'   'string'  { 'ersptime' 'erp' 'spec' } 'spec' }, 'std_movie', 'ignore');

if isstr(opt), error(opt); end;
tmpchanlocs =  ALLEEG(1).chanlocs;
if isempty(opt.channels), opt.channels = { tmpchanlocs.labels }; end;
if ~strcmpi(opt.moviemode, 'spec'), error('Only spec has been implemented so far'); end;

% read data once
% --------------
STUDY = std_specplot(STUDY, ALLEEG, 'channels', opt.channels, 'topofreq', [10 11]); close;

% find first data channel with info
% ---------------------------------
for cind = 1:length(STUDY.changrp)
    if ~isempty(STUDY.changrp(cind).specdata), break; end;
end;

% generate movie
% --------------
if isempty(opt.movieparams),
    opt.movieparams(1) = STUDY.changrp(cind).specfreqs(1);
    opt.movieparams(2) = STUDY.changrp(cind).specfreqs(end);
end;
if length(opt.movieparams) == 2
    opt.movieparams(3) = opt.movieparams(2);
    opt.movieparams(2) = STUDY.changrp(cind).specfreqs(2)-STUDY.changrp(cind).specfreqs(1);
end;
if length(opt.movieparams) == 3
    opt.movieparams = [opt.movieparams(1):opt.movieparams(2):opt.movieparams(3)];
end;

% find limits
% -----------
if isempty(opt.limitbeg)
    [STUDY specdata] = std_specplot(STUDY, ALLEEG, 'channels', opt.channels, 'topofreq', opt.movieparams(1)); close;
    opt.limitbeg = [ min(specdata{1}) max(specdata{1}) ];
    [STUDY specdata] = std_specplot(STUDY, ALLEEG, 'channels', opt.channels, 'topofreq', opt.movieparams(end)); close;
    opt.limitend = [ min(specdata{1}) max(specdata{1}) ];
end;
lowlims  = linspace(opt.limitbeg(1), opt.limitend(1), length(opt.movieparams));
highlims = linspace(opt.limitbeg(2), opt.limitend(2), length(opt.movieparams));

% limits at specific frequencies
% ------------------------------
if ~isempty(opt.freqslim)
    if opt.freqslim(1)   ~= opt.movieparams(1)  , opt.freqslim = [ opt.movieparams(1) opt.freqslim ]; end;
    if opt.freqslim(end) ~= opt.movieparams(end), opt.freqslim = [ opt.freqslim opt.movieparams(end) ]; end;

    for ind = 1:length(opt.freqslim)
        [tmp indf(ind)] = min(abs(opt.freqslim(ind) - opt.movieparams));
        [STUDY specdata] = std_specplot(STUDY, ALLEEG, 'channels', opt.channels, 'topofreq', opt.movieparams(indf(ind))); close;
        minimum(ind) = min(specdata{1});
        maximum(ind) = max(specdata{1});
    end;
    indf(1)  = 0;
    lowlims  = [ ];
    highlims = [ ];
    for ind = 2:length(opt.freqslim)
        lowlims  = [lowlims linspace(minimum(ind-1), minimum(ind), indf(ind)-indf(ind-1)) ];
        highlims = [highlims linspace(maximum(ind-1), maximum(ind), indf(ind)-indf(ind-1)) ];
    end;
end;

% make movie
% ----------
for ind = 1:length(opt.movieparams)
    STUDY = std_specplot(STUDY, ALLEEG, 'channels', opt.channels, 'topofreq', opt.movieparams(ind), 'caxis', [lowlims(ind) highlims(ind)]);
    pos = get(gcf, 'position');
    set(gcf, 'position', [pos(1) pos(2) pos(3)*2 pos(4)*2]);
    M(ind) = getframe(gcf);
    close;
end;
figure; axis off; movie(M);


classdef HybridSystem < handle & matlab.mixin.Copyable
    % HybridSystem defines a hybrid dynamical system that has both
    % continuous and discrete dynamics, such as bipedal locomotion.
    %
    % This class provides basic elements and functionalities of a hybrid
    % dynamicsl system. The mathematical definition of the hybrid system is
    % given as
    % \f{eqnarray*}{
    % \mathscr{HC} = \{\Gamma, \mathcal{D}, U, S, \Delta, FG\}
    % \f}
    %
    % The implementation of the hybrid system class is heavily based on the
    % Matlab's digraph data type, with wrapper functions with additional
    % validation.
    %
    % @author Ayonga Hereid @date 2016-09-26
    %
    % Copyright (c) 2016, AMBER Lab
    % All right reserved.
    %
    % Redistribution and use in source and binary forms, with or without
    % modification, are permitted only in compliance with the BSD 3-Clause
    % license, see
    % http://www.opensource.org/licenses/bsd-license.php

    %% public properties
    properties (GetAccess = public, SetAccess = protected)
        % This is the name of the object that gives the object an universal
        % identification
        %
        % @type char @default ''
        Name

        % The directed graph that describes the hybrid dynamical system
        % structure
        %
        % @type digraph
        Gamma

        % The option of the hybrid system model
        %
        % @type struct
        Options

    end

    properties (Dependent)
        % A structure describes the name, type, attributes, and
        % default value of each properties of the vertex
        %
        % @type struct
        VertexProperties

        % A structure describes the name, type, attributes, and
        % default value of each properties of the edge
        %
        % @type struct
        EdgeProperties
    end
    methods
        function VertexProperties = get.VertexProperties(~)

            VertexProperties = struct();
            VertexProperties.Name =  {'Domain','Control','Param'};
            VertexProperties.Type = {{'ContinuousDynamics'},{'Controller'},{'struct'}};
            VertexProperties.Attribute = {{},{},{}};
            VertexProperties.DefaultValue =  {{[]},{[]},{[]}};
        end

        function EdgeProperties = get.EdgeProperties(~)

            EdgeProperties = struct();
            EdgeProperties.Name =  {'Guard', 'Param', 'Weights'};
            EdgeProperties.Type = {{'DiscreteDynamics'}, {'struct'}, {'numeric'}};
            EdgeProperties.Attribute = {{}, {}, {'scalar'}};
            EdgeProperties.DefaultValue = {{[]},{[]}, NaN};
        end
    end

    %% Public methods
    methods (Access = public)
        function obj = HybridSystem(name, varargin)
            % the default calss constructor
            %
            % Parameters:
            % name: the name of the hybrid system model @type char
            % varargin: variable parameter input argument @type varargin
            %
            % Return values:
            % obj: the class object

            assert(ischar(name), 'The object name must be a character vector.');
            obj.Name = name;

            % initialize an empty directed graph with specified properties
            obj.Gamma = digraph;
            % add a dummy node and remove it to initialize the properties
            obj = addVertex(obj,'dummy');
            obj = rmVertex(obj,'dummy');

            % load default options
            obj.Options = struct;
            obj.Options.Logger = 'SimLogger';
            obj.Options.OdeSolver = @ode45;

            % parse the input options
            if nargin > 1
                % create a structure from the input argument
                opts = struct(varargin{:});
                % overwrite the default options
                obj.setOption(opts);
            end

        end

        function obj = setOption(obj, varargin)
            % Sets the object options, and return the complete list of option
            % structure including unchanged default options.
            %
            % Parameters:
            % varargin: name-value pairs of option field value
            %
            % Return values:
            % options: the complete list of final option structure

            new_opts = struct(varargin{:});

            obj.Options = struct_overlay(obj.Options, new_opts,{'AllowNew',true});

        end

        function ret = isdag(obj)
            % returns true if the directed graph is a directed acyclic
            % graph
            %
            % @see ISDAG

            ret = isdag(obj.Gamma);

        end
        function ret = isDirectedCycle(obj)
            % returns true if the underlying directed graph is a simple
            % directed cycle.
            %
            % A simple directed cycle is a directed graph that has uniform
            % in-degree 1 and uniform out-degree 1.

            g = obj.Gamma;

            ret = all(indegree(g)==1) && all(outdegree(g)==1);

        end

        function sys = subGraph(obj, nodeIDs)
            % extract the subgraph of the hybrid system to create a new
            % hybrid system object with the same name
            %
            % Parameters:
            % nodeIDs: the node ids of the subgraph
            %
            % Return values:
            % sys: a new HybridSystem object with the subgraph specified by
            % the nodeIDs @type HybridSystem

            sys = HybridSystem(obj.Name);

            sub_g = subgraph(obj.Gamma, nodeIDs);

            sys.Gamma = sub_g;
        end

    end

    %% methods defined in separate files
    methods
        obj = addVertex(obj, varargin);

        obj = addEdge(obj, varargin);

        obj = rmVertex(obj, vertex_name);

        obj = rmEdge(obj, s, t);

        obj = setEdgeProperties(obj, s, t, varargin);

        obj = setVertexProperties(obj, vertex, varargin);

        obj = simulate(obj, t0, x0, tf, logger, options, varargin);

        obj = compile(obj, export_path, varargin);

        obj = saveExpression(obj, export_path, varargin);
    end

    %% Private methods
    methods (Static, Access=private)
        function validatePropAttribute(name, value, type, attribute)

            if iscell(value)
                cellfun(@(x)validateattributes(x,type,attribute,...
                    'HybridSystem', name), value);
            elseif isnumeric(value)
                % if a property is a numeric value, it must be a row vector
                for i=1:size(value,1)
                    validateattributes(value(i,:),type,attribute,'HybridSystem', name);
                end
            else
                error('The input argument must be a cell array of objects.');
            end

        end
    end

end


function [ fitted_shape, fitted_app ] = fit_2d(AAM, init_shape, image_data, max_it)
% [ fitted_shape fitted_app ] = fit_2d(AAM, init_shape, image_data, max_it)
%
% Fit an Active Appearance Model to an image, starting from the provided initial shape.
% Based on the 'Active Appearance Models Revisited' paper by Iain Matthews and Simon Baker.
%
%                         PARAMETERS
%
% AAM Active Appearance Models structure as returned by build_model_2d
%
% init_shape Shape to be used to start the fitting process.
%            It is A Nx2 matrix containing N landmarks over image_data,
%            with image coordinates stored on rows: [i j]
%
% image_data The image to be fitted.
%            It is A HxWxC floating point image, with pixels arranged such
%            that app_data(i,j,c) actually returns the pixel value
%            associated with position (i,j). Must be in floating point
%            format, ranging between 0 and 1.
%
% max_it Maximum number of iterations to be performed.
%
%
%                        RETURN VALUES
%
% fitted_shape The shape produced by the fitting process. It has the
%              same format as init_shape.
%              Due to the local nature of the optimization process, there is no
%              guarantee of a meaningful result. Hopefully, the closer
%              the initial guess is, the better the fit.
%
%
% fitted_app (Optional) The fitted appearance. It is the best approximation of
%            the appearance that the model can do.
%            It is a MHxMWxC image matrix, where MH = size(AAM.warp_map, 1) and
%            MW = size(AAM.warp_map, 2) and C is the number of colors and
%            is the same as image_data.
%
% Author: Luca Vezzaro (elvezzaro@gmail.com)

	if isa(image_data, 'float') == 0
		warning 'Image data is not in floating point format, assuming unsigned integer data.'
		image_data = double(image_data) / 255;
	end

	% Number of points in the shape
	np = size(init_shape, 1);

	% Number of colors
	nc = size(image_data, 3);

	% Size of the model
	modelh = size(AAM.warp_map, 1);
	modelw = size(AAM.warp_map, 2);

	% Check that the shape has the right size
	if np ~= (size(AAM.s0, 1) / 2) || nc ~= (size(AAM.A0, 1) / (modelw * modelh))
		msg = strcat('Number of landmarks in init_shape and/or number of ', ...
		             ' colors in image_data are not consistent with the model.');
		error(msg)
	end

	% Initialize current shape
	cur_shape = init_shape;

	% Used to estimate convergence, it's the root mean
	% square change on the shapes
	%rms_change = inf;

	iter = 1;

	%while (rms_change > 1 || iter < 7) && iter <= max_it
	while iter <= max_it
		% Warp the appearance to compute the error image, as a column vector
		%warped_app = reshape(pa_warp(AAM, cur_shape, image_data), [], 1);

		% Error image, simply the difference between the image and A0
		error_img = reshape(pa_warp(AAM, cur_shape, image_data), [], 1) - AAM.A0;

		% In the first 5 iterations, try to align the shape to the image using only
		% the affine transform. This will maximize convergence by using smaller error
		% images in determining the incremental shape parameters, giving smaller but
		% more meaningful updates.
		if iter > 5 || max_it < 10
			% Incremental parameter updates which should minimize the error.
			% The first four will be the delta_q and the latter ones are the
			% delta_p
			delta_qp = AAM.R * error_img;

			% Damping factor, used to scale parameters updates, thus avoiding
			% large updates to parameters associated with less important
			% modes.
			% FIXME: this improves fitting stability, but at the expense of precision.
			% damping = min(AAM.shape_eiv / AAM.shape_eiv(min(round((iter-6)/4+1),numel(AAM.shape_eiv))), ones(size(AAM.shape_eiv)));
			% damping = AAM.shape_eiv / AAM.shape_eiv(1);
			%
			% Compute the incremental warp using the *negated* incremental
			% shape parameters and reshape to prepare for the affine transform
			% d_s0 = reshape(AAM.s0 - sum(AAM.s * (damping .* delta_qp(5:end)), 2), np, 2);

			d_s0 = reshape(AAM.s0 - sum(AAM.s * delta_qp(5:end), 2), np, 2);

			% Compute the incremental affine transformation using the *negated*
			% parameters
			[ A trans ] = to_affine(AAM, -delta_qp(1:4)');

			d_s0 = d_s0 * A + repmat(trans, np, 1);

			comp_warp = pa_warp_composition(AAM, cur_shape, d_s0);
		else
			delta_q = AAM.R(1:4,:) * error_img;

			% Compute the incremental affine transformation using the *negated*
			% parameters
			[ A trans ] = to_affine(AAM, -delta_q');
			d_s0 = reshape(AAM.s0, np, 2);
			d_s0 = (d_s0 * A + repmat(trans, np, 1)) - d_s0;
			comp_warp = cur_shape + d_s0;
		end

%figure(1);
%hold off;
%imshow(image_data);
%hold on;
%triplot(AAM.shape_mesh, cur_shape(:,2), cur_shape(:,1), 'b');
%triplot(AAM.shape_mesh, comp_warp(:,2), comp_warp(:,1), 'r');
%
%pause;

		% Convergence estimate is simply given by relative average vertex rms_change
		%rms_change = sqrt(sum(sum((cur_shape - comp_warp).^2)) / np);
		%pause

		iter = iter + 1;
		% Update current shape
		cur_shape = comp_warp;
	end

	% Set return value
	fitted_shape = cur_shape;

	if nargout > 1
		% Error image, simply the difference between the warped image and A0
		error_img = reshape(pa_warp(AAM, cur_shape, image_data), [], 1) - AAM.A0;

		% Project the error image onto the appearance modes and use the resulting parameters
		% to build the reconstructed appearance
		fitted_app = reshape(AAM.A0 + AAM.A * (AAM.A' * error_img), [modelh modelw nc]);
	end


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% QPSK demonstration packet-based transceiver for Chilipepper
% Toyon Research Corp.
% http://www.toyon.com/chilipepper.php
% Created 10/17/2012
% embedded@toyon.com
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%#codegen
function [d_i_out, d_q_out] = qpsk_rx_srrc(d_i_in, d_q_in)

persistent buf_i buf_q

OS_RATE = 8;
f = SRRC;

if isempty(buf_i)
    buf_i = zeros(1,OS_RATE*2+1);
    buf_q = zeros(1,OS_RATE*2+1);
end

buf_i = [buf_i(2:end) d_i_in];
buf_q = [buf_q(2:end) d_q_in];

d_i_out = buf_i*f;
d_q_out = buf_q*f;


clear all;
addpath(genpath('../CoreModules'));
n_epoch=25;
dataset_name='cifar';
network_name='cnn';
use_gpu=(gpuDeviceCount>0) %use gpu or not
if use_gpu
    %Requires Neural Network Toolbox to use it.
    opts.use_nntoolbox=license('test','neural_network_toolbox')
end
%function handle to prepare your data
PrepareDataFunc=@PrepareData_CIFAR_CNN;
%function handle to initialize the network
NetInit=@net_init_cifar_cnn;

%automatically select learning rates
use_selective_sgd=1;
%select a new learning rate every n epochs
ssgd_search_freq=10;
learning_method=@sgd2;%training method: @sgd,@adagrad,@rmsprop,@adam,@sgd2
opts.parameters.weightDecay=5e-4;
sgd_lr=1.0;%This is unnecessary as lr is automatically determined.

Main_Template(); %call training template


classdef ComplianceAndAdjointPrinter < CompositeResultsPrinter

    methods (Access = public)

        function obj = ComplianceAndAdjointPrinter(d)
            obj.init(d);
        end

    end

    methods (Access = protected)

        function createPrinters(obj,d)
            obj.printers{1} = obj.createCompliancePrinter(d);
            obj.printers{2} = obj.createAdjointPrinter(d);
          %  obj.printers{3} = obj.createRegularizedDensityPrinter(d);
        end

        function storeFieldsToPrint(obj,d)
            obj.storeComplianceFields(d);
            obj.storeAdjointFields(d);
       %     obj.storeRegularizedDensity(d);
        end

        function createHeadPrinter(obj,d,dh)
            phyPr = d.cost.shapeFunctions{1}.getPhysicalProblems();
            d.quad = phyPr{1}.element.quadrature;
            obj.printers{1}.createHeadPrinter(d,dh);
            h = obj.printers{1}.getHeadPrinter();
            obj.headPrinter = h;
        end

    end

    methods (Access = private, Static)

        function p = createCompliancePrinter(d)
            p =  ResultsPrinter.create('Elasticity',d);
            p.setStrVariablesNames('Stress','Strain','Disp');
        end

        function p = createAdjointPrinter(d)
            p =  ResultsPrinter.create('Elasticity',d);
            p.setStrVariablesNames('StressAdj','StrainAdj','DispAdj');
        end

     %   function p = createRegularizedDensityPrinter(d)
     %       p = ResultsPrinter.create('DensityGauss',d);
     %   end

        function storeFieldsToPrintFromPhyPr(printer,phyPr)
            d.fields = phyPr.variables;
            printer.storeFieldsToPrint(d);
        end

    end

    methods (Access = private)

        function storeComplianceFields(obj,d)
            d.fields = d.phyProblems{1}.variables;
            obj.printers{1}.storeFieldsToPrint(d);
        end

        function storeAdjointFields(obj,d)
            d.fields = d.phyProblems{2}.variables;
            obj.printers{2}.storeFieldsToPrint(d);
        end

        function storeRegularizedDensity(obj,d)
     %       d.fields = d.regDensity;
     %       obj.printers{3}.storeFieldsToPrint(d);
        end

    end
end


clc
clear

I=imread('peppers.png');
I=double(I);
class_number=3;
potential=0.5;
maxIter=30;
seg=ICM(I,class_number,potential,maxIter);
figure;
imshow(I);


%kvdetilt 'Remove Illumination Gradient by Subtracting Best-Fit Plane (K1)'
% This MatLab function was automatically generated by a converter (KhorosToMatLab) from the Khoros vdetilt.pane file
%
% Parameters:
% InputFile: i 'Input Image ', required: 'input image'
% InputFile: m 'Masking Image ', optional: 'find tilt operation masking image'
% OutputFile: o 'Output Image ', required: 'resulting detilted image'
%
% Example: o = kvdetilt({i, m}, {'i','';'m','';'o',''})
%
% Khoros helpfile follows below:
%
%  PROGRAM
% vdetilt - Remove Illumination Gradient by Subtracting Best-Fit Plane  (K1)
%
%  DESCRIPTION
% .I vdetilt
% computes the best-fit plane for an image, and then subtracts that plane
% from the image to produce the output.  This is very handy for correcting
% illumination gradients in a poorly digitized image.  The "remove mean" option
% also removes the mean from the image, giving it zero-mean statistics.
% If "remove mean" is set to yes (default), then remove mean from image.
%
% The optional mask image must be of the same data type as the input images,
% and is used to gate the operation.  A non-zero mask pixel enables the output
% pixel to contain the adjusted value. A zero mask pixel just transfers
% the value of the corresponding pixel in the first input image to the output
% pixel.
%
%
%
%  EXAMPLES
%
%  "SEE ALSO"
% vgettilt, lvgettilt, vtilt, lvtilt
%
%  RESTRICTIONS
% .I vdetilt
% will not work on BIT, transform or COMPLEX data storage types.
%
%  REFERENCES
%
%  COPYRIGHT
% Copyright (C) 1993 - 1997, Khoral Research, Inc. ("KRI")  All rights reserved.
%

function varargout = kvdetilt(varargin)
if nargin ==0
  Inputs={};arglist={'',''};
elseif nargin ==1
  Inputs=varargin{1};arglist={'',''};
elseif nargin ==2
  Inputs=varargin{1}; arglist=varargin{2};
else error('Usage: [out1,..] = kvdetilt(Inputs,arglist).');
end
if size(arglist,2)~=2
  error('arglist must be of form {''ParameterTag1'',value1;''ParameterTag2'',value2}')
 end
narglist={'i', '__input';'m', '__input';'o', '__output'};
maxval={0,1,0};
minval={0,1,0};
istoggle=[0,1,0];
was_set=istoggle * 0;
paramtype={'InputFile','InputFile','OutputFile'};
% identify the input arrays and assign them to the arguments as stated by the user
if ~iscell(Inputs)
Inputs = {Inputs};
end
NumReqOutputs=1; nextinput=1; nextoutput=1;
  for ii=1:size(arglist,1)
  wasmatched=0;
  for jj=1:size(narglist,1)
   if strcmp(arglist{ii,1},narglist{jj,1})  % a given argument was matched to the possible arguments
     wasmatched = 1;
     was_set(jj) = 1;
     if strcmp(narglist{jj,2}, '__input')
      if (nextinput > length(Inputs))
        error(['Input ' narglist{jj,1} ' has no corresponding input!']);
      end
      narglist{jj,2} = 'OK_in';
      nextinput = nextinput + 1;
     elseif strcmp(narglist{jj,2}, '__output')
      if (nextoutput > nargout)
        error(['Output nr. ' narglist{jj,1} ' is not present in the assignment list of outputs !']);
      end
      if (isempty(arglist{ii,2}))
        narglist{jj,2} = 'OK_out';
      else
        narglist{jj,2} = arglist{ii,2};
      end

      nextoutput = nextoutput + 1;
      if (minval{jj} == 0)
         NumReqOutputs = NumReqOutputs - 1;
      end
     elseif isstr(arglist{ii,2})
      narglist{jj,2} = arglist{ii,2};
     else
        if strcmp(paramtype{jj}, 'Integer') & (round(arglist{ii,2}) ~= arglist{ii,2})
            error(['Argument ' arglist{ii,1} ' is of integer type but non-integer number ' arglist{ii,2} ' was supplied']);
        end
        if (minval{jj} ~= 0 | maxval{jj} ~= 0)
          if (minval{jj} == 1 & maxval{jj} == 1 & arglist{ii,2} < 0)
            error(['Argument ' arglist{ii,1} ' must be bigger or equal to zero!']);
          elseif (minval{jj} == -1 & maxval{jj} == -1 & arglist{ii,2} > 0)
            error(['Argument ' arglist{ii,1} ' must be smaller or equal to zero!']);
          elseif (minval{jj} == 2 & maxval{jj} == 2 & arglist{ii,2} <= 0)
            error(['Argument ' arglist{ii,1} ' must be bigger than zero!']);
          elseif (minval{jj} == -2 & maxval{jj} == -2 & arglist{ii,2} >= 0)
            error(['Argument ' arglist{ii,1} ' must be smaller than zero!']);
          elseif (minval{jj} ~= maxval{jj} & arglist{ii,2} < minval{jj})
            error(['Argument ' arglist{ii,1} ' must be bigger than ' num2str(minval{jj})]);
          elseif (minval{jj} ~= maxval{jj} & arglist{ii,2} > maxval{jj})
            error(['Argument ' arglist{ii,1} ' must be smaller than ' num2str(maxval{jj})]);
          end
        end
     end
     if ~strcmp(narglist{jj,2},'OK_out') &  ~strcmp(narglist{jj,2},'OK_in')
       narglist{jj,2} = arglist{ii,2};
     end
   end
   end
   if (wasmatched == 0 & ~strcmp(arglist{ii,1},''))
        error(['Argument ' arglist{ii,1} ' is not a valid argument for this function']);
   end
end
% match the remaining inputs/outputs to the unused arguments and test for missing required inputs
 for jj=1:size(narglist,1)
     if  strcmp(paramtype{jj}, 'Toggle')
        if (narglist{jj,2} ==0)
          narglist{jj,1} = '';
        end;
        narglist{jj,2} = '';
     end;
     if  ~strcmp(narglist{jj,2},'__input') && ~strcmp(narglist{jj,2},'__output') && istoggle(jj) && ~ was_set(jj)
          narglist{jj,1} = '';
          narglist{jj,2} = '';
     end;
     if strcmp(narglist{jj,2}, '__input')
      if (minval{jj} == 0)  % meaning this input is required
        if (nextinput > size(Inputs))
           error(['Required input ' narglist{jj,1} ' has no corresponding input in the list!']);
        else
          narglist{jj,2} = 'OK_in';
          nextinput = nextinput + 1;
        end
      else  % this is an optional input
        if (nextinput <= length(Inputs))
          narglist{jj,2} = 'OK_in';
          nextinput = nextinput + 1;
        else
          narglist{jj,1} = '';
          narglist{jj,2} = '';
        end;
      end;
     else
     if strcmp(narglist{jj,2}, '__output')
      if (minval{jj} == 0) % this is a required output
        if (nextoutput > nargout & nargout > 1)
           error(['Required output ' narglist{jj,1} ' is not stated in the assignment list!']);
        else
          narglist{jj,2} = 'OK_out';
          nextoutput = nextoutput + 1;
          NumReqOutputs = NumReqOutputs-1;
        end
      else % this is an optional output
        if (nargout - nextoutput >= NumReqOutputs)
          narglist{jj,2} = 'OK_out';
          nextoutput = nextoutput + 1;
        else
          narglist{jj,1} = '';
          narglist{jj,2} = '';
        end;
      end
     end
  end
end
if nargout
   varargout = cell(1,nargout);
else
  varargout = cell(1,1);
end
global KhorosRoot
if exist('KhorosRoot') && ~isempty(KhorosRoot)
w=['"' KhorosRoot];
else
if ispc
  w='"C:\Program Files\dip\khorosBin\';
else
[s,w] = system('which cantata');
w=['"' w(1:end-8)];
end
end
[varargout{:}]=callKhoros([w 'vdetilt"  '],Inputs,narglist);


function signal = set_selinterval(varargin)
% Selects a time interval from a data set.
% Signal = set_selinterval(Signal,Intervals,Unit)
%
% In:
%   Signal    :   Continuous EEGLAB data set from which an interval should be selected
%
%   Intervals :   Selection interval(s) formatted as [start end; start end; start end; ...] or
%                 a MATLAB index range for the samples (if IntervalUnit is set to 'range')
%
%   IntervalUnit : Unit of measurement for the interval. Either 'seconds', 'samples', 'fraction',
%                  or 'range' (default: seconds)
%
%   InsertBoundaryMarkers : Whether to insert boundary markers (default: false)
%
% Out:
%   Signal    :   data set restricted to the selected range. The following fields are updated:
%                 * .data and all other time series fields are reduced to the selected intervals
%                 * .event is reduced to selected events
%                 * .xmax/.pnts are updated
%                 * optionally boundary events are inserted
%
% Examples:
%   % for a continuous data set, retain only the data within 50s and 200s, as well as 1200s and 1500s
%   eeg = set_selinterval(eeg,[50 200; 1200 1500])
%
%   % for a continuous data set, retain only the data within the 1000's and the 10000's sample
%   eeg = set_selinterval(eeg,[1000 10000],'samples')
%
%   % for a continuous data set, retain only the last half of the data
%   eeg = set_selinterval(eeg,[0.5 1],'fraction')
%
%   % as before, but pass the arguments by name
%   eeg = set_selinterval('Signal',eeg, 'Intervals',[0.5 1], 'Unit','fraction')
%
% See also:
%   set_selepos, set_partition
%
%                                Christian Kothe, Swartz Center for Computational Neuroscience, UCSD
%                                2010-04-01
dp;

% set_selinterval_version<2.0> -- for the cache

if ~exp_beginfun('editing') return; end

declare_properties('independent_channels',true,'independent_trials',false);

arg_define(varargin, ...
    arg_norep({'signal','Signal'}), ...
    arg({'intervals','Intervals'},[],[], 'Selection intervals. Array of the form [start end; start end; start end; ...].'), ...
    arg({'selunit','IntervalUnit','Unit','unit'},'seconds',{'seconds','samples','fraction','range'}, 'Interval unit. The unit of measurement for selection intervals (does not apply to SampleRange).'), ...
    arg({'insert_boundary_markers','InsertBoundaryMarkers'},false,[], 'Insert boundary markers. Whether to insert boundary markers (for EEGLAB compatibility).'));

utl_check_fields(signal,{'epoch','event','data','pnts','srate','xmin'},'signal','signal');
if signal.srate == 0
    error('Your signal needs to have a nonzero .srate value.'); end
if ~isempty(signal.epoch) || size(signal.data,3) > 1
    error('This function cannot be applied to epoched data. Use flt_window to perform interval selection on epoched data.'); end

switch selunit
    case 'fraction'
        if any(intervals<0 | intervals>1) %#ok<*NODEF>
            error('Fractional intervals must be in the [0,1] range.'); end
        intervals = 1+round(intervals*(size(signal.data,2)-1));
        samplerange = [];
    case 'seconds'
        intervals(intervals==Inf) = (size(signal.data,2)-1)/signal.srate;
        if any(intervals<0 | (1+round(intervals*signal.srate)) > size(signal.data,2))
            error('Your intervals exceed the data limit.'); end
        intervals = min(1+round(intervals*signal.srate),size(signal.data,2));
        samplerange = [];
    case 'range'
        samplerange = intervals(:)';
        intervals = [];
    case 'samples'
        samplerange = [];
    otherwise
        error(['Unsupported selection unit: ' selunit]);
end

% generate samplerange from intervals if not yet present
if isempty(samplerange)
    samplerange = cell(1,size(intervals,1));
    for k=1:length(samplerange)
        samplerange{k} = intervals(k,1):intervals(k,2); end
    samplerange = [samplerange{:}];
elseif isa(samplerange,'logical')
    samplerange = find(samplerange);
end
if any(round(samplerange) ~= samplerange)
    error('Your sample range contains non-integer indices: %s',hlp_tostring(samplerange)); end
if any(samplerange<1 | samplerange>size(signal.data,2))
    error('Your sample range exceeds the data bounds (%i); range is: %s.',size(signal.data,2),hlp_tostring(samplerange)); end

if isempty(samplerange)
    % clear data
    for field = utl_timeseries_fields(signal)
        signal.(field{1}) = []; end
    signal.event = [];
    signal.pnts = 0;
elseif ~isequal(samplerange,1:size(signal.data,2))
    % select range within the time series fields
    for field = utl_timeseries_fields(signal)
        if ~isempty(signal.(field{1}))
            try
                signal.(field{1}) = signal.(field{1})(:,samplerange,:,:,:,:,:,:);
            catch e
                error('The given sample range could not be extracted from the time-series field .%s with error: %s. Field size was: %i, epoch indices were: %s',field{1},e.message,size(signal.(field{1}),2),hlp_tostring(samplerange));
            end
        end
    end
    % select range within the events
    if ~isempty(signal.event)
        if ~isfield(signal.event,'latency')
            error('Your signal is lacking the required .event.latency field.'); end
        latency_numels = cellfun('prodofsize',{signal.event.latency});
        if any(latency_numels == 0)
            error('The given signal has one or more events with empty .latency field. This is not permitted.');
        elseif any(latency_numels ~= 1)
            error('The given signal has one or more events with a .latency value that is not a scalar. This is not permitted.');
        end
        % trim out-of-bounds events
        event_latency = [signal.event.latency];
        out_of_bounds = event_latency<1 | event_latency>signal.pnts;
        if any(out_of_bounds)
            disp_once('WARNING: your signal had %i events at out-of-bounds latencies that were removed.',nnz(out_of_bounds)); end
        signal.event(out_of_bounds) = [];
        % bin latencies into sparse array and apply selection
        [event_positions,residuals] = sparse_binning([signal.event.latency],[],signal.pnts);
        [ranks,sample_indices,event_indices] = find(event_positions(:,samplerange));
        residuals = residuals(:); sample_indices = sample_indices(:); event_indices = event_indices(:);
        % write event subset and update latencies
        if ~isempty(ranks)
            signal.event = signal.event(event_indices);
            [signal.event.latency] = arraydeal(sample_indices+residuals(event_indices));
        else
            signal.event = [];
        end
    end
    % optionally insert boundary markers
    if insert_boundary_markers
        % calculate gaps between intervals
        if isempty(intervals)
            tmp = diff(samplerange)-1;
            gap_offsets = find(tmp);
            gap_lengths = tmp(gap_offsets);
        else
            gap_offsets = intervals(1:end-1,2);
            gap_lengths = intervals(2:end,1) - intervals(1:end-1,2) - 1;
        end
        % append new events for each boundary between intervals
        insert_range = length(signal.event)+(1:length(gap_offsets));
        if insert_range
            [signal.event(insert_range).type] = deal('boundary');
            [signal.event(insert_range).latency] = arraydeal(gap_offsets+0.5);
            [signal.event(insert_range).duration] = arraydeal(gap_lengths);
            % resort events
            [dummy,order] = sort([signal.event.latency]); %#ok<ASGLU>
            signal.event = signal.event(order);
        end
    end
    % update misc fields
    signal.pnts = size(signal.data,2);
    signal.xmax = signal.xmin + (max(samplerange)-1)/signal.srate;
    signal.xmin = signal.xmin + (min(samplerange)-1)/signal.srate;
end

exp_endfun;


function test_tutorial_beamformingextended

% MEM 5gb
% WALLTIME 00:30:00
% DEPENDENCY ft_read_mri ft_redefinetrial ft_freqanalysis ft_volumesegment ft_appenddata ft_selectdata ft_prepare_singleshell ft_sourceanalysis ft_prepare_leadfield ft_prepare_headmodel ft_prepare_sourcemodel ft_plot_headmodel ft_plot_sens ft_plot_mesh ft_sourceinterpolate ft_sourceplot

datadir = dccnpath('/home/common/matlab/fieldtrip/data/ftp/tutorial/sensor_analysis');
mridir = dccnpath('/home/common/matlab/fieldtrip/data/ftp/tutorial/beamformer_extended');
templatedir  = dccnpath('/home/common/matlab/fieldtrip/template/sourcemodel');

load(fullfile(datadir, 'subjectK.mat'));

% Time windows of interest
data = ft_appenddata([], data_left, data_right);
cfg = [];
cfg.toilim = [-0.8 1.1];
cfg.minlength = 'maxperlen';
data = ft_redefinetrial(cfg, data);

cfg = [];
cfg.toilim = [-0.8 0];
data_bsl = ft_redefinetrial(cfg, data);

cfg.toilim = [0.3 1.1];
data_exp = ft_redefinetrial(cfg, data);

cfg = [];
data_cmb = ft_appenddata(cfg, data_bsl, data_exp);
% code the trial: 0 = baseline, 1 = experimental condition
data_cmb.trialinfo = [zeros(length(data_bsl.trial), 1); ones(length(data_exp.trial), 1)];

%% calculating the cross spectral density matrix
cfg = [];
cfg.method        = 'mtmfft';
cfg.output        = 'fourier'; % add hint: why fourier?
cfg.tapsmofrq     = 15;
cfg.foi           = 55;
cfg.keeptrials    = 'yes';
freq_cmb = ft_freqanalysis(cfg, data_cmb);

cfg = [];
cfg.trials = freq_cmb.trialinfo == 0;
freq_bsl = ft_selectdata(cfg, freq_cmb);
freq_bsl.cumtapcnt = freq_cmb.cumtapcnt(cfg.trials);
freq_bsl.cumsumcnt = freq_cmb.cumsumcnt(cfg.trials);
cfg.trials = freq_cmb.trialinfo == 1;
freq_exp = ft_selectdata(cfg, freq_cmb);
freq_exp.cumtapcnt = freq_cmb.cumtapcnt(cfg.trials);
freq_exp.cumsumcnt = freq_cmb.cumsumcnt(cfg.trials);

%%foward model and lead field

mri = ft_read_mri(fullfile(mridir, 'subjectK.mri'));
% cfg = [];
% [segmentedmri] = ft_volumesegment(cfg, mri);
%
oldsegmented = load(fullfile(mridir, 'segmentedmri.mat'));
segmentedmri = oldsegmented.segmentedmri;
%
% % check whether the segmentation gives results of more than 99% consistency
% assert(max(abs(oldsegmented.segmentedmri.gray(:)-segmentedmri.gray(:))) < .01, 'Gray matter segmentation differs from stored data')
% assert(max(abs(oldsegmented.segmentedmri.csf(:)-segmentedmri.csf(:))) < .01, 'CSF segmentation differs from stored data')
% assert(max(abs(oldsegmented.segmentedmri.white(:)-segmentedmri.white(:))) < .01, 'White matter segmentation differs from stored data')
% % transformation should be absolutely identical
% assert(isequal(oldsegmented.segmentedmri.transform, segmentedmri.transform), 'Transform differs from stored data')
%
% %save segmentedmri segmentedmri
%
% % add anatomical information to the segmentation
% segmentedmri.transform = mri.transform;
% segmentedmri.anatomy   = mri.anatomy;
% % call ft_sourceplot
% cfg = [];
% cfg.funparameter = 'gray';
% ft_sourceplot(cfg,segmentedmri);

% create ht head model from the segmented brain surface
cfg = [];
cfg.method = 'singleshell';
hdm = ft_prepare_headmodel(cfg, segmentedmri);

template = load(fullfile(templatedir, 'standard_sourcemodel3d8mm'));
% inverse-warp the subject specific grid to the template grid
cfg = [];
cfg.sourcemodel.warpmni   = 'yes';
cfg.sourcemodel.template  = template.sourcemodel;
cfg.sourcemodel.nonlinear = 'yes'; % use non-linear normalization
cfg.mri            = mri;
sourcemodel        = ft_prepare_sourcemodel(cfg);

figure;
hold on;
% note that when calling different plotting routines, all objects that we plot
% need to be in the same unit and coordinate space, here, we need to transform
% the head model to 'cm'
ft_plot_headmodel(ft_convert_units(hdm, freq_cmb.grad.unit), 'edgecolor', 'none');
alpha 0.4;
ft_plot_mesh(sourcemodel.pos(sourcemodel.inside,:));
ft_plot_sens(freq_cmb.grad);

cfg         = [];
cfg.sourcemodel    = sourcemodel;
cfg.headmodel = hdm;
cfg.channel = {'MEG'};
cfg.grad    = freq_cmb.grad;
sourcemodel_lf     = ft_prepare_leadfield(cfg, freq_cmb);

%% contrasting source activity
cfg                   = [];
cfg.frequency         = freq_cmb.freq;
cfg.grad              = freq_cmb.grad;
cfg.method            = 'dics';
cfg.keeptrials        = 'yes';
cfg.channel           = 'MEG';
cfg.sourcemodel       = sourcemodel_lf;
cfg.headmodel         = hdm;
cfg.keeptrials        = 'yes';
cfg.dics.lambda       = '5%';
cfg.dics.keepfilter   = 'yes';
cfg.dics.fixedori     = 'yes';
cfg.dics.realfilter   = 'yes';
source  = ft_sourceanalysis(cfg, freq_cmb);

% beam pre- and poststim by using the common filter
cfg.sourcemodel.label    = source.avg.label;
cfg.sourcemodel.filter   = source.avg.filter;
source_bsl  = ft_sourceanalysis(cfg, freq_bsl);
source_exp  = ft_sourceanalysis(cfg, freq_exp);

source_diff = source_exp;
source_diff.avg.pow = (source_exp.avg.pow ./ source_bsl.avg.pow) - 1;
source_diff.pos = template.sourcemodel.pos;
source_diff.dim = template.sourcemodel.dim;

% note that the exact directory is user-specific
templatefile = dccnpath('/home/common/matlab/fieldtrip/external/spm8/templates/T1.nii');
template_mri = ft_read_mri(templatefile);
template_mri.coordsys = 'spm';

cfg              = [];
cfg.parameter    = 'pow';
cfg.interpmethod = 'nearest';
source_diff_int  = ft_sourceinterpolate(cfg, source_diff, template_mri);

cfg               = [];
cfg.method        = 'slice';
cfg.funparameter  = 'pow';
cfg.maskparameter = cfg.funparameter;
cfg.funcolorlim   = [0.0 1.2];
cfg.opacitylim    = [0.0 1.2];
cfg.opacitymap    = 'rampup';
ft_sourceplot(cfg,source_diff_int);

cfg.method = 'ortho';
cfg.atlas  = dccnpath('/home/common/matlab/fieldtrip/template/atlas/aal/ROI_MNI_V4.nii');
ft_sourceplot(cfg,source_diff_int);

cfg.method = 'surface';
cfg.projmethod     = 'nearest';
cfg.surffile       = 'surface_white_both.mat';
cfg.surfdownsample = 10;
ft_sourceplot(cfg,source_diff_int);

%% coherence beaming

data = ft_appenddata([], data_left, data_right);
cfg                 = [];
cfg.toilim          = [-1 -0.0025];
cfg.minlength       = 'maxperlen'; % this ensures all resulting trials are equal length
data_stim           = ft_redefinetrial(cfg, data);

cfg                 = [];
cfg.output          = 'powandcsd';
cfg.method          = 'mtmfft';
cfg.taper           = 'dpss';
cfg.tapsmofrq       = 5;
cfg.foi             = 20;
cfg.keeptrials      = 'yes';
cfg.channel         = {'MEG' 'EMGlft' 'EMGrgt'};
cfg.channelcmb      = {'MEG' 'MEG'; 'MEG' 'EMGlft'; 'MEG' 'EMGrgt'};
freq_csd            = ft_freqanalysis(cfg, data_stim);

cfg                 = [];
cfg.method          = 'dics';
cfg.refchan         = 'EMGlft';
cfg.frequency       = 20;
cfg.headmodel       = hdm;
cfg.sourcemodel     = sourcemodel;
source_coh_lft      = ft_sourceanalysis(cfg, freq_csd);

source_coh_lft.pos = template.sourcemodel.pos;
source_coh_lft.dim = template.sourcemodel.dim;

% note that the exact directory is user-specific
templatefile = dccnpath('/home/common/matlab/fieldtrip/external/spm8/templates/T1.nii');
template_mri = ft_read_mri(templatefile);
template_mri.coordsys = 'spm';

cfg              = [];
cfg.parameter    = 'coh';
cfg.interpmethod = 'nearest';
source_coh_int   = ft_sourceinterpolate(cfg, source_coh_lft, template_mri);

cfg              = [];
cfg.method       = 'ortho';
cfg.funparameter = 'coh';
cfg.funcolormap = 'jet';

cfg.funcolorlim   = [00 .15];
cfg.opacitylim    = [00 .15];

cfg.maskparameter = cfg.funparameter;
cfg.opacitymap    = 'rampup';

cfg.atlas         = dccnpath('/home/common/matlab/fieldtrip/template/atlas/aal/ROI_MNI_V4.nii');

ft_sourceplot(cfg, source_coh_int);