function varargout = pdeSolve(pdeFun, tIn, u0, bc, varargin)
%PDESOLVE   Solve PDEs using Chebfun.
%
%   PDESOLVE() solves solves an initial-boundary value problem via a method of
%   lines approach. See @CHEBFUN/PDE15S() for documentation.
%
% See also PDE15S, PDE23T, PDESET.

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

global DIFFORDER SYSSIZE
DIFFORDER = 0;
SYSSIZE = 0;

% Default options:
tol = 1e-6;             % 'eps' in Chebfun terminology
% The default behaviour with no outputs is to plot. If the method is called with
% outputs, by default, we don't plot.
doPlot = ( nargout == 0 );
doHold = false;         % Hold plot?
plotOpts = {'-'};       % Plotting style
adjustBCs = true;       % Adjust inconsistent BCs
throwBCwarning = true;  % Throw a warning for inconsistent BCs
timeChunks = 51;        % Default number of time slices if not specified

% Parse the variable inputs:
if ( numel(varargin) == 2 )
    opt = varargin{1};
    opt.N = varargin{2};
elseif ( numel(varargin) == 1 )
    if ( isstruct(varargin{1}) )
        opt = varargin{1};
    else
        opt = pdeset;
        opt.N = varargin{1};
    end
else
    opt = pdeset;
end
optN = opt.N;
if ( isempty(optN) )
    optN = NaN;
end

% Get the domain:
DOMAIN = domain(u0, 'ends');

if ( isfield(opt, 'AdjustBCs') && ~isempty(opt.AdjustBCs) && ~opt.AdjustBCs )
    throwBCwarning = false;
    adjustBCs = false;
end

% PDE solver options:
if ( ~isempty(opt.Eps) )
    tol = opt.Eps;
end
if ( ~isempty(opt.Plot) )
    doPlot = strcmpi(opt.Plot, 'on');
end
if ( ~isempty(opt.HoldPlot) )
    doHold = strcmpi(opt.HoldPlot, 'on') || strcmp(opt.HoldPlot, '1');
end
if ( ~isempty(opt.PlotStyle) )
    plotOpts = opt.PlotStyle;
end
if ( ~isempty(opt.ODESolver) )
    ODESOLVER = opt.ODESolver;
else
    ODESOLVER = @ode15s;
end

% Set time chunks:
if ( length(tIn) == 2 )
    tt = linspace(tIn(1), tIn(2), timeChunks);
else
    tt = tIn;
end

userMassSet = false;
ISPERIODIC = false;
DONE = false;
COUNTER = 1;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% %%%%%%%%%%%%%%%%%%%%%%%%%%  PARSE INPUTS TO PDEFUN  %%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% Determine the size of the system, i.e., number of dependent variables.
SYSSIZE = min(size(u0));
[pdeFun, varNamesParsed] = parseFun(pdeFun);
if ( isfield(opt, 'difforder') )
    DIFFORDER = opt.difforder;
else
    getDIFFORDER(pdeFun, DOMAIN);
end

if ( (max(DIFFORDER) < 2) && isequal(ODESOLVER, @ode15s) )
    warning('CHEBFUN:PdeSolver', ...
        'PDE23T() is recommended for non-diffusive problems.');
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%  EVENT SETUP  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

if ( ~isnan(optN) )
    opt.OutputFcn = @nonAdaptiveEvent;
else
    opt.OutputFcn = @adaptiveEvent;
end

    function status = nonAdaptiveEvent(t, U, flag)
        % This event is called at the end of each chunk in non-adaptive mode.
        status = false;
        if ( ~isempty(flag) )
            return
        end

        % Sometimes we get given more than one time slice.
        for kk = 1:numel(t)

            if ( ~any(abs(tSpan - t(kk)) < 1e-6) )
                % This is not a designated time slice!
                continue
            end

            % Reshape solution:
            Uk = reshape(U(:,kk), n, SYSSIZE);
            uCurrent = chebfun(Uk, DOMAIN, 'tech', techHandle);
            tCurrent = t(kk);
            % Store for output:
            COUNTER = COUNTER + 1;
            uOut{COUNTER} = uCurrent;

            % Plot current solution:
            if ( doPlot )
                plotFun(uCurrent, t(kk));
            end
        end

        if ( guiFlag )
            status = guiEvent(status);
        end

    end

    function status = adaptiveEvent(t, U, flag)
        % This event is called at the end of each chunk in adaptive mode.

        status = false;
        if ( ~isempty(flag) )
            return
        end

        % Sometimes we get given more than one time slice.
        for kk = 1:numel(t)
            % Reshape solution:
            Uk = reshape(U(:,kk), currentLength, SYSSIZE);

            % Happiness check:
            c = (1+sin(1:SYSSIZE)).'; % Arbitrarily linear combination.
            Uk2 = (Uk*c/sum(c));
            uk2 = tech.make(Uk2, pref);
            [ishappy, cutoff] = happinessCheck(uk2, Uk2, [], [], pref); %#ok<ASGLU>

            if ( ishappy )

                if ( ~any(abs(tSpan - t(kk)) < 1e-6) )
                    % This is not a designated time slice!
                    continue
                end

                % Store these values:
                tCurrent = t(kk);
                uCurrent = chebfun(Uk, DOMAIN, 'tech', techHandle);
                uCurrent = simplify(uCurrent);

                COUNTER = COUNTER + 1;
                uOut{COUNTER} = uCurrent;
                tOut(COUNTER) = tCurrent;

                % Plot current solution (if plotting is on)
                if ( doPlot )
                    plotFun(uCurrent, tCurrent);
                end
                % TODO: Re-insert this?
%                 % If we have 2.5 times as many coefficients as we need then
%                 % shorten the representation and cause the integrator to stop.
%                 if ( cutoff < 0.4*n && n > 17)
%                     currentLength = round(1.25*cutoff)';
%                     %currentLength = floor( currentLength / 1.5  );
%                     currentLength = currentLength + 1 - rem(currentLength,2);
%                     currentLength = max(currentLength, 17);
%                     status = true;
%                     return
%                 end

            else

                % Increase length and bail out:
                currentLength = 2*currentLength-1;
                status = true;
                break

            end

            if ( guiFlag )
                status = guiEvent(status);
            end

        end

    end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%  PLOTTING SETUP  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% Determine which figure to plot to (for CHEBGUI) and set default display values
% for variables.
YLim = opt.YLim;
gridOn = 0;
guiFlag = false;
if ( isfield(opt, 'handles') )
    if ( opt.handles.gui )
        guiFlag = true;
        axesSol = opt.handles.fig_sol;
        axesNorm = opt.handles.fig_norm;
        gridOn = opt.handles.guifile.options.grid;
        solveButton = opt.handles.button_solve;
        clearButton = opt.handles.button_clear;
        panelSol = opt.handles.panel_figSol;
    end
    varNames = opt.handles.varnames;
    xLabel = opt.handles.indVarName{1};
    tlabel = opt.handles.indVarName{2};
    fontsize = opt.handles.fontsizePanels;
    set(axesSol, 'fontsize', fontsize);
else
    % Obtain the correct variable names from the parsing of the operator
    if ( length(varNamesParsed) == numColumns(u0) )
        % Space and time variables did not get passed explicitly to operator
        tlabel = 't';
        xLabel = 'x';
    else
        tlabel = varNamesParsed{1};
        xLabel = varNamesParsed{2};
    end
    varNames = varNamesParsed(end-numColumns(u0)+1:end);
    if ( doPlot )    % Set up ploting in non-gui mode
        axesSol = gca;
        cla(axesSol)
        xlabel(axesSol, xLabel);
        % Bring attention to the figure
        shg
    end
end

% Set plot options
if ( doPlot )

    % Use linear scale. Always.
    if ( ~ishold )
        set(gca, 'XScale', 'Linear', 'YScale', 'Linear');
    end

    set(axesSol, 'NextPlot', 'replacechildren');

    % Fix x limits
    set(axesSol, 'xLim', u0.domain);

    % Fix y limits if they were specified
    if ( ~isempty(YLim) )
        set(axesSol, 'ylim', YLim);
    end

    % Initialize lines for plotting
    hLines = plot(axesSol, DOMAIN, NaN(length(DOMAIN), numColumns(u0)), ...
        plotOpts{:});

    % Grid on?
    if ( gridOn )
        grid(axesSol, 'on')
    else
        grid(axesSol, 'off')
    end

    % Hold on?
    if ( doHold )
        hold(axesSol, 'on')
    else
        hold(axesSol, 'off')
    end

    % For plotting, it's useful to know whether we're running in old or new
    % Matlab graphics mode
    if ( ~verLessThan('matlab', '8.4') )
        newMatlabVersion = true;
    else
        newMatlabVersion = false;
    end
