%   Class for Hi-D matching with merging of data subsets using
%       QF match or F-measure or both
%   This produces a visual table and histogram of dissimilarities and
%   overlaps between subset matches.
%
%   The function getAverages answers the question of overall goodness
%   by returning the median/mean dissimilarity or overlap
%
%
%   QF Algorithm is described in
%   https://www.ncbi.nlm.nih.gov/pmc/articles/PMC6586874/
%   and
%   https://www.ncbi.nlm.nih.gov/pmc/articles/PMC5818510/
%
%   Bioinformatics lead: Darya Orlova <dyorlova@gmail.com>
%   Software Developer: Stephen Meehan <swmeehan@stanford.edu>
%
%   Provided by the Herzenberg Lab at Stanford University
%   License: BSD 3 clause
%
classdef QfTable < handle
    properties
        contextDescription;
        context;
        fcnFpnSubsets;
    end

    properties(SetAccess=private)
        fig;
        data;
        unmatched;
        sortTable;
        tb;
        qf;
        tClrs;
        priorFig;
        fHistFig;
        qHistFig;
        falsePosNegFig;
        otherFigs={};
        R;
    end

    properties(Constant)
        PROP_OUTER_POS2='OuterPosition2_V2';
        DEBUG=false;
    end

    methods
        function this=QfTable(qf, tClrs, props, priorFig, visible)
            if nargin<5
                visible=true;
                if nargin<4
                    priorFig=get(0, 'currentFig');
                    if nargin<3
                        props=[];
                    end
                end
            end
            app=BasicMap.Global;
            if isempty(props)
                props=app;
            end
            this.tClrs=tClrs;
            this.qf=qf;
            PROP_COL_W='mdsColumnWidthsV1.33';
            PROP_COL_ORD='mdsColumnOrder_v2';
            PROP_SORT='mdsRowOrder_v2';
            path=BasicMap.Path;
            if visible
                pu=PopUp('Preparing QF dissimilarity table', 'north west+', ...
                    'Note...', false, true, fullfile(path, 'genieSearch.png'));
            else
                pu=[];
            end
            this.priorFig=priorFig;
            [this.fig, tb_]=Gui.Figure;
            this.tb=tb_;
            figName=['QF dissimilarity  ' ...
                num2str(length(qf.tIds)) ' X ' num2str(length(qf.sIds))...
                ' subsets'];
            set(this.fig, 'CloseRequestFcn', @(h, e)hush(h), ...
                'Name', figName);
            [this.data, labels, fmts, tips,  this.unmatched, groupIdx, ...
                freqIdx, rankIdx, symIdx]=QfTable.Contents(qf, tClrs, pu);
            [this.R, C]=size(this.data);
            J=edu.stanford.facs.swing.Basics;
            [sData, widths]=SortTable.Convert(this.data, fmts);
            this.sortTable=SortTable(this.fig, sData, ...
                labels, [], @(h,e)select(h,e), tips);
            st=this.sortTable;
            jt=st.jtable;
            N=length(widths);
            for i=1:N
                if app.highDef
                    factor=app.toolBarFactor;
                else
                    factor=1;
                end
                st.setColumnWidth(i, widths(i)*factor)
                st.setColumnWidth(i, widths(i)*factor)
            end
            preSorted=SortTable.SetRowOrder(jt, ...
                BasicMap.GetNumbers(props, PROP_SORT));
            if ~preSorted
                jt.sortColumn(rankIdx-1, true, true);
                jt.sortColumn(groupIdx-1, false, true);
                jt.sortColumn(freqIdx-1, false, false);
            end
            startingRowOrder=SortTable.GetRowOrder(jt);
            if ~isempty(qf.matrixHtml)
                ToolBarMethods.addButton(tb_, ...
                    fullfile(path, 'world_16.png'), 'See table in default `r', ...
                    @(h,e)browse(h));
            end
            bb=ToolBarMethods.addButton(tb_,fullfile(path, 'table.gif'), ...
                'Restore default row and column order', ...
                @(h,e)defaultOrder());
            ToolBarMethods.addButton(tb_,fullfile(path, 'leftArrowNarrow.png'), ...
                'Shift sorted columns to the left', ...
                @(h,e)shiftSortLeft());

            ToolBarMethods.addButton(tb_, fullfile(path, 'histQF.png'), ...
                'Open histogram of QF scores', ...
                @(h,e)doHistQF(this));
            ToolBarMethods.addButton(tb_, fullfile(path, 'histF.png'), ...
                'Open histogram of F-measure scores', ...
                @(h,e)doHistF(this, true));
            savedOrder=BasicMap.GetNumbers(props, PROP_COL_ORD);
            if ~isempty(savedOrder)
                try
                    SortTable.SetColumnOrder(jt, savedOrder,...
                        BasicMap.GetNumbers(props, PROP_COL_W));
                catch
                end
            else
                defaultColumnOrder;
            end
            op=BasicMap.GetNumbers(props, QfTable.PROP_OUTER_POS2);
            if length(op)==4
                newFigPos=Gui.RepositionOnSameScreenIfRequired(op);
                set(this.fig, 'OuterPosition', newFigPos);
                Gui.FitFigToScreen(this.fig);
            elseif ~isempty(priorFig)
                Gui.SetToRight(this.priorFig, this.fig, false, 80);
            end
            if visible
                pu.close;
                Gui.SetFigVisible(this.fig);
            end
            if preSorted
                jt.unsort;
                SortTable.SetRowOrder(jt, ...
                    BasicMap.GetNumbers(props, PROP_SORT));
            elseif isempty(savedOrder)
                bb.doClick;
            end
            MatBasics.DoLater(@(h,e)heighten(), .31);
            this.qf.tClrs=this.tClrs;
            try
                if isempty(this.qf.falsePosNegs)
                    this.qf.getFalsePosNegRecords;
                end
                if ~isempty(this.qf.falsePosNegs)
                    try
                        this.qf.getFalsePosNegsTab(false);
                        this.qf.getFalsePosNegSubsets(true);
                    catch
                        %this.qf is a structure and not an instance
                        %so the 2 getFalse*() methods were called and
                        %results are stored in structure
                    end
                    ToolBarMethods.addButton(tb_, ...
                        fullfile(path, 'plusMinus.png'), ...
                        'See false positive/negative results', ...
                        @(h,e)browseFalsePosNeg(this));
                    fpn=this.qf.falsePosNegSubsetsFile;
                    if ~isempty(fpn) && exist(fpn, 'file')
                        ToolBarMethods.addButton(tb_, ...
                            fullfile(path, 'tree.png'), ...
                            'Import false +/- into AutoGate GatingTree', ...
                            @(h,e)importFalsePosNegGates(this));
                    end
                end
            catch ex
                if ~isstruct(this.qf)
                    ex.getReport
                else
                    this.qf.falsePosNegs=[];
                end
            end
            function heighten
                drawnow;
                height=jt.getRowHeight;
                jt.setRowHeight(floor(height*1.33));
                try
                    if props.multiProps.is('mds_figHistQF', false)
                        this.doHistQF(visible);
                    end
                    if props.multiProps.is('mds_figHistF', false)
                        this.doHistF;
                    end
                catch ex
                    %disp('Using QF table outside of CytoGenie''s AutoGate')
                    %ex.getReport
                end
            end

            function shiftSortLeft(h)
                if ~SortTable.MoveSortColumnsLeft(jt)
                    if ismac
                        key='command';
                    else
                        key='control';
                    end
                    msg(Html.Wrap(['There is no sort order currently.'...
                        '
To sort the table you
'...
                        '
Click on a column to sort it'...
                        '
Click again for descending order'...
                        '
Hold ' key ' key for multi column
']), ...
                        8, 'north', 'No sort order...');
                else
                    r=jt.getSelectedRow;
                    if r<0
                        r=0;
                    end
                    rect=jt.getCellRect(r,0, true);
                    jt.scrollRectToVisible(rect);
                end
            end

            function defaultOrder()
                defaultRowOrder;
                defaultColumnOrder;
            end

            function defaultRowOrder
                SortTable.SetRowOrder(jt, [(rankIdx-1) true 0; ...
                    (groupIdx-1) false 0; (freqIdx-1) false 1]);
            end

            function defaultColumnOrder()
                idxs=1:jt.getColumnCount;
                idxs(rankIdx)=[];
                idxs(symIdx)=[];
                idxs=[rankIdx symIdx idxs]-1;
                SortTable.SetColumnOrder(jt, idxs, [], true);
            end

            function browse(h)
                Html.Browse(Html.Wrap([SortTable.ToHtml(jt) '
' ...
                    Html.remove(qf.matrixHtml)]));
            end

            function hush(h)
                if ~isempty(priorFig) && ishandle(this.priorFig)
                    [columnOrder, widths]=SortTable.GetColumnOrder(jt);
                    props.set(PROP_COL_ORD, num2str(columnOrder));
                    props.set(PROP_COL_W, num2str(widths));
                    rowOrder=SortTable.GetRowOrder(jt);
                    if ~isequal(rowOrder, startingRowOrder)
                        props.set(PROP_SORT, MatBasics.Encode(...
                            rowOrder));
                    end
                    props.set(QfTable.PROP_OUTER_POS2, ...
                        num2str(get(h, 'OuterPosition')));
                    figure(this.priorFig);
                    Gui.CloseFigs(this.otherFigs);
                end
                delete(h);
            end

            function showSelected()
                selectedIdxs=SortTable.SelectedRows(jt);
                try
                    N_=length(selectedIdxs);
                    for ii=1:N_

                    end
                catch ex
                    ex.getReport
                end
            end
            function select(h, e)
                if ~isempty(e.Indices)
                    rowIdx=e.Indices(1,1)-1;
                    if rowIdx>=0
                        try
                            colIdx=jt.convertColumnIndexToModel(...
                                e.Indices(1,2)-1);
                            st.showTip(colIdx+1);
                            rowIdx=jt.getActualRowAt(...
                                jt.convertRowIndexToModel(rowIdx))+1;

                            showSelected;
                        catch ex
                            ex.getReport
                        end
                        return;
                    end
                end
                st.showTip(0);
            end
        end

        function [medianDissimilarity, meanDissimilarity, ...
                medianOverlap, meanOverlap]=getAverages(this)
            [~,medianDissimilarity, meanDissimilarity]=this.getData(true);
            [~,medianOverlap, meanOverlap]=this.getData(false);
        end

        function [qfData, mdn, mn]=getData(this, dissimilarity)
            if nargin<2 || dissimilarity
                idx=4;
            else
                idx=5;
            end
            qfData=cell2mat(this.data(:, idx))*100;
            qfData=qfData(~isnan(qfData));
            mdn=median(qfData);
            mn=mean(qfData);
        end

        function browseFalsePosNeg(this)
            if ~isempty(this.falsePosNegFig) ...
                    && ishandle(this.falsePosNegFig)
                figure(this.falsePosNegFig);
                return;
            end
            set(0, 'CurrentFigure', this.fig);
            [fig_,that]=FalsePositiveNegative.Plot([0 1], this.qf.falsePosNegFile);
            this.otherFigs{end+1}=fig_;
            figure(fig_);
            that.fcnImportGates=@()importFalsePosNegGates(this);
            that.fcnMoreHtml=@()getFalsePosNegMatrixHtml(this);
            Gui.Locate (fig_, this.fig, 'north east+');
            this.falsePosNegFig=fig_;
        end

        function html=getFalsePosNegMatrixHtml(this)
            html=FalsePositiveNegative.MatrixHtml(this.qf);
        end

        function importFalsePosNegGates(this)
            fpn=this.qf.falsePosNegSubsetsFile;
            if ~isempty(this.fcnFpnSubsets)
                feval(this.fcnFpnSubsets);
            else
                FalsePositiveNegative.ImportGates(fpn);
            end
        end

        function ok=doHistQF(this, visible)
            if nargin<2
                visible=true;
            end
            ok=true;
            if ishandle(this.qHistFig)
                figure(this.qHistFig);
            else
                fig_=Gui.NewFigure;
                if ~visible
                    set(fig_, 'visible', 'off');
                end
                this.qHistFig=fig_;
                Gui.Resize(fig_, .66);
                Gui.Locate(fig_, this.fig, 'south west+');
                ax=gca;
                [qfData, avgMdn, avgMn]=this.getData;
                if isempty(qfData)
                    ok=false;
                    return;
                end

                histogram(ax, qfData, length(unique(qfData)));
                set(ax, 'units', 'normalized', 'Position', ...
                    [.1 .16 .8 .7]);
                Gui.StretchUpper(ax, @ylim, .1);
                Gui.StretchUpper(ax, @xlim, .1);
                xlabel(ax, '% dissimilarity', 'FontName', 'Arial')
                xlim(ax, [-5 100]);
                ylabel(ax, '# of subset matches', 'FontName', 'Arial')
                figName=['Dissimilarity ' ...
                    num2str(length(this.qf.tIds)) ' X ' ...
                    num2str(length(this.qf.sIds))];
                this.addTitleToHist(fig_, ax, ...
                    figName, avgMdn, avgMn);
                this.addFalsePosNegButton(fig_);
                %disp(num2str(get(fig_, 'OuterPosition')));
                set(fig_, 'CloseRequestFcn', @(h, e)hush(h));
            end

            function hush(h)
                if ishandle(this.fig)
                    figure(this.fig);
                end
                delete(h);
            end
        end

        function addTitleToHist(this, fig, ax, score, avgMdn, avgMn)
            ttl=[score ', median/mean=\color{blue}' ...
                String.encodeRounded(avgMdn, 1) ...
                '\color{black}/\color{blue}'...
                String.encodeRounded(avgMn, 1) '\color{black}% '];
            set(fig, 'name', String.RemoveTex(score), 'NumberTitle', 'off');
            if this.unmatched>0
                title(ax, {ttl, [ ...
                    num2str(this.R-this.unmatched) ' subsets matched, ' ...
                    '\color{red}' num2str(this.unmatched) ...
                    ' \color{black}NOT matched ']},'FontName', 'Arial');
            else
                title(ax, {ttl, [ ...
                    num2str(this.R) ' matches']}, ...
                    'FontName', 'Arial');
            end
        end

        function doHistF(this, visible)
            if nargin<3
                showFalsePosNeg=false;
                if nargin<2
                    visible=true;
                end
            end
            if ishandle(this.fHistFig)
                figure(this.fHistFig);
            else
                fig_=Gui.NewFigure;
                if ~visible
                    set(fig_, 'visible', 'off');
                end
                this.fHistFig=fig_;
                Gui.Resize(fig_, .66);
                Gui.Locate(fig_, this.fig, 'south east+');
                ax=axes('parent', fig_);
                [fData, avgMdn, avgMn]=this.getData(false);
                histogram(ax, fData, length(unique(fData)));

                xlabel(ax, '% overlap', 'FontName', 'Arial')
                xlim(ax, [-5 100]);
                ylabel(ax, '# of subset matches', 'FontName', 'Arial')
                set(ax, 'xlim', [0 100]);
                this.addTitleToHist(fig_, ax, ...
                    'Overlap ^{(F-measure)}', avgMdn, avgMn);
                set(ax, 'units', 'normalized', 'Position', ...
                    [.1 .16 .8 .7]);
                Gui.StretchUpper(ax, @ylim, .1);
                lbl='See false +/-';
                width=.2;
                this.addFalsePosNegButton(fig_);
                set(fig_, 'CloseRequestFcn', @(h, e)hush(h));
            end

            function hush(h)
                if ishandle(this.fig)
                    figure(this.fig);
                end
                delete(h);
            end
        end

        function addFalsePosNegButton(this, fig_, lbl, width)
            if nargin<4
                width=.2;
                if nargin<3
                    lbl='See false +/-';
                end
            end
            if isempty(this.qf.falsePosNegs)
                return;
            end
            uicontrol(fig_, 'style', 'pushbutton','String', lbl,...
                'Units', 'normalized', ...
                'FontWeight', 'bold', ...
                'ForegroundColor', 'blue',...
                'BackgroundColor', [1 1 .80],...
                'Position',[1-(width+.01), .015, width, .071],...
                'Callback', @(btn,event) browseFalsePosNeg(this));
        end

        function QF=save(this, qf, file)
            QF.tIds=qf.tIds;
            QF.sIds=qf.sIds;
            %QF.matches=qf.matches;
            %QF.getStudNames=qf.getStudNames();
            QF.tNames=qf.tNames;
            QF.tSizes=qf.tSizes;
            QF.sSizes=qf.sSizes;
            QF.matrixHtml=qf.matrixHtml;
            QF.qfTableData=this.data;
            QF.unmatched=this.unmatched;
            QF.tClrs=this.tClrs;
            QF.falsePosNegs=qf.falsePosNegs;
            qf.relocateFalsePosNegFiles(file);
            QF.falsePosNegFile=qf.falsePosNegFile;
            QF.falsePosNegSubsetsFile=qf.falsePosNegSubsetsFile;
            QF.falsePosNegCnts=qf.falsePosNegCnts;
            QF.falsePosCulprits=qf.falsePosCulprits;
            QF.falseNegCulprits=qf.falseNegCulprits;
            QF.sNames=qf.sNames;
            if nargin>2 && ~isempty(file)
                save(file, 'QF');
            end
        end

        function addSuffixToFigs(this, suffix)
            renameFig(this.fig);
            renameFig(this.qHistFig);
            renameFig(this.fHistFig);
            function renameFig(fig)
                if ~isempty(fig)
                    set(fig, 'name', [ get(fig, 'name') ' ' suffix])
                end
            end
        end

        function closeFigs(this)
            Gui.CloseFig(this.fig);
            Gui.CloseFig(this.qHistFig);
            Gui.CloseFig(this.fHistFig);
        end

        function [tUnmatched, tN, sUnmatched, sN]=getMatchCounts(this)
            [tUnmatched, tN, sUnmatched, sN]=this.qf.getMatchCounts;
        end
    end

    methods(Static)

        function QF=Load(file)
            load(file, 'QF');
        end

        function [data, names, widths, tips, unmatched, plotIdx, freqIdx,...
                rankIdx, symIdx]=Contents(qf, clrs, pu)
            unmatched=0;
            plotIdx=6;
            freqIdx=7;
            widths=[3 0; 17 nan; 5 0; 3 -2; 3 -2; ...
                    6 nan; 3 -1;  10 0; 5 nan; 8 0];
            names={'#', ...
                'Name', ...
                Html.WrapSm('Mat-
ches '), ...
                Html.WrapSm('QF dis-
similaritry'), ...
                Html.WrapSm('Overlap
F-measure'),...
                Html.WrapSm('
Trainer'), ...
                Html.WrapSm('
Freq.'), ...
                Html.WrapSm('# of
events'), ...
                '', ...
                Html.WrapSm('QF
Rank')};
            rankIdx=length(names);
            symIdx=rankIdx-1;
            tips={...
                'The subset''s item # ',...
                'The name of the subset including match name if applicable`',...
                'The number of subsets in this match set',...
                'The lower the % the more similar the match',...
                'The higher the % the more overlap (similarity) of cells (or bins)',...
                'yes for training set, no for test set ',...
                'The frequency of the subset within this selection of cells/events',...
                'The # of cells/events for the subset ',...
                'The subset''s frequency-sized symbol',...
                'The ranking of the match set'};
            try
                [data, unmatched]=qf.getTableData(clrs);
            catch ex
                if isstruct(qf)
                    data=qf.qfTableData;
                    unmatched=qf.unmatched;
                else
                    data=[];
                    unmatched=0;
                    ex.getReport
                end
            end
            if exist('pu', 'var') && ~isempty(pu)
                if ~isempty(pu.pb)
                    pu.pb.setValue(N);
                end
            end
        end

        function qft=RunWithGt(supervisors, gt, gid, fcs, fcsIdxs, ...
                data, file, embedding, visible, pu)
            qft=[];
            pFig=get(0, 'currentFig');
            if exist(file, 'file') && ~QfTable.DEBUG
                qf=QfTable.Load(file);
                qft=QfTable(qf, qf.tClrs, [], pFig, visible);
                qft.doHistQF(visible);
                if ~isempty(qft.qf.falsePosNegs)
                    qft.doHistF(visible);
                end
                return;
            end
            if isempty(fcsIdxs)
                return;
            end
            newPu=nargin<10||isempty(pu);
            if newPu
                path=BasicMap.Path;
                pu=PopUp('Generating QF dissimilarity table', 'north', ...
                    'Note...', false, true, fullfile(path, 'genieSearch.png'));
                pu.setTimeSpentTic(tic);
            else
                pu.setText2('Generating QF dissimilarity table');
            end
            [gtData, ~, ~, gtIds]=QfHiDM.GetRequiredData2(fcs, fcsIdxs, ...
                gt, gid, pu, visible);
            if isempty(gtData)
                return;
            end
            if isequal(gtData, data)
                matchStrategy=2;
            else
                matchStrategy=1;
            end
            gids=MatBasics.GetUniqueIdsAndCntIfGt0(gtIds);
            hghl=gt.highlighter;
            N=length(gids);
            gtNames=cell(1,N);
            for i=1:N
                id=num2str(gids(i));
                gtNames{i}=gt.tp.getDescription(id);
            end
            [sNames, sLbls, clrs]=supervisors.getQfTrained(embedding);

            qf=run_HiD_match(gtData, gtIds, data, sLbls, ...
                'trainingNames', gtNames, ...
                'testNames', sNames, ...
                'matchStrategy', matchStrategy, 'log10', true, 'pu', pu);
            [~,~,tIdxForS, tIdForS]=qf.getMatches;
            gtClrs=zeros(N, 3);
            for i=1:N
                clrIdx=find(tIdxForS==i, 1, 'first');
                if clrIdx>0
                    gid=tIdForS(clrIdx);
                    gtClrs(i,:)=clrs(clrIdx,:);
                else
                    gid=gids(i);
                    gtClrs(i,:)=gt.highlighter.getColor(num2str(gid));
                end
            end
            if ~isempty(qf)
                qft=QfTable(qf, gtClrs, [], pFig, visible);
                qft.doHistQF(visible);
                if matchStrategy==2
                    qft.doHistF(visible);
                end
                if ~isempty(file) && ~exist(file, 'file')
                    qft.save(qf, file);
                end
            end
            if newPu
                pu.close;
            end
        end

        function qft=Run(fcs, fcsIdxs, gt, gid1, gid2, file, visible, pu)
            pFig=get(0, 'currentFig');
            qft=[];
            if exist(file, 'file')
                qf=QfTable.Load(file);
                qft=QfTable(qf, qf.tClrs, [], pFig, visible);
                qft.doHistQF(visible);
                return;
            end
            if isempty(fcsIdxs)
                return;
            end
            newPu=nargin<8|| isempty(pu);
            if newPu
                path=BasicMap.Path;
                pu=PopUp('Generating QF dissimilarity table', 'north', ...
                    'Note...', false, true, fullfile(path, 'genieSearch.png'));
                pu.setTimeSpentTic(tic);
            else
                pu.setText2('Generating QF dissimilarity table');
            end
            [data1, ids1, names1, clrs1]=go(gid1);
            if isempty(data1)
                return;
            end
            [data2, ids2, names2, clrs2]=go(gid2);
            if isempty(data2)
                return;
            end
            matchStrategy=1;
            qf=run_HiD_match(data1, ids1, data2, ids2, ...
                'trainingNames', names1, 'testNames', names2, ...
                'matchStrategy', matchStrategy, 'log10', true, ...
                'pu', pu);
            if ~isempty(qf)
                qft=QfTable(qf, clrs1, [], pFig, visible);
                qft.doHistQF(visible);
                if ~isempty(file) && ~exist(file, 'file')
                    qft.save(qf, file);
                end
            end
            if newPu
                pu.close;
            end

            function [gtData, gtIds, names, clrs]=go(gid)
                [gtData, ~, ~, gtIds]=QfHiDM.GetRequiredData2(fcs, ...
                    fcsIdxs, gt, gid, pu, visible);
                if isempty(gtData)
                    return;
                end
                gids=MatBasics.GetUniqueIdsAndCntIfGt0(gtIds);
                hghl=gt.highlighter;
                N=length(gids);
                clrs=zeros(N, 3);
                names=cell(1,N);
                for i=1:N
                    id=num2str(gids(i));
                    names{i}=gt.tp.getDescription(id);
                    clrs(i,:)=hghl.getColor(id, Gui.HslColor(i, N));
                end
            end
        end
    end
end


function bar_plot( res, column, tasks, dets, varargin)

opts.legend = true;
opts.legendArgs = {'Location', 'SouthOutside', 'Interpreter', 'none', 'Orientation', 'Horizontal'};
opts.sort = true;
opts.detnames = {dets.name};
opts.multiplier = 100;
opts = vl_argparse(opts, varargin);

if isfield(dets, 'normname')
  normnames = {dets.normname};
else
  normnames = [];
end
data = rproc.pick(res, tasks, {dets.name}, column, 'normname', normnames);
data = data .* opts.multiplier;
avg = mean(data, 1);
if opts.sort
  [avg, order] = sort(avg, 'ascend');
  dets = dets(order);
  data = data(:, order);
  opts.detnames = opts.detnames(order);
end

for ni = numel(dets):-1:1
  position = ni;
  barh(position, avg(ni), 'FaceColor', dets(ni).color, dets(ni).bararg{:});
  hold on;

  text(101, position, sprintf('%0.2f%%', avg(ni)),...
    'HorizontalAlignment', 'left',...
    'VerticalAlignment', 'middle');

  li = zeros(1, numel(tasks));
  for ti = 1:numel(tasks)
    li(ti) = plot(data(ti, ni), position, '.', tasks(ti).style{:});
  end
end
set(gca,'Ydir','reverse')
set(gca, 'YTick', 1:numel(dets));
set(gca, 'TickLabelInterpreter', 'none');
set(gca, 'YTickLabel', opts.detnames);

if opts.legend
  legend(li, tasks, opts.legendArgs{:});
end
axis tight;
set(gca, 'XLim', [0, 100]);
grid on;
end


function kern = velotransKernParamInit(kern)

% VELOTRANSKERNPARAMINIT VELOTRANS kernel parameter initialisation.
% This kernel allows you to take any other kernel in the tool box and
% translate with respect to the input space with respect to time. The translation allows a moving field such as clouds moving over a land surface.
%
% SEEALSO : cmpndKernCreate
%
% FORMAT
% DESC initialises the velocity translate
%  kernel structure with some default parameters.
% ARG kern : the kernel structure which requires initialisation.
% RETURN kern : the kernel structure with the default parameters placed in.
%
% SEEALSO : kernCreate, kernParamInit
%
% COPYRIGHT : Neil D. Lawrence, 2011

% KERN

kern = cmpndKernParamInit(kern);

% Add in the translation parameters
kern.nParams = kern.nParams + kern.inputDimension-1;
kern.velocity = zeros(1, kern.inputDimension-1);


function varargout = plotsparsemarkers(varargin)
%
%  [h =] plotsparsemarkers(hp, hl, markerStyle[, numberOfMarkers = 6 [,staggered = true]])
%
%  Adds only a limited number of markers to a set of lines given by
%  the handle array hp. If hl is not [] it should be the handle of
%  the legend corresponding to the lines.
%
%  markerStyle is a cell array of styles, e.g. {'o', 'x'} describing
%  the markers for the lines in hp, the length of the cell array has
%  to be the same as hp (or shorter in which case not all lines will get
%  markers).
%
%  numberOfMarkers is an optional parameter, default = 6, that sets
%  the number of markers on each line, this number must be equal or less
%  than the number of points plotted for each line.
%
%  staggered is a boolean that tries to stagger the markers so that the
%  are not at the same x value for all lines. This defaults to true and
%  is intended to avoid markers overwriting each other if the x-values
%  are identical for the lines.
%

%%
% Check input parameters
if nargin <3
    disp('[sparsemarker]: wrong number of arguments')
    help sparsemarker
    return
end

hp = varargin{1};
hl = varargin{2};
markStyle = varargin{3};

if nargin >3
    numberOfMarkers = varargin{4};
else
    numberOfMarkers = 6;
end

if nargin >4
    staggered = varargin{5};
else
    staggered = true;
end

%%
% Define marker lines
nrLines =    length(markStyle);

%Create stagger distance
if staggered
    x  = 1:nrLines;
    stag = -1.^(mod(x,2)+1).*x./nrLines./2;
else
    stag = zeros(1,nrLines);
end

for ii = 1:nrLines
    % Determine number of points in line ii
    X = get(hp(ii),'XDATA');
    Y = get(hp(ii),'YDATA');
    interval = (X(end)-X(1))./(numberOfMarkers+2);
    targetx  = stag(ii)*interval + ...  %Stagger offset
                  linspace(X(1)+interval,X(end)-interval,...
                           numberOfMarkers);
    for jj = 1:numberOfMarkers
       [~,ind] = min(abs(targetx(jj)-X));
       mx(ii,jj) = X(ind);
       my(ii,jj) = Y(ind);
    end
end

whos mx my

%%
% plot marker lines
hpp = get(hp(ii),'Parent');
set(hpp,'LineStyleOrder',markStyle); %This is a bad solution
                                     %that works
hold on
h = plot(mx',my');
hold off
%Change markers to same color as the line
for ii = 1:nrLines
    set(h(ii),'MarkerEdgeColor',get(hp(ii),'Color'));
    set(h(ii),'Marker',markStyle{ii});
    set(h(ii),'MarkerSize',12);  %These are sizes I like
    set(h(ii),'LineWidth',2);    %you can change them using
end                              %the handle that is returned

%%
% update legend
if ~isempty(hl)
    hlc = get(hl,'Children');
    for ii = 1:nrLines
        jj = (nrLines-ii)*3+1;
        set(hlc(jj),'Marker',markStyle{ii});
        set(hlc(jj),'Marker',markStyle{ii});
        set(hlc(jj),'MarkerEdgeColor',get(hp(ii),'Color'));
        set(hlc(jj),'MarkerSize',12);
    end
end

%%
% Return handle to markers
if nargout > 0
    varargout{1} = h;
end


function exact = p04_exact ( )

%*****************************************************************************80
%
%% P04_EXACT returns the estimated integral for problem 4.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    04 November 2009
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Output, real EXACT, the estimated value of the integral.
%
  exact = 0.47942822668880166736;

  return
end


function circle()
    %   This subroutine "circle"  selects the Ni closest earthquakes
    %   around a interactively selected point.  Resets ZG.newcat and ZG.newt2
    %   Operates on "primeCatalog".
    %
    % axis: h1
    % plots to: plos1 as xk
    % inCatalog: a
    % outCatalog: newt2, newcat
    % mouse controlled
    % closest events
    %
    % turned into function by Celso G Reyes 2017
    %
%
%
    ZG=ZmapGlobal.Data; % used by get_zmap_globals
    report_this_filefun();

    ShapeGeneral.clearplot(); % was tag plos1

    % interactively get the circle of interest
    shape=ShapeCircle();

    [ZG.newt2, max_km] = selectCircle(newa, shape.toStruct());

    fprintf('Radius of selected Circle: %s km\n', num2str(max_km) );

    % plot Ni clostest events on map as 'x':

    set(gca,'NextPlot','add')
    plot(ZG.newt2.Longitude,ZG.newt2.Latitude,'xk','Tag','plos1');

    %
    ZG.newcat = ZG.newt2;                   % resets ZG.newcat and ZG.newt2

    timeplot(2)

end


function f = fgplvmSequenceObjective(xvec, model, Y, varargin)

% FGPLVMSEQUENCEOBJECTIVE Wrapper function for objective of a single sequence in latent space and the corresponding output sequence.
% FORMAT
% DESC provides a wrapper function for the negative log probability
% of a given data sequence under the posterior distribution of the
% Gaussian process induced by the training data..
% ARG vecx : time ordered locations in input space for the sequence
% placed as a vector using the matlab X(:)' notation.
% ARG model : the model structure for which the negative log
% probability of the given data under the posterior is to be computed.
% ARG Y : time ordered locations in data spaces for the sequence.
% ARG P1, P2, P3 ... : optional additional arguments to be passed
% to the model sequence log likelihood.
% RETURN f : the negative of the log probability of the given data
% sequence under the posterior distribution induced by the training data.
%
% SEEALSO : fgplvmCreate, fgplvmSequenceLogLikelihood, fgplvmOptimiseSequence
%
% COPYRIGHT : Neil D. Lawrence, 2006

% FGPLVM

X = reshape(xvec, size(Y, 1), model.q);
f = - fgplvmSequenceLogLikelihood(model, X, Y, varargin{:});


function [g1, g2] = simwhiteXrbfwhiteKernGradient(simKern, rbfKern, t1, varargin)

% SIMWHITEXRBFWHITEKERNGRADIENT Compute a cross gradient between a SIM-WHITE
% and an RBF-WHITE kernels.
% FORMAT
% DESC computes cross gradient of parameters of a cross kernel between a
% SIM-WHITE and an RBF-WHITE kernels for the multiple output kernel.
% ARG simKern : the kernel structure associated with the SIM-WHITE kernel.
% ARG rbfKern : the kernel structure associated with the RBF-WHITE kernel.
% ARG t1 : inputs for which kernel is to be computed.
% ARG covGrad : gradient of the objective function with respect to
% the elements of the cross kernel matrix.
% RETURN g1 : gradient of the parameters of the SIM-WHITE kernel, for
% ordering see simwhiteKernExtractParam.
% RETURN g2 : gradient of the parameters of the RBF-WHITE kernel, for
% ordering see rbfwhiteKernExtractParam.
%
% FORMAT
% DESC computes cross kernel terms between a SIM-WHITE and an RBF-WHITE
% kernels for the multiple output kernel.
% ARG simKern : the kernel structure associated with the SIM-WHITE kernel.
% ARG rbfKern : the kernel structure associated with the RBF-WHITE kernel.
% ARG t1 : row inputs for which kernel is to be computed.
% ARG t2 : column inputs for which kernel is to be computed.
% ARG covGrad : gradient of the objective function with respect to
% the elements of the cross kernel matrix.
% RETURN g1 : gradient of the parameters of the SIM-WHITE kernel, for
% ordering see simwhiteKernExtractParam.
% RETURN g2 : gradient of the parameters of the RBF-WHITE kernel, for
% ordering see rbfwhiteKernExtractParam.
%
% SEEALSO : multiKernParamInit, multiKernCompute, simwhiteKernParamInit,
% rbfwhiteKernParamInit, simwhiteKernExtractParam, rbfwhiteKernExtractParam
%
% COPYRIGHT : David Luengo, 2009

% KERN

if nargin < 5
    t2 = t1;
else
    t2 = varargin{1};
end
covGrad = varargin{end};

if size(t1, 2) > 1 | size(t2, 2) > 1
  error('Input can only have one column');
end
if simKern.variance ~= rbfKern.variance
  error('Kernels cannot be cross combined if they have different variances.')
end
if simKern.isStationary ~= rbfKern.isStationary
  error('Stationary and non-stationary kernels cannot be cross combined.')
end

g1 = zeros(1, simKern.nParams);
g2 = zeros(1, rbfKern.nParams);

T1 = repmat(t1, 1, size(t2, 1));
T2 = repmat(t2.', size(t1, 1), 1);
deltaT = T1 - T2;
indT = double(deltaT >= 0);

% Parameters required for further computations
isStationary = simKern.isStationary;
variance = simKern.variance;
decay = simKern.decay;
sensitivity = simKern.sensitivity;
invWidth = rbfKern.inverseWidth;

% Computing a normalised (i.e. variance = 1 and sensitivity = 1) kernel
simKern.variance = 1;
simKern.sensitivity = 1;
rbfKern.variance = 1;
K = simwhiteXrbfwhiteKernCompute(simKern, rbfKern, t1, t2);

% Gradient w.r.t. the decay (simKern)
g1(1) = variance * sensitivity * sum(sum( ((-deltaT+decay/invWidth) .* K  ...
    + 1/sqrt(2*pi*invWidth) ...
        * exp(-decay*deltaT + 0.5*(decay^2)/invWidth) ...
        .* (exp(-0.5*invWidth*((T2+decay/invWidth).^2)) ...
        - exp(-0.5*invWidth*((abs(deltaT).*(1-indT)+decay/invWidth).^2)))) ...
    .* covGrad));

% Gradient w.r.t. the inverse width (rbfKern)
g2(1) = variance * sensitivity * sum(sum( (-0.5*((decay/invWidth)^2) .* K  ...
    + 1/sqrt(8*pi*invWidth) ...
        * exp(-decay*deltaT + 0.5*(decay^2)/invWidth) ...
        .* ((T2-decay/invWidth).*exp(-0.5*invWidth*((T2+decay/invWidth).^2)) ...
        - (abs(deltaT).*(1-indT)-decay/invWidth) ...
            .* exp(-0.5*invWidth*((abs(deltaT).*(1-indT)+decay/invWidth).^2)))) ...
    .* covGrad));

% Gradient w.r.t. sigma_r^2
g1(2) = sensitivity * sum(sum(K .* covGrad));
g2(2) = 0; % Otherwise it is counted twice

% Gradient w.r.t. sensitivity (only simKern)
g1(3) = variance * sum(sum(K .* covGrad));


classdef AnalysisBvalues < AnalysisWindow
    % ANALYSISBVALUES shows b-value plot (FMD)
    properties
        bobj; % points to an existing bobj (bdiff2 object)
    end

    methods
        function obj = AnalysisBvalues(ax, other_bobj)
            obj@AnalysisWindow(ax);
            obj.nMarkers = 2;
            if exist('other_bobj','var')
                obj.bobj = other_bobj;
            end
        end

        function prepare_axes(obj)
            % label and scale the axes
            obj.ax.Tag          = 'dvBval';
            obj.ax.YScale       = 'log';
            obj.ax.YLim         = [1 10000];
            obj.ax.XLim         = [-inf inf];
            obj.ax.Title.String = 'B-Value';
            obj.ax.XLabel.String= 'Magnitude';
            obj.ax.YLabel.String= '# Events';
        end

        function h=add_series(obj, catalog, tagID, varargin)
            % obj.ADD_SERIES(catalog, tagID, [[Name, Value],...])
            % fit line
            xlm = obj.ax.XLimMode;
            xl = obj.ax.XLim;

            p = inputParser();
            p.addRequired('tagID', @(x)ischarlike(tagID));
            p.addParameter('Ypos', 0.6);
            p.KeepUnmatched = true;
            p.parse(tagID, varargin{:});

            countProps = p.Unmatched;
            countProps.LineStyle = 'none';
            countProps.MarkerIndices = 'all';
            countProps.LineWidth = 1;
            obj.ax.XLimMode = 'auto';

            h = add_series@AnalysisWindow(obj, catalog, tagID, countProps);

            obj.ax.XLimMode = 'Manual';

            seriesText = obj.bobj.descriptive_text([]);
            textHandle = findobj('Type', 'text', '-and', '-regexp', 'Tag', [tagID,' txt']);
            xStart = range(xlim(obj.ax)) * 0.6 + min(xlim(obj.ax));
            if ~isempty(textHandle)
                textHandle.String = seriesText;
                textHandle.Position(1) = xStart;
            else
                logLim = log10(ylim(obj.ax));
                myLogY = p.Results.Ypos * range(logLim) + min(logLim);
                if ~isfield(p.Unmatched,'Color')
                    p.Unmatched.Color = [.2 .2 .2];
                end
                text(obj.ax, xStart, 10^myLogY, seriesText, 'Color', p.Unmatched.Color .* 0.75,...
                    'Tag', [tagID,' txt'], 'EdgeColor', 'none');
            end

            % maybe each other add_series should contribute to h, too.

            % magnitude of completeness point
            %McProps = p.Unmatched;
            %McProps.DisplayName = '';
            %if ~isfield(McProps,'Color')
            %    McProps.MarkerFaceColor = get(findobj(obj.ax,'Tag',tagID),'Color');
            %else
            %    McProps.MarkerFaceColor = McProps.Color;
            %end
            %add_series@AnalysisWindow(obj, catalog, [tagID ' Mc'], 'UseCalculation',@obj.getMc, McProps);

            % linear fit
            lineProps = p.Unmatched;
            lineProps.LineWidth         = 2;
            lineProps.MarkerFaceColor   = 'auto';
            lineProps.DisplayName       = '';
            lineProps.MarkerIndices     = 2;
            lineProps.MarkerSize        = 10;
            add_series@AnalysisWindow(obj, catalog, [tagID, ' line'], 'UseCalculation', @obj.getBvalLine, lineProps);
            obj.ax.XLim     = xl;
            obj.ax.XLimMode = xlm;
        end

        function [x,y] = calculate(obj,catalog)
            % Cumulative events by magnitude
            if isempty(catalog)
                x = nan;
                y = nan;
            else
                if isa(catalog,'ZmapCatalog')
                    obj.bobj.RawCatalog = catalog;
                else
                    obj.bobj.RawCatalog = catalog.Catalog;
                end
                obj.bobj.Calculate();
                x = obj.bobj.mag_bin_centers;
                y = obj.bobj.cum_b_values;
            end
        end

        function [x,y] = getMc(obj,~)
            % Determine magnitude of completion. X is magnitude, y is approximately # of events below MC
            if isempty(obj.bobj)
                y = nan;
                x = nan;
            else
                x = obj.bobj.Result.Mc_value;
                y = obj.bobj.cum_b_values(obj.bobj.Result.index_low) * 1.5;
            end
        end

        function [x,y] = getBvalLine(obj,~)
            % Show the B-value trend line
            if isempty(obj.bobj) || isempty(obj.bobj.Result.mag_zone)
                x = [nan nan];
                y = [nan nan];
            else
                x = obj.bobj.Result.mag_zone([1 end]);
                y = obj.bobj.fitted([1 end]);
            end
        end

        function remove_series(obj,tagID)
            % remove  B-value graphical objects associated with this tag
            if iscell(tagID) || isstring(tagID)
                for j=1:numel(tagID)
                    obj.remove_series(tagID{j});
                end
            else
                remove_series@AnalysisWindow(obj,tagID);
                % remove_series@AnalysisWindow(obj,[tagID ' Mc']);
                remove_series@AnalysisWindow(obj,[tagID ' line']);
                remove_series@AnalysisWindow(obj,[tagID ' txt']);
            end
        end
    end
end


%CODEGENERATOR.GENGRAVLOAD Generate code for gravitational load
%
%  G = cGen.gengravload() is a symbolic vector (1xN) of joint load
%  forces/torques due to gravity.
%
% Notes::
% - Side effects of execution depends on the cGen flags:
%   - saveresult: the symbolic expressions are saved to
%     disk in the directory specified by cGen.sympath
%   - genmfun: ready to use m-functions are generated and
%     provided via a subclass of SerialLink stored in cGen.robjpath
%   - genslblock: a Simulink block is generated and stored in a
%     robot specific block library cGen.slib in the directory
%     cGen.basepath
%   - genccode: generates C-functions and -headers in the directory
%     specified by the ccodepath property of the CodeGenerator object.
%   - mex: generates robot specific MEX-functions as replacement for the
%     m-functions mentioned above. Access is provided by the SerialLink
%     subclass. The MEX files rely on the C code generated before.
%
% Author::
%  Joern Malzahn, (joern.malzahn@tu-dortmund.de)
%
% See also CodeGenerator, CodeGenerator.geninvdyn, CodeGenerator.genfdyn.

% Copyright (C) 2012-2014, by Joern Malzahn
%
% This file is part of The Robotics Toolbox for Matlab (RTB).
%
% RTB is free software: you can redistribute it and/or modify
% it under the terms of the GNU Lesser General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% (at your option) any later version.
%
% RTB is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
% GNU Lesser General Public License for more details.
%
% You should have received a copy of the GNU Leser General Public License
% along with RTB.  If not, see <http://www.gnu.org/licenses/>.
%
% http://www.petercorke.com
%
% The code generation module emerged during the work on a project funded by
% the German Research Foundation (DFG, BE1569/7-1). The authors gratefully
% acknowledge the financial support.

function [G] = gengravload(CGen)

%% Derivation of symbolic expressions
CGen.logmsg([datestr(now),'\tDeriving gravitational load vector']);

q = CGen.rob.gencoords;
tmpRob = CGen.rob.nofriction;
G = tmpRob.gravload(q);

CGen.logmsg('\t%s\n',' done!');

%% Save symbolic expressions
if CGen.saveresult
    CGen.logmsg([datestr(now),'\tSaving symbolic expression for gravitational load']);

    CGen.savesym(G,'gravload','gravload.mat');

    CGen.logmsg('\t%s\n',' done!');
end

% M-Functions
if CGen.genmfun
    CGen.genmfungravload;
end

% Embedded Matlab Function Simulink blocks
if CGen.genslblock
    CGen.genslblockgravload;
end

%% C-Code
if CGen.genccode
    CGen.genccodegravload;
end

%% MEX
if CGen.genmex
    CGen.genmexgravload;
end

end


function F = in_fread_avg(sFile, sfid, SamplesBounds)
% IN_FREAD_AVG:  Read an epoch from a Neuroscan .avg file (averaged file).
%
% USAGE:  F = in_fread_eeg(sFile, sfid, SamplesBounds) : Read selected times
%         F = in_fread_eeg(sFile, sfid)                : Read all file

% @=============================================================================
% 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.
% =============================================================================@
%
% Author: Francois Tadel, 2009-2011

%% ===== PARSE INPUTS =====
if (nargin < 3)
    SamplesBounds = [];
end
nTime = sFile.header.data.pnts;
nChannels = sFile.header.data.nchannels;

%% ===== READ DATA =====
% Initialize data structure
F = zeros(nChannels, nTime);
% Position at the beginning of the data block
fseek(sfid, double(sFile.header.data.datapos), 'bof');
% Read averaged data [nChannels x nTime]
for i = 1:nChannels
    % Read the channel data
    unused_header = fread(sfid, 5, 'char');
    F(i,:) = fread(sfid, nTime, 'float')';
end
% Keep only specific time indices
if ~isempty(SamplesBounds)
    iTimes = (SamplesBounds(1):SamplesBounds(2)) - round(sFile.prop.times(1) .* sFile.prop.sfreq) + 1;
    F = F(:,iTimes);
end
% Calibrate data
F = neuroscan_apply_calibration(F, sFile.header);


function Y = sin(X)
    % Symbolic sine function.

    % Convert inputs to SymExpression
    % X = SymExpression(X);

    % construct the operation string
    sstr = ['Sin[' X.s ']'];

    % create a new object with the evaluated string
    Y = SymExpression(sstr);
end


filename = 'bridge_10_2';
ptype = 'MACRO';
method = 'SIMP_P3';
materialType = 'ISOTROPIC';
initial_case = 'holes';
cost = {'volume'};
weights = [1];
constraint = {'complianceConstraintC2','complianceConstraintC2','complianceConstraintC2'};
constraint_case = {'INEQUALITY','INEQUALITY','INEQUALITY'};
optimizerUnconstrained = 'SLERP';%'PROJECTED GRADIENT';
optimizer = 'AlternatingPrimalDual';%'DualNestedInPrimal';'DualNestedInPrimal';%'AlternatingPrimalDual';%'AlternatingPrimalDual';
incrementFactor = 1;
designVariable = 'LevelSet';%'Density';
filterType = 'P1';

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

Vfrac_initial = 1;
optimality_initial = 1e-3;
constr_initial = 1e-3;
Perimeter_target = 5;

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

% For all tests
plotting = true;
printing = true;
printing_physics = false;
monitoring = true;
monitoring_interval = 1;
maxiter = 3e4;


function [dat] = bz_scrubTracking(dat)
% USAGE
%  [dat] = bz_scrubTracking(dat)
%
% INPUTS
%   dat - struct created by calling dat = importdata(file.csv) on a csv
%         file exported from a .tak file
%
% OUTPUTS
%   dat - the same struct 1) outliers removed
%                         2) median filtering
%                         3) interpolation over missing frames
%
%
%
% this functions takes the unedited *.csv exported from Motive, smoothes and
% resorts columns to match the position tracking formatting standards
%
% David Tingley, 2017
%
%TODO
% check that units are in meters...

% find extreme outliers outside of tracking volume
dat.data((abs(dat.data(:,7))>mean(dat.data(:,7))+std(dat.data(:,7))*5),7)=nan;
dat.data((abs(dat.data(:,8))>mean(dat.data(:,8))+std(dat.data(:,8))*5),8)=nan;
dat.data((abs(dat.data(:,9))>mean(dat.data(:,9))+std(dat.data(:,9))*5),9)=nan;

% find poorly tracked frames using the error per marker column
dat.data(find(dat.data(:,10)>mean(dat.data(:,10))+std(dat.data(:,10))*10),:)=nan;

% find dropped frames or large translational shifts and call them nan's here
f = find(diff(dat.data(:,8))==0 | abs(diff(dat.data(:,8)))> mean(std(abs(diff(dat.data(:,8)))))+ std(abs(diff(dat.data(:,8)))) * 10);
dat.data(f+1,8)=nan;
f = find(diff(dat.data(:,9))==0 | abs(diff(dat.data(:,9)))> mean(std(abs(diff(dat.data(:,9)))))+std(abs(diff(dat.data(:,9)))) * 10);
dat.data(f+1,9)=nan;
f = find(diff(dat.data(:,7))==0 | abs(diff(dat.data(:,7)))> mean(std(abs(diff(dat.data(:,7)))))+std(abs(diff(dat.data(:,7)))) * 10);
dat.data(f+1,7)=nan;

% median filtering
dat.data(:,7) = medfilt1(dat.data(:,7),12);
dat.data(:,8) = medfilt1(dat.data(:,8),12);
dat.data(:,9) = medfilt1(dat.data(:,9),12);

% pattern matching interpolation
dat.data(:,7) = fillmissing(dat.data(:,7),'pchip');
dat.data(:,8) = fillmissing(dat.data(:,8),'pchip');
dat.data(:,9) = fillmissing(dat.data(:,9),'pchip');

%  now change column order to new standards
% columns = [7 9 8 3 5 4 6 10 1 2]; % optitrack is Y-oriented instead of Z
% dat.data = dat.data(:,columns);

end


% Copyright 2017 Lime Microsystems Ltd.
%
% Licensed under the Apache License, Version 2.0 (the "License");
% you may not use this file except in compliance with the License.
% You may obtain a copy of the License at
%
%    http://www.apache.org/licenses/LICENSE-2.0
%
% Unless required by applicable law or agreed to in writing, software
% distributed under the License is distributed on an "AS IS" BASIS,
% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
% See the License for the specific language governing permissions and
% limitations under the License.
%
% Page indication channel
% requires STTD?
% 288 bits/frame in 15 slots, last 12 bits not used
% PI relates to system frame number SFN
% table 4.3 and section 4.4.5 of Richardson W-CDMA book (ISBN 978-0-521-18782-4)
function y=WCDMADLpich(sfn,dly) % delay is 30x256 chips earlier than sccpch
	y=[((1-2*randint(1,144))+i*(1-2*randint(1,144))),zeros(1,6)]/sqrt(2); % last 6x2 bits unused
	y=shift( y, dly );
end


function video_feat = process_box_feat_prune(box_feat, video_info, prune, lp, pyramid)

nVideo = size(video_info, 1);
video_feat = zeros(size(box_feat, 1) * (pyramid), nVideo);
for n = 1:nVideo
    feature_set = box_feat(:, video_info(n, 1):video_info(n, 2));
    feature_set = double(feature_set);

    if prune ~= 0
        %% prune video track ****************
        prune_feat = feature_set;

        sum_val = sqrt(sum(prune_feat.^2));
        for qqq = 1:size(prune_feat, 1)
            prune_feat(qqq, :) = prune_feat(qqq, :)./sum_val;
        end

        dist_prune = pdist2(prune_feat', prune_feat');
        avg_sum = sum(dist_prune, 2);
        avg = mean(avg_sum);

        feature_set = feature_set(:, avg_sum(:, 1) <= avg*prune);

    end
    %% pyramid pooling
    if pyramid ~= 1

        video_len = size(feature_set, 2);

        video_feat(1: 1024, n) = sum(feature_set.^lp, 2).^(1/lp) * (1/size(feature_set, 2)).^(1/lp);

        py_2_1 = round(video_len/4);
        py_2_2 = 2 *round(video_len/4);
        py_2_3 = 3 * round(video_len/4);

        %% pyramid 2
        if video_len <= 20

            video_feat(1025 : 2048, n) = sum(feature_set.^lp, 2).^(1/lp) * (1/size(feature_set, 2)).^(1/lp);
            video_feat(2049 : 3072, n) = sum(feature_set.^lp, 2).^(1/lp) * (1/size(feature_set, 2)).^(1/lp);
            video_feat(3073 : 4096, n) = sum(feature_set.^lp, 2).^(1/lp) * (1/size(feature_set, 2)).^(1/lp);
            video_feat(4097 : 5120, n) = sum(feature_set.^lp, 2).^(1/lp) * (1/size(feature_set, 2)).^(1/lp);

        else
            feature_set_2_1 = feature_set(:, 1: py_2_1);
            feature_set_2_2 = feature_set(:, py_2_1  : py_2_2);
            feature_set_2_3 = feature_set(:, py_2_2 : py_2_3);
            feature_set_2_4 = feature_set(:, py_2_3 : end);
            video_feat(1025 : 2048, n) = sum(feature_set_2_1.^lp, 2).^(1/lp) * (1/size(feature_set_2_1, 2)).^(1/lp);
            video_feat(2049 : 3072, n) = sum(feature_set_2_2.^lp, 2).^(1/lp) * (1/size(feature_set_2_2, 2)).^(1/lp);
            video_feat(3073 : 4096, n) = sum(feature_set_2_3.^lp, 2).^(1/lp) * (1/size(feature_set_2_3, 2)).^(1/lp);
            video_feat(4097 : 5120, n) = sum(feature_set_2_4.^lp, 2).^(1/lp) * (1/size(feature_set_2_4, 2)).^(1/lp);
        end

    else
        %% lp-norm pooling
        feature_set = (feature_set).^lp;
        video_feat(:, n) = sum(feature_set, 2).^(1/lp) * (1/size(feature_set, 2)).^(1/lp);

    end

    %% **********************
    % video_feat(:, n) = max(feature_set, [], 2); % max pooling
    %     video_feat(:, n) = mean(feature_set, 2); % avg pooling
end

%%% normalize train and test features
sum_val = sqrt(sum(video_feat.^2));
for n = 1:size(video_feat, 1)
    video_feat(n, :) = video_feat(n, :)./sum_val;
end


classdef m_22_vic_10p_3s < MARRMoT_model
% Class for hydrologic conceptual model:  VIC

% Copyright (C) 2019, 2021 Wouter J.M. Knoben, Luca Trotter
% This file is part of the Modular Assessment of Rainfall-Runoff Models
% Toolbox (MARRMoT).
% MARRMoT is a free software (GNU GPL v3) and distributed WITHOUT ANY
% WARRANTY. See <https://www.gnu.org/licenses/> for details.

% Model references
% Clark, M. P., Slater, A. G., Rupp, D. E., Woods, R. A., Vrugt, J. A.,
% Gupta, H. V., � Hay, L. E. (2008). Framework for Understanding Structural
% Errors (FUSE): A modular framework to diagnose differences between
% hydrological models. Water Resources Research, 44(12), W00B02.
% http://doi.org/10.1029/2007WR006735
%
% Liang, X., Lettenmaier, D. P., Wood, E. F., & Burges, S. J. (1994). A
% simple hydrologically based model of land surface water and energy fluxes
% for general circulation models. Journal of Geophysical Research, 99,
% 14415�14428.

    properties
        % model-specific attributes
        aux_theta     % auxiliary parameter set
    end
    methods

        % creator method
        function obj = m_22_vic_10p_3s()
            obj.numStores = 3;                                             % number of model stores
            obj.numFluxes = 11;                                            % number of model fluxes
            obj.numParams = 10;

            obj.JacobPattern  = [1,0,0;
                                 1,1,0;
                                 1,1,1];                                   % Jacobian matrix of model store ODEs

            obj.parRanges = [   0.1 , 5;        % ibar,     Mean interception capacity [mm]
                                0   , 1;        % idelta,   Seasonal interception change as fraction of mean [-]
                                1   , 365;      % ishift,   Maximum interception peak timing [-]
                                1   , 2000;     % stot,    Maximum soil moisture capacity [mm]
                                0.01, 0.99;     % fsm, Fraction of stot that constitutes maximum soil moisture smmax [-]
                                0   ,10;        % b,        Infiltration excess shape parameter [-]
                                0   , 1;        % k1,       Percolation time parameter [d-1]
                                0   ,10;        % c1,       Percolation non-linearity parameter [-]
                                0   ,1;         % k2,       Baseflow time parameter [d-1]
                                1   , 5];       % c2,       Baseflow non-linearity parameter

            obj.StoreNames = {"S1", "S2" "S3"};                             % Names for the stores
            obj.FluxNames  = {"ei",   "peff", "iex", "qie",  "inf", "et1",...
                              "qex1", "pc",   "et2", "qex2", "qb"};        % Names for the fluxes

            obj.FluxGroups.Ea = [1 6 9];                                   % Index or indices of fluxes to add to Actual ET
            obj.FluxGroups.Q  = [4 7 10 11];                               % Index or indices of fluxes to add to Streamflow

        end

        % INITialisation function
        function obj = init(obj)
            %Parameters
            theta = obj.theta;
            stot    = theta(4);     % Total available storage [mm]
            fsm     = theta(5);     % Fraction of stot that constitutes maximum soil mositure storage [-]

            % Auxiliary parameter
            smmax   = fsm*stot;     % Maximum soil moisture capacity [mm]
            gwmax   = (1-fsm)*stot; % Maximum groundwater storage [mm]
            tmax    = 365.25;       % Length of one growing cycle [d]
            obj.aux_theta = [smmax; gwmax; tmax];

        end

        % MODEL_FUN are the model governing equations in state-space formulation
        function [dS, fluxes] = model_fun(obj, S)
            % parameters
            theta   = obj.theta;
            ibar    = theta(1);     % Mean interception capacity [mm]
            idelta  = theta(2);     % Seasonal interception change as fraction of mean [-]
            ishift  = theta(3);     % Maximum interception peak timing [-]
            b       = theta(6);     % Infiltration excess shape parameter [-]
            k1      = theta(7);     % Percolation time parameter [d-1]
            c1      = theta(8);     % Percolation non-linearity parameter [-]
            k2      = theta(9);     % Baseflow time parameter [d-1]
            c2      = theta(10);    % Baseflow non-linearity parameter

            aux_theta = obj.aux_theta;
            smmax = aux_theta(1);
            gwmax = aux_theta(2);
            tmax = aux_theta(3);

            % delta_t
            delta_t = obj.delta_t;

            % stores
            S1 = S(1);
            S2 = S(2);
            S3 = S(3);

            % climate input
            t = obj.t;                             % this time step
            climate_in = obj.input_climate(t,:);   % climate at this step
            P  = climate_in(1);
            Ep = climate_in(2);
            T  = climate_in(3);

            % fluxes functions
            aux_imax  = phenology_2(ibar,idelta,ishift,obj.t,tmax,delta_t);
            flux_ei   = evap_7(S1,aux_imax,Ep,delta_t);
            flux_peff = interception_1(P,S1,aux_imax);
            flux_iex  = excess_1(S1,aux_imax,delta_t);
            flux_qie  = saturation_2(S2,smmax,b,flux_peff+flux_iex);
            flux_inf  = effective_1(flux_peff+flux_iex,flux_qie);
            flux_et1  = evap_7(S2,smmax,max(0,Ep-flux_ei),delta_t);
            flux_qex1 = saturation_1(flux_inf,S2,smmax);
            flux_pc   = percolation_5(k1,c1,S2,smmax,delta_t);
            flux_et2  = evap_7(S3,gwmax,max(0,Ep-flux_ei-flux_et1),delta_t);
            flux_qex2 = saturation_1(flux_pc,S3,gwmax);
            flux_qb   = baseflow_5(k2,c2,S3,gwmax,delta_t);

            % stores ODEs
            dS1 = P - flux_ei - flux_peff - flux_iex;
            dS2 = flux_inf - flux_et1 - flux_qex1 - flux_pc;
            dS3 = flux_pc  - flux_et2 - flux_qex2 - flux_qb;

            % outputs
            dS = [dS1 dS2 dS3];
            fluxes = [flux_ei  flux_peff flux_iex  flux_qie ...
                      flux_inf flux_et1  flux_qex1 flux_pc ...
                      flux_et2 flux_qex2 flux_qb];
        end

        % STEP runs at the end of every timestep
        function obj = step(obj)
        end
    end
end


% SYNTAX:
% [yAvg, yAvgStd, tHRF, nTrials] = hmrS_SessAvg_Nirs(yAvgRuns, yAvgStdRuns, ySum2Runs, tHRFRuns, mlActRuns, nTrialsRuns)
%
% UI NAME:
% Run_Average
%
% DESCRIPTION:
% Calculate avearge HRF of all runs for one subject.
%
% INPUTS:
% yAvgRuns:
% yAvgStdRuns:
% tHRFRuns:
% mlActRuns:
% nTrialsRuns:
% trange: defines the range for the block average
% thresh: Threshold for excluding channels if it's data deviates too much
%         from mean
%
% OUTPUTS:
% yavg: the averaged results
% yAvgStd: the standard deviation across trials
% tHRF: the time vector
% nTrials: the number of trials averaged for each condition across all runs
%
% USAGE OPTIONS:
% Run_Average_on_Concentration_Data:  [dcAvg, dcAvgStd, tHRF, nTrials]    = hmrS_SessAvg_Nirs(dcAvgRuns, dcAvgStdRuns, dcSum2Runs, tHRFRuns, mlActRuns, nTrialsRuns)
% Run_Average_on_Delta_OD_Data:       [dodAvg, dodAvgStd, tHRF, nTrials]  = hmrS_SessAvg_Nirs(dodAvgRuns, dodAvgStdRuns, dodSum2Runs, tHRFRuns, mlActRuns, nTrialsRuns)
%

function [yAvg, yAvgStd, tHRF, nTrials] = hmrS_SessAvg_Nirs(yAvgRuns, yAvgStdRuns, ySum2Runs, tHRFRuns, mlActRuns, nTrialsRuns)

yAvg = [];
yAvgStd = [];
tHRF = [];
nTrials_tot = [];
grp1=[];

for iRun = 1:length(yAvgRuns)

    yAvg      = yAvgRuns{iRun};
    yAvgStd   = yAvgStdRuns{iRun};
    ySum2     = ySum2Runs{iRun};
    tHRF      = tHRFRuns{iRun};
    nTrials   = nTrialsRuns{iRun};
    mlAct     = mlActRuns{iRun};

    if isempty(yAvg)
        break;
    end

    nCond = size(nTrials,2);

    if ndims(yAvg) == (3-(nCond<2))

        if isempty(mlAct)
            mlAct = 1:size(yAvg,2);
        end

        % grab tHRF to make common for group average
        if iRun==1
            grp1 = zeros(size(yAvg,1), size(yAvg,2), nCond);
            grp1Sum2 = zeros(size(yAvg,1), size(yAvg,2), nCond);
            nTrials_tot = zeros(size(yAvg,2), nCond);
        end

        for iC = 1:nCond
            nT = nTrials(iC);
            if nT>0
                if iRun==1
                    grp1(:,mlAct,iC) = yAvg(:,mlAct,iC) * nT;
                    grp1Sum2(:,mlAct,iC) = ySum2(:,mlAct,iC);
                    nTrials_tot(mlAct,iC) = nT;
                else
                    for iCh=1:length(mlAct) %size(yAvg,2)
                        grp1(:,mlAct(iCh),iC) = grp1(:,mlAct(iCh),iC) + interp1(tHRF,yAvg(:,mlAct(iCh),iC),tHRF(:)) * nT;
                        grp1Sum2(:,mlAct(iCh),iC) = grp1Sum2(:,mlAct(iCh),iC) + interp1(tHRF,ySum2(:,mlAct(iCh),iC),tHRF(:));
                        nTrials_tot(mlAct(iCh),iC) = nTrials_tot(mlAct(iCh),iC) + nT;
                    end
                end
            end
        end
        yAvg    = [];
        yAvgStd = [];
        if ~isempty(grp1)
            for iC = 1:size(grp1,3)
                for iCh = 1:size(grp1,2)
                    yAvg(:,iCh,iC) = grp1(:,iCh,iC) / nTrials_tot(iCh,iC);

                    % We want to distinguish between no trials and 1 trial:
                    % If there are no trials, we have no HRF data and no std which
                    % the first case will calculate as opposed to one trial (2nd case)
                    % where we have all zeros.
                    if(nTrials_tot(iCh,iC)~=1)
                        yAvgStd(:,iCh,iC) = ( (1/(nTrials_tot(iCh,iC)-1))*grp1Sum2(:,iCh,iC) - (nTrials_tot(iCh,iC)/(nTrials_tot(iCh,iC)-1))*(grp1(:,iCh,iC) / nTrials_tot(iCh,iC)).^2).^0.5 ;
                    else
                        yAvgStd(:,iCh,iC) = zeros(size(grp1Sum2(:,iCh,iC)));
                    end
                end
            end
        end

    elseif ndims(yAvg) == (4-(nCond<2))

        if isempty(mlAct)
            mlAct = 1:size(yAvg,3);
        end

        % grab tHRF to make common for group average
        if iRun==1
            grp1 = zeros(size(yAvg,1), size(yAvg,2), size(yAvg,3), nCond);
            grp1Sum2 = zeros(size(yAvg,1), size(yAvg,2), size(yAvg,3), nCond);
            nTrials_tot = zeros(size(yAvg,3), nCond);
        end

        for iC = 1:1:nCond
            nT = nTrials(iC);
            if nT>0
                if iRun==1
                    grp1(:,:,mlAct,iC) = yAvg(:,:,mlAct,iC) * nT;
                    grp1Sum2(:,:,mlAct,iC) = ySum2(:,:,mlAct,iC);
                    nTrials_tot(mlAct,iC) = nT;
                else
                    for iCh=1:length(mlAct) %size(yAvg,3)
                        for iHb=1:size(yAvg,2)
                            grp1(:,iHb,mlAct(iCh),iC) = grp1(:,iHb,mlAct(iCh),iC) + interp1(tHRF,yAvg(:,iHb,mlAct(iCh),iC),tHRF(:)) * nT;
                            grp1Sum2(:,iHb,mlAct(iCh),iC) = grp1Sum2(:,iHb,mlAct(iCh),iC) + interp1(tHRF,ySum2(:,iHb,mlAct(iCh),iC),tHRF(:));
                        end
                        nTrials_tot(mlAct(iCh),iC) = nTrials_tot(mlAct(iCh),iC) + nT;
                    end
                end
            end
        end
        yAvg    = [];
        yAvgStd = [];
        if ~isempty(grp1)
            for iC = 1:size(grp1,4)
                for iCh = 1:size(grp1,3)
                    yAvg(:,:,iCh,iC) = grp1(:,:,iCh,iC) / nTrials_tot(iCh,iC);

                    % We want to distinguish between no trials and 1 trial:
                    % If there are no trials, we have no HRF data and no std which
                    % the first case will calculate as opposed to one trial (2nd case)
                    % where we have all zeros.
                    if(nTrials_tot(iCh,iC)~=1)
                        yAvgStd(:,:,iCh,iC) = ( (1/(nTrials_tot(iCh,iC)-1))*grp1Sum2(:,:,iCh,iC) - (nTrials_tot(iCh,iC)/(nTrials_tot(iCh,iC)-1))*(grp1(:,:,iCh,iC) / nTrials_tot(iCh,iC)).^2).^0.5 ;
                    else
                        yAvgStd(:,:,iCh,iC) = zeros(size(grp1Sum2(:,:,iCh,iC)));
                    end
                end
            end
        end
    end
end
nTrials = nTrials_tot;


function fx = toValues(disc, f, flag)
%TOVALUES   Convert a CHEBFUN to its TRIGSPEC discretization.
%   C = TOVALUES(DISC, F) converts the smooth chebfun F to coefficients C for
%   use by a TRIGSPEC discretization DISC.
%
%   C = TOVALUES(DISC, F, 1) converts the (perhaps piecewise smooth) chebfun F
%   to coefficients C for use by a TRIGSPEC discretization DISC.

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

if ( isnumeric(f) )
    fx = f;
    return
end

dim = disc.dimension;
tech = disc.returnTech();
tech = tech();
xx = trigpts(dim, f.domain);
fx = tech.vals2coeffs(f(xx));

end


function f = constructor(f, op, varargin)
%CONSTRUCTOR   CHEBFUN3 constructor.
%   Given a function OP of three variables, this code represents it as a
%   CHEBFUN3 object. A CHEBFUN3 object is a low-rank representation
%   expressing a function as a trilinear product of a discrete core tensor
%   and three quasimatrices consisting of univariate functions.
%
%
%   Since March 2023, chebfun3f is used by default to construct CHEBFUN3
%   objects. The legacy constructor chebfun3classic can be accessed by
%   providing the 'classic' flag as additional input argument.
%
% See also CHEBFUN2, CHEBFUN3T and CHEBFUN3V.

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

% Parse the inputs:
[op, dom, pref, fiberDim, vectorize, isEqui, fixedRank] = ...
    parseInputs(op, varargin{:});

% The 'equi' flag can be used only with numeric data:
if ( isEqui && ~isa(op, 'double') )
    error('CHEBFUN:CHEBFUN3:constructor:equi', ...
        'The EQUI flag is valid only when constructing from numeric data');
end

if ( isa(op, 'chebfun3') )     % CHEBFUN3( CHEBFUN3 )
    f = op;
    return
elseif ( isa(op, 'double') )   % CHEBFUN3( DOUBLE )
    f = chebfun3.chebfun3double(f, op, dom, pref, isEqui);
elseif ( strcmpi(pref.cheb3Prefs.constructor, 'classic') )
    f = chebfun3.chebfun3classic(f, op, pref, dom, vectorize, fiberDim);
else
    f = chebfun3.chebfun3f(f, op, pref, dom, vectorize);
end

if ( fixedRank )
    % Simplify the rank if requested.
    f = fixTheRank(f , fixedRank);
end
end
%% End of constructor

%%
function [op, dom, pref, fiberDim, vectorize, isEqui, ...
    fixedRank] = parseInputs(op, varargin)
vectorize = 0;
isEqui = 0;
isCoeffs = 0;
fixedRank = 0;
pref = chebfunpref();
fiberDim = [];
dom = [-1 1 -1 1 -1 1];

% Preferences structure given?
isPref = find(cellfun(@(p) isa(p, 'chebfunpref'), varargin));
if ( any(isPref) )
    pref = varargin{isPref};
    varargin(isPref) = [];
end

if ( isa(op, 'char') )     % CHEBFUN3( CHAR )
    op = str2op(op);
end

for k = 1:length(varargin)
    if any(strcmpi(varargin{k}, {'trig', 'periodic'}))
        pref.tech = @trigtech;
    elseif strcmpi(varargin{k}, 'eps')
        pref.cheb3Prefs.chebfun3eps = varargin{k+1};
    elseif strcmpi(varargin{k}, 'classic')
        pref.cheb3Prefs.constructor = 'classic';
    elseif strcmpi(varargin{k}, 'chebfun3f')
        pref.cheb3Prefs.constructor = 'chebfun3f';
    elseif strcmpi(varargin{k}, 'rank') % rank is specified.
        fixedRank = varargin{k+1};
    elseif ( isnumeric(varargin{k}) )
        if ( numel(varargin{k}) == 6 ) % domain is specified.
            dom = varargin{k};
        elseif ( numel(varargin{k}) == 3 ) % length is specified.
            if ( k > 1 && strcmpi(varargin{k-1}, 'rank') )
                % Rank is specified, not length. Don't confuse them.
                continue
            else
                % Interpret this as the user wants a fixed degree chebfun3
                % on the domain DOM.
                len = varargin{k};
                [xx, yy, zz] = chebpts3(len(1), len(2), len(3), dom);
                op = op(xx, yy, zz);
            end
        end
    elseif strcmpi(varargin{k}, {'fiberDim'})
        fiberDim = varargin{k+1};
    elseif any(strcmpi(varargin{k}, {'vectorize', 'vectorise'}))
        vectorize = true;
    elseif strcmpi(varargin{k}, 'coeffs')
        isCoeffs = 1;
    elseif strcmpi(varargin{k}, 'equi')
        isEqui = 1;
    end
end

if ( isCoeffs )
    op = chebfun3.coeffs2vals(op);
end

% If the vectorize flag is off, do we need to give user a warning?
if ( ~vectorize && ~isnumeric(op) ) % another check
    [vectorize, op] = vectorCheck(op, dom);
end

end

%%
function [vectorize, op] = vectorCheck(op, dom)
% Check for cases like op = @(x,y,z) x*y^2*z

vectorize = false;
[xx, yy, zz] = ndgrid(dom(1:2), dom(3:4), dom(5:6));
try
    A = feval(op, xx, yy, zz);
catch
    throwVectorWarning();
    vectorize = true;
    return
end

A = feval(op, xx, yy, zz);
if ( any(isinf(A(:) ) ) )
    error('CHEBFUN:CHEBFUN3:constructor:inf', ...
        'Function returned INF when evaluated');
elseif ( any(isnan(A(:)) ) )
    error('CHEBFUN:CHEBFUN3:constructor:nan', ...
        'Function returned NaN when evaluated');
end

if ( isscalar(A) )
    op = @(x,y,z) op(x,y,z) + 0*x + 0*y + 0*z;
end

end

%%
function throwVectorWarning()
warning('CHEBFUN:CHEBFUN3:constructor:vectorize',...
    ['Function did not correctly evaluate on an array.\n', ...
    'Turning on the ''vectorize'' flag. Did you intend this?\n', ...
    'Use the ''vectorize'' flag in the CHEBFUN3 constructor\n', ...
    'call to avoid this warning message.']);
end
%%
function op = str2op(op)
% OP = STR2OP(OP), finds independent variables in a string and returns an
% op handle than can be evaluated.

vars = symvar(op);        % Independent variables
numVars = numel(vars);
if ( numVars == 0 )
    op = @(x,y,z) eval (op);

elseif ( numVars == 1 )
    op = eval(['@(' vars{1} ', myVarBeta, myVarGamma)' op]);

elseif ( numVars == 2 )
    op = eval(['@(' vars{1} ',' vars{2} ', myVarGamma)' op]);

elseif ( numVars == 3 )
    op = eval(['@(' vars{1} ',' vars{2} ',' vars{3} ')' op]);

else
    error('CHEBFUN:CHEBFUN3:constructor:str2op:depvars', ...
        'Too many independent variables in string input.');
end

end

%%
function f = fixTheRank(f , fixedRank)
% Fix the rank of a CHEBFUN3. Used for calls to constructor with specified
% rank.

if ( any(fixedRank) < 0 )
    error('CHEBFUN:CHEBFUN3:constructor:fixTheRank:negative', ...
        'Ranks should all be nonnegative.')
elseif ( all(fixedRank) )
    [r1, r2, r3] = rank(f);
    t1 = fixedRank(1);
    t2 = fixedRank(2);
    t3 = fixedRank(3);

    % What to do with cols?
    if ( r1 > t1 )
        % Truncate cols:
        f.cols = f.cols(:, 1:t1);
        f.core = f.core(1:t1, :, :);
        r1 = t1; % New size of core
    elseif ( r1 < t1 )
        % Pad cols with approprate number of zero cols:
        zCols = chebfun(0, f.cols.domain);
        for jj = r1 : t1 - 1
            f.cols = [f.cols zCols];
        end
        % Pad mode 1 of the core tensor with zeros:
        tempCore = zeros(t1, r2, r3);
        tempCore(1:r1, :, :) = f.core;
        f.core = tempCore;
        r1 = t1; % New size of core
    end

    % What to do with rows?
    if ( r2 > t2 )
        % Truncate rows:
        f.rows = f.rows(:,1:t2);
        f.core = f.core(:, 1:t2, :);
        r2 = t2; % New size of core
    elseif ( r2 < t2 )
        % Pad rows with approprate number of zero rows:
        zRows = chebfun(0, f.rows.domain);
        for jj = r2 : t2 - 1
            f.rows = [f.rows zRows];
        end
        % Pad mode 2 of the core tensor with zeros:
        tempCore = zeros(r1, t2, r3);
        tempCore(:, 1:r2, :) = f.core;
        f.core = tempCore;
        r2 = t2; % New size of core
    end

    % What to do with tubes?
    if ( r3 > t3 )
        % Truncate tubes:
        f.tubes = f.tubes(:, 1:t3);
        f.core = f.core(:, :, 1:t3);
    elseif ( r3 < t3 )
        % Pad tubes with approprate number of zero tubes:
        zTubes = chebfun(0, f.tubes.domain);
        for jj = r3 : t3 - 1
            f.tubes = [f.tubes zTubes];
        end
        % Pad mode 3 of the core tensor with zeros:
        tempCore = zeros(r1, r2, t3);
        tempCore(:, :, 1:r3) = f.core;
        f.core = tempCore;
    end
end

end


function region = p00_region ( problem )

%*****************************************************************************80
%
%% P00_REGION returns the name of the integration region for any problem.
%
%  Discussion:
%
%    I thought I was going to use this idea a lot, but most of my test
%    regions are boxes.
%
%    BALL
%      the interior of a 2D circle,
%      the interior of a 3D sphere,
%      the interior of an ND sphere.
%
%    BOX
%      a 1D finite line segment,
%      a 2D finite rectangle,
%      a 3D box,
%      an ND box.
%
%    SIMPLEX
%      a 2D triangle,
%      a 3D tetrahedron,
%      an ND simplex.
%      The "unit simplex" in ND is the set of nonnegative points X
%      such that sum ( X(1:N) ) <= 1.
%
%    SPACE
%      a 1D infinite line,
%      a 2D infinite place,
%      a 3D space,
%      an ND space.
%
%    SPHERE
%      the circumference of a 2D circle,
%      the surface of a 3D sphere,
%      the surface of an ND sphere.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    18 March 2007
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer PROBLEM, the number of the desired test problem.
%
%    Output, string REGION, the name of the integration region.
%
  if ( problem == 1 )
    region = p01_region ( );
  elseif ( problem == 2 )
    region = p02_region ( );
  elseif ( problem == 3 )
    region = p03_region ( );
  elseif ( problem == 4 )
    region = p04_region ( );
  elseif ( problem == 5 )
    region = p05_region ( );
  elseif ( problem == 6 )
    region = p06_region ( );
  elseif ( problem == 7 )
    region = p07_region ( );
  elseif ( problem == 8 )
    region = p08_region ( );
  elseif ( problem == 9 )
    region = p09_region ( );
  elseif ( problem == 10 )
    region = p10_region ( );
  elseif ( problem == 11 )
    region = p11_region ( );
  elseif ( problem == 12 )
    region = p12_region ( );
  elseif ( problem == 13 )
    region = p13_region ( );
  elseif ( problem == 14 )
    region = p14_region ( );
  elseif ( problem == 15 )
    region = p15_region ( );
  elseif ( problem == 16 )
    region = p16_region ( );
  elseif ( problem == 17 )
    region = p17_region ( );
  elseif ( problem == 18 )
    region = p18_region ( );
  elseif ( problem == 19 )
    region = p19_region ( );
  elseif ( problem == 20 )
    region = p20_region ( );
  elseif ( problem == 21 )
    region = p21_region ( );
  elseif ( problem == 22 )
    region = p22_region ( );
  elseif ( problem == 23 )
    region = p23_region ( );
  elseif ( problem == 24 )
    region = p24_region ( );
  elseif ( problem == 25 )
    region = p25_region ( );
  elseif ( problem == 26 )
    region = p26_region ( );
  elseif ( problem == 27 )
    region = p27_region ( );
  elseif ( problem == 28 )
    region = p28_region ( );
  elseif ( problem == 29 )
    region = p29_region ( );
  elseif ( problem == 30 )
    region = p30_region ( );
  elseif ( problem == 31 )
    region = p31_region ( );
  elseif ( problem == 32 )
    region = p32_region ( );
  else
    fprintf ( 1, '\n' );
    fprintf ( 1, 'P00_REGION - Fatal error!\n' );
    fprintf ( 1, '  Illegal problem number = %d\n', problem );
    error ( 'P00_REGION - Fatal error!' );
  end

  return
end


% Version 1.000
%
% Code provided by Ruslan Salakhutdinov and Geoff Hinton
%
% Permission is granted for anyone to copy, use, modify, or distribute this
% program and accompanying programs and documents for any purpose, provided
% this copyright notice is retained and prominently displayed, along with
% a note saying that the original programs are available from our
% web page.
% The programs and documents are distributed without any warranty, express or
% implied.  As the programs were written for research purposes only, they have
% not been tested to the degree that would be advisable in any important
% application.  All use of these programs is entirely at the user's own risk.

% This program reads raw MNIST files available at
% http://yann.lecun.com/exdb/mnist/
% and converts them to files in matlab format
% Before using this program you first need to download files:
% train-images-idx3-ubyte.gz train-labels-idx1-ubyte.gz
% t10k-images-idx3-ubyte.gz t10k-labels-idx1-ubyte.gz
% and gunzip them. You need to allocate some space for this.

% This program was originally written by Yee Whye Teh

% Work with test files first
fprintf(1,'You first need to download files:\n train-images-idx3-ubyte.gz\n train-labels-idx1-ubyte.gz\n t10k-images-idx3-ubyte.gz\n t10k-labels-idx1-ubyte.gz\n from http://yann.lecun.com/exdb/mnist/\n and gunzip them \n');

f = fopen('t10k-images.idx3-ubyte','r');
[a,count] = fread(f,4,'int32');

g = fopen('t10k-labels.idx1-ubyte','r');
[l,count] = fread(g,2,'int32');

fprintf(1,'Starting to convert Test MNIST images (prints 10 dots) \n');
n = 1000;

Df = cell(1,10);
for d=0:9,
  Df{d+1} = fopen(['test' num2str(d) '.ascii'],'w');
end;

for i=1:10,
  fprintf('.');
  rawimages = fread(f,28*28*n,'uchar');
  rawlabels = fread(g,n,'uchar');
  rawimages = reshape(rawimages,28*28,n);

  for j=1:n,
    fprintf(Df{rawlabels(j)+1},'%3d ',rawimages(:,j));
    fprintf(Df{rawlabels(j)+1},'\n');
  end;
end;

fprintf(1,'\n');
for d=0:9,
  fclose(Df{d+1});
  D = load(['test' num2str(d) '.ascii'],'-ascii');
  fprintf('%5d Digits of class %d\n',size(D,1),d);
  save(['test' num2str(d) '.mat'],'D','-mat');
end;

% Work with trainig files second
f = fopen('train-images.idx3-ubyte','r');
[a,count] = fread(f,4,'int32');

g = fopen('train-labels.idx1-ubyte','r');
[l,count] = fread(g,2,'int32');

fprintf(1,'Starting to convert Training MNIST images (prints 60 dots)\n');
n = 1000;

Df = cell(1,10);
for d=0:9,
  Df{d+1} = fopen(['digit' num2str(d) '.ascii'],'w');
end;

for i=1:60,
  fprintf('.');
  rawimages = fread(f,28*28*n,'uchar');
  rawlabels = fread(g,n,'uchar');
  rawimages = reshape(rawimages,28*28,n);

  for j=1:n,
    fprintf(Df{rawlabels(j)+1},'%3d ',rawimages(:,j));
    fprintf(Df{rawlabels(j)+1},'\n');
  end;
end;

fprintf(1,'\n');
for d=0:9,
  fclose(Df{d+1});
  D = load(['digit' num2str(d) '.ascii'],'-ascii');
  fprintf('%5d Digits of class %d\n',size(D,1),d);
  save(['digit' num2str(d) '.mat'],'D','-mat');
end;

dos('rm *.ascii');


%% Copyright (C) 2014-2022 Colin B. Macdonald
%%
%% This file is part of OctSymPy.
%%
%% OctSymPy 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 software 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 software; see the file COPYING.
%% If not, see <http://www.gnu.org/licenses/>.

%% -*- texinfo -*-
%% @documentencoding UTF-8
%% @defmethod @@sym abs (@var{x})
%% Symbolic abs function.
%%
%% Example:
%% @example
%% @group
%% syms x
%% y = abs (x)
%%   @result{} y = (sym) │x│
%% @end group
%% @end example
%%
%% Note: this file is autogenerated: if you want to edit it, you might
%% want to make changes to 'generate_functions.py' instead.
%%
%% @end defmethod

function y = abs(x)
  if (nargin ~= 1)
    print_usage ();
  end
  y = elementwise_op ('Abs', x);
end

%!error abs (sym(1), 2)
%!assert (isequaln (abs (sym(nan)), sym(nan)))

%!shared x, d
%! d = -1;
%! x = sym('-1');

%!test
%! f1 = abs(x);
%! f2 = abs(d);
%! assert( abs(double(f1) - f2) < 1e-15 )

%!test
%! D = [d d; d d];
%! A = [x x; x x];
%! f1 = abs(A);
%! f2 = abs(D);
%! assert( all(all( abs(double(f1) - f2) < 1e-15 )))

%!test
%! % round trip
%! y = sym('y');
%! A = abs (d);
%! f = abs (y);
%! h = function_handle (f);
%! B = h (d);
%! assert (A, B, -eps)


function plot_square_hhmm(ev)
% Plot the square shape implicit in the evidence.
% ev{i,t} is the value of node i in slice t.
% The observed node contains a velocity (delta increment), which is converted
% into a position.
% The Q2 node specifies which model is used, and hence which color
% to use: 1=red, 2=green, 3=blue, 4=black.

Q1 = 1; Q2 = 2; Q3 = 3; F3 = 4; F2 = 5; Onode = 6;

delta = cell2num(ev(Onode,:)); % delta(:,t)
Q2label = cell2num(ev(Q2,:));

T = size(delta, 2);
pos = zeros(2,T+1);
hold on
cols = {'r', 'g', 'b', 'k'};
for t=2:T+1
  pos(:,t) = pos(:,t-1) + delta(:,t-1);
  plot(pos(1,t), pos(2,t), sprintf('%c.', cols{Q2label(t-1)}));
  if (t==2)
    text(pos(1,t-1),pos(2,t-1),sprintf('%d',t))
  elseif (mod(t,20)==0)
    text(pos(1,t),pos(2,t),sprintf('%d',t))
  end
end


function drawSurfPatch(u, v, z, varargin)
%DRAWSURFPATCH Draw a 3D surface patch, with 2 parametrized surfaces.
%
%   usage:
%   drawSurfPatch(u, v, zuv)
%   where u, v, and zuv are three matrices the same size, u and
%   corresponding to each parameter, and zuv being equal to a function of u
%   and v.
%
%   drawSurfPatch(u, v, zuv, p0)
%   If p0 is specified, two lines with u(p0(1)) and v(p0(2)) are drawn on
%   the surface, and corresponding tangent are also shown.
%
%
%   ---------
%
%   author : David Legland
%   INRA - TPV URPOI - BIA IMASTE
%   created the 24/05/2005.
%

%   HISTORY
%   2005-06-08 add doc.
%   2007-01-04 remove unused variables and change function name
%   2010-03-08 code cleanup, use drawPolyline3d

% prepare figure
hold on;

% draw the surface interior
surf(u, v, z, 'FaceColor', 'g', 'EdgeColor', 'none');

% draw the surface boundaries
drawPolyline3d(u(1,:), v(1,:), z(1,:))
drawPolyline3d(u(end,:), v(end,:), z(end,:))
drawPolyline3d(u(:,end), v(:,end), z(:,end))
drawPolyline3d(u(:,1), v(:,1), z(:,1))

% eventually draw two perpendicular lines on the surface
if ~isempty(varargin)
    pos = varargin{1};
    drawPolyline3d(u(pos(1),:), v(pos(1),:), z(pos(1),:));
    drawPolyline3d(u(:,pos(2)), v(:,pos(2)), z(:,pos(2)));
end


%% Copyright (C) 2014-2016, 2018-2019 Colin B. Macdonald
%%
%% This file is part of OctSymPy.
%%
%% OctSymPy 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 software 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 software; see the file COPYING.
%% If not, see <http://www.gnu.org/licenses/>.

%% -*- texinfo -*-
%% @documentencoding UTF-8
%% @deftypemethod  @@sym {} disp (@var{x})
%% @deftypemethodx @@sym {} disp (@var{x}, 'unicode')
%% @deftypemethodx @@sym {} disp (@var{x}, 'ascii')
%% @deftypemethodx @@sym {} disp (@var{x}, 'flat')
%% @deftypemethodx @@sym {@var{s} =} disp (@var{x})
%% Display the value of a symbolic expression.
%%
%% Examples:
%% @example
%% @group
%% syms x a c
%% str = disp(sin(2*sym(pi)*x))
%%   @result{} str =   sin(2⋅π⋅x)
%%
%% A = [sin(x/2) floor(a^(x*c)); acosh(2*x/pi) ceil(sin(x/gamma(x)))];
%% disp(A, 'unicode')
%%   @print{}   ⎡     ⎛x⎞      ⎢ c⋅x⎥   ⎤
%%   @print{}   ⎢  sin⎜─⎟      ⎣a   ⎦   ⎥
%%   @print{}   ⎢     ⎝2⎠               ⎥
%%   @print{}   ⎢                       ⎥
%%   @print{}   ⎢     ⎛2⋅x⎞  ⎡   ⎛ x  ⎞⎤⎥
%%   @print{}   ⎢acosh⎜───⎟  ⎢sin⎜────⎟⎥⎥
%%   @print{}   ⎣     ⎝ π ⎠  ⎢   ⎝Γ(x)⎠⎥⎦
%% @end group
%%
%% @group
%% disp(A, 'ascii')
%%   @print{}   [     /x\              / c*x\      ]
%%   @print{}   [  sin|-|         floor\a   /      ]
%%   @print{}   [     \2/                          ]
%%   @print{}   [                                  ]
%%   @print{}   [     /2*x\         /   /   x    \\]
%%   @print{}   [acosh|---|  ceiling|sin|--------||]
%%   @print{}   [     \ pi/         \   \Gamma(x)//]
%%
%% disp(A, 'flat')
%%   @print{} Matrix([[sin(x/2), floor(a**(c*x))], [acosh(2*x/pi), ceiling(sin(x/gamma(x)))]])
%% @end group
%% @end example
%%
%% @seealso{@@sym/pretty}
%% @end deftypemethod

function varargout = disp(x, wh)

  if (nargin == 1)
    %% read config to see how to display x
    wh = sympref('display');
  end

  switch lower(wh)
    case 'flat'
      s = x.flat;
    case 'ascii'
      s = x.ascii;
    case 'unicode'
      s = x.unicode;
    otherwise
      print_usage ();
  end
  s = make_indented(s);

  if (nargout == 0)
    disp(s)
  else
    varargout = {[s sprintf('\n')]};  % add a newline
  end
end

function s = make_indented(s, n)
  if (nargin == 1)
    n = 2;
  end
  pad = char (double (' ')*ones (1,n));
  newl = sprintf('\n');
  s = strrep (s, newl, [newl pad]);
  s = [pad s];  % first line
end

%!test
%! syms x
%! s = disp(sin(x));
%! assert(strcmp(s, sprintf('  sin(x)\n')))

%!test
%! syms x
%! s = disp(sin(x/2), 'flat');
%! assert(strcmp(s, sprintf('  sin(x/2)\n')))

%!test
%! % Examples of 2x0 and 0x2 empty matrices:
%! a = sym([1 2; 3 4]);
%! b2x0 = a([true true], [false false]);
%! b0x2 = a([false false], [true true]);
%! assert (isequal (size (b2x0), [2 0]))
%! assert (isequal (size (b0x2), [0 2]))
%! s = disp(b2x0);
%! assert(strcmp(s, sprintf('  []\n')))
%! s = disp(b0x2);
%! assert(strcmp(s, sprintf('  []\n')))


function H = lbfgsinit(n,k,dscale)
%lbfgsinit  initializes an L-BFGS matrix.
%
%   H = lbfgsinit(n,k,dscale)
%
%   See also lbfgsadd, lbfgsdel, lbfgshprod, lbfgsupdate.

% $Id: lbfgsinit.m 385 2008-09-02 22:22:41Z mpf $

if nargin < 3 || isempty(dscale)
   dscale = 1;
end

H.jNew  = 1;
H.jOld  = 1;
H.jMax  = k;
H.S     = zeros(n,k);
H.Y     = zeros(n,k);

% Data specific to H
H.gamma = dscale;
H.r     = zeros(1,k);

% Data specific to B
H.delta = 1 / dscale;
H.valid = false(k,1);
H.rank  = 0;
H.STS   = zeros(k,k);
H.L     = zeros(k,k);
H.D     = zeros(k,k);
H.M     = zeros(2*k,2*k);
H.ML    = [];
H.MU    = [];

% Status information and options
H.status = 0; % 0=updated, 1=corrected, 2=no update


function munu_l_i
%
%  Generates the data used in 'munu_l'. Data are stored in global
%  variables.
%
%  NOTE that 'munu_m_i' must be called before calling this routine.
%
%  Calling sequence:
%
%    munu_l_i
%
%  Remarks:
%
%    This routine has access to data, which is provided as global
%    data by the routine 'munu_m_i'.
%
%
%  LYAPACK 1.0 (Thilo Penzl, September 1999)

global LP_N LP_NL LP_NU

if ~length(LP_N)
  error('This routine needs global data which must be generated by calling ''munu_m_i'' first.');
end

[LP_NL,LP_NU] = lu(LP_N);