end

    function status = guiEvent(status)
        %GUIEVENT   Deal with GUI events ('stop', 'pause', etc).
        % OUTPUTS:
        %   status = true exits the current time chunk.
        %   DONE = true exits PDE solver. (Note DONE is a GLOBAL variable).

        % Interrupt computation if stop or pause button is pressed in the GUI.
        if ( strcmp(get(solveButton, 'String'), 'Solve') )
            % Stop.
            tt = tt( tt <= tCurrent );
            uOut(COUNTER+1:end) = [];
            status = true;
            DONE = true;
        elseif ( strcmp(get(clearButton, 'String'), 'Continue') )
            % Wait, pause.

            % Plot a waterfall plot to the bottom figure window:
            axes(axesNorm)
            uuTmp = prepare4output(uOut(1:COUNTER));
            if ( SYSSIZE == 1 )
                waterfall(uuTmp, tt(tt<=tCurrent), 'linewidth', 2);
            else
                cols = get(0, 'DefaultAxesColorOrder');
                waterfall(uuTmp, tt(tt<=tCurrent), 'linewidth', 2, ...
                    'EdgeColors', cols);
            end
            xlabel(xLabel), ylabel(tlabel), zlabel(varNames)
            if ( gridOn )
                grid on
            end
            view([322.5 30])
            box off
            axes(axesSol)
            % Hang around until 'CONTINUE' or 'STOP' is presed.
            waitfor(clearButton, 'String');
            % Call again to see if 'STOP' was pressed.
            status = guiEvent(status);

            set(axesNorm, 'fontsize', fontsize);
        end
    end

    function plotFun(U, t)
        %PLOTFUN    Plot current solution U at a time t.

        % Obtain plotting data for the solution
        Udata = plotData(U);
        UdataX = Udata.xLine;
        UdataY = Udata.yLine;

        % Update the YData of the lines plotted
        if ( ~doHold )
            for hCounter = 1:length(hLines)
                set(hLines(hCounter), 'XData', UdataX);
                set(hLines(hCounter), 'YData', UdataY(:, hCounter)');
            end
        else
            % Reset color cycle prior to point plot if running R2014b.
            if ( newMatlabVersion )
                set(axesSol, 'ColorOrderIndex', 1);
            end
            hLines = plot(axesSol, UdataX, UdataY, plotOpts{:});
        end

        % Update the title, either of the GUI panel or the figure
        if ( guiFlag )
            set(panelSol, 'Title', sprintf('%s = %.3f', tlabel, t))
        elseif ( nargin > 1 )
            title(sprintf('%s = %.3f', tlabel, t))
        end

        % Update the plot
        drawnow

    end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% %%%%%%%%%%%%%%%%%%%%%%%%% PARSE BOUNDARY CONDITIONS %%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

if ( ischar(bc) && (strcmpi(bc, 'neumann') || strcmpi(bc, 'dirichlet')) )
    bc = struct( 'left', bc, 'right', bc);
elseif ( iscell(bc) && numel(bc) == 2 )
    bc = struct( 'left', bc{1}, 'right', bc{2});
end
if ( isstruct(bc) && ~isfield(bc, 'middle') )
    bc.middle.op = [];
end

% Initialise some global variables.
numLinBCs = 0; % linear BCS (total)
numLBCs = 0;   % nonlinear LBCs
numMBCs = 0;   % nonlinear other constraints
numRBCs = 0;   % nonlinear RBCs
BCRHS = {};    % RHS values for BCs

    % Differentiation matrices at left and right endpoints:
    function D = neumannLeft(n)
        x = -cos(pi*(1:n-2)/(n-1));
        D = [0, 2./(1+x), .5];
        D(1:2:end) = -D(1:2:end);
        D(1) = -sum(D);
    end
    function D = neumannRight(n)
        x = -cos(pi*(1:n-2)/(n-1));
        D = [-.5, 2./(x-1), 0];
        D(end:-2:1) = -D(end:-2:1);
        D(end) = -sum(D);
    end

if ( ischar(bc) && any(strcmpi(bc, {'periodic', 'trig'})) )
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%% PERIODIC BCS  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

    ISPERIODIC = true;

    % One can still use a Chebyshev basis and enforce periodic conditions by
    % enforcing suitable constraints on the derivative. However, using a
    % periodic basis (for now, trigtech) is much more efficient.
%     r = cell(sum(DIFFORDER), 1);
%     count = 1;
%     for j = 1:SYSSIZE
%         for k = 0:DIFFORDER(j)-1
%             c = (diff(DOMAIN)/2)^k;
%             A = @(n) [1 zeros(1, n-2) -1]*chebcolloc2.diffmat(n, k)*c;
%             r{count} = @(n) [zeros(1, (j-1)*n) A(n) zeros(1,(SYSSIZE-j)*n)];
%             count = count + 1;
%         end
%     end
%     bc = struct( 'left', [], 'right', []);
%     bc.left.op = r(1:2:end);
%     bc.right.op = r(2:2:end);
%     BCRHS = num2cell(zeros(1, numel(r)));

else
    %%%%%%%%%%%%%%%%%%%%%%%%%%% NONPERIODIC BCS  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    if ( isfield(bc, 'left') && ~isfield(bc, 'right') )
        bc.right = [];
    elseif ( isfield(bc, 'right') && ~isfield(bc, 'left') )
        bc.left = [];
    elseif ( ~isfield(bc, 'left') && ~isfield(bc, 'right') )
        bc = struct('left', [], 'right', [], 'middle', bc);
    end

    % Deal with struct and numeric input:
    bc.left = dealWithStructInput(bc.left);
    bc.right = dealWithStructInput(bc.right);

    % Temporary chebdouble for evaluating BC function handles:
    uTmp = chebdouble(ones(1, SYSSIZE), DOMAIN);

    %% LEFT BCS
    if ( isempty(bc.left) )
        bc.left = struct('op', []);

    elseif ( ischar(bc.left) || (iscell(bc.left) && ischar(bc.left{1})) )
        %%%%%%%%%%%%%%%%%%% DIRICHLET AND NEUMANN BCS (LEFT) %%%%%%%%%%%%%%%%%
        if ( iscell(bc.left) )
            v = bc.left{2};
            bc.left = bc.left{1};
        else
            v = 0;
        end
        if ( ~isnumeric(v) )
            error('CHEBFUN:CHEBFUN:pde15s:nonNumericVal1', ...
                'For BCs of the form {char, val} val must be numeric.')
        end
        BCRHS = num2cell(repmat(v, SYSSIZE, 1));
        numLinBCs = numel(v)*SYSSIZE;

        if ( strcmpi(bc.left, 'dirichlet') )
            A = @(n) [1, zeros(1, n-1)];
        elseif ( strcmpi(bc.left, 'neumann') )
            A = @(n) neumannLeft(n)*(diff(DOMAIN)/2);
        else
            error('CHEBFUN:CHEBFUN:pde15s:bcSyntax1', 'Unknown BC syntax');
        end
        bc.left = struct('op', []);
        bc.left.op = cell(SYSSIZE, 1);
        for k = 1:SYSSIZE
            bc.left.op{k} = ...
                @(n) [zeros(1,(k-1)*n),  A(n),  zeros(1, (SYSSIZE-k)*n)];
        end

    elseif ( numel(bc.left) == 1 && isa(bc.left, 'function_handle') )
        %%%%%%%%%%%%%%%%%%%%%%%  GENERAL BCS (LEFT)  %%%%%%%%%%%%%%%%%%%%%%%%%%
        opLBC = parseFun(bc.left, 'lbc');
        numLBCs = max(size(opLBC(0, DOMAIN(1), uTmp)));
        bc.left = struct('op', []);

    else
        error('CHEBFUN:CHEBFUN:pde15s:bcSyntax2', 'Unknown BC syntax');

    end

    %% MIDDLE BCS / OTHER CONSTRAINTS
    if ( isfield(bc, 'middle') && isa(bc.middle, 'function_handle') )
        %%%%%%%%%%%%%%%%%%%%%  GENERAL BCS (MIDDLE)  %%%%%%%%%%%%%%%%%%%%%%%%%%
        opMBC = parseFun(bc.middle, 'bc');
        numMBCs = max(size(opMBC(0, mean(DOMAIN), uTmp)));
        bc.middle = [];

    end

    %% RIGHT BCS
    if ( isempty(bc.right) )
        bc.right = struct('op', []);

    elseif ( ischar(bc.right) || (iscell(bc.right) && ischar(bc.right{1})) )
        %%%%%%%%%%%%%%%%%%% DIRICHLET AND NEUMANN BCS (RIGHT) %%%%%%%%%%%%%%%%%
        if ( iscell(bc.right) )
            v = bc.right{2};
            bc.right = bc.right{1};
        else
            v = 0;
        end
        if ( ~isnumeric(v) )
            error('CHEBFUN:CHEBFUN:pde15s:nonNumericVal1', ...
                'For BCs of the form {char, val} val must be numeric.')
        end
        BCRHS = [BCRHS, num2cell(repmat(v, SYSSIZE, 1))];
        numLinBCs = numLinBCs + numel(v)*SYSSIZE;

        if ( strcmpi(bc.right, 'dirichlet') )
            A = @(n) [zeros(1, n-1), 1];
        elseif ( strcmpi(bc.right, 'neumann') )
            A = @(n) neumannRight(n)*(diff(DOMAIN)/2);
        else
            error('CHEBFUN:CHEBFUN:pde15s:bcSyntax3', 'Unknown BC syntax');
        end
        bc.right = struct('op', []);
        bc.right.op = cell(SYSSIZE, 1);
        for k = 1:SYSSIZE
            bc.right.op{k} = ...
                @(n) [zeros(1,(k-1)*n),  A(n),  zeros(1,(SYSSIZE-k)*n)];
        end

    elseif ( numel(bc.right) == 1 && isa(bc.right, 'function_handle') )
        %%%%%%%%%%%%%%%%%%%%%%  GENERAL BCS (RIGHT)  %%%%%%%%%%%%%%%%%%%%%%%%%%
        opRBC = parseFun(bc.right, 'rbc');
        numRBCs = max(size(opRBC(0, DOMAIN(end), uTmp)));
        bc.right = struct('op', []);

    else
        error('CHEBFUN:CHEBFUN:pde15s:bcSyntax4', 'Unknown BC syntax');

    end

end

% Total number of BCs:
numBCs = numLinBCs + numLBCs + numMBCs + numRBCs;

if ( isstruct(bc) && ~isfield(bc, 'middle') )
    bc.middle.op = [];
end

%% %%%%%%%%%%%%%%%%%%%%%%% SUPPORT FOR COUPLED BVP-PDES! %%%%%%%%%%%%%%%%%%%%%%%
% Experimental feature for coupled ode/pde systems: (An entry equal to 1 denotes
% that the corresponding variable appears with a time derivative. 0 otherwise.)
if ( isfield(opt, 'PDEflag') && ~isempty(opt.PDEflag) )
    pdeFlag = opt.PDEflag;
else
    pdeFlag = true;
end
if ( numel(pdeFlag) == 1 )
    pdeFlag = repmat(pdeFlag, 1, SYSSIZE);
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% PERIODIC %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

if ( ~ISPERIODIC )
    techHandle = @chebtech2;
    points = @chebpts;
    mydouble = @chebdouble;
else
    techHandle = @trigtech;
    points = @trigpts;
    mydouble = @trigdouble;
    u0 = chebfun(u0, 'trig', 'eps', tol);
    u0 = simplify(u0, tol);
end
tech = techHandle();

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% %%%%%%%%%%%%%%%%%%% SETUP TOLERANCES AND INITIAL CONDITION %%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% ODE tolerances:
aTol = odeget(opt, 'AbsTol', tol);
rTol = odeget(opt, 'RelTol', tol);
if ( isnan(optN) )
    aTol = min(aTol, tol);
    rTol = min(rTol, tol);
end
opt.AbsTol = aTol;
opt.RelTol = rTol;

% Check for (and try to remove) piecewise initial conditions:
u0Trans = u0(1).isTransposed;
if ( u0Trans )
    u0 = transpose(u0);
end
for k = 1:numel(u0)
    if ( numel(u0(k).funs) > 1 )
        u0(k) = merge(u0(k), 'all', 1025, tol);
        if ( u0(k).nfuns > 1 )
            error('CHEBFUN:CHEBFUN:pde15s:piecewise', ...
                'Piecewise initial conditions are not supported.');
        end
    end
end

% Simplify initial condition to tolerance or fixed size in optN:
if ( isnan(optN) )
    u0 = simplify(u0);
else
    for k = 1:numel(u0)
        u0(k).funs{1}.onefun = prolong(u0(k).funs{1}.onefun, optN);
    end
end

% Initial condition:
uCurrent = u0;
% Storage:
uOut = cell(1, numel(tt));
uOut{1} = uCurrent;
tOut(1) = tt(1);

% Initialise variables for ONESTEP():
M = []; P = []; n = []; B = 0;

% Set the preferences:
pref = tech.techPref();
pref.chebfuneps = tol;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%% TIME CHUNKS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% Plot initial condition:
currentLength = length(u0);
if ( doPlot )
    plotFun(u0, tt(1));
    leg = legend(axesSol, varNames);
    set(leg, 'HandleVisibility', 'off')
    drawnow
end
DONE = false;

if ( ~isnan(optN) )
    % Non-adaptive in space:
    currentLength = optN;
    tSpan = tt;
    solvePDE(tSpan); % Do all chunks at once!

else
    % Adaptive in space

    % Min-length is 9
    currentLength = max(currentLength, 9);
    tCurrent = tt(1);
    tSpan = tt;
    while ( tCurrent < tt(end) && ~DONE )
        tSpan(tSpan < tCurrent) = [];
        solvePDE(tSpan);
    end

end

if ( doPlot && ~doHold )
    hold off
end

% Ensure tOut is a column vector
tOut = tOut(:);

uOut = prepare4output(uOut);

    function uOut = prepare4output(uIn)
        % If we only had one dependent variable, return an array valued CHEBFUN
        % instead of a QUASIMATRIX.
        if ( SYSSIZE == 1 )
            uOut = horzcat(uIn{:});
        else
            blocks = cell(SYSSIZE, numel(uIn));
            for kk = 1:SYSSIZE
                blocks(kk,:) = cellfun(@(u) extractColumns(u, kk), uIn, ...
                    'UniformOutput', false);
            end
            uOut = chebmatrix(blocks); % CHEBMATRIX
        end
    end

switch nargout
    case 0
    case 1
        if ( length(tIn) == 2 )     % Only T_start and T_end were given.
            uOut = uOut(:,[1,end]); % Strip intermediate steps.
        end
        varargout{1} = uOut;
    case 2
        varargout{1} = tOut;
        varargout{2} = uOut;
    case 1 + size(uOut, 1)
        varargout{1} = tOut;
        for varCounter = 1:size(uOut, 1)
            varargout{varCounter + 1} = chebfun(uOut(varCounter,:)); %#ok<AGROW>
        end
    otherwise
        error('CHEBFUN:CHEBFUN:pdeSolve:nargout', ...
            'Incorrect number of output arguments.');
end

clear global

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%  ONESTEP  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

    function solvePDE(tSpan)
        % Constructs the result of one time chunk at fixed discretization.

        % Evaluate the chebfun at discrete points:
        x = points(currentLength, DOMAIN);
        U0 = feval(uCurrent, x);

        if ( ISPERIODIC )

            % The discretisation length
            n = currentLength;

        elseif ( isempty(n) || (n ~= currentLength) )
            % This depends only on the size of n. If this is the same, reuse!

            % The new discretisation length
            n = currentLength;

            % Linear constraints:
            if ( numLinBCs > 0 )
                bcop = [bc.left.op ; bc.middle.op ; bc.right.op];
                B = cell2mat(cellfun(@(f) feval(f, n), bcop, 'UniformOutput', false));
            end

            % Zero matrix for algebraic (boundary) terms:
            Z = zeros(numBCs, SYSSIZE*n);
            % Initialise evaluated BC rhs vector:
            q = zeros(numBCs, 1);

            % Project / mass matrix.
            P = cell(1, SYSSIZE);
            M = cell(1, SYSSIZE);

            for kk = 1:SYSSIZE
%                 xk = chebpts(n-DIFFORDER(kk), DOMAIN, 1);
                xk = chebpts(n-DIFFORDER(kk)+2, DOMAIN, 2); xk([1,end]) = [];
                P{kk} = barymat(xk, x);
                M{kk} = pdeFlag(kk)*P{kk};
            end
            P = [ Z ; blkdiag(P{:}) ];
            M = [ Z ; blkdiag(M{:}) ];

            % Multiply by user-defined mass matrix
            if ( userMassSet )
                M = feval(userMass, n)*M;
            end

            % ODE options: (mass matrix)
            opt = odeset(opt, 'Mass', M, 'MassSingular', 'yes', ...
                'MStateDependence', 'none');

        end

        if ( ~ISPERIODIC )
            % We have to ensure the starting condition satisfies the boundary
            % conditions, or else we will get a singularity in time. We do this
            % by tweaking the BC values to match the reality. Changing the IC
            % itself is much trickier. Find out what the BC deviance from
            % nominal really is:
            BCVALOFFSET = 0;             % recover nominal value in next call
            F = odeFun(tSpan(1), U0(:)); % also assigns values to "q"

            % If this is for the initial chunk, check whether the initial
            % condition nearly satisfies the BCs. We're quite lax about this,
            % because discretization at low N can cause derivatives to look
            % fairly bad.
            BCrows = 1:numBCs;
            if ( throwBCwarning && (length(uOut) > 1) && ...
                    (norm(F(BCrows)) > 0.05*norm(F)) )
                warning('CHEBFUN:CHEBFUN:pde15s:BadIC',...
                    'Initial state may not satisfy the boundary conditions.')
                throwBCwarning = false;
            end
            if ( adjustBCs )
                BCVALOFFSET = F(BCrows) - q;
            end
        end

        % Solve ODE over time chunk with the selected solver:
        try
            [~, ~] = ODESOLVER(@odeFun, tSpan, U0, opt);
        catch ME
            if ( strcmp(ME.identifier, 'MATLAB:odearguments:SizeIC') )
                error('CHEBFUN:CHEBFUN:pde15s:dims', ...
                    'Dimension mismatch. Check boundary conditions.');
            else
                rethrow(ME)
            end
        end

        %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
        %% %%%%%%%%%%%%%%%%%%%%%%%%%%%  ODEFUN  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
        %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
        function F = odeFun(t, U)
            % This is what the ODESOLVER() calls.

            % Reshape to n by SYSSIZE:
            U = reshape(U, n, SYSSIZE);

            % Evaluate the PDEFUN:
            myU = mydouble(U, DOMAIN);
            F = double(pdeFun(t, x, myU));
            F = F(:);

            if ( ISPERIODIC )
                return
            end

            % Enforce boundary constraints:

            % Project:
            F = P*F;

            % Replacements for the BC algebraic conditions:
            if ( numLinBCs > 0 )
                % Get the right-hand sides: (may be time-dependent)
                for l = 1:numLinBCs
                    if ( isa(BCRHS{l}, 'function_handle') )
                        q(l,1) = feval(BCRHS{l}, t);
                    else
                        q(l,1) = BCRHS{l};
                    end
                end
                indx = 1:numLinBCs;
                F(indx) = B*U(:) - q(indx);
            else
                indx = 0;
            end

            % Replacements for the nonlinear BC conditions:
            if ( numLBCs > 0 )
                indx = indx(end) + (1:numLBCs);
                fTmp = double(opLBC(t, x, myU));
                F(indx) = fTmp(1, :);
            end
            if ( numMBCs > 0 )
                % TODO: This won't work if there are also left nonlin BCs
                indx = indx(end) + (1:numMBCs);
                F(indx) = double(opMBC(t, x, myU));
            end
            if ( numRBCs > 0 )
                indx = indx(end) + (1:numRBCs);
                fTmp = double(opRBC(t, x, myU));
                F(indx) = fTmp(end,:);
            end

            % Adjust BC rows by the needed offset from original time:
            indx = 1:numBCs;
            F(indx) = F(indx) - BCVALOFFSET;

        end
    end

end

%% %%%%%%%%%%%%%%%%%%%%%%%%%%  MISC  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%

function [outFun, varNamesParsed] = parseFun(inFun, bcFlag)
% Rewrites the input function handle to call the right DIFF, SUM, methods, etc,
% and convert the input @(t, x, u, v, w, ...) to @(t, x, U).
%
% For PARSEFUN(INFUN), if the INFUN has only one independent variable as an
% input, we assume this is x. PARSEFUN(INFUN, 'lbc') or PARSEFUN(INFUN, 'rbc')
% will assume the variable is t. PARSEFUN(INFUN, 'bc') will assume space, but
% throw a warning.

global SYSSIZE

% Ensure backwards compatibility by processing the input function.
[inFun, varNamesParsed] = backCompat(inFun);

% V4: We don't accept only time or space as input args (i.e., both or nothing).
% V5: Actually, we now accept @(u, x) diff(u, 2) + sin(x).*u (for CHEBOPs).
Nind = nargin(inFun) - SYSSIZE;
if ( Nind == 0 )
    outFun = @(t, x, u) conv2cell(inFun, u);
elseif ( Nind == 1 )
    if ( nargin > 1 )
        if ( strcmp(bcFlag, 'bc') )
            warning('CHEBFUN:CHEBFUN:pde15s:bcInput', ...
                ['Please input both time and space independent variable to ' ...
                 '.BC function handle inputs.\nAssuming given variable is ' ...
                 'time.']);
             outFun = @(t, x, u) conv2cell(inFun, u, x);
        else
            outFun = @(t, x, u) conv2cell(inFun, u, t);
        end
    else
        outFun = @(t, x, u) conv2cell(inFun, u, x);
    end
elseif ( Nind == 2 )
    outFun = @(t, x, u) conv2cell(inFun, u, t, x);
else
    error('CHEBFUN:CHEBFUN:pde15s:inputs_ind', ...
        ['Incorrect number of independent variables in input function. ' ...
         '(Must be 0, 1, or 2).']);
end

end

function newFun = conv2cell(oldFun, u, varargin)
% This function allows the use of different variables in the anonymous function,
% rather than using the clunky quasi-matrix notation.
%
% Inputs:
%   oldFun in the original function handle
%   u will be the solution we evaluate at
%   varargin possibly contains the time and space variables
% Output
%   newFun the modified function handle

global SYSSIZE
% Note. This is faster than mat2cell!
tmpCell = cell(1, SYSSIZE);
for qk = 1:SYSSIZE
    tmpCell{qk} = extractColumns(u, qk);
end
newFun = oldFun(varargin{:}, tmpCell{:});
end

function getDIFFORDER(pdeFun, DOMAIN)
% Extract the DIFFORDER by evaluating the operator at some NaN values.
global DIFFORDER SYSSIZE
% Find the order of each of the variables:
tmp = chebdouble(zeros(SYSSIZE), DOMAIN);
v = pdeFun(0, 0, tmp);
DIFFORDER = get(v, 'diffOrder');
DIFFORDER = max(DIFFORDER, 0);
end

function out = dealWithStructInput(in)
% Parse the inputs for BCs.
% Throw a warning or an error for a struct. Convert numeric value to 'dirichet'.
if ( isstruct(in) )
    if ( numel(in) == 1)
        warning('CHEBFUN:CHEBFUN:pde15s:bcstruct', ...
            'PDE15S no longer supports struct inputs for bc.left and bc.right.')
        if ( isfield(in, 'val') )
            out = {in.op, in.val};
        else
            out = in.op;
        end
    else
        error('CHEBFUN:CHEBFUN:pde15s:bcstruct', ...
            'PDE15S no longer supports struct inputs for bc.left and bc.right.')
    end
elseif ( isempty(in) )
    out = [];
elseif ( isnumeric(in) )
    out = {'dirichlet', in};
else
    out = in;
end
end

function [outFun, varNamesOut] = backCompat(inFun)
% In V4 PDE15S required the user to pass in dummy function handles for the
% differential operators. For example, u'' + u' + sum(u) would have needed
%  pdefun = @(u, t, x, diff, sum) diff(u, 2) + diff(u) + sum(u).
% V5 deals with this in a different way (by using the CHEBDOUBLE class), but we
% would still like to support the old syntax (at least for now).
%
% To do this, we parse the input function for the strings 'diff', 'Diff', and
% 'D'. If we find any of these, we assume the old syntax is being used, and
% redefine the function handle appropriately. Of course, this is not fool-proof,
% but it should work most of the time.

global SYSSIZE

% Determine the variable names:
str = func2str(inFun);
parLoc = strfind(str, ')');
varList = str(3:parLoc(1)-1);
commaLoc = strfind(varList, ',');
varNames = cell(1, numel(commaLoc)+1);
for k = 1:numel(commaLoc)
    varNames{k} = varList(1:commaLoc(k)-1);
    varList(1:commaLoc(k)) = [];
    commaLoc = commaLoc - commaLoc(k);
end
varNames{numel(commaLoc)+1} = varList;

% Look for 'diff', 'Diff', or 'D':
diffLoc = cellfun(@(v) any(strcmp(v, {'diff', 'Diff', 'D'})), varNames);
idx = find(diffLoc);
if ( isempty(idx) )
    % None present - new syntax!
    outFun = inFun;
    varNamesOut = varNames;
    return
end

warning('CHEBFUN:CHEBFUN:pde15s:oldSyntax', ...
    ['The syntax\n ' str '\nis deprecated and may not be supported in ', ...
     'future releases.\n Please see the PDE15S documentation for details.'] )

% Switch the order for V5 syntax:
varNamesNewOrder = varNames([(1+SYSSIZE):(idx-1), 1:SYSSIZE]);

% Get the new list of variables:
varList1 = varNamesNewOrder{1};
varList2 = varNames{1};
for k = 2:idx-1
    varList1 = [varList1, ',', varNamesNewOrder{k}]; %#ok<AGROW>
    varList2 = [varList2, ',', varNames{k}]; %#ok<AGROW>
end

% Compile the new function string:
funList = {'@diff', '@sum', '@cumsum', '@fred'};
newStr = ['@(', varList1, ')' 'inFun(' varList2];
for k = 0:(nargin(inFun) - idx)
    newStr = [newStr, ',', funList{k+1}]; %#ok<AGROW>
end
newStr = [newStr, ')'];

% Make the new function handle:
outFun = eval(newStr);

% Return the obtained variable names
varNamesOut = varNamesNewOrder;
end


classdef testStoredComputedChecker < handle

    properties (Abstract, Access = protected)
        variablesToStore
        storedVar
        error
    end

    properties (Access = protected)
        computedVar
    end

    methods (Access = protected)
        function computeError(obj)
            d = numel(obj.variablesToStore);
            err = ones(d,1);
            for ivar = 1:d
                sV = obj.storedVar{ivar};
                cV = obj.computedVar{ivar};
                err(ivar) = norm(sV - cV)/norm(sV);
            end
            obj.error = norm(err);
        end
    end
end


function gee_its_simple_test
%GEE_ITS_SIMPLE_TEST tests the "Gee! It's Simple!" package
% Exhaustive test of the "Gee! It's Simple!" package.  Returns the largest
% relative residual for any solution to A*x=b (using the inf norm).  This test
% exercises all statements in the package.  Note that the rand state is
% modified.
%
% Example:
%   gee_its_simple_test ;
%
% See also: gee_its_simple, gee_its_short, rand, mldivide, gee_its_simple_resid

% Copyright 2007, Timothy A. Davis.
% http://www.cise.ufl.edu/research/sparse

%-------------------------------------------------------------------------------
% error-handling tests
%-------------------------------------------------------------------------------

fprintf ('\nTesting error handling (expect error and warning messages):\n\n');

gunk = 0 ;
ok = 0 ;

lasterr ('') ;

try
    % too many inputs
    gee_its_simple_factorize (A,gunk) ;                                     %#ok
catch
    ok = ok + 1 ;
    disp (lasterr) ;
end
fprintf ('\n') ;

try
    % too many outputs
    [LU,p,rcnd,gunk] = gee_its_simple_factorize (A) ;                       %#ok
catch
    ok = ok + 1 ;
    disp (lasterr) ;
end
fprintf ('\n') ;

try
    % too few inputs
    gee_its_simple_factorize ;                                              %#ok
catch
    ok = ok + 1 ;
    disp (lasterr) ;
end
fprintf ('\n') ;

try
    % too many inputs
    x = gee_its_simple (A,b,gunk) ;                                         %#ok
catch
    ok = ok + 1 ;
    disp (lasterr) ;
end
fprintf ('\n') ;

try
    % too many outputs
    [x,rcnd,gunk] = gee_its_simple (A,b) ;                                  %#ok
catch
    ok = ok + 1 ;
    disp (lasterr) ;
end
fprintf ('\n') ;

try
    % too few inputs
    x = gee_its_simple ;                                                    %#ok
catch
    ok = ok + 1 ;
    disp (lasterr) ;
end
fprintf ('\n') ;

try
    % too many inputs
    x = gee_its_simple_forwardsolve (A,b,gunk) ;                            %#ok
catch
    ok = ok + 1 ;
    disp (lasterr) ;
end
fprintf ('\n') ;

try
    % too many outputs
    [x,gunk] = gee_its_simple_forwardsolve (A,b) ;                          %#ok
catch
    ok = ok + 1 ;
    disp (lasterr) ;
end
fprintf ('\n') ;

try
    % too few inputs
    x = gee_its_simple_forwardsolve ;                                       %#ok
catch
    ok = ok + 1 ;
    disp (lasterr) ;
end
fprintf ('\n') ;

try
    % too many inputs
    x = gee_its_simple_backsolve (A,b,gunk) ;                               %#ok
catch
    ok = ok + 1 ;
    disp (lasterr) ;
end
fprintf ('\n') ;

try
    % too many outputs
    [x,gunk] = gee_its_simple_backsolve (A,b) ;                             %#ok
catch
    ok = ok + 1 ;
    disp (lasterr) ;
end
fprintf ('\n') ;

try
    % too few inputs
    x = gee_its_simple_backsolve ;                                          %#ok
catch
    ok = ok + 1 ;
    disp (lasterr) ;
end
fprintf ('\n') ;

try
    % rectangular A
    x = gee_its_simple (eye (4,3), ones (4,1)) ;                            %#ok
catch
    ok = ok + 1 ;
    disp (lasterr) ;
end
fprintf ('\n') ;

try
    % A is 3D
    x = gee_its_simple (ones (9,3,3), ones (9,1)) ;                         %#ok
catch
    ok = ok + 1 ;
    disp (lasterr) ;
end
fprintf ('\n') ;

try
    % b is 3D
    x = gee_its_simple (eye (3,3), ones (3,3,3)) ;                          %#ok
catch
    ok = ok + 1 ;
    disp (lasterr) ;
end
fprintf ('\n') ;

try
    % dimensions of A and b do not matrix
    x = gee_its_simple (eye (3,3), ones (4,1)) ;                            %#ok
catch
    ok = ok + 1 ;
    disp (lasterr) ;
end
fprintf ('\n') ;

try
    % dimensions of L and b do not matrix
    x = gee_its_simple_forwardsolve (eye (3,3), ones (4,1)) ;               %#ok
catch
    ok = ok + 1 ;
    disp (lasterr) ;
end
fprintf ('\n') ;

try
    % dimensions of U and b do not matrix
    x = gee_its_simple_backsolve (eye (3,3), ones (4,1)) ;                  %#ok
catch
    ok = ok + 1 ;
    disp (lasterr) ;
end
fprintf ('\n') ;

% singular matrix
lastwarn ('') ;
x = gee_its_simple (0, 1) ;                                                 %#ok
[msg, id] = lastwarn ;
if (~isempty (msg) & ~isempty (id))                                         %#ok
    ok = ok + 1 ;
end
fprintf ('\n') ;

% ill-conditioned matrix
lastwarn ('') ;
x = gee_its_simple ([1e30 2e30 ; 1 1], [1 ; 1]) ;                           %#ok
[msg, id] = lastwarn ;
if (~isempty (msg) & ~isempty (id))                                         %#ok
    ok = ok + 1 ;
end

if (ok ~= 20)
    error ('test failed') ;
end

fprintf ('\n\nError-handing tests complete (all error messages and warnings\n');
fprintf ('shown above were expected).  Now testing for accuracy:\n\n') ;

%-------------------------------------------------------------------------------
% compare accuracy vs. backslash
%-------------------------------------------------------------------------------

maxerr1 = 0 ;   % largest residual for A\b (gee_its_sweet)
maxerr2 = 0 ;   % largest residual for gee_its_simple (A,b)
maxerr3 = 0 ;   % largest residual for gee_its_short (A,b)
maxerr4 = 0 ;   % largest residual for gee_its_too_short (A,b)
rmax = 0 ;      % largest relative difference in rcond

nmax = 50 ;     % largest dimension of A to test
cmax = 10 ;     % largest number of columns of b to test
ntrials = 2 ;   % number of trials for each x=A\b

rand ('state', 0) ;

for n = 0:nmax
    fprintf ('.') ;
    for c = 0:cmax
        for trial = 1:ntrials

            % set up the system
            A = rand (n) ;
            b = rand (n,c) ;

            % solve it four different ways
            x1 = gee_its_sweet (A,b) ;      % this is just a one-liner: x=A\b
            x2 = gee_its_simple (A,b) ;
            x3 = gee_its_short (A,b) ;
            x4 = gee_its_too_short (A,b) ;

            % get the relative residuals
            err1 = gee_its_simple_resid (A, x1, b) ;
            err2 = gee_its_simple_resid (A, x2, b) ;
            err3 = gee_its_simple_resid (A, x3, b) ;
            err4 = gee_its_simple_resid (A, x4, b) ;

            maxerr1 = max (maxerr1, err1) ;
            maxerr2 = max (maxerr2, err2) ;
            maxerr3 = max (maxerr3, err3) ;
            maxerr4 = max (maxerr4, err4) ;

            if (max ([err1 err2 err3]) > 1e-14)
                error ('test failed') ;
            end

            % test rcond
            if (n > 0)
                [L,U,p] = lu (A) ;                                          %#ok
                r1 = min (abs (diag (U))) / max (abs (diag (U))) ;
                [LU,p,r2] = gee_its_simple_factorize (A) ;
                if (r1 ~= 0)
                    r = abs (r1 - r2) / r1 ;
                    rmax = max (rmax, r) ;
                end
                if (r > 1e-10)
                    error ('test failed') ;
                end
            end
        end
    end
end
fprintf ('\n') ;

fprintf ('max residual for backslash:         %g\n', maxerr1) ;
fprintf ('max residual for gee_its_simple:    %g\n', maxerr2) ;
fprintf ('max residual for gee_its_short:     %g\n', maxerr3) ;
fprintf ('max residual for gee_its_too_short: %g (no pivoting!)\n', maxerr4) ;

fprintf ('\n\nAll tests passed OK\n') ;


% Description:
%
%     Return a list of unique values.  A must be a cell array of vectors of
%     numeric data.  B will be sorted in the order of the first occurrance of
%     each unique value in A.
%
% Syntax:
%
%     [ B, I, J ] = mUnique(A)
%
% Inputs:
%
%     A - [ 1 x N ] (cell)
%
% Outputs:
%
%     B - [ 1 x U ] (cell)
%     I - [ 1 x U ] (int)
%     J - [ 1 x N ] (int)
%
% Details:
%
% Examples:
%
% Notes:
%
% Author(s):
%
%     William Gruner (williamgruner@gmail.com)
%
% References:
%
% Acknowledgements:
%
%     Many thanks to Dr. Erik Erhardt and Dr. Elena Allen of the Mind Research
%     Network (www.mrn.org) for their continued collaboration.
%
% Version:
%
%     $Author: williamgruner $
%     $Date: 2010-04-01 11:39:09 -0600 (Thu, 01 Apr 2010) $
%     $Revision: 482 $

function [ B, I, J ] = mUnique(A)

    temp = cell(0);

    for i = 1 : length(A)
        temp{i} = num2str(A{i}, '%d ');
    end

    [ B, I, J ] = unique(temp);

    I = sort(I);
    B = A(I);


function cMap=spectral(varargin)

%%

switch nargin
    case 0
        n=250;
    case 1
        n=varargin{1};
end

%%

cMap=[0.368627450980392,0.309803921568627,0.635294117647059;...
      0.196078431372549,0.533333333333333,0.741176470588235;...
      0.400000000000000,0.760784313725490,0.647058823529412;...
      0.670588235294118,0.866666666666667,0.643137254901961;...
      0.901960784313726,0.960784313725490,0.596078431372549;...
      1,1,0.749019607843137;...
      0.996078431372549,0.878431372549020,0.545098039215686;...
      0.992156862745098,0.682352941176471,0.380392156862745;...
      0.956862745098039,0.427450980392157,0.262745098039216;...
      0.835294117647059,0.243137254901961,0.309803921568627;...
      0.619607843137255,0.00392156862745098,0.258823529411765];

if n~=size(cMap,1)
    cMap=resampleColormap(cMap,n);
end

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


function kern = sqexpKernExpandParam(kern, params)

% SQEXPKERNEXPANDPARAM Create kernel structure from SQEXP kernel's parameters.
% FORMAT
% DESC returns a pre-built compound squared exponential kernel structure filled with the
% parameters in the given vector. This is used as a helper function to
% enable parameters to be optimised in, for example, the NETLAB
% optimisation functions.
% ARG kern : the kernel structure in which the parameters are to be
% placed.
% ARG param : vector of parameters which are to be placed in the
% kernel structure.
% RETURN kern : kernel structure with the given parameters in the
% relevant locations.
%
% SEEALSO : sqexpKernParamInit, sqexpKernExtractParam, kernExpandParam
%
% COPYRIGHT : Neil D. Lawrence, 2004

% KERN

kern.inverseWidth = params(1);
kern.rbfVariance = params(2);
kern.biasVariance = params(3);
kern.whiteVariance = params(4);


function test_suite = test_minimumCaliperDiameter
%TESTMINIMUMCALIPERDIAMETER  One-line description here, please.
%
%   output = testMinimumCaliperDiameter(input)
%
%   Example
%   testMinimumCaliperDiameter
%
%   See also
%
%
% ------
% Author: David Legland
% e-mail: david.legland@grignon.inra.fr
% Created: 2011-04-14,    using Matlab 7.9.0.529 (R2009b)
% Copyright 2011 INRA - Cepia Software Platform.

test_suite = functiontests(localfunctions);

function testSquare(testCase) %#ok<*DEFNU>

p1 = [10 10];
p2 = [20 10];
p3 = [20 20];
p4 = [10 20];
square = [p1;p2;p3;p4];

width = minimumCaliperDiameter(square);
testCase.assertEqual(10, width);

function testRectangle(testCase)

p1 = [10 10];
p2 = [20 10];
p3 = [20 50];
p4 = [10 50];
square = [p1;p2;p3;p4];

width = minimumCaliperDiameter(square);
testCase.assertEqual(10, width);

function testCross(testCase)

pts = [...
    10 40; ...
    50 40; ...
    50 20; ...
    70 20; ...
    70 40; ...
    110 40; ...
    110 60; ...
    70 60; ...
    70 80; ...
    50 80; ...
    50 60; ...
    10 60];
width = minimumCaliperDiameter(pts);
testCase.assertEqual(60, width);

% try again by shuffling vertices
pts = pts([ 4 10 6 12 2 11 8 1 5 3 9 7], :);
width = minimumCaliperDiameter(pts);
testCase.assertEqual(60, width);


function p = preprocess_bilinear_bounds(p)

if ~isempty(p.integer_variables)
    for i = 1:size(p.bilinears,1)
        if ismember(p.bilinears(i,2),p.integer_variables)
            if ismember(p.bilinears(i,3),p.integer_variables)
                p.integer_variables = [p.integer_variables p.bilinears(i,1)];
            end
        end
    end
    if p.K.f > 0
        for i = 1:p.K.f
            if all(ismember(p.F_struc(i,:),[0 1 -1]))
                involved = find(p.F_struc(i,2:end));
                % One variable is linear combination of  integer variables
                if (nnz(ismember(involved,p.integer_variables)) == length(involved)-1) & length(involved)>1
                    p.integer_variables = [p.integer_variables involved];
                end
            end
        end
        p.integer_variables  = unique(p.integer_variables );
    end
end

if ~isempty(p.binary_variables)
    for i = 1:size(p.bilinears,1)
        if ismember(p.bilinears(i,2),p.binary_variables)
            if ismember(p.bilinears(i,3),p.binary_variables)
                p.binary_variables = [p.binary_variables p.bilinears(i,1)];
            end
        end
    end
    for i = 1:p.K.f
        if all(p.F_struc(i,:) == fix(p.F_struc(i,:)))
            involved = find(p.F_struc(i,2:end));
            % One variable is linear combination of binary variables
            if (nnz(ismember(involved,p.binary_variables)) == length(involved)-1) & length(involved)>1
                p.integer_variables = [p.integer_variables involved];
            end
        end
    end
    p.binary_variables  = unique(p.binary_variables );

end

if isempty(p.ub)
    p.ub = repmat(inf,length(p.c),1);
end
if isempty(p.lb)
    p.lb = repmat(-inf,length(p.c),1);
end
if ~isempty(p.F_struc)
    [lb,ub,used_rows_eq,used_rows_lp] = findulb(p.F_struc,p.K);
    if ~isempty([used_rows_eq;used_rows_lp])
        lower_defined = find(~isinf(lb));
        if ~isempty(lower_defined)
            p.lb(lower_defined) = max(p.lb(lower_defined),lb(lower_defined));
        end
        upper_defined = find(~isinf(ub));
        if ~isempty(upper_defined)
            p.ub(upper_defined) = min(p.ub(upper_defined),ub(upper_defined));
        end
        % Remove linear bound inequalities
        if ~isempty(used_rows_lp)
            used_rows_lp = used_rows_lp(find(~any(p.F_struc(p.K.f+used_rows_lp,1+p.nonlinears),2)));
            not_used_rows = setdiff(1:p.K.l,used_rows_lp);
             newKCutl = [];
            for i = 1:length(p.KCut.l)
                 newKCutl  = [newKCutl  find(not_used_rows==p.KCut.l(i))];
               % p.KCut.l(i) = find(not_used_rows == p.KCut.l(i));
               % p.originalModel.KCut.l(i) = find(not_used_rows == p.originalModel.KCut.l(i) );
            end
             p.KCut.l = newKCutl;
            if ~isempty(used_rows_lp)
                p.F_struc(p.K.f+used_rows_lp,:)=[];
              %  p.originalModel.F_struc(p.originalModel.K.f+used_rows_lp,:)=[];
                p.K.l = p.K.l - length(used_rows_lp);
               % p.originalModel.K.l = p.originalModel.K.l - length(used_rows_lp);
            end
        end
        % Remove linear bound inequalities
        if ~isempty(used_rows_eq)
            used_rows_eq = used_rows_eq(find(~any(p.F_struc(used_rows_eq,1+p.nonlinears),2)));
            not_used_rows = setdiff(1:p.K.f,used_rows_eq);
            newKCutf = [];
            for i = 1:length(p.KCut.f)
                newKCutf  = [newKCutf  find(not_used_rows==p.KCut.f(i))];
               % p.KCut.f(i) = find(not_used_rows==p.KCut.f(i));
              %  p.originalModel.KCut.f(i) = find(not_used_rows==p.originalModel.KCut.f(i));
            end
            p.KCut.f = newKCutf;
            if ~isempty(used_rows_eq)
                p.F_struc(used_rows_eq,:)=[];
              %  p.originalModel.F_struc(used_rows_eq,:)=[];
                p.K.f = p.K.f - length(used_rows_eq);
              %  p.originalModel.K.f = p.originalModel.K.f - length(used_rows_eq);
            end
        end
    end
end
p.lb(p.binary_variables) = max(0,p.lb(p.binary_variables));
p.ub(p.binary_variables) = min(1,p.ub(p.binary_variables));
p.lb(p.integer_variables) = ceil(p.lb(p.integer_variables));
p.ub(p.integer_variables) = floor(p.ub(p.integer_variables));
p = clean_bounds(p);
if ~isempty(p.bilinears)
    p = updatemonomialbounds(p);
end


function jac = autoJac(fun,x0,varargin)
%AUTOJAC  Returns a Automatically Differentiated (AD) Jacobian
%
%   jac = autoJac(fun,x0) uses the user supplied Matlab function to
%   generate the Jacobian using automatic differentiation. The user
%   function must be a Matlab function and must not call external code or
%   class / toolbox functions. If your function breaks any of these
%   conditions consider using mklJac instead.
%
%   The underlying AD algorithm is adiff by William McIlhagga and its
%   documentation pdf is provided in the Differentiation folder. See the
%   BSD license below the code.

%   Copyright (C) 2011 Jonathan Currie (IPL)

if(~isa(fun,'function_handle'))
    error('Fun should be a function handle!');
end

[~,jac] = adiffget(fun(adiff(x0),varargin{:}));

end

% Copyright (c) 2010, William McIlhagga
% All rights reserved.
%
% Redistribution and use in source and binary forms, with or without
% modification, are permitted provided that the following conditions are
% met:
%
%     * Redistributions of source code must retain the above copyright
%       notice, this list of conditions and the following disclaimer.
%     * Redistributions in binary form must reproduce the above copyright
%       notice, this list of conditions and the following disclaimer in
%       the documentation and/or other materials provided with the distribution
%
% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
% POSSIBILITY OF SUCH DAMAGE.


function tests = test_rotateStack90(varargin)
%TEST_ROTATESTACK90  One-line description here, please.
%   output = test_rotateStack90(input)
%
%   Example
%   test_rotateStack90
%
%   See also
%
%
% ------
% Author: David Legland
% e-mail: david.legland@grignon.inra.fr
% Created: 2010-05-18,    using Matlab 7.9.0.529 (R2009b)
% Copyright 2010 INRA - Cepia Software Platform.

tests = functiontests(localfunctions);

function testRotateX(testCase)

img = createBasicTestImage;

rotX1th = cat(3, [5 6;1 2], [7 8;3 4]);
imgRX1 = rotateStack90(img, 2, 1);
assertEqual(testCase, rotX1th, imgRX1);
imgRX1 = rotateStack90(img, 'x', 1);
assertEqual(testCase, rotX1th, imgRX1);

rotX2th = cat(3, [7 8;5 6], [3 4;1 2]);
imgRX2 = rotateStack90(img, 2, 2);
assertEqual(testCase, rotX2th, imgRX2);
imgRX2 = rotateStack90(img, 'x', 2);
assertEqual(testCase, rotX2th, imgRX2);

rotX3th = cat(3, [3 4;7 8], [1 2;5 6]);
imgRX3 = rotateStack90(img, 2, 3);
assertEqual(testCase, rotX3th, imgRX3);
imgRX3 = rotateStack90(img, 'x', 3);
assertEqual(testCase, rotX3th, imgRX3);

function testRotateY(testCase)

img = createBasicTestImage;

rotY1th = cat(3, [2 6;4 8], [1 5;3 7]);
imgRY1 = rotateStack90(img, 1, 1);
assertEqual(testCase, rotY1th, imgRY1);
imgRY1 = rotateStack90(img, 'y', 1);
assertEqual(testCase, rotY1th, imgRY1);

rotY2th = cat(3, [6 5;8 7], [2 1;4 3]);
imgRY2 = rotateStack90(img, 1, 2);
assertEqual(testCase, rotY2th, imgRY2);
imgRY2 = rotateStack90(img, 'y', 2);
assertEqual(testCase, rotY2th, imgRY2);

rotY3th = cat(3, [5 1;7 3], [6 2;8 4]);
imgRY3 = rotateStack90(img, 1, 3);
assertEqual(testCase, rotY3th, imgRY3);
imgRY3 = rotateStack90(img, 'y', 3);
assertEqual(testCase, rotY3th, imgRY3);

function testRotateZ(testCase)

img = createBasicTestImage;

rotZ1th = cat(3, [3 1;4 2], [7 5;8 6]);
imgRZ1 = rotateStack90(img, 3, 1);
assertEqual(testCase, rotZ1th, imgRZ1);
imgRZ1 = rotateStack90(img, 'z', 1);
assertEqual(testCase, rotZ1th, imgRZ1);

rotZ2th = cat(3, [4 3;2 1], [8 7;6 5]);
imgRZ2 = rotateStack90(img, 3, 2);
assertEqual(testCase, rotZ2th, imgRZ2);
imgRZ2 = rotateStack90(img, 'z', 2);
assertEqual(testCase, rotZ2th, imgRZ2);

rotZ3th = cat(3, [2 4;1 3], [6 8;5 7]);
imgRZ3 = rotateStack90(img, 3, 3);
assertEqual(testCase, rotZ3th, imgRZ3);
imgRZ3 = rotateStack90(img, 'z', 3);
assertEqual(testCase, rotZ3th, imgRZ3);

function testRotateGrayscaleY(testCase)

img = createTestImageGray;
dim = size(img);

% rotate around Y-axis
img2 = rotateStack90(img, 1, 1);

% check dimension
assertEqual(testCase, dim([1 3 2]), size(img2));

function testRotateGrayscaleX(testCase)

img = createTestImageGray;
dim = size(img);

% rotate around X-axis
img2 = rotateStack90(img, 2, 1);
assertEqual(testCase, dim([3 2 1]), size(img2));

function testRotateGrayscaleZ(testCase)

img = createTestImageGray;
dim = size(img);

% rotate around Z-axis
img2 = rotateStack90(img, 3, 1);
assertEqual(testCase, dim([2 1 3]), size(img2));

function testColorImage(testCase)

% Create rgb image with non equal size in each dimension
lx = 1:50;
ly = 1:52;
lz = 1:54;
r = uint8(discreteBall(lx, ly, lz, [20 30 30], 15)*255);
g = uint8(discreteBall(lx, ly, lz, [30 20 30], 15)*255);
b = uint8(discreteBall(lx, ly, lz, [30 30 20], 15)*255);
img = imMergeChannels(r, g, b);

% basic checks
assertEqual(testCase, 'uint8', class(img));
assertEqual(testCase, [52 50 3 54], size(img));

% dimension of base image, without color
dim = size(r);

% rotate around Y-axis
img2 = rotateStack90(img, 1, 1);
assertEqual(testCase, [dim(1) dim(3) 3 dim(2)], size(img2));
assertEqual(testCase, img(1,1,:,1), img2(1, end,:,1));
assertEqual(testCase, img(1,1,:,end), img2(1, 1,:,1));

% rotate around X-axis
img2 = rotateStack90(img, 2, 1);
assertEqual(testCase, [dim(3) dim(2) 3 dim(1)], size(img2));

% rotate around Z-axis
img2 = rotateStack90(img, 3, 1);
assertEqual(testCase, [dim(2) dim(1) 3 dim(3)], size(img2));

function img = createBasicTestImage
img = cat(3, [1 2;3 4], [5 6;7 8]);

function img = createTestImageGray
% create a 3D gray-scale test image
%  ______
% |1    2|\
% | 5    |6|
% |3____4| |
%  \7____\8|
%
lx = 1:10;
ly = 30:5:60;
lz = 50:10:200;
[x, y, z] = meshgrid(lx, ly, lz);
img = uint8(x+y+z);


function atlas_obj = atlas_add_L_R_to_labels(atlas_obj)
% Removes some strings indicating lateralization from atlas labels and adds new _L and _R suffixes for lateralized regions.
% - strings replaced are L_ R_ Left_ Right_ _L _R _Left _Right

labels = atlas_obj.labels;

mypatterns = {'L_' 'R_' 'Left_' 'Right_' '_L' '_R' '_Left' '_Right'};

for i = 1:length(mypatterns)
    labels = regexprep(labels, mypatterns{i}, '');
end

mysuffix = get_suffix(atlas_obj);

for i = 1:length(labels)

    labels{i} = [labels{i} mysuffix{i}];

end

atlas_obj.labels = labels;

end % function

function mysuffix = get_suffix(atlas_obj)

r = atlas2region(atlas_obj);

k = length(r);

for j = 1:k

    voxsign = sign(r(j).XYZmm(1, :)); % sign of each x coord. - = left
    modal_x = mode(voxsign);

    switch modal_x
        case -1
            labelstr = '_L';
        case 1
            labelstr = '_R';
        case 0
            labelstr = '';
    end

    % proportional asymmetry. 1 is vary lateralized. 0 is balanced
    % across L/R.  If symmetrical, then label _M
    prop_asym = abs(sum(voxsign == -1) - sum(voxsign == 1)) ./ length(voxsign);

    if prop_asym < .5, labelstr = ''; end

    mysuffix{j} = labelstr;

end % j

end % subfunction


function a = imag(a)
%IMAG         imaginary part of gradients
%
%   c = imag(a)
%
%

% written  10/16/98     S.M. Rump
% modified 12/18/02     S.M. Rump  improved performance
% modified 04/04/04     S.M. Rump  set round to nearest for safety
% modified 04/06/05     S.M. Rump  rounding unchanged
%

  a.x = imag(a.x);
  a.dx = imag(a.dx);


%Voice Based Biometric System
%By Ambavi K. Patel.

%finds 20 centroids from equally partitioned mfcc matrix
function cb2=centr(mfcc1)
%partitioning mfcc1 matrix into 20 region and taking centroid from each
[r,c]=size(mfcc1);
dc=floor(c/10);
dr=floor(r/2);
for i=1:10
    for j=1:2
        x=(dr*(j-1))+1:dr*j;
        y=(dc*(i-1))+1:dc*i;
        t=dc+dr;
        cb1(j,i)=sum(sum(mfcc1(x,y)))/t;
    end;
end;
k=1;
for i=1:10
    for j=1:2
        cb2(1,k)=cb1(j,i);
        k=k+1;
    end;
end;


function Q = spm_MDP_VB_game(MDP)
% auxiliary plotting routine for spm_MDP_VB - multiple trials
% FORMAT Q = spm_MDP_VB_game(MDP)
%
% MDP.P(M,T)      - probability of emitting action 1,...,M at time 1,...,T
% MDP.X           - conditional expectations over hidden states
% MDP.R           - conditional expectations over policies
% MDP.O(O,T)      - a sparse matrix encoding outcomes at time 1,...,T
% MDP.S(N,T)      - a sparse matrix encoding states at time 1,...,T
% MDP.U(M,T)      - a sparse matrix encoding action at time 1,...,T
% MDP.W(1,T)      - posterior expectations of precision
%
% MDP.xn  = Xn    - simulated neuronal encoding of policies
% MDP.wn  = wn    - simulated neuronal encoding of precision
% MDP.da  = dn    - simulated dopamine responses (deconvolved)
% MDP.rt  = rt    - simulated dopamine responses (deconvolved)
%
% returns summary of performance:
%
%     Q.X  = x    - expected hidden states
%     Q.R  = u    - final policy expectations
%     Q.S  = s    - initial hidden states
%     Q.O  = o    - final outcomes
%     Q.p  = p    - performance
%     Q.q  = q    - reaction times
%
% please see spm_MDP_VB
%__________________________________________________________________________
% Copyright (C) 2005 Wellcome Trust Centre for Neuroimaging

% Karl Friston
% $Id: spm_MDP_VB_game.m 7766 2020-01-05 21:37:39Z karl $

% numbers of transitions, policies and states
%--------------------------------------------------------------------------
if iscell(MDP(1).X)
    Nf = numel(MDP(1).B);                 % number of hidden state factors
    Ng = numel(MDP(1).A);                 % number of outcome factors
else
    Nf = 1;
    Ng = 1;
end

% graphics
%==========================================================================
Nt    = numel(MDP);                      % number of trials
Ne    = MDP(1).T;                        % number of outcomes per trial
Np    = size(MDP(1).V,2);                % number of policies
for i = 1:Nt

    % assemble expectations of hidden states and outcomes
    %----------------------------------------------------------------------
    for j = 1:Ne
        for k = 1:Ne
            for f = 1:Nf
                try
                    x{f}{i,1}{k,j} = gradient(MDP(i).xn{f}(:,:,j,k)')';
                catch
                    x{f}{i,1}{k,j} = gradient(MDP(i).xn(:,:,j,k)')';
                end
            end
        end
    end

    % fill-in selected policies if missing
    %----------------------------------------------------------------------
    try
        MDP(i).v;
    catch
        MDP(i).v = [];
    end

    s(:,i) = MDP(i).s(:,1);
    o(:,i) = MDP(i).o(:,end);
    u{1,i} = MDP(i).R;
    v{1,i} = MDP(i).v;
    w(:,i) = MDP(i).dn,2;

    % assemble context learning
    %----------------------------------------------------------------------
    for f = 1:Nf
        try
            try
                D = MDP(i).d{f};
            catch
                D = MDP(i).D{f};
            end
        catch
            try
                D = MDP(i).d;
            catch
                D = MDP(i).D;
            end
        end
        d{f}(:,i) = D/sum(D);
    end

    % assemble performance
    %----------------------------------------------------------------------
    p(i)  = 0;
    for g = 1:Ng
        try
            U = spm_softmax(MDP(i).C{g});
        catch
            U = spm_softmax(MDP(i).C);
        end
        for t = 1:Ne
            p(i) = p(i) + log(U(MDP(i).o(g,t),t))/Ne;
        end
    end

    % reaction time
    %----------------------------------------------------------------------
    try
        q(i) = sum(MDP(i).rt(2:end));
    catch
        q(i) = 0;
    end

end

% assemble output structure if required
%--------------------------------------------------------------------------
if nargout
    Q.X  = x;            % expected hidden states
    Q.R  = u;            % final policy expectations
    Q.S  = s;            % inital hidden states
    Q.O  = o;            % final outcomes
    Q.p  = p;            % performance
    Q.q  = q;            % reaction times
    return
end

% Initial states and expected policies (habit in red)
%--------------------------------------------------------------------------
col   = {'r.','g.','b.','c.','m.','k.'};
u     = spm_cat(u);
v     = spm_cat(v);
t     = 1:Nt;
subplot(6,1,1)
if Nt < 64
    MarkerSize = 24;
else
    MarkerSize = 16;
end
image(t,1:Np,64*(1 - u)),  hold on
plot(linspace(1,Nt,numel(v)),v,'c.','MarkerSize',16)
for f = 1:Nf
    for i = 1:max(s(f,:))
        j = find(s(f,:) == i);
        plot(t(j),j - j + f - Nf,col{rem(i - 1,6)+ 1},'MarkerSize',MarkerSize)
    end
end
set(gca,'YLim',[-Nf,Np] + 1/2), box off
try
    E = spm_softmax(spm_cat({MDP.e}));
    plot(Np*(1 - E(end,:)),'r:')
end
title('Initial state and policy selection')
xlabel('Trial'),ylabel('Policy'), hold off

% Performance
%--------------------------------------------------------------------------
q     = q - mean(q);
q     = q/std(q);
subplot(6,1,2), bar(p,'k'),   hold on
plot(q,'.c','MarkerSize',16), hold on
plot(q,':c')
for g = 1:Ng
    for i = 1:max(o(g,:))
        j = find(o(g,:) == i);
        plot(t(j),j - j + 3 + g,col{rem(i - 1,6)+ 1},'MarkerSize',MarkerSize)
    end
end
title('Final outcome, performance and reaction times')
ylabel('Expected utility'), spm_axis tight, hold off, box off

% Initial states (context)
%--------------------------------------------------------------------------
subplot(6,1,3)
col   = {'r','b','g','c','m','k','r','b','g','c','m','k'};
for f = 1:Nf
    if Nf > 1
        plot(spm_cat(x{f}),col{f}), hold on
    else
        plot(spm_cat(x{f}))
    end
end
title('State estimation (ERPs)'), ylabel('Response'),
spm_axis tight, hold off, box off

% Precision (dopamine)
%--------------------------------------------------------------------------
subplot(6,1,4)
w   = spm_vec(w);
if Nt > 8
    fill([1 1:length(w) length(w)],[0; w.*(w > 0); 0],'k'), hold on
    fill([1 1:length(w) length(w)],[0; w.*(w < 0); 0],'k'), hold off
else
    bar(w,1.1,'k')
end
title('Precision (dopamine)')
ylabel('Precision','FontSize',12), spm_axis tight, box off
YLim = get(gca,'YLim'); YLim(1) = 0; set(gca,'YLim',YLim);
set(gca,'XTickLabel',{});

% learning - D
%--------------------------------------------------------------------------
for f = 1:Nf
    subplot(6*Nf,1,Nf*4 + f), image(64*(1 - d{f}))
    if f < 2
        title('Context Learning')
    end
    set(gca,'XTick',1:Nt);
    if f < Nf
        set(gca,'XTickLabel',{});
    end
    set(gca,'YTick',1);
    try
        set(gca,'YTickLabel',MDP(1).label.factor{f});
    end
    try
        set(gca,'YTickLabel',MDP(1).Bname{f});
    end

end
if isfield(MDP(1),'c')
    title('Learning (C and D)')
else
    return
end

% Habit learning
%--------------------------------------------------------------------------
k     = round(linspace(1,Nt,6));
for j = 1:length(k)
    h = MDP(k(j)).c;
    h = h*diag(1./sum(h));
    subplot(6,6,30 + j), image(64*(1 - h))
    axis image
end


function [njm] = tapas_zeromat(jm)
%% Zeros all but the first entry in each column of jm.
%
% Input
%   jm      -- Matrix.
%
% Output
%   njm     -- Matrix.
%

% aponteeduardo@gmail.com
% copyright (C) 2015
%

njm = zeros(size(jm));

j = 0;
i = 0;

while j < size(jm, 2)
    j = j + 1;
    i = 0;
    while i < size(jm, 1)
        i = i + 1;
        if jm(i, j);
            njm(i, j) = 1;
            break
        end
    end
end
end


function [TorF, vstr, rdate] = have_feature_intlinprog()
%HAVE_FEATURE_INTLINPROG  Detect availability/version info for INTLINPROG
%
%   Feature detection function implementing 'intlinprog' tag for HAVE_FEATURE
%   to detect availability/version of INTLINPROG, MILP solver from MATLAB
%   Optimization Toolbox 7.0 (R2014a) and later.
%
%   See also HAVE_FEATURE, MIQPS_MASTER, INTLINPROG.

%   MP-Opt-Model
%   Copyright (c) 2004-2020, Power Systems Engineering Research Center (PSERC)
%   by Ray Zimmerman, PSERC Cornell
%
%   This file is part of MP-Opt-Model.
%   Covered by the 3-clause BSD License (see LICENSE file for details).
%   See https://github.com/MATPOWER/mp-opt-model for more info.

TorF = 0;
vstr = '';
rdate = '';
v = have_feature('optim', 'all');
if v.av && have_feature('matlab')   %% ignore Octave version
    TorF = exist('intlinprog', 'file') == 2;
    if TorF
        vstr = v.vstr;
        rdate = v.date;
    end
end


classdef Balls<handle

%--------------------------------------------------------------------------
%Collision Ball Libraries
%Version 1.00
%Created by Stepen
%Created 20 March 2013
%Last modified 20 May 2013
%--------------------------------------------------------------------------
%Constructor Format:
%- Balls()
%  creates an empty Collision Ball object.
%- Balls(x,y,u,v)
%  creates a Collision Ball where x, y, u, and v respectively are column
%  array with same row length defining balls position and velocity.
%- Balls(x,y,u,v,m,r,c)
%  creates a Collision Ball where x, y, u, v, m, r, and c respectively are
%  column array (except for c is 3column array) with same row length
%  defining balls position, velocity, mass, radius, and color.
%Public Non-static Method:
%- addBall(x,y,u,v,m,r,c)
%  add a ball to Collision Ball object.
%- addRandomBall(x,y,u,v,m,r,c)
%  add a random ball to Collision Ball object.
%- removeBall(ballID)
%  remove selected ball from Collision Ball object.
%- isOccupied(x,y,r)
%  check whether a ball at x and y position with radius of r can be added
%  to Collision Ball ojbect without conflict.
%- moveBall(dt)
%  move all balls in Collision Ball object for given time interval.
%- draw()
%  plot all balls in Collision Ball object at current time.
%- play(dt)
%  move and animate all balls in Collision Ball object using given time
%  interval as animation refresh rate.
%Public Static Method:
%- isOutBound(x,y,radius)
%  check a ball at x and y position with radius of r is within Collision
%  Ball object's containment space.
%--------------------------------------------------------------------------

%CodeStart-----------------------------------------------------------------
%Declaring object properties
    properties(SetAccess=protected)
        n_ball
        mass
        radius
        color
        x
        y
        u
        v
        ANIMATIONSTAT=false;
    end
%Declaring constant
    properties(Constant=true)
        MASS_MAX=100;
        RADIUS_MAX=20;
        XMIN=-100;
        XMAX=100;
        YMIN=-100;
        YMAX=100;
        UMIN=-25;
        UMAX=25;
        VMIN=-25;
        VMAX=25;
    end
%Declaring constructor
    methods
        function this=Balls(varargin)
            %Initiating field
            this.n_ball=0;
            this.mass=zeros(0,1);
            this.radius=zeros(0,1);
            this.color=zeros(0,3);
            this.x=zeros(0,1);
            this.y=zeros(0,1);
            this.u=zeros(0,1);
            this.v=zeros(0,1);
            %Assigning input arguments to its appropriate field
            switch nargin
                case 7
                    for i=1:numel(varargin{1})
                        this.addBall(varargin{1}(i),varargin{2}(i),...
                                     varargin{3}(i),varargin{4}(i),...
                                     varargin{5}(i),varargin{6}(i),...
                                     varargin{7}(i,:))
                    end
                case 4
                    for i=1:numel(varargin{1})
                        this.addBall(varargin{1}(i),varargin{2}(i),...
                                     varargin{3}(i),varargin{4}(i),...
                                     10,10,[0,0,0])
                    end
                case 1
                    Balls.checksinglevarargin(varargin{1});
                    for i=1:varargin{1}
                        ballValidity=false;
                        while ~ballValidity
                            %Creating random ball
                            [xr,yr,ur,vr,mr,rr,cr]=Balls.generateball();
                            %Checking random ball validity
                            ballValidity=(~this.isOccupied(xr,yr,rr))&&...
                                         (~Balls.isOutBound(xr,yr,rr));
                            %Adding ball
                            if ballValidity
                                this.addBall(xr,yr,ur,vr,mr,rr,cr)
                            end
                        end
                    end
                case 0

                otherwise
                    error('Unexpected constructor format!');
            end
        end
    end
%Declaring public method
    methods
        %Declaring method to add a ball
        function addBall(this,x,y,u,v,mass,radius,color)
            %Checking input arguments
            switch nargin
                case 1
                    this.addRandomBall()
                    return;
                case 5
                    Balls.checkfourvarargins(x,u,v,y)
                    mass=10;
                    radius=10;
                    color=[0,0,255];
                case 8
                    Balls.checksevenvarargins(x,y,u,v,mass,radius,color);
                otherwise
                    error('Unexpected format of input argument!');
            end
            %Checking for validity
            if this.isOccupied(x,y,radius)
                error('New ball is conflicting with the existing balls!');
            end
            if Balls.isOutBound(x,y,radius)
                error('New ball is out of bounds!');
            end
            %Adding ball
            this.n_ball=this.n_ball+1;
            this.mass=[this.mass;mass];
            this.radius=[this.radius;radius];
            this.color=[this.color;color];
            this.x=[this.x;x];
            this.y=[this.y;y];
            this.u=[this.u;u];
            this.v=[this.v;v];
            %Updating drawing plot (if drawing already exist)
            mainfig=Balls.getfigurehandle();
            if ~isempty(mainfig)
                rectangle('Parent',Balls.getaxeshandle(),...
                          'FaceColor',color/255,...
                          'Curvature',[1,1],...
                          'Position',[x-radius,y-radius,...
                                      2*radius,2*radius]);
            end
        end
        %Declaring method to add a random ball
        function addRandomBall(this)
            %Initiating variable for while loop
            canBeAdded=false;
            %Generating random ball until it is valid
            while ~canBeAdded
                %Generating random position
                [xr,yr,ur,vr,mr,rr,cr]=Balls.generateball();
                %Checking random ball validity
                canBeAdded=(~this.isOccupied(xr,yr,rr))&&...
                           (~Balls.isOutBound(xr,yr,rr));
            end
            %Adding random ball
            this.addBall(xr,yr,ur,vr,mr,rr,cr);
        end
        %Declaring method to remove a ball
        function removeBall(this,ballID)
            %Checking input value
            Balls.checksinglevarargin(ballID);
            if ballID>this.n_ball
                error('Ball ID should be less that number of balls!')
            end
            %Deleting ball
            this.mass(ballID)=[];
            this.radius(ballID)=[];
            this.color(ballID,:)=[];
            this.x(ballID)=[];
            this.y(ballID)=[];
            this.u(ballID)=[];
            this.v(ballID)=[];
            this.n_ball=this.n_ball-1;
            %Updating ball drawing (if drawing already exist)
            mainfig=Balls.getfigurehandle();
            if ~isempty(mainfig)
                rectlist=findobj(mainfig,'Type','rect');
                delete(rectlist(this.n_ball+2-ballID))
            end
        end
        %Declaring method to check whether a position is available
        function stat=isOccupied(this,x,y,radius)
            %Initiating output variable
            stat=false;
            %Checking space for each ball
            for i=1:this.n_ball
                S=norm([this.x(i)-x,this.y(i)-y]);
                if S<(this.radius(i)+radius)
                    stat=true;
                    return
                end
            end
        end
        %Declaring function to move the balls
        function moveBall(this,dt)
            %Predicting ball position without considering collision
            [xf,yf]=predictposition(this,dt);
            %Finding collision
            stat=Balls.findcollision(xf,yf,this.radius);
            %while ~isempty(stat)
                %Correcting the speed of colliding balls
                for i=1:size(stat,1)
                    ID1=stat(i,1);
                    ID2=stat(i,2);
                    [this.u(ID1),this.v(ID1),...
                     this.u(ID2),this.v(ID2)]=Balls.collide(this.mass(ID1),...
                                                            this.x(ID1),...
                                                            this.y(ID1),...
                                                            this.u(ID1),...
                                                            this.v(ID1),...
                                                            this.mass(ID2),...
                                                            this.x(ID2),...
                                                            this.y(ID2),...
                                                            this.u(ID2),...
                                                            this.v(ID2));
                end
                %Finding out-of-bount balls
                hstat=(xf<Balls.XMIN+this.radius)|...
                      (xf>Balls.XMAX-this.radius);
                this.u(hstat)=-this.u(hstat);
                vstat=(yf<Balls.YMIN+this.radius)|...
                      (yf>Balls.YMAX-this.radius);
                this.v(vstat)=-this.v(vstat);
                %Limiting maximum speed
                this.u(this.u<Balls.UMIN)=Balls.UMIN;
                this.u(this.u>Balls.UMAX)=Balls.UMAX;
                this.v(this.v<Balls.VMIN)=Balls.VMIN;
                this.v(this.v>Balls.VMAX)=Balls.VMAX;
                %Recalculating ball position with updated velocity
                [xf,yf]=predictposition(this,dt);
                %Rechecking collision
                %stat=Balls.findcollision(xf,yf,this.radius);
                %Updating ball position
                this.x=xf;
                this.y=yf;
            %end
        end
        %Declaring function to draw the balls
        function draw(this)
            %Checking for existing figure
            mainfig=Balls.getfigurehandle();
            %Creating or getting figure handle
            if isempty(mainfig)
                %Creating brand new figure and axes
                WinSize=[1,1,800,800];
                ScrSize=get(0,'ScreenSize');
                mainfig=figure('Name','Elastic Collision Balls',...
                               'NumberTitle','off',...
                               'Menubar','none',...
                               'Units','pixels',...
                               'Position',[(ScrSize(3)-WinSize(3))/2,...
                                           (ScrSize(4)-WinSize(4))/2,...
                                           WinSize(3),WinSize(4)]);
                mainaxs=axes('Parent',mainfig,...
                             'Units','normalized',...
                             'Position',[0.1,0.1,0.8,0.8]);
            else
                %Getting axes handle
                mainaxs=Balls.getaxeshandle();
                %Deleting previous rectangle object
                delete(Balls.getrecthandle());
            end
            %Drawing white rectangle as field
            rectangle('Parent',mainaxs,...
                      'FaceColor','w',...
                      'Position',[Balls.XMIN,Balls.YMIN,...
                                  Balls.XMAX-Balls.XMIN,...
                                  Balls.YMAX-Balls.YMIN]);
            %Drawing balls as curve rectangle
            for i=1:this.n_ball
                rectangle('Parent',mainaxs,...
                          'FaceColor',this.color(i,:)/255,...
                          'Curvature',[1,1],...
                          'Position',[this.x(i)-...
                                      this.radius(i),...
                                      this.y(i)-...
                                      this.radius(i),...
                                      2*this.radius(i),...
                                      2*this.radius(i)]);
            end
            %Correcting axes
            axis(mainaxs,[Balls.XMIN,Balls.XMAX,Balls.YMIN,Balls.YMAX]);
            axis(mainaxs,'equal');
            axis(mainaxs,'off');
        end
        %Declaring function to animate the balls
        function play(this,dt)
            %Checking for existing figure
            mainfig=Balls.getfigurehandle();
            %Creating or getting figure handle
            if isempty(mainfig)
                %Creating brand new figure and axes
                WinSize=[1,1,800,800];
                ScrSize=get(0,'ScreenSize');
                mainfig=figure('Name','Elastic Collision Balls',...
                               'NumberTitle','off',...
                               'Menubar','none',...
                               'Units','pixels',...
                               'Position',[(ScrSize(3)-WinSize(3))/2,...
                                           (ScrSize(4)-WinSize(4))/2,...
                                           WinSize(3),WinSize(4)]);
                mainaxs=axes('Parent',mainfig,...
                             'Units','normalized',...
                             'Position',[0.1,0.1,0.8,0.8]);
            else
                %Getting axes handle
                mainaxs=Balls.getaxeshandle();
                %Deleting previous rectangle object
                delete(Balls.getrecthandle());
            end
            %Drawing rectangle for visualization field
            rectangle('Parent',mainaxs,...
                      'FaceColor','w',...
                      'Position',[Balls.XMIN,Balls.YMIN,...
                                  Balls.XMAX-Balls.XMIN,...
                                  Balls.YMAX-Balls.YMIN]);
            %Drawing balls as curved rectangle
            ballrect=zeros(this.n_ball,1);
            for i=1:this.n_ball
                ballrect(i)=rectangle('Parent',mainaxs,...
                                      'FaceColor',this.color(i,:)/255,...
                                      'Curvature',[1,1],...
                                      'Position',[this.x(i)-...
                                                  this.radius(i),...
                                                  this.y(i)-...
                                                  this.radius(i),...
                                                  2*this.radius(i),...
                                                  2*this.radius(i)]);
            end
            %Animating balls
            this.ANIMATIONSTAT=true;
            while this.ANIMATIONSTAT
                %Timing process for animation delay
                t=tic;
                %Moving balls
                moveBall(this,dt)
                %Redrawing balls
                for i=1:this.n_ball
                    try
                        set(ballrect(i),'Position',[this.x(i)-...
                                                    this.radius(i),...
                                                    this.y(i)-...
                                                    this.radius(i),...
                                                    2*this.radius(i),...
                                                    2*this.radius(i)])
                    catch
                        this.ANIMATIONSTAT=false;
                        return
                    end
                end
                %Correcting axes
                axis(mainaxs,[Balls.XMIN,Balls.XMAX,Balls.YMIN,Balls.YMAX]);
                axis(mainaxs,'equal');
                axis(mainaxs,'off');
                %Delaying animation
                tt=toc(t);
                pause(dt-tt)
                if (dt-tt)<0
                    disp('Interval time too short for animation!')
                    disp('Animation might not be displayed properly!')
                end
            end
        end
    end
%Declaring public static method
    methods(Static=true)
    %Declaring method to check whether a ball is out of bound
        function stat=isOutBound(x,y,radius)
            %Initiating output variable
            stat=false;
            %Checking space for ball position
            if (x<Balls.XMIN+radius)||(x>Balls.XMAX-radius)||...
               (y<Balls.YMIN+radius)||(y>Balls.YMAX-radius)
                stat=true;
                return;
            end
        end
    end
%Declaring private method
    methods(Access=protected)
        %Declaring function to move ball by neglecting collision
        function [x,y]=predictposition(this,dt)
            x=this.x+(this.u*dt)/2;
            y=this.y+(this.v*dt)/2;
        end
    end
%Declaring static private methods
    methods(Access=protected,...
            Static=true)
        %Declaring function to get Balls' figure
        function fig=getfigurehandle()
            %Initiating output variable
            fig=[];
            %Finding all figure objects
            list=findobj(0,'type','figure');
            %Checking each figure name
            for i =1:numel(list)
                if isequal(get(list(i),'Name'),'Elastic Collision Balls')
                    fig=list(i);
                    return;
                end
            end
        end
        %Declaring object to get Balls' axes
        function axs=getaxeshandle()
            axs=findobj(Balls.getfigurehandle(),'type','axes');
        end
        %Declaring function to get Balls' rectangle object
        function rectlist=getrecthandle()
            rectlist=findobj(Balls.getfigurehandle(),'type','rect');
        end
        %Declaring function to check input argument for constructor
        function checksinglevarargin(var1)
            %Checking varargin array size
            if numel(var1)~=1
                error('Input number of balls must be a scalar!');
            end
            %Checking varargin data type
            if (var1<=0)||(mod(var1,1)~=0)
                error('Input number of balls must be a positive integer!');
            end
        end
        function checkfourvarargins(var1,var2,var3,var4)
            %Checking varargin array size
            if (numel(var1)~=1)||(numel(var2)~=1)||...
               (numel(var3)~=1)||(numel(var4)~=1)
                error('Input x, y, u, and v must be a scalar!');
            end
            %Checking varargin data type
            if (~isreal(var1))||(~isreal(var2))||...
               (~isreal(var3))||(~isreal(var4))
                error('Input x, y, u, and v must be real numbers!');
            end
        end
        function checksevenvarargins(var1,var2,var3,var4,var5,var6,var7)
            %Checking varargin for x,y,u,v
            Balls.checkfourvarargins(var1,var2,var3,var4);
            %Checking varargin dimension size
            if (numel(var5)~=1)||(numel(var6)~=1)||(numel(var7)~=3)
                error(['Input mass, radius, and color must be a 2D',...
                       ' column array (3 column array for color)!']);
            end
            %Checking varargin data type
            if (~isreal(var5))||(var5<=0)%||(var5>Balls.MASS_MAX)
                error(['Input mass must be a positive real number',...
                       ' smaller than ',num2str(Balls.MASS_MAX),'!']);
            end
            if (~isreal(var6))||(var6<=0)||(var6>Balls.RADIUS_MAX)
                error(['Input radius must be a positive real number',...
                       ' smaller than ',num2str(Balls.RADIUS_MAX),'!']);
            end
            if (sum(var7<0)~=0)||(sum(var7>255)~=0)||...
               (sum(mod(var7,1)~=0)~=0)
                error(['Input color must be a positive integer',...
                       ' between 0 and 255!']);
            end
        end
        %Declaring function to generate random ball
        function [xr,yr,ur,vr,mr,rr,cr]=generateball()
            mr=rand*Balls.MASS_MAX;
            rr=rand*Balls.RADIUS_MAX;
            cr=floor(rand(1,3)*256);
            xr=Balls.XMIN+rr+...
                (rand*(Balls.XMAX-Balls.XMIN-(2*rr)));
            yr=Balls.YMIN+rr+...
                (rand*(Balls.YMAX-Balls.XMIN)-(2*rr));
            ur=Balls.UMIN+(rand*(Balls.UMAX-Balls.UMIN));
            vr=Balls.VMIN+(rand*(Balls.VMAX-Balls.VMIN));
        end
        %Declaring function to find colliding balls
        function stat=findcollision(x,y,r)
            %Preallocating array for output variable
            stat=zeros(0,2);
            %Finding collision
            for i=1:numel(x)-1
                if isempty(find(stat(:,2)==i, 1))
                    for j=i+1:numel(x)
                        S=norm([x(i)-x(j),y(i)-y(j)]);
                        if S<(r(i)+r(j))
                            stat=[stat;[i,j]];
                            %break
                        end
                    end
                end
            end
        end
        %Declaring function to resolve 2D collision
        function [uf1,vf1,uf2,vf2]=collide(m1,x1,y1,u1,v1,m2,x2,y2,u2,v2)
            %Finding normal angle between two balls
            theta=atan2(y2-y1,x2-x1);
            %Transforming balls velocity to normal coordinate
            VN1=(u1*cos(theta))+(v1*sin(theta));
            VT1=(-u1*sin(theta))+(v1*cos(theta));
            VN2=(u2*cos(theta))+(v2*sin(theta));
            VT2=(-u2*sin(theta))+(v2*cos(theta));
            %Resolving colllision for normal axis
            VN1_F=((VN1*(m1-m2))+(2*m2*VN2))/(m1+m2);
            VN2_F=((VN2*(m2-m1))+(2*m1*VN1))/(m1+m2);
            %Retransforming balls velocity to original coordinate
            uf1=(VN1_F*cos(theta))-(VT1*sin(theta));
            vf1=(VN1_F*sin(theta))+(VT1*cos(theta));
            uf2=(VN2_F*cos(theta))-(VT2*sin(theta));
            vf2=(VN2_F*sin(theta))+(VT2*cos(theta));
        end
    end
%CodeEnd-------------------------------------------------------------------

end


%% rrt_star
% *RRT** is probabilistically optimal variant of RRT which converges
% to the optimal solution asymtotically. Due to the fact that RRT* takes
% cost of the path into consideration, the computational diffuculty
% increases with each node added to tree, a good example of this
% problem is finding neighbors.
%
%% Syntax
%  problem = rrt_star(map, max_iter, is_benchmark, rand_seed, variant)
%  function returns the object of the respective class with the result
%
%% Input
%  map           -- struct with appropriate fields (developer of
%                the class provides more information on this topic)
%  max_iter      -- number of iteration to solve the problem
%  is_benchmark  -- if true saves snapshots of the tree in a special directory
%                boolean variable
%  rand_seed     -- a random seed
%  variant       -- what class to choose, class used defines the problem space
%% Output
% *problem* is an object of an appropriate problem space representing class
%


function V2=tform(T,V)

%%
nDim_T=size(T,2);
nDim_V=size(V,2);

if nDim_T==(nDim_V+1)
        VV=V;
        VV(:,end+1)=1;
        V2=(T*VV')';
        V2=V2(:,[1 2 3]);
elseif nDim_T==nDim_V
        V2=(T*V')';
end

%%

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


function sizes = getVarSizes(obj, inputSizes)
%GETVARSIZES  Get the size of the variables
%   SIZES = GETVARSIZES(OBJ, INPUTSIZES) computes the SIZES of the
%   DagNN variables given the size of the inputs. `inputSizes` is
%   a cell array of the type `{'inputName', inputSize, ...}`
%   Returns a cell array with sizes of all network variables.
%
%   Example, compute the storage needed for a batch size of 256 for an
%   imagenet-like network:
%   ```
%   batch_size = 256; single_num_bytes = 4;
%   input_size = [net.meta.normalization.imageSize, batch_size];
%   var_sizes = net.getVarSizes({'data', input_size});
%   fprintf('Network activations will take %.2fMiB in single.\n', ...
%     sum(prod(cell2mat(var_sizes, 1))) * single_num_bytes ./ 1024^3);
%   ```

% Copyright (C) 2015 Andrea Vedaldi, Karel Lenc.
% All rights reserved.
%
% This file is part of the VLFeat library and is made available under
% the terms of the BSD license (see the COPYING file).

nv = numel(obj.vars) ;
sizes = num2cell(NaN(nv, 4),2)' ;

for i = 1:2:numel(inputSizes)
  v = obj.getVarIndex(inputSizes{i}) ;
  if isnan(v)
    error('Variable `%s` not found in the network.', inputSizes{i});
  end;
  sizes{v} = [inputSizes{i+1}(:)' ones(1, 4 - numel(inputSizes{i+1}))] ;
end

for layer = obj.layers(obj.executionOrder)
  in = layer.inputIndexes ;
  out = layer.outputIndexes ;
  sizes(out) = layer.block.getOutputSizes(sizes(in)) ;
end


function L= DoPyrDec(x,level)

%% use the raised-cosine function to get a smooth transition band.
smooth_func = @rcos;
Pyr_mode=1.5;
L = PyrNDDec_mm(x, 'S', level, Pyr_mode, smooth_func);