function neuron = run_cnmfe_matlab(nam)

    %% make a sources object to store results
    neuron = Sources2D();
    nam = neuron.select_data(nam);

    %% don't ask about anything
    choose_params = false; % manually choose parameters

    %% parameters
    % -------------------------    COMPUTATION    -------------------------  %
    pars_envs = struct('memory_size_to_use', 20, ...   % GB, memory space you allow to use in MATLAB
        'memory_size_per_patch', 5, ...   % GB, space for loading data within one patch
        'patch_dims', [64, 64]);  %GB, patch size

    % -------------------------      SPATIAL      -------------------------  %
    gSig = 3;           % pixel, gaussian width of a gaussian kernel for filtering the data. 0 means no filtering
    gSiz = 10;          % pixel, neuron diameter
    ssub = 1;           % spatial downsampling factor
    with_dendrites = true;   % with dendrites or not
    if with_dendrites
        % determine the search locations by dilating the current neuron shapes
        updateA_search_method = 'dilate';  %#ok<UNRCH>
        updateA_bSiz = 5;
        updateA_dist = neuron.options.dist;
    else
        % determine the search locations by selecting a round area
        updateA_search_method = 'ellipse'; %#ok<UNRCH>
        updateA_dist = 5;
        updateA_bSiz = neuron.options.dist;
    end
    spatial_constraints = struct('connected', true, 'circular', false);  % you can include following constraints: 'circular'
    spatial_algorithm = 'hals_thresh';

    % -------------------------      TEMPORAL     -------------------------  %
    Fs = 30;             % frame rate
    tsub = 1;           % temporal downsampling factor
    deconv_options = struct('type', 'ar1', ... % model of the calcium traces. {'ar1', 'ar2'}
        'method', 'foopsi', ... % method for running deconvolution {'foopsi', 'constrained', 'thresholded'}
        'smin', -1, ...         % minimum spike size. When the value is negative, the actual threshold is abs(smin)*noise level (was -2)
        'optimize_pars', true, ...  % optimize AR coefficients
        'optimize_b', true, ...% optimize the baseline);
        'max_tau', 100);    % maximum decay time (unit: frame);

    nk = 3;             % detrending the slow fluctuation. usually 1 is fine (no detrending)
    % when changed, try some integers smaller than total_frame/(Fs*30)
    detrend_method = 'spline';  % compute the local minimum as an estimation of trend.

    % -------------------------     BACKGROUND    -------------------------  %
    bg_model = 'ring';  % model of the background {'ring', 'svd'(default), 'nmf'}
    nb = 1;             % number of background sources for each patch (only be used in SVD and NMF model)
    bg_neuron_factor = 1.4;
    ring_radius = round(bg_neuron_factor * gSiz);  % when the ring model used, it is the radius of the ring used in the background model.
    %otherwise, it's just the width of the overlapping area
    num_neighbors = 50; % number of neighbors for each neuron

    % -------------------------      MERGING      -------------------------  %
    show_merge = false;  % if true, manually verify the merging step
    merge_thr = 0.65;     % thresholds for merging neurons; [spatial overlap ratio, temporal correlation of calcium traces, spike correlation]
    method_dist = 'max';   % method for computing neuron distances {'mean', 'max'}
    dmin = 4;       % minimum distances between two neurons. it is used together with merge_thr
    dmin_only = 1;  % merge neurons if their distances are smaller than dmin_only.
    merge_thr_spatial = [0.8, 0.4, -inf];  % merge components with highly correlated spatial shapes (corr=0.8) and small temporal correlations (corr=0.1)

    % -------------------------  INITIALIZATION   -------------------------  %
    K = [];             % maximum number of neurons per patch. when K=[], take as many as possible.
    min_corr = 0.4;     % minimum local correlation for a seeding pixel
    min_pnr = 5;       % minimum peak-to-noise ratio for a seeding pixel
    min_pixel = gSig^2;      % minimum number of nonzero pixels for each neuron
    bd = 0;             % number of rows/columns to be ignored in the boundary (mainly for motion corrected data)
    frame_range = [];   % when [], uses all frames
    save_initialization = false;    % save the initialization procedure as a video.
    use_parallel = true;    % use parallel computation for parallel computing
    show_init = true;   % show initialization results
    center_psf = true;  % set the value as true when the background fluctuation is large (usually 1p data)
    % set the value as false when the background fluctuation is small (2p)

    % -------------------------  Residual   -------------------------  %
    min_corr_res = 0.7;
    min_pnr_res = 6;
    seed_method_res = 'auto';  % method for initializing neurons from the residual
    update_sn = true;

    % ----------------------  WITH MANUAL INTERVENTION  --------------------  %
    with_manual_intervention = false;

    % -------------------------  FINAL RESULTS   -------------------------  %
    save_demixed = true;    % save the demixed file or not
    kt = 3;                 % frame intervals

    % -------------------------    UPDATE ALL    -------------------------  %
    neuron.updateParams('gSig', gSig, ...       % -------- spatial --------
        'gSiz', gSiz, ...
        'ring_radius', ring_radius, ...
        'ssub', ssub, ...
        'search_method', updateA_search_method, ...
        'bSiz', updateA_bSiz, ...
        'dist', updateA_bSiz, ...
        'spatial_constraints', spatial_constraints, ...
        'spatial_algorithm', spatial_algorithm, ...
        'tsub', tsub, ...                       % -------- temporal --------
        'deconv_options', deconv_options, ...
        'nk', nk, ...
        'detrend_method', detrend_method, ...
        'background_model', bg_model, ...       % -------- background --------
        'nb', nb, ...
        'ring_radius', ring_radius, ...
        'num_neighbors', num_neighbors, ...
        'merge_thr', merge_thr, ...             % -------- merging ---------
        'dmin', dmin, ...
        'method_dist', method_dist, ...
        'min_corr', min_corr, ...               % ----- initialization -----
        'min_pnr', min_pnr, ...
        'min_pixel', min_pixel, ...
        'bd', bd, ...
        'center_psf', center_psf);
    neuron.Fs = Fs;

    %% distribute data and be ready to run source extraction
    neuron.getReady(pars_envs);

    %% initialize neurons from the video data within a selected temporal range
    if choose_params
        % change parameters for optimized initialization
        [gSig, gSiz, ring_radius, min_corr, min_pnr] = neuron.set_parameters();
    end

    [center, Cn, PNR] = neuron.initComponents_parallel(K, frame_range, save_initialization, use_parallel);
    neuron.compactSpatial();
    if show_init
        figure();
        ax_init= axes();
        imagesc(Cn, [0, 1]); colormap gray;
        hold on;
        plot(center(:, 2), center(:, 1), '.r', 'markersize', 10);
    end

    %% estimate the background components
    neuron.update_background_parallel(use_parallel);
    neuron_init = neuron.copy();

    %%  merge neurons and update spatial/temporal components
    neuron.merge_neurons_dist_corr(show_merge);
    neuron.merge_high_corr(show_merge, merge_thr_spatial);

    %% update spatial components

    % %% pick neurons from the residual
    % [center_res, Cn_res, PNR_res] =neuron.initComponents_residual_parallel([], save_initialization, use_parallel, min_corr_res, min_pnr_res, seed_method_res);
    % if show_init
    %     axes(ax_init);
    %     plot(center_res(:, 2), center_res(:, 1), '.g', 'markersize', 10);
    % end
    % neuron_init_res = neuron.copy();
    if size(neuron.A, 2) == 0
        disp('NO NEURONS FOUND WITH CURRENT INITIALIZATION SETTINGS')
        return
    end

    %% udpate spatial&temporal components, delete false positives and merge neurons
    % update spatial
    if update_sn
        neuron.update_spatial_parallel(use_parallel, true);
        udpate_sn = false;
    else
        neuron.update_spatial_parallel(use_parallel);
    end
    % merge neurons based on correlations
    neuron.merge_high_corr(show_merge, merge_thr_spatial);

    for m=1:2
        % update temporal
        neuron.update_temporal_parallel(use_parallel);

        % delete bad neurons
        neuron.remove_false_positives();

        % merge neurons based on temporal correlation + distances
        neuron.merge_neurons_dist_corr(show_merge);
    end

    %% add a manual intervention and run the whole procedure for a second time
    neuron.options.spatial_algorithm = 'nnls';
    if with_manual_intervention
        show_merge = true;
        neuron.orderROIs('snr');   % order neurons in different ways {'snr', 'decay_time', 'mean', 'circularity'}
        neuron.viewNeurons([], neuron.C_raw);

        % merge closeby neurons
        neuron.merge_close_neighbors(true, dmin_only);

        % delete neurons
        tags = neuron.tag_neurons_parallel();  % find neurons with fewer nonzero pixels than min_pixel and silent calcium transients
        ids = find(tags>0);
        if ~isempty(ids)
            neuron.viewNeurons(ids, neuron.C_raw);
        end
    end
    %% run more iterations
    neuron.update_background_parallel(use_parallel);
    neuron.update_spatial_parallel(use_parallel);
    neuron.update_temporal_parallel(use_parallel);

    K = size(neuron.A,2);
    tags = neuron.tag_neurons_parallel();  % find neurons with fewer nonzero pixels than min_pixel and silent calcium transients
    neuron.remove_false_positives();
    neuron.merge_neurons_dist_corr(show_merge);
    neuron.merge_high_corr(show_merge, merge_thr_spatial);

    if K~=size(neuron.A,2)
        neuron.update_spatial_parallel(use_parallel);
        neuron.update_temporal_parallel(use_parallel);
        neuron.remove_false_positives();
    end

    %% save the workspace for future analysis
    neuron.orderROIs('snr');
    cnmfe_path = neuron.save_workspace();

    %% show neuron contours
    Coor = neuron.show_contours(0.6);

    %% create a video for displaying the
    %amp_ac = 140;
    %range_ac = 5+[0, amp_ac];
    %multi_factor = 10;
    %range_Y = 1300+[0, amp_ac*multi_factor];
    %avi_filename = neuron.show_demixed_video(save_demixed, kt, [], amp_ac, range_ac, range_Y, multi_factor);

    %% save neurons shapes
    neuron.save_neurons();

end


function [K, Knovar, argExp] = linardVardistPsi1Compute(linardkern, vardist, Z)

% LINARDVARDISTPSI1COMPUTE description.

% VARGPLVM

% variational means
N  = size(vardist.means,1);
%  inducing variables
M = size(Z,1);

A = rbfardKern.inverseWidth;

scales = sparse(diag(sqrt(kern.inputScales)));
x = x*scales;

if nargin < 3
  sk = x*x';
else
  x2 = x2*scales;
  sk = x*x2';
end
k = sk*kern.variance;


function [zValues,outputStatistics] = ...
      findEmbeddings(projections,trainingData,trainingEmbedding,parameters)
%findEmbeddings finds the optimal embedding of a data set into a previously
%found t-SNE embedding
%
%   Input variables:
%
%       projections -> N x (pcaModes x numPeriods) array of projection values
%       trainingData -> Nt x (pcaModes x numPeriods) array of wavelet
%                       amplitudes containing Nt data points
%       trainingEmbedding -> Nt x 2 array of embeddings
%       parameters -> struct containing non-default choices for parameters
%
%
%   Output variables:
%
%       zValues -> N x 2 array of embedding results
%       outputStatistics -> struct containing embedding outputs
%
%
% (C) Gordon J. Berman, 2014
%     Princeton University

    if nargin < 4
        parameters = [];
    end
    parameters = setRunParameters(parameters);

    setup_parpool(parameters.numProcessors)

    d = length(projections(1,:));
    numModes = parameters.pcaModes;
    numPeriods = parameters.numPeriods;

    if d == numModes*numPeriods

        data = projections;
        data(:) = bsxfun(@rdivide,data,sum(data,2));

        minT = 1 ./ parameters.maxF;
        maxT = 1 ./ parameters.minF;
        Ts = minT.*2.^((0:numPeriods-1).*log(maxT/minT)/(log(2)*(numPeriods-1)));
        f = fliplr(1./Ts);

    else

        fprintf(1,'Finding Wavelets\n');
        [data,f] = findWavelets(projections,numModes,parameters);
        data(:) = bsxfun(@rdivide,data,sum(data,2));

    end

    fprintf(1,'Finding Embeddings\n');
    [zValues,zCosts,zGuesses,inConvHull,meanMax,exitFlags] = ...
        findTDistributedProjections_fmin(data,trainingData,...
                                    trainingEmbedding,parameters);

    outputStatistics.zCosts = zCosts;
    outputStatistics.f = f;
    outputStatistics.numModes = numModes;
    outputStatistics.zGuesses = zGuesses;
    outputStatistics.inConvHull = inConvHull;
    outputStatistics.meanMax = meanMax;
    outputStatistics.exitFlags = exitFlags;

    if parameters.numProcessors > 1  && parameters.closeMatPool
        close_parpool
    end


function run_training ( )

%% loading the setup %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
options = setup( );

%% learning other requirements %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
if options.learningShape == 1
    disp('Learning shap model...');
    do_learn_shape( options );
end

if options.learningVariation == 1
    disp('Learning data variation...');
    do_learn_variation( options );
end

load( ['model/' options.datasetName '_ShapeModel.mat']    );
load( ['model/' options.datasetName '_DataVariation.mat'] );

%% learn cascaded regression %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

imgDir = options.trainingImageDataPath;
ptsDir = options.trainingTruthDataPath;

%% loading data
disp('Loading training data...');
Data = load_all_data2( imgDir, ptsDir, options );

n_cascades = options.n_cascades;
LearnedCascadedModel{n_cascades}.R = [];

rms = zeros(n_cascades,1);

for icascade = 1 : n_cascades

    options.current_cascade = icascade;

    %% learning single regressors
    if icascade == 1

        new_init_shape = [];
        [R,new_init_shape,rms(icascade)] = learn_single_regressor( ...
            ShapeModel, DataVariation, Data, new_init_shape, options );

        LearnedCascadedModel{icascade}.R = R;

        %% save other parameters %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
        LearnedCascadedModel{icascade}.n_cascades = n_cascades;
        LearnedCascadedModel{icascade}.descSize   = options.descSize;
        LearnedCascadedModel{icascade}.descBins   = options.descBins;

    else

        [R, new_init_shape,rms(icascade)] = learn_single_regressor( ...
            ShapeModel, DataVariation, Data, new_init_shape, options );

        LearnedCascadedModel{icascade}.R = R;

    end

end

%save('result/Trained_RMS.mat' , 'rms');

save([options.modelPath options.slash ...
    'LearnedCascadedModel.mat'],'LearnedCascadedModel');
clear;


function vis_people( expidx, firstidx, nImgs)

if (nargin < 3)
    nImgs = 1;
end

colors = {'r','g','b','c','m','y'};
markerSize = 6;
lineWidth = 4;
edges = [1 2; 2 3; 3 13; 4 13; 4 5; 5 6; 7 8; 8 9; 10 11; 11 12; 9 13; 10 13; 13 14];

p = exp_params(expidx);
load(p.testGT,'annolist');

lastidx = firstidx+nImgs-1;

figure;

for imgidx = firstidx:lastidx

    imgname = annolist(imgidx).image.name;
    img = imread(imgname);

    clf;
    imagesc(img); axis equal; hold on;

    load([p.multicutDir '/prediction_' padZeros(num2str(imgidx),4)], 'people');

    for j = 1:length(people)
        person = people{j};
        person_color = colors{mod(j-1,6)+1};

        for i = 1:size(edges,1)
            pos1 = person(edges(i,1), :);
            pos2 = person(edges(i,2), :);
            if (~isnan(pos1(1)) && ~isnan(pos2(1)))
                plot([pos1(1);pos2(1)],[pos1(2);pos2(2)],[person_color '-'],'linewidth',lineWidth);
            end
        end

        for i = 1:size(person, 1)
            if isnan(person(i, 1))
                continue;
            end
            pos = person(i, :);
            cp = colors{mod(i-1,6)+1};
            plot(pos(:,1),pos(:,2),[cp 'o'],'MarkerSize',markerSize,'MarkerFaceColor',cp,'MarkerEdgeColor','k');
        end

    end
end

end


function samplingPoints = index2sample(this, arrayIndices, iDims)
% Returns index (coordinate) as given by dim-info for specific voxel samplingPoints
%
%   Y = MrDimInfo()
%   samplingPoints = Y.index2sample(arrayIndices, iDims)
%
% If the samplingPoints-arrays are filled explicitly, these values are taken. If
% not, then labelIndex = range(1) + (arrayIndex-1)*resolution is returned
% for the respective dimension
%
% This is a method of class MrDimInfo.
%
% IN
%   arrayIndices        matrix [nVoxels, nDims] of absolute
%                       voxel samplingPoints (one per row) within array
%   iDims               if specified, arrayIndices are assumed to be subset
%                       of all dimensions only
% OUT
%   samplingPoints      matrix [nVoxels, nDims] of voxel
%                       samplingPoints in coordinate system given by dimInfo
%
% EXAMPLE
%   index2sample([3 4 5])
%       => returns
%
%   See also MrDimInfo demo_dim_info MrImageGeometry MrDimInfo.get_voxels

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

if nargin < 3
    iDims = 1:this.nDims;
end

nDimsSubset = numel(iDims);

isRowVector = nDimsSubset == 1 && size(arrayIndices,1) == 1;

if isRowVector
   arrayIndices = arrayIndices(:); % allows row/column vectors as input for 1-dim trafo
end

nVoxels = size(arrayIndices,1);

samplingPoints = zeros(nVoxels,nDimsSubset);

for v = 1:nVoxels
    % for each dimension, take explicit samplingPoints
    for d = 1:nDimsSubset
        iDim = iDims(d);
        samplingPoints(v,d) = this.samplingPoints{iDim}(arrayIndices(v,d));
    end
end

if isRowVector % transform back to row vector for output
    samplingPoints = reshape(samplingPoints, 1, []);
end


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Max manipulability index ALONG A LINE.
% Use stomp like to optimize along a surface/line
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function experiment2B_K20_N30
close all;
global parameters

%STOMP PARAMETERS
%number of particles
K = 20;

%repeat experiment number of times
parameters.n_repeat = 30;
parameters.experiment_name = 'experiment2B_K20_N30.mat';
parameters.animate = 0;

%repeat the experiment E times
random_manips=[];
Gout = [];
for i=1:parameters.n_repeat
    close all
    [pk, final_manip] = path_planning_SCO_4DOF(K);
    Gout{i}=pk;
    random_manips = [random_manips; final_manip];
    save(parameters.experiment_name)
end

'ended'
parameters.experiment_name


function varargout = waterfall(varargin)
%WATERFALL   Waterfall plot of a CHEBFUN2.
%   WATERFALL(F) displays the waterfall plot of F.
%
%   WATERFALL(F, S) displays the column and row chebfuns of F that are used for
%   its approximation.  This is a 3D version of plot(f,S), where S is a string
%   (see PLOT).
%
%   WATERFALL(F, S, 'nslices', N) displays the first min(N,length(f)) columns
%   and rows.
%
%   WATERFALL supports passing options to the plot, similar to standard Matlab
%   plot commands. The options supported are:
%       'color':      Color of lines and markers plotted.
%       'marker':     Marker for pivot points.
%       'markersize': Size of markers plotted at pivot points.
%
%   H = WATERFALL(...) returns a handle to a waterfall plot object.
%
% See also PLOT.

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

[varargout{1:nargout}] = waterfall@separableApprox(varargin{:});

end


function b = imfilter(varargin)
%IMFILTER N-D filtering of multidimensional images.
%   B = IMFILTER(A,H) filters the multidimensional array A with the
%   multidimensional filter H.  A can be logical or it can be a
%   nonsparse numeric array of any class and dimension.  The result,
%   B, has the same size and class as A.
%
%   Each element of the output, B, is computed using double-precision
%   floating point.  If A is an integer or logical array, then output
%   elements that exceed the range of the given type are truncated,
%   and fractional values are rounded.
%
%   B = IMFILTER(A,H,OPTION1,OPTION2,...) performs multidimensional
%   filtering according to the specified options.  Option arguments can
%   have the following values:
%
%   - Boundary options
%
%       X            Input array values outside the bounds of the array
%                    are implicitly assumed to have the value X.  When no
%                    boundary option is specified, IMFILTER uses X = 0.
%
%       'symmetric'  Input array values outside the bounds of the array
%                    are computed by mirror-reflecting the array across
%                    the array border.
%
%       'replicate'  Input array values outside the bounds of the array
%                    are assumed to equal the nearest array border
%                    value.
%
%       'circular'   Input array values outside the bounds of the array
%                    are computed by implicitly assuming the input array
%                    is periodic.
%
%   - Output size options
%     (Output size options for IMFILTER are analogous to the SHAPE option
%     in the functions CONV2 and FILTER2.)
%
%       'same'       The output array is the same size as the input
%                    array.  This is the default behavior when no output
%                    size options are specified.
%
%       'full'       The output array is the full filtered result, and so
%                    is larger than the input array.
%
%   - Correlation and convolution
%
%       'corr'       IMFILTER performs multidimensional filtering using
%                    correlation, which is the same way that FILTER2
%                    performs filtering.  When no correlation or
%                    convolution option is specified, IMFILTER uses
%                    correlation.
%
%       'conv'       IMFILTER performs multidimensional filtering using
%                    convolution.
%
%   Notes
%   -----
%   This function may take advantage of hardware optimization for datatypes
%   uint8, uint16, int16, single, and double to run faster.
%
%   Example
%   -------------
%       originalRGB = imread('peppers.png');
%       h = fspecial('motion',50,45);
%       filteredRGB = imfilter(originalRGB,h);
%       figure, imshow(originalRGB), figure, imshow(filteredRGB)
%       boundaryReplicateRGB = imfilter(originalRGB,h,'replicate');
%       figure, imshow(boundaryReplicateRGB)
%
%   See also FSPECIAL, CONV2, CONVN, FILTER2.

%   Copyright 1993-2013 The MathWorks, Inc.

% Testing notes
% Syntaxes
% --------
% B = imfilter(A,H)
% B = imfilter(A,H,Option1, Option2,...)
%
% A:       numeric, full, N-D array.  May not be uint64 or int64 class.
%          May be empty. May contain Infs and Nans. May be complex. Required.
%
% H:       double, full, N-D array.  May be empty. May contain Infs and Nans.
%          May be complex. Required.
%
% A and H are not required to have the same number of dimensions.
%
% OptionN  string or a scalar number. Not case sensitive. Optional.  An
%          error if not recognized.  While there may be up to three options
%          specified, this is left unchecked and the last option specified
%          is used.  Conflicting or inconsistent options are not checked.
%
%        A choice between these options for boundary options
%        'Symmetric'
%        'Replicate'
%        'Circular'
%         Scalar #  - Default to zero.
%       A choice between these strings for output options
%        'Full'
%        'Same'  - default
%       A choice between these strings for functionality options
%        'Conv'
%        'Corr'  - default
%
% B:   N-D array the same class as A.  If the 'Same' output option was
%      specified, B is the same size as A.  If the 'Full' output option was
%      specified the size of B is size(A)+size(H)-1, remembering that if
%      size(A)~=size(B) then the missing dimensions have a size of 1.
%
%
% IMFILTER should use a significantly less amount of memory than CONVN.

% MATLAB Compiler pragma: iptgetpref is indirectly invoked by the code that
% loads the Intel IPP library.
%#function iptgetpref

[a, h, boundary, sameSize, convMode] = parse_inputs(varargin{:});

[finalSize, pad] = computeSizes(a, h, sameSize);

%Empty Inputs
% 'Same' output then size(b) = size(a)
% 'Full' output then size(b) = size(h)+size(a)-1
if isempty(a)

  b = handleEmptyImage(a, sameSize, finalSize);
  return

elseif isempty(h)

  b = handleEmptyFilter(a, sameSize, finalSize);
  return

end

% Pad input based on dimensions of filter kernel.
a = padImage(a,pad,boundary);

% Separate real and imaginary parts of the filter (h) in MATLAB and
% filter imaginary and real parts of the image (a) in the mex code.
if (isSeparable(a, h))

  % extract the components of the separable filter
  [u,s,v] = svd(h);
  s = diag(s);
  hcol = u(:,1) * sqrt(s(1));
  hrow = v(:,1)' * sqrt(s(1));

  % intermediate results should be stored in doubles in order to
  % maintain sufficient precision
  class_of_a = class(a);
  if ~isa(a,'double')
    change_class = true;
    a = double(a);
  else
    change_class = false;
  end

  % apply the first component of the separable filter (hrow)
  out_size_row = [size(a,1) finalSize(2:end)];
  start = [0 pad(2:end)];
  b_tmp = filterPartOrWhole(a, out_size_row, hrow, start, sameSize, convMode);

  % apply the other component of the separable filter (hcol)
  start = [pad(1) 0 pad(3:end)];
  b = filterPartOrWhole(b_tmp, finalSize, hcol, start, sameSize, convMode);

  if change_class
      b = cast(b, class_of_a);
  end

else % non-separable filter case
    a1=gpuArray(a);
    finalSize1=gpuArray(finalSize);
    h1=gpuArray(h);
    pad1=gpuArray(pad);
    sameSize1=gpuArray(sameSize);
  convMode1=gpuArray(convMode);
  b1 = filterPartOrWhole(a1, finalSize1, h1, pad1, sameSize1, convMode1);
  b=gather(b1);

end

%======================================================================

%--------------------------------------------------------------
function [a, h, boundary, sameSize, convMode] = parse_inputs(a, h, varargin)

narginchk(2,5);

validateattributes(a,{'numeric' 'logical'},{'nonsparse'},mfilename,'A',1);
validateattributes(h,{'double'},{'nonsparse'},mfilename,'H',2);

%Assign defaults
boundary = 0;  %Scalar value of zero
output = 'same';
do_fcn = 'corr';

allStrings = {'replicate', 'symmetric', 'circular', 'conv', 'corr', ...
              'full','same'};

for k = 1:length(varargin)
  if ischar(varargin{k})
    string = validatestring(varargin{k}, allStrings,...
                          mfilename, 'OPTION',k+2);
    switch string
     case {'replicate', 'symmetric', 'circular'}
      boundary = string;
     case {'full','same'}
      output = string;
     case {'conv','corr'}
      do_fcn = string;
    end
  else
    validateattributes(varargin{k},{'numeric'},{'nonsparse'},mfilename,'OPTION',k+2);
    boundary = varargin{k};
  end %else
end

sameSize = strcmp(output,'same');

convMode = strcmp(do_fcn,'conv');

%--------------------------------------------------------------
function separable = isSeparable(a, h)

% check for filter separability
sep_threshold = getSeparableFilterThreshold(class(a));

if ((numel(h) >= sep_threshold) && ...
    (ismatrix(h)) && ...
    all(size(h) ~= 1) && ...
    all(isfinite(h(:))))

  [~,s,~] = svd(h);
  s = diag(s);
  tol = length(h) * max(s) * eps;
  rank = sum(s > tol);

  if (rank == 1)
    separable = true;
  else
    separable = false;
  end

else

  separable = false;

end

%--------------------------------------------------------------
function b = handleEmptyImage(a, sameSize, im_size)

if (sameSize)

  b = a;

else

  if all(im_size >= 0)

    b = zeros(im_size, class(a));

  else

    error(message('images:imfilter:negativeDimensionBadSizeB'))

  end

end

%--------------------------------------------------------------
function b = handleEmptyFilter(a, sameSize, im_size)

if (sameSize)

    b = zeros(size(a), class(a));

else

  if all(im_size>=0)

    b = zeros(im_size, class(a));

  else

    error(message('images:imfilter:negativeDimensionBadSizeB'))

  end

end

%--------------------------------------------------------------
function ippFlag = useIPPL(a,outSize,h,nonzero_h)

% Querying the Java preference for IPP use is computationally expensive,
% particularly in cases where imfilter is called in a loop. As a
% performance optimization, store the preference as persistent state. This
% state is reset whenever iptsetpref('UseIPPL',___) is called.
persistent prefFlag;
needToQueryJavaPreference = isempty(prefFlag);
if needToQueryJavaPreference
    prefFlag = iptgetpref('UseIPPL');
end

%We are disabling the use of IPP for double precision inputs on win32.
if ~isImageIPPFilterType(class(a))
    ippFlag = false;
    return;
end

if numel(nonzero_h)/numel(h) > 0.05
    densityFlag = true;
else
    densityFlag = false;
end

hDimsFlag = ismatrix(h);

% Determine if the image is big depending on datatype
tooBig = isImageTooBigForIPPFilter(a, outSize);

ippFlag = prefFlag && densityFlag && hDimsFlag && (~tooBig);

%--------------------------------------------------------------
function [finalSize, pad] = computeSizes(a, h, sameSize)

rank_a = ndims(a);
rank_h = ndims(h);

% Pad dimensions with ones if filter and image rank are different
size_h = [size(h) ones(1,rank_a-rank_h)];
size_a = [size(a) ones(1,rank_h-rank_a)];

if (sameSize)
  %Same output
  finalSize = size_a;

  %Calculate the number of pad pixels
  filter_center = floor((size_h + 1)/2);
  pad = size_h - filter_center;
else
  %Full output
  finalSize = size_a+size_h-1;
  pad = size_h - 1;
end

%--------------------------------------------------------------
function result = filterPartOrWhole(a, outSize, h, start, sameSize, convMode)

% Create connectivity matrix.  Only use nonzero values of the filter.
conn = h~=0;
nonzero_h = h(conn);

ippFlag  = useIPPL(a,outSize,h,nonzero_h);

if (isreal(h))

  result = imfilter_mex(a, outSize, h, nonzero_h,...
                        conn, start, sameSize, convMode, ippFlag);

else

  b1 = imfilter_mex(a, outSize, real(h), real(nonzero_h),...
                    conn, start, sameSize, convMode, ippFlag);

  b2 = imfilter_mex(a, outSize, imag(h), imag(nonzero_h), ...
                    conn, start, sameSize, convMode, ippFlag);

  if isreal(a)

    % b1 and b2 will always be real; result will always be complex
    result = complex(b1, b2);

  else

    % b1 and/or b2 may be complex;
    % result will always be complex
    result = complex(real(b1) - imag(b2),...
                     imag(b1) + real(b2));

  end

end

%--------------------------------------------------------------
function a = padImage(a,padSize,padding)

if ischar(padding)
    method = padding;
    padVal = [];
else
    method = 'constant';
    padVal = padding;
end

a = padarray_algo(a, padSize, method, padVal, 'both');


function [value, isterminal, direction, causes] = getSoITransitionOdeEvents(ut, rVect, vVect, bodyInfo, celBodyData)
        value = [];
        isterminal = [];
        direction = [];
%         causes = AbstractIntegrationTerminationCause.empty(0,1);

        %Max Radius (SoI Radius) Constraint (Leave SOI Upwards)
        parentBodyInfo = bodyInfo.getParBodyInfo(celBodyData);
%         rSOI = getSOIRadius(bodyInfo, parentBodyInfo);
        rSOI = bodyInfo.getCachedSoIRadius();
        radius = norm(rVect);

%         if(isempty(parentBodyInfo))
%             parentBodyInfo = KSPTOT_BodyInfo.empty(0,1);
%         end

        value(end+1) = rSOI - radius;
        isterminal(end+1) = 1;
        direction(end+1) = -1;
        causes(1) = SoITransitionUpIntTermCause(bodyInfo, parentBodyInfo, celBodyData);

        %Leave SoI Downwards
        [sma, ecc, ~, ~, ~, ~] = getKeplerFromState(rVect, vVect, bodyInfo.gm, false);
        [rApSC, rPeSC] = computeApogeePerigee(sma, ecc);

        if(ecc >= 1)
            rApSC = Inf;
        end

        children = bodyInfo.getChildrenBodyInfo(celBodyData);
        if(~isempty(children))
%             soiDownCauses(length(children)) = SoITransitionDownIntTermCause(bodyInfo, children(end), celBodyData);
            for(i=1:length(children)) %#ok<*NO4LP>
                childBodyInfo = children(i);
                rSOI = childBodyInfo.getCachedSoIRadius();
                [rApCB, rPeCB] = computeApogeePerigee(childBodyInfo.sma, childBodyInfo.ecc);

                if((rApSC < (rPeCB - rSOI)) || ...
                    rPeSC > (rApCB + rSOI))
                    val = realmax;

                else
                    dVect = getAbsPositBetweenSpacecraftAndBody(ut, rVect, bodyInfo, childBodyInfo, celBodyData);
                    distToChild = norm(dVect);

                    val = distToChild - rSOI;
                end

                value(end+1) = val; %#ok<AGROW>
                direction(end+1) = -1; %#ok<AGROW>
                isterminal(end+1) = 1; %#ok<AGROW>
                soiDownCauses(i) = SoITransitionDownIntTermCause(bodyInfo, childBodyInfo, celBodyData); %#ok<AGROW>
            end
            causes = [causes, soiDownCauses];
        end
end


function [g, gdata, gprior] = gp_g(w, gp, x, y, varargin)
%GP_G  Evaluate the gradient of energy (GP_E) for Gaussian Process
%
%  Description
%    G = GP_G(W, GP, X, Y, OPTIONS) takes a full GP parameter
%    vector W, GP structure GP, a matrix X of input vectors and a
%    matrix Y of target vectors, and evaluates the gradient G of
%    the energy function (gp_e). Each row of X corresponds to one
%    input vector and each row of Y corresponds to one target
%    vector.
%
%    [G, GDATA, GPRIOR] = GP_G(W, GP, X, Y, OPTIONS) also returns
%    separately the data and prior contributions to the gradient.
%
%    OPTIONS is optional parameter-value pair
%      z - optional observed quantity in triplet (x_i,y_i,z_i)
%          Some likelihoods may use this. For example, in case of
%          Poisson likelihood we have z_i=E_i, that is, expected
%          value for ith case.
%
%  See also
%    GP_E, GP_PAK, GP_UNPAK, GPCF_*
%
% Copyright (c) 2007-2011 Jarno Vanhatalo
% Copyright (c) 2010 Aki Vehtari
% Copyright (c) 2010 Heikki Peura
% Copyright (c) 2014 Arno Solin and Jukka Koskenranta

% This software is distributed under the GNU General Public
% License (version 3 or later); please refer to the file
% License.txt, included with the software, for details.

if isfield(gp,'latent_method') && ~strcmp(gp.latent_method,'MCMC')
    % use an inference specific method
    fh_g = gp.fh.g;
    switch nargout
        case {0 1}
            [g] = fh_g(w, gp, x, y, varargin{:});
        case 2
            [g, gdata] = fh_g(w, gp, x, y, varargin{:});
        case 3
            [g, gdata, gprior] = fh_g(w, gp, x, y, varargin{:});
    end
    return
end

ip=inputParser;
ip.FunctionName = 'GP_G';
ip.addRequired('w', @(x) isvector(x) && isreal(x));
ip.addRequired('gp',@isstruct);
ip.addRequired('x', @(x) ~isempty(x) && isreal(x) && all(isfinite(x(:))))
ip.addRequired('y', @(x) ~isempty(x) && isreal(x) && all(isfinite(x(:))))
ip.addParamValue('z', [], @(x) isreal(x) && all(isfinite(x(:))))
ip.parse(w, gp, x, y, varargin{:});
z=ip.Results.z;
if ~all(isfinite(w(:)));
    % instead of stopping to error, return NaN
    g=NaN;
    gdata = NaN;
    gprior = NaN;
    return;
end

% unpak the parameters
gp=gp_unpak(gp, w);
[tmp,tmp,hier]=gp_pak(gp);
ncf = length(gp.cf);
if isfield(gp.lik, 'nondiagW')
    % Likelihoods with non-diagonal Hessian
    switch gp.lik.type
        case {'LGP', 'LGPC'}
            % Do nothing
        case {'Softmax', 'Multinom'}
            n=size(x,1);
            nout=size(y(:),1)./n;
            %       [n,nout]=size(y);
            nl=cumsum([0 repmat(n,1,nout)]);
        otherwise
            n=size(x,1);
            nout=length(gp.comp_cf);

            % Help indices for latent processes
            if ~isfield(gp.lik, 'xtime')
                nl=[0 repmat(n,1,nout)];
            else
                xtime=gp.lik.xtime;
                ntime=size(xtime,1);
                nl=[0 ntime n];
            end
            nl=cumsum(nl);
    end
    if isfield(gp, 'comp_cf')  % own covariance for each ouput component
        multicf = true;
        if length(gp.comp_cf) ~= nout
            error('GP2_G: the number of component vectors in gp.comp_cf must be the same as number of outputs.')
        end
    else
        multicf = false;
    end
else
    n=size(x,1);
end

g = [];
gdata = [];
gprior = [];

if isfield(gp,'savememory') && gp.savememory
    savememory=1;
else
    savememory=0;
end

switch gp.type
    case 'FULL'
        % ============================================================
        % FULL
        % ============================================================

        if ~isfield(gp.lik, 'nondiagW') || ismember(gp.lik.type, {'LGP' 'LGPC'})
            % Evaluate covariance
            if isfield(gp, 'lik_mono')
                [K,C] = gp_dtrcov(gp, x, gp.xv);
                %         if isequal(gp.lik.type, 'Gaussian')
                %           % Condition the derivatives on the observations
                %           cc=C(size(x,1)+1:end,size(x,1)+1:end);
                %           cy=C(size(x,1)+1:end,1:size(x,1));
                %           cyy=C(1:size(x,1),1:size(x,1));
                %           C=cc - cy*(cyy\cy');
                %           %C=0.5.*(C+C')+1e-10.*eye(size(C));
                %           meany=cy*(cyy\y(1:n));
                %           y=(y(n+1:end)-meany);
                %           n=length(y);
                %         end
            else
                [~, C] = gp_trcov(gp, x);
            end

            if issparse(C)
                % evaluate the sparse inverse
                [LD, notpositivedefinite] = ldlchol(C);
                invC = spinv(LD,1);
                if notpositivedefinite
                    % instead of stopping to chol error, return NaN
                    g=NaN;
                    gdata = NaN;
                    gprior = NaN;
                    return;
                end
                if  ~isfield(gp,'meanf')
                    b = ldlsolve(LD,y);
                else
                    [invNM invAt HinvC]=mean_gf(gp,x,C,LD,[],[],y,'gaussian');
                end
            else
                % evaluate the full inverse
                ws1=warning('off','MATLAB:nearlySingularMatrix');
                ws2=warning('off','MATLAB:SingularMatrix');
                invC = inv(C);
                if  ~isfield(gp,'meanf')
                    b = C\y;
                else
                    [invNM invAt HinvC]=mean_gf(gp,x,C,invC,[],[],y,'gaussian');
                    if isnan(invNM)
                        g=NaN;gdata=NaN;gprior=NaN;
                        return
                    end
                end
                warning(ws1);
                warning(ws2);
            end
        else
            b = zeros(nl(end),1);
            y=y(:);

            switch gp.lik.type
                case 'Coxph'
                    % In Cox-Ph, latent processes have different inputs so stacking in invC
                    % is not possible.
                    invC = zeros(nl(end),nl(end));
                    if multicf
                        [tmp, C] = gp_trcov(gp, xtime, gp.comp_cf{1});
                        invC(1+nl(1):nl(2),1+nl(1):nl(2)) = inv(C);
                        b(nl(1)+1:nl(2)) = C\y(nl(1)+1:nl(2));
                        [tmp, C] = gp_trcov(gp, x, gp.comp_cf{2});
                        invC(1+nl(2):nl(3),1+nl(2):nl(3)) = inv(C);
                        b(1+nl(2):nl(3)) = C\y(1+nl(2):nl(3));
                    else
                        error('Specify covariance function for time process and input process, when using Cox-Ph likelihood');
                    end
                otherwise
                    invC = zeros(n,n,nout);
                    if multicf
                        for i1=1:nout
                            [tmp, C] = gp_trcov(gp, x, gp.comp_cf{i1});
                            invC(:,:,i1) = inv(C);
                            b(1+nl(i1):nl(i1+1)) = C\y(1+nl(i1):nl(i1+1));
                            %             b(:,i1) = C\y(:,i1);
                        end
                    else
                        [tmp, C] = gp_trcov(gp, x);
                        invCtmp = inv(C);
                        for i1=1:nout
                            invC(:,:,i1) = invCtmp;
                            b(1+nl(i1):nl(i1+1)) = C\y(1+nl(i1):nl(i1+1));
                            %             b(:,i1) = C\y(:,i1);
                        end
                    end
            end
        end

        % =================================================================
        % Gradient with respect to covariance function parameters
        i1=0;
        if ~isempty(strfind(gp.infer_params, 'covariance'))

            if isfield(gp,'deriv') && gp.deriv  % derivative observations in use
                % initialize few variables related to derivative observations
                [n,m] = size(x);
                ind_Ddim = x(:,gp.deriv);
                ind_Ddim_derivs = ind_Ddim(ind_Ddim>0);
                uDdim = unique(ind_Ddim_derivs);
                x1 = x(:,setdiff(1:m,gp.deriv));   % Take only the non-index columns
            end

            for i=1:ncf

                gpcf = gp.cf{i};

                if isfield(gp.lik, 'nondiagW') && ~ismember(gp.lik.type, {'LGP' 'LGPC'})
                    % check in which components the covariance function is present
                    % for likelihoods with non-diagonal Hessian
                    do = false(nout,1);
                    if multicf
                        for z1=1:nout
                            if any(gp.comp_cf{z1}==i)
                                do(z1) = true;
                            end
                        end
                    else
                        do = true(nout,1);
                    end
                end

                if ~(isfield(gp,'deriv') && gp.deriv)
                    % No derivative observations
                    if ~savememory
                        if isfield(gp.lik, 'nondiagW') && isfield(gp,'comp_cf') && ~isempty(intersect(gp.comp_cf{1},i)) && isfield(gp.lik, 'xtime')
                            DKffc = gpcf.fh.cfg(gpcf, xtime);
                        else
                            DKffc = gpcf.fh.cfg(gpcf, x);
                        end
                        np=length(DKffc);
                    else
                        % If savememory option is used, just get the number of
                        % hyperparameters and calculate gradients later
                        np=gpcf.fh.cfg(gpcf,[],[],[],0);
                    end
                    gprior_cf = -gpcf.fh.lpg(gpcf);
                else
                    [n,m]=size(x);
                    gprior_cf = -gpcf.fh.lpg(gpcf);
                    if (~isfield(gpcf, 'selectedVariables') || any(ismember(gpcf.selectedVariables,uDdim)))
                        % !!! Note. The check whether to calculate derivative
                        % matrices for a covariance function could/should be made
                        % nicer
                            DKffa = gpcf.fh.cfg(gpcf, x1(ind_Ddim==0,:));
                            np=length(DKffa);
                            for inp=1:np
                                % the block of covariance matrix
                                DKffc{inp}(ind_Ddim==0,ind_Ddim==0) = DKffa{inp};
                            end
                            for u1 = 1:length(uDdim)
                                % the blocks on the left side, below Kff
                                Kdf = gpcf.fh.cfdg(gpcf, x1(ind_Ddim==uDdim(u1),:), x1(ind_Ddim==0,:), uDdim(u1));
                                D = gpcf.fh.cfdg2(gpcf, x1(ind_Ddim==uDdim(u1),:), x1(ind_Ddim==uDdim(u1),:), uDdim(u1), uDdim(u1));
                                for inp=1:np
                                    DKffc{inp}(ind_Ddim==uDdim(u1),ind_Ddim==0) = Kdf{inp};
                                    DKffc{inp}(ind_Ddim==0,ind_Ddim==uDdim(u1)) = Kdf{inp}';
                                    DKffc{inp}(ind_Ddim==uDdim(u1),ind_Ddim==uDdim(u1)) = D{inp};
                                end

                                uDdim2 = uDdim(u1+1:end);
                                for u2=1:length(uDdim2)
                                    Kdf2 = gpcf.fh.cfdg2(gpcf, x1(ind_Ddim==uDdim(u1),:) ,x1(ind_Ddim==uDdim2(u2),:), uDdim(u1), uDdim2(u2));
                                    for inp=1:np
                                        DKffc{inp}(ind_Ddim==uDdim(u1),ind_Ddim==uDdim2(u2)) = Kdf2{inp};
                                        DKffc{inp}(ind_Ddim==uDdim2(u2),ind_Ddim==uDdim(u1)) = Kdf2{inp}';
                                    end
                                end

                            end

                            %DKffc{inp} = Ktemp;

                    else
                        % A covariance function that does not use any
                        % of the input dimensions with respect to which
                        % derivatives are observed
                        %warning('not tested yet')
                        DKffa = gpcf.fh.cfg(gpcf, x(ind_Ddim==0,:));
                        np=length(DKffa);
                        for inp=1:np
                            Ktemp = sparse(n,n);
                            Ktemp(ind_Ddim==0,ind_Ddim==0) = DKffa{inp};
                            DKffc{inp} = Ktemp;
                        end
                    end

                end

                % Are there specified mean functions
                if  ~isfield(gp,'meanf')
                    % Evaluate the gradient with respect to covariance function
                    % parameters
                    for i2 = 1:np
                        if savememory
                            if isfield(gp.lik, 'nondiagW') && isfield(gp,'comp_cf') && ~isempty(intersect(gp.comp_cf{1},i)) && isfield(gp.lik, 'xtime')
                                DKff=gpcf.fh.cfg(gpcf,xtime,[],[],i2);
                            else
                                DKff=gpcf.fh.cfg(gpcf,x,[],[],i2);
                            end
                        else
                            DKff=DKffc{i2};
                        end
                        i1 = i1+1;
                        if ~isfield(gp.lik, 'nondiagW') || ismember(gp.lik.type, {'LGP' 'LGPC'})
                            Bdl = b'*(DKff*b);
                            Cdl = sum(sum(invC.*DKff)); % help arguments
                        else
                            % Non-diagonalizable likelihoods
                            Bdl=0; Cdl=0;
                            if isfield(gp.lik,'xtime');
                                if do(1)
                                    Bdl = Bdl + b(1:ntime)'*(DKff*b(1:ntime));
                                    Cdl = Cdl + sum(sum(invC(1:ntime,1:ntime).*DKff)); % help arguments
                                end
                                if do(2)
                                    Bdl = Bdl + b(ntime+1:end)'*(DKff*b(ntime+1:end));
                                    Cdl = Cdl + sum(sum(invC(ntime+1:end,ntime+1:end).*DKff)); % help arguments
                                end
                            else
                                for z1=1:nout
                                    if do(z1)
                                        Bdl = Bdl + b(1+nl(z1):nl(z1+1))'*(DKff*b(1+nl(z1):nl(z1+1)));
                                        Cdl = Cdl + sum(sum(invC(:,:,z1).*DKff)); % help arguments
                                    end
                                end
                            end
                        end
                        gdata(i1)=0.5.*(Cdl - Bdl);
                    end
                else
                    for i2 = 1:np
                        if savememory
                            DKff=gpcf.fh.cfg(gpcf,x,[],[],i2);
                        else
                            DKff=DKffc{i2};
                        end
                        i1=i1+1;
                        dA = -1*HinvC*DKff*HinvC';                  % d A / d th
                        trA = sum(invAt(:).*dA(:));                 % d log(|A|) / dth
                        dMNM = invNM'*(DKff*invNM);           % d M'*N*M / d th
                        trK = sum(sum(invC.*DKff));       % d log(Ky�?�) / d th
                        gdata(i1)=0.5*(-1*dMNM + trK + trA);
                    end
                end

                gprior = [gprior gprior_cf];
            end
        end

        % =================================================================
        % Gradient with respect to Gaussian likelihood function parameters
        if ~isempty(strfind(gp.infer_params, 'likelihood')) && isfield(gp.lik.fh,'trcov')
            % Evaluate the gradient from Gaussian likelihood
            gprior_lik = -gp.lik.fh.lpg(gp.lik);
            if ~isempty(gprior_lik)
                DCff = gp.lik.fh.cfg(gp.lik, x);
                if isfield(gp, 'lik_mono')
                    DCff{1}=diag(DCff{1}*[ones(size(x,1),1); zeros(size(gp.xv,1)*length(nvd),1)]);
                end
                for i2 = 1:length(DCff)
                    i1 = i1+1;
                    if ~isfield(gp,'meanf')
                        if size(DCff{i2}) > 1
                            yKy = b'*(DCff{i2}*b);
                            trK = sum(sum(invC.*DCff{i2})); % help arguments
                            gdata_zeromean(i1)=0.5.*(trK - yKy);
                        else
                            yKy=DCff{i2}.*(b'*b);
                            trK = DCff{i2}.*(trace(invC));
                            gdata_zeromean(i1)=0.5.*(trK - yKy);
                        end
                        gdata(i1)=gdata_zeromean(i1);
                    else
                        if size(DCff{i2}) > 1
                            trK = sum(sum(invC.*DCff{i2})); % help arguments
                        else
                            trK = DCff{i2}.*(trace(invC));
                        end
                        dA = -1*HinvC*DCff{i2}*HinvC';            % d A / d th
                        trA = sum(invAt(:).*dA(:));               % d log(|A|) / dth
                        dMNM = invNM'*(DCff{i2}*invNM);           % d M'*N*M / d th
                        gdata(i1)=0.5*(-1*dMNM + trA + trK);
                    end
                end
            end
            gprior = [gprior gprior_lik];
        end

        if ~isempty(strfind(gp.infer_params, 'mean')) && isfield(gp,'meanf')
            notpositivedefinite2 = 0; notpositivedefinite3 = 0;

            nmf=numel(gp.meanf);
            [H,b,B]=mean_prep(gp,x,[]);
            M = H'*b-y;

            if issparse(C)
                [LD, notpositivedefinite] = ldlchol(C);
                if ~notpositivedefinite
                    KH = ldlsolve(LD, H');
                end
                [LB, notpositivedefinite2] = chol(B);
                if ~notpositivedefinite2
                    A = LB\(LB'\eye(size(B))) + H*KH;
                    LA = chol(A);
                    a = ldlsolve(LD, M) - KH*(LA\(LA'\(KH'*M)));
                    iNH = ldlsolve(LD, H') - KH*(LA\(LA'\(KH'*H')));
                end
            else
                N = C + H'*B*H;
                [LN, notpositivedefinite3] = chol(N);
                if ~notpositivedefinite3
                    a = LN\(LN'\M);
                    iNH = LN\(LN'\H');
                end
            end
            if (~notpositivedefinite2 && ~notpositivedefinite3)
                Ha=H*a;
                g_bb = (-H*a)';     % b and B parameters are log transformed in packing
                indB = find(B>0);
                for i=1:length(indB)
                    Bt = zeros(size(B)); Bt(indB(i))=1;
                    BH = Bt*H;
                    g_B(i) = 0.5* ( Ha'*Bt*Ha - sum(sum(iNH.*(BH'))) );
                end
                g_BB = g_B.*B(indB)';
                % Interweave the mean functions parameters in correct order
                g_bB = [-g_bb; -g_BB];
                g_bB = g_bB(:)';
                for i=1:nmf
                    gpmf = gp.meanf{i};
                    [lpg_b, lpg_B] = gpmf.fh.lpg(gpmf);
                    gprior = [gprior -lpg_b -lpg_B];
                    % Check whether parameters are fixed or not
                    if isempty(lpg_b)
                        g_bB(1+(i-1)*2)=NaN;
                    end
                    if isempty(lpg_B)
                        g_bB(2+(i-1)*2)=NaN;
                    end
                end
                gdata = [gdata g_bB(~isnan(g_bB))];
            else
                g=NaN;
                gdata = NaN;
                gprior = NaN;
                return
            end
        end

    case 'FIC'
        % ============================================================
        % FIC
        % ============================================================
        g_ind = zeros(1,numel(gp.X_u));
        gdata_ind = zeros(1,numel(gp.X_u));
        gprior_ind = zeros(1,numel(gp.X_u));

        u = gp.X_u;
        DKuu_u = 0;
        DKuf_u = 0;

        % First evaluate the needed covariance matrices
        % v defines that parameter is a vector
        [Kv_ff, Cv_ff] = gp_trvar(gp, x);  % 1 x f  vector
        K_fu = gp_cov(gp, x, u);         % f x u
        K_uu = gp_trcov(gp, u);          % u x u, noiseles covariance K_uu
        K_uu = (K_uu+K_uu')./2;          % ensure the symmetry of K_uu
        [Luu, notpositivedefinite] = chol(K_uu,'lower');
        if notpositivedefinite
            % If not positive definite, return NaN
            g=NaN; gdata=NaN; gprior=NaN;
            return;
        end
        % Evaluate the Lambda (La)
        % Q_ff = K_fu*inv(K_uu)*K_fu'
        % Here we need only the diag(Q_ff), which is evaluated below
        B=Luu\(K_fu');
        Qv_ff=sum(B.^2)';
        Lav = Cv_ff-Qv_ff;   % 1 x f, Vector of diagonal elements
        % iLaKfu = diag(inv(Lav))*K_fu = inv(La)*K_fu
        iLaKfu = zeros(size(K_fu));  % f x u,
        for i=1:n
            iLaKfu(i,:) = K_fu(i,:)./Lav(i);  % f x u
        end
        % ... then evaluate some help matrices.
        % A = K_uu+K_uf*inv(La)*K_fu
        A = K_uu+K_fu'*iLaKfu;
        A = (A+A')./2;               % Ensure symmetry
        [A, notpositivedefinite] = chol(A,'upper');
        if notpositivedefinite
            % If not positive definite, return NaN
            g=NaN; gdata=NaN; gprior=NaN;
            return;
        end
        L = iLaKfu/A;
        b = y'./Lav' - (y'*L)*L';
        iKuuKuf = Luu'\(Luu\K_fu');
        La = Lav;
        LL = sum(L.*L,2);

        % =================================================================
        % Gradient with respect to covariance function parameters
        if ~isempty(strfind(gp.infer_params, 'covariance'))
            % Loop over the covariance functions
            i1=0;
            for i=1:ncf

                % Get the gradients of the covariance matrices
                % and gprior from gpcf_* structures
                gpcf = gp.cf{i};
                if savememory
                    % If savememory option is used, just get the number of
                    % hyperparameters and calculate gradients later
                    np=gpcf.fh.cfg(gpcf,[],[],[],0);
                else
                    DKffc = gpcf.fh.cfg(gpcf, x, [], 1);
                    DKuuc = gpcf.fh.cfg(gpcf, u);
                    DKufc = gpcf.fh.cfg(gpcf, u, x);
                    np=length(DKffc);
                end
                gprior_cf = -gpcf.fh.lpg(gpcf);

                for i2 = 1:np
                    i1 = i1+1;
                    if savememory
                        DKff=gpcf.fh.cfg(gpcf,x,[],1,i2);
                        DKuu=gpcf.fh.cfg(gpcf,u,[],[],i2);
                        DKuf=gpcf.fh.cfg(gpcf,u,x,[],i2);
                    else
                        DKff=DKffc{i2};
                        DKuu=DKuuc{i2};
                        DKuf=DKufc{i2};
                    end
                    KfuiKuuKuu = iKuuKuf'*DKuu;
                    gdata(i1) = -0.5.*((2*b*DKuf'-(b*KfuiKuuKuu))*(iKuuKuf*b') + 2.*sum(sum(L'.*(L'*DKuf'*iKuuKuf))) - ...
                        sum(sum(L'.*((L'*KfuiKuuKuu)*iKuuKuf))));

                    gdata(i1) = gdata(i1) - 0.5.*(b.*DKff')*b';
                    gdata(i1) = gdata(i1) + 0.5.*(2.*b.*sum(DKuf'.*iKuuKuf',2)'*b'- b.*sum(KfuiKuuKuu.*iKuuKuf',2)'*b');
                    gdata(i1) = gdata(i1) + 0.5.*(sum(DKff./La) - sum(LL.*DKff));
                    gdata(i1) = gdata(i1) + 0.5.*(2.*sum(LL.*sum(DKuf'.*iKuuKuf',2)) - sum(LL.*sum(KfuiKuuKuu.*iKuuKuf',2)));
                end

                gprior = [gprior gprior_cf];
            end
        end

        % =================================================================
        % Gradient with respect to Gaussian likelihood function parameters
        if ~isempty(strfind(gp.infer_params, 'likelihood')) && isfield(gp.lik.fh,'trcov')
            % Evaluate the gradient from Gaussian likelihood
            DCff = gp.lik.fh.cfg(gp.lik, x);
            gprior_lik = -gp.lik.fh.lpg(gp.lik);
            for i2 = 1:length(DCff)
                i1 = i1+1;
                gdata(i1)= -0.5*DCff{i2}.*b*b';
                gdata(i1)= gdata(i1) + 0.5*sum(DCff{i2}./La-sum(L.*L,2).*DCff{i2});
            end
            %       % Set the gradients of hyperparameter
            %       if length(gprior_lik) > length(DCff)
            %         for i2=length(DCff)+1:length(gprior_lik)
            %           i1 = i1+1;
            %           gdata(i1) = 0;
            %         end
            %       end
            gprior = [gprior gprior_lik];
        end

        % =================================================================
        % Gradient with respect to inducing inputs
        if ~isempty(strfind(gp.infer_params, 'inducing'))
            if isfield(gp.p, 'X_u') && ~isempty(gp.p.X_u)
                m = size(gp.X_u,2);
                st=0;
                if ~isempty(gdata)
                    st = length(gdata);
                end

                gdata(st+1:st+length(gp.X_u(:))) = 0;
                i1 = st+1;
                gprior_ind=[];
                if iscell(gp.p.X_u) % Own prior for each inducing input
                    for i = 1:size(gp.X_u,1)
                        pr = gp.p.X_u{i};
                        gprior_ind =[gprior_ind -pr.fh.lpg(gp.X_u(i,:), pr)];
                    end
                else % One prior for all inducing inputs
                    gprior_ind = -gp.p.X_u.fh.lpg(gp.X_u(:)', gp.p.X_u);
                end
                gprior = [gprior gprior_ind];
                %         gdata=[gdata zeros(1,length(gprior) - length(gdata))];

                % Loop over the covariance functions
                for i=1:ncf
                    i1 = st;
                    gpcf = gp.cf{i};

                    if savememory
                        % If savememory option is used, just get the number of
                        % covariates in X and calculate gradients later
                        np=gpcf.fh.ginput(gpcf,u,[],0);
                    else
                        np=1;
                        DKuu = gpcf.fh.ginput(gpcf, u);
                        DKuf = gpcf.fh.ginput(gpcf, u, x);
                    end
                    for i3=1:np
                        if savememory
                            DKuu=gpcf.fh.ginput(gpcf,u,[],i3);
                            DKuf=gpcf.fh.ginput(gpcf,u,x,i3);
                        end
                        for i2 = 1:length(DKuu)
                            if savememory
                                i1 = st + (i2-1)*np+i3;
                                %i1 = st + 2*i2 - (i3==1);
                            else
                                i1 = i1+1;
                            end
                            KfuiKuuKuu = iKuuKuf'*DKuu{i2};
                            gdata(i1) = gdata(i1) - 0.5.*((2*b*DKuf{i2}'-(b*KfuiKuuKuu))*(iKuuKuf*b') + ...
                                2.*sum(sum(L'.*(L'*DKuf{i2}'*iKuuKuf))) - sum(sum(L'.*((L'*KfuiKuuKuu)*iKuuKuf))));
                            gdata(i1) = gdata(i1) + 0.5.*(2.*b.*sum(DKuf{i2}'.*iKuuKuf',2)'*b'- b.*sum(KfuiKuuKuu.*iKuuKuf',2)'*b');
                            gdata(i1) = gdata(i1) + 0.5.*(2.*sum(LL.*sum(DKuf{i2}'.*iKuuKuf',2)) - ...
                                sum(LL.*sum(KfuiKuuKuu.*iKuuKuf',2)));
                        end
                    end
                end
            end
        end

    case {'PIC' 'PIC_BLOCK'}
        % ============================================================
        % PIC
        % ============================================================
        g_ind = zeros(1,numel(gp.X_u));
        gdata_ind = zeros(1,numel(gp.X_u));
        gprior_ind = zeros(1,numel(gp.X_u));

        u = gp.X_u;
        ind = gp.tr_index;
        DKuu_u = 0;
        DKuf_u = 0;

        % First evaluate the needed covariance matrices
        % if they are not in the memory
        % v defines that parameter is a vector
        K_fu = gp_cov(gp, x, u);         % f x u
        K_uu = gp_trcov(gp, u);          % u x u, noiseles covariance K_uu
        K_uu = (K_uu+K_uu')./2;          % ensure the symmetry of K_uu
        [Luu, notpositivedefinite] = chol(K_uu,'lower');
        if notpositivedefinite
            % If not positive definite, return NaN
            g=NaN; gdata=NaN; gprior=NaN;
            return;
        end
        % Evaluate the Lambda (La)
        % Q_ff = K_fu*inv(K_uu)*K_fu'
        % Here we need only the diag(Q_ff), which is evaluated below
        %B=K_fu/Luu;
        B=Luu\K_fu';
        iLaKfu = zeros(size(K_fu));  % f x u
        for i=1:length(ind)
            Qbl_ff = B(:,ind{i})'*B(:,ind{i});
            [Kbl_ff, Cbl_ff] = gp_trcov(gp, x(ind{i},:));
            la = Cbl_ff - Qbl_ff;
            La{i} = (la + la')./2;
            iLaKfu(ind{i},:) = La{i}\K_fu(ind{i},:);
        end
        % ... then evaluate some help matrices.
        % A = chol(K_uu+K_uf*inv(La)*K_fu))
        A = K_uu+K_fu'*iLaKfu;
        A = (A+A')./2;            % Ensure symmetry

        [LA,notpositivedefinite]=chol(A,'upper');
        if notpositivedefinite
            % instead of stopping to chol error, return NaN
            g=NaN; gdata = NaN; gprior = NaN;
            return;
        end
        L = iLaKfu/LA;
        b = zeros(1,n);
        b_apu=(y'*L)*L';
        for i=1:length(ind)
            b(ind{i}) = y(ind{i})'/La{i} - b_apu(ind{i});
        end
        iKuuKuf = Luu'\(Luu\K_fu');

        % =================================================================
        % Gradient with respect to covariance function parameters

        if ~isempty(strfind(gp.infer_params, 'covariance'))
            % Loop over the  covariance functions
            i1=0;
            for i=1:ncf

                % Get the gradients of the covariance matrices
                % and gprior from gpcf_* structures
                gpcf = gp.cf{i};
                if savememory
                    % If savememory option is used, just get the number of
                    % hyperparameters and calculate gradients later
                    np=gpcf.fh.cfg(gpcf,[],[],[],0);
                else
                    DKuuc = gpcf.fh.cfg(gpcf, u);
                    DKufc = gpcf.fh.cfg(gpcf, u, x);
                    for kk = 1:length(ind)
                        DKffc{kk} = gpcf.fh.cfg(gpcf, x(ind{kk},:));
                    end
                    np=length(DKuuc);
                end
                gprior_cf = -gpcf.fh.lpg(gpcf);

                for i2 = 1:np
                    i1 = i1+1;
                    if savememory
                        DKuu=gpcf.fh.cfg(gpcf,u,[],[],i2);
                        DKuf=gpcf.fh.cfg(gpcf,u,x,[],i2);
                    else
                        DKuu=DKuuc{i2};
                        DKuf=DKufc{i2};
                    end
                    KfuiKuuKuu = iKuuKuf'*DKuu;
                    %            H = (2*K_uf'- KfuiKuuKuu)*iKuuKuf;
                    % Here we evaluate  gdata = -0.5.* (b*H*b' + trace(L*L'H)
                    gdata(i1) = -0.5.*((2*b*DKuf'-(b*KfuiKuuKuu))*(iKuuKuf*b') + 2.*sum(sum(L'.*(L'*DKuf'*iKuuKuf))) - ...
                        sum(sum(L'.*((L'*KfuiKuuKuu)*iKuuKuf))));
                    for kk=1:length(ind)
                        if savememory
                            DKff=gpcf.fh.cfg(gpcf,x(ind{kk},:),[],[],i2);
                        else
                            DKff=DKffc{kk}{i2};
                        end
                        gdata(i1) = gdata(i1) ...
                            + 0.5.*(-b(ind{kk})*DKff*b(ind{kk})' ...
                            + 2.*b(ind{kk})*DKuf(:,ind{kk})'*iKuuKuf(:,ind{kk})*b(ind{kk})'- ...
                            b(ind{kk})*KfuiKuuKuu(ind{kk},:)*iKuuKuf(:,ind{kk})*b(ind{kk})' ...
                            +trace(La{kk}\DKff)...
                            - trace(L(ind{kk},:)*(L(ind{kk},:)'*DKff)) ...
                            + 2.*sum(sum(L(ind{kk},:)'.*(L(ind{kk},:)'*DKuf(:,ind{kk})'*iKuuKuf(:,ind{kk})))) - ...
                            sum(sum(L(ind{kk},:)'.*((L(ind{kk},:)'*KfuiKuuKuu(ind{kk},:))*iKuuKuf(:,ind{kk})))));
                    end
                end
                gprior = [gprior gprior_cf];
            end
        end

        % =================================================================
        % Gradient with respect to Gaussian likelihood function parameters
        if ~isempty(strfind(gp.infer_params, 'likelihood')) && isfield(gp.lik.fh,'trcov')
            % Evaluate the gradient from Gaussian likelihood
            DCff = gp.lik.fh.cfg(gp.lik, x);
            gprior_lik = -gp.lik.fh.lpg(gp.lik);
            for i2 = 1:length(DCff)
                i1 = i1+1;
                gdata(i1)= -0.5*DCff{i2}.*b*b';
                for kk=1:length(ind)
                    gdata(i1)= gdata(i1) + 0.5*trace((inv(La{kk})-L(ind{kk},:)*L(ind{kk},:)')).*DCff{i2};
                end
            end
            gprior = [gprior gprior_lik];
        end

        % =================================================================
        % Gradient with respect to inducing inputs
        if ~isempty(strfind(gp.infer_params, 'inducing'))
            if isfield(gp.p, 'X_u') && ~isempty(gp.p.X_u)
                m = size(gp.X_u,2);

                st=0;
                if ~isempty(gdata)
                    st = length(gdata);
                end
                gdata(st+1:st+length(gp.X_u(:))) = 0;

                i1 = st+1;
                gprior_ind=[];
                if iscell(gp.p.X_u) % Own prior for each inducing input
                    for i = 1:size(gp.X_u,1)
                        pr = gp.p.X_u{i};
                        gprior_ind =[gprior_ind -pr.fh.lpg(gp.X_u(i,:), pr)];
                    end
                else % One prior for all inducing inputs
                    gprior_ind = -gp.p.X_u.fh.lpg(gp.X_u(:)', gp.p.X_u);
                end
                gprior = [gprior gprior_ind];

                % Loop over the  covariance functions
                for i=1:ncf
                    i1=st;
                    gpcf = gp.cf{i};
                    if savememory
                        % If savememory option is used, just get the number of
                        % covariates in X and calculate gradients later
                        np=gpcf.fh.ginput(gpcf,u,[],0);
                    else
                        np=1;
                        DKuu = gpcf.fh.ginput(gpcf, u);
                        DKuf = gpcf.fh.ginput(gpcf, u, x);
                    end

                    for i3 = 1:np
                        if savememory
                            DKuu=gpcf.fh.ginput(gpcf, u, [], i3);
                            DKuf=gpcf.fh.ginput(gpcf, u, x, i3);
                        end
                        for i2 = 1:length(DKuu)
                            if savememory
                                i1 = st + (i2-1)*np+i3;
                                %i1 = st + 2*i2 - (i3==1);
                            else
                                i1 = i1+1;
                            end
                            KfuiKuuDKuu_u = iKuuKuf'*DKuu{i2};
                            gdata(i1) = gdata(i1) -0.5.*((2*b*DKuf{i2}'-(b*KfuiKuuDKuu_u))*(iKuuKuf*b') + 2.*sum(sum(L'.*((L'*DKuf{i2}')*iKuuKuf))) - ...
                                sum(sum(L'.*((L'*KfuiKuuDKuu_u)*iKuuKuf))));

                            for kk=1:length(ind)
                                gdata(i1) = gdata(i1) + 0.5.*(2.*b(ind{kk})*DKuf{i2}(:,ind{kk})'*iKuuKuf(:,ind{kk})*b(ind{kk})'- ...
                                    b(ind{kk})*KfuiKuuDKuu_u(ind{kk},:)*iKuuKuf(:,ind{kk})*b(ind{kk})' ...
                                    + 2.*sum(sum(L(ind{kk},:)'.*(L(ind{kk},:)'*DKuf{i2}(:,ind{kk})'*iKuuKuf(:,ind{kk})))) - ...
                                    sum(sum(L(ind{kk},:)'.*((L(ind{kk},:)'*KfuiKuuDKuu_u(ind{kk},:))*iKuuKuf(:,ind{kk})))));
                            end
                        end
                    end
                end
            end
        end

    case 'CS+FIC'
        % ============================================================
        % CS+FIC
        % ============================================================
        g_ind = zeros(1,numel(gp.X_u));
        gdata_ind = zeros(1,numel(gp.X_u));
        gprior_ind = zeros(1,numel(gp.X_u));

        u = gp.X_u;
        DKuu_u = 0;
        DKuf_u = 0;

        cf_orig = gp.cf;

        cf1 = {};
        cf2 = {};
        j = 1;
        k = 1;
        for i = 1:ncf
            if ~isfield(gp.cf{i},'cs')
                cf1{j} = gp.cf{i};
                j = j + 1;
            else
                cf2{k} = gp.cf{i};
                k = k + 1;
            end
        end
        gp.cf = cf1;

        % First evaluate the needed covariance matrices
        % if they are not in the memory
        % v defines that parameter is a vector
        [Kv_ff, Cv_ff] = gp_trvar(gp, x);  % 1 x f  vector
        K_fu = gp_cov(gp, x, u);         % f x u
        K_uu = gp_trcov(gp, u);          % u x u, noiseles covariance K_uu
        K_uu = (K_uu+K_uu')./2;          % ensure the symmetry of K_uu
        [Luu, notpositivedefinite] = chol(K_uu,'lower');
        if notpositivedefinite
            % If not positive definite, return NaN
            g=NaN; gdata=NaN; gprior=NaN;
            return;
        end
        % Evaluate the Lambda (La)
        % Q_ff = K_fu*inv(K_uu)*K_fu'
        % Here we need only the diag(Q_ff), which is evaluated below
        B=Luu\(K_fu');
        Qv_ff=sum(B.^2)';
        Lav = Cv_ff-Qv_ff;   % 1 x f, Vector of diagonal elements

        gp.cf = cf2;
        K_cs = gp_trcov(gp,x);
        La = sparse(1:n,1:n,Lav,n,n) + K_cs;
        gp.cf = cf_orig;

        LD = ldlchol(La);
        %        iLaKfu = La\K_fu;
        iLaKfu = ldlsolve(LD, K_fu);

        % ... then evaluate some help matrices.
        % A = chol(K_uu+K_uf*inv(La)*K_fu))
        A = K_uu+K_fu'*iLaKfu;
        A = (A+A')./2;            % Ensure symmetry
        [LA, notpositivedefinite] = chol(A,'upper');
        if notpositivedefinite
            % If not positive definite, return NaN
            g=NaN; gdata=NaN; gprior=NaN;
            return;
        end
        L = iLaKfu/LA;
        %b = y'/La - (y'*L)*L';
        b = ldlsolve(LD,y)' - (y'*L)*L';

        siLa = spinv(La);
        idiagLa = diag(siLa);
        iKuuKuf = K_uu\K_fu';
        LL = sum(L.*L,2);

        % =================================================================
        % Gradient with respect to covariance function parameters
        if ~isempty(strfind(gp.infer_params, 'covariance'))
            % Loop over covariance functions
            i1=0;
            for i=1:ncf

                gpcf = gp.cf{i};

                % Evaluate the gradient for FIC covariance functions
                if ~isfield(gpcf,'cs')
                    % Get the gradients of the covariance matrices
                    % and gprior from gpcf_* structures
                    if savememory
                        % If savememory option is used, just get the number of
                        % hyperparameters and calculate gradients later
                        np=gpcf.fh.cfg(gpcf,[],[],[],0);
                    else
                        DKffc = gpcf.fh.cfg(gpcf, x, [], 1);
                        DKuuc = gpcf.fh.cfg(gpcf, u);
                        DKufc = gpcf.fh.cfg(gpcf, u, x);
                        np=length(DKuuc);
                    end
                    gprior_cf = -gpcf.fh.lpg(gpcf);

                    for i2 = 1:np
                        i1 = i1+1;
                        if savememory
                            DKff=gpcf.fh.cfg(gpcf,x,[],1,i2);
                            DKuu=gpcf.fh.cfg(gpcf,u,[],[],i2);
                            DKuf=gpcf.fh.cfg(gpcf,u,x,[],i2);
                        else
                            DKff=DKffc{i2};
                            DKuu=DKuuc{i2};
                            DKuf=DKufc{i2};
                        end
                        KfuiKuuKuu = iKuuKuf'*DKuu;
                        gdata(i1) = -0.5.*((2*b*DKuf'-(b*KfuiKuuKuu))*(iKuuKuf*b') + 2.*sum(sum(L'.*(L'*DKuf'*iKuuKuf))) - ...
                            sum(sum(L'.*((L'*KfuiKuuKuu)*iKuuKuf))));

                        temp1 = sum(KfuiKuuKuu.*iKuuKuf',2);
                        temp2 = sum(DKuf'.*iKuuKuf',2);
                        temp3 = 2.*DKuf' - KfuiKuuKuu;
                        gdata(i1) = gdata(i1) - 0.5.*(b.*DKff')*b';
                        gdata(i1) = gdata(i1) + 0.5.*(2.*b.*temp2'*b'- b.*temp1'*b');
                        gdata(i1) = gdata(i1) + 0.5.*(sum(idiagLa.*DKff - LL.*DKff));   % corrected
                        gdata(i1) = gdata(i1) + 0.5.*(2.*sum(LL.*temp2) - sum(LL.*temp1));

                        %gdata(i1) = gdata(i1) + 0.5.*sum(sum(La\((2.*K_uf') - KfuiKuuKuu).*iKuuKuf',2));
                        gdata(i1) = gdata(i1) + 0.5.*sum(sum(ldlsolve(LD,temp3).*iKuuKuf',2));
                        gdata(i1) = gdata(i1) - 0.5.*( idiagLa'*(sum(temp3.*iKuuKuf',2)) ); % corrected
                    end

                    % Evaluate the gradient for compact support covariance functions
                else
                    % Get the gradients of the covariance matrices
                    % and gprior from gpcf_* structures
                    if savememory
                        % If savememory option is used, just get the number of
                        % hyperparameters and calculate gradients later
                        np=gpcf.fh.cfg(gpcf,[],[],[],0);
                    else
                        DKffc = gpcf.fh.cfg(gpcf, x);
                        np=length(DKffc);
                    end
                    gprior_cf = -gpcf.fh.lpg(gpcf);

                    for i2 = 1:np
                        i1 = i1+1;
                        if savememory
                            DKff=gpcf.fh.cfg(gpcf,x,[],[],i2);
                        else
                            DKff=DKffc{i2};
                        end
                        gdata(i1) = 0.5*(sum(sum(siLa.*DKff',2)) - sum(sum(L.*(L'*DKff')')) - b*DKff*b');
                    end
                end
                gprior = [gprior gprior_cf];
            end
        end

        % =================================================================
        % Gradient with respect to Gaussian likelihood function parameters
        if ~isempty(strfind(gp.infer_params, 'likelihood')) && isfield(gp.lik.fh,'trcov')
            % Evaluate the gradient from Gaussian likelihood
            DCff = gp.lik.fh.cfg(gp.lik, x);
            gprior_lik = -gp.lik.fh.lpg(gp.lik);
            for i2 = 1:length(DCff)
                i1 = i1+1;
                gdata(i1)= -0.5*DCff{i2}.*b*b';
                gdata(i1)= gdata(i1) + 0.5*sum(idiagLa-LL).*DCff{i2};
            end
            gprior = [gprior gprior_lik];
        end

        % =================================================================
        % Gradient with respect to inducing inputs
        if ~isempty(strfind(gp.infer_params, 'inducing'))
            if isfield(gp.p, 'X_u') && ~isempty(gp.p.X_u)
                m = size(gp.X_u,2);
                st=0;
                if ~isempty(gdata)
                    st = length(gdata);
                end

                gdata(st+1:st+length(gp.X_u(:))) = 0;
                i1 = st+1;
                gprior_ind=[];
                if iscell(gp.p.X_u) % Own prior for each inducing input
                    for i = 1:size(gp.X_u,1)
                        pr = gp.p.X_u{i};
                        gprior_ind =[gprior_ind -pr.fh.lpg(gp.X_u(i,:), pr)];
                    end
                else % One prior for all inducing inputs
                    gprior_ind = -gp.p.X_u.fh.lpg(gp.X_u(:)', gp.p.X_u);
                end
                gprior = [gprior gprior_ind];

                for i=1:ncf
                    i1=st;
                    gpcf = gp.cf{i};
                    if ~isfield(gpcf,'cs')
                        if savememory
                            % If savememory option is used, just get the number of
                            % covariates in X and calculate gradients later
                            np=gpcf.fh.ginput(gpcf,u,[],0);
                        else
                            np=1;
                            DKuu = gpcf.fh.ginput(gpcf, u);
                            DKuf = gpcf.fh.ginput(gpcf, u, x);
                        end

                        for i3 = 1:np
                            if savememory
                                DKuu = gpcf.fh.ginput(gpcf,u,[],i3);
                                DKuf = gpcf.fh.ginput(gpcf,u,x,i3);
                            end
                            for i2 = 1:length(DKuu)
                                if savememory
                                    i1 = st + (i2-1)*np+i3;
                                    %i1 = st + 2*i2 - (i3==1);
                                else
                                    i1 = i1+1;
                                end
                                KfuiKuuKuu = iKuuKuf'*DKuu{i2};

                                gdata(i1) = gdata(i1) - 0.5.*((2*b*DKuf{i2}'-(b*KfuiKuuKuu))*(iKuuKuf*b') + ...
                                    2.*sum(sum(L'.*(L'*DKuf{i2}'*iKuuKuf))) - sum(sum(L'.*((L'*KfuiKuuKuu)*iKuuKuf))));
                                gdata(i1) = gdata(i1) + 0.5.*(2.*b.*sum(DKuf{i2}'.*iKuuKuf',2)'*b'- b.*sum(KfuiKuuKuu.*iKuuKuf',2)'*b');
                                gdata(i1) = gdata(i1) + 0.5.*(2.*sum(sum(L.*L,2).*sum(DKuf{i2}'.*iKuuKuf',2)) - ...
                                    sum(sum(L.*L,2).*sum(KfuiKuuKuu.*iKuuKuf',2)));

                                gdata(i1) = gdata(i1) + 0.5.*sum(sum(ldlsolve(LD,(2.*DKuf{i2}') - KfuiKuuKuu).*iKuuKuf',2));
                                gdata(i1) = gdata(i1) - 0.5.*( idiagLa'*(sum((2.*DKuf{i2}' - KfuiKuuKuu).*iKuuKuf',2)) ); % corrected
                            end
                        end
                    end
                end
            end
        end

    case {'DTC' 'VAR' 'SOR'}
        % ============================================================
        % DTC/VAR/SOR
        % ============================================================
        g_ind = zeros(1,numel(gp.X_u));
        gdata_ind = zeros(1,numel(gp.X_u));
        gprior_ind = zeros(1,numel(gp.X_u));

        u = gp.X_u;
        DKuu_u = 0;
        DKuf_u = 0;

        % First evaluate the needed covariance matrices
        % v defines that parameter is a vector
        [Kv_ff, Cv_ff] = gp_trvar(gp, x);  % 1 x f  vector
        K_fu = gp_cov(gp, x, u);         % f x u
        K_uu = gp_trcov(gp, u);          % u x u, noiseles covariance K_uu
        K_uu = (K_uu+K_uu')./2;          % ensure the symmetry of K_uu
        [Luu, notpositivedefinite] = chol(K_uu,'lower');
        if notpositivedefinite
            % If not positive definite, return NaN
            g=NaN; gdata=NaN; gprior=NaN;
            return;
        end
        % Evaluate the Lambda (La)
        % Q_ff = K_fu*inv(K_uu)*K_fu'
        % Here we need only the diag(Q_ff), which is evaluated below
        B=Luu\(K_fu');
        Qv_ff=sum(B.^2)';
        Lav = Cv_ff-Kv_ff;   % 1 x f, Vector of diagonal elements
        % iLaKfu = diag(inv(Lav))*K_fu = inv(La)*K_fu
        iLaKfu = zeros(size(K_fu));  % f x u,
        for i=1:n
            iLaKfu(i,:) = K_fu(i,:)./Lav(i);  % f x u
        end
        % ... then evaluate some help matrices.
        % A = K_uu+K_uf*inv(La)*K_fu
        A = K_uu+K_fu'*iLaKfu;
        A = (A+A')./2;               % Ensure symmetry
        [LA, notpositivedefinite] = chol(A);
        if notpositivedefinite
            % If not positive definite, return NaN
            g=NaN; gdata=NaN; gprior=NaN;
            return;
        end
        L = iLaKfu/LA;
        b = y'./Lav' - (y'*L)*L';
        iKuuKuf = Luu'\(Luu\K_fu');
        La = Lav;
        LL = sum(L.*L,2);
        iLav=1./Lav;

        LL1=iLav-LL;

        % =================================================================

        if ~isempty(strfind(gp.infer_params, 'covariance'))
            % Loop over the covariance functions
            i1=0;
            for i=1:ncf

                % Get the gradients of the covariance matrices
                % and gprior from gpcf_* structures
                gpcf = gp.cf{i};
                if savememory
                    np=gpcf.fh.cfg(gpcf,[],[],[],0);
                else
                    DKffc = gpcf.fh.cfg(gpcf, x, [], 1);
                    DKuuc = gpcf.fh.cfg(gpcf, u);
                    DKufc = gpcf.fh.cfg(gpcf, u, x);
                    np=length(DKuuc);
                end
                gprior_cf = -gpcf.fh.lpg(gpcf);

                for i2 = 1:np
                    i1 = i1+1;
                    if savememory
                        % If savememory option is used, just get the number of
                        % hyperparameters and calculate gradients later
                        DKff=gpcf.fh.cfg(gpcf,x,[],1,i2);
                        DKuu=gpcf.fh.cfg(gpcf,u,[],[],i2);
                        DKuf=gpcf.fh.cfg(gpcf,u,x,[],i2);
                    else
                        DKff=DKffc{i2};
                        DKuu=DKuuc{i2};
                        DKuf=DKufc{i2};
                    end

                    KfuiKuuKuu = iKuuKuf'*DKuu;
                    gdata(i1) = -0.5.*((2*b*DKuf'-(b*KfuiKuuKuu))*(iKuuKuf*b'));
                    gdata(i1) = gdata(i1) + 0.5.*(2.*(sum(iLav'*sum(DKuf'.*iKuuKuf',2))-sum(sum(L'.*(L'*DKuf'*iKuuKuf))))...
                        - sum(iLav'*sum(KfuiKuuKuu.*iKuuKuf',2))+ sum(sum(L'.*((L'*KfuiKuuKuu)*iKuuKuf))));

                    if strcmp(gp.type, 'VAR')
                        gdata(i1) = gdata(i1) + 0.5.*(sum(iLav.*DKff)-2.*sum(iLav'*sum(DKuf'.*iKuuKuf',2)) + ...
                            sum(iLav'*sum(KfuiKuuKuu.*iKuuKuf',2))); % trace-term derivative
                    end
                end
                gprior = [gprior gprior_cf];
            end
        end

        % =================================================================
        % Gradient with respect to Gaussian likelihood function parameters
        if ~isempty(strfind(gp.infer_params, 'likelihood')) && isfield(gp.lik.fh,'trcov')
            % Evaluate the gradient from Gaussian likelihood
            DCff = gp.lik.fh.cfg(gp.lik, x);
            gprior_lik = -gp.lik.fh.lpg(gp.lik);
            for i2 = 1:length(DCff)
                i1 = i1+1;
                gdata(i1)= -0.5*DCff{i2}.*b*b';
                gdata(i1)= gdata(i1) + 0.5*sum(DCff{i2}./La-sum(L.*L,2).*DCff{i2});
                if strcmp(gp.type, 'VAR')
                    gdata(i1)= gdata(i1) - 0.5*(sum((Kv_ff-Qv_ff)./La));
                end
            end
            gprior = [gprior gprior_lik];
        end

        % =================================================================
        % Gradient with respect to inducing inputs
        if ~isempty(strfind(gp.infer_params, 'inducing'))
            if isfield(gp.p, 'X_u') && ~isempty(gp.p.X_u)
                m = size(gp.X_u,1);
                st=0;
                if ~isempty(gdata)
                    st = length(gdata);
                end

                gdata(st+1:st+length(gp.X_u(:))) = 0;
                i1 = st+1;
                gprior_ind=[];
                if iscell(gp.p.X_u) % Own prior for each inducing input
                    for i = 1:size(gp.X_u,1)
                        pr = gp.p.X_u{i};
                        gprior_ind =[gprior_ind -pr.fh.lpg(gp.X_u(i,:), pr)];
                    end
                else % One prior for all inducing inputs
                    gprior_ind = -gp.p.X_u.fh.lpg(gp.X_u(:)', gp.p.X_u);
                end
                gprior = [gprior gprior_ind];
                i1 = i1 + m;
                % Loop over the covariance functions
                for i=1:ncf
                    i1 = st;
                    gpcf = gp.cf{i};
                    if savememory
                        % If savememory option is used, just get the number of
                        % covariates in X and calculate gradients later
                        np=gpcf.fh.ginput(gpcf,u,[],0);
                    else
                        np=1;
                        DKuu = gpcf.fh.ginput(gpcf, u);
                        DKuf = gpcf.fh.ginput(gpcf, u, x);
                    end

                    for i3 = 1:np
                        if savememory
                            DKuu=gpcf.fh.ginput(gpcf,u,[],i3);
                            DKuf=gpcf.fh.ginput(gpcf,u,x,i3);
                        end
                        for i2 = 1:length(DKuu)
                            if savememory
                                i1 = st + (i2-1)*np+i3;
                                %i1 = st + 2*i2 - (i3==1);
                            else
                                i1 = i1+1;
                            end
                            KfuiKuuKuu = iKuuKuf'*DKuu{i2};
                            gdata(i1) = gdata(i1) - 0.5.*((2*b*DKuf{i2}'-(b*KfuiKuuKuu))*(iKuuKuf*b'));
                            gdata(i1) = gdata(i1) + 0.5.*(2.*(sum(iLav'*sum(DKuf{i2}'.*iKuuKuf',2))-sum(sum(L'.*(L'*DKuf{i2}'*iKuuKuf))))...
                                - sum(iLav'*sum(KfuiKuuKuu.*iKuuKuf',2))+ sum(sum(L'.*((L'*KfuiKuuKuu)*iKuuKuf))));

                            if strcmp(gp.type, 'VAR')
                                gdata(i1) = gdata(i1) + 0.5.*(0-2.*sum(iLav'*sum(DKuf{i2}'.*iKuuKuf',2)) + ...
                                    sum(iLav'*sum(KfuiKuuKuu.*iKuuKuf',2)));
                            end
                        end
                    end
                end
            end
        end

    case {'KALMAN'}
        % ============================================================
        % Kalman filtering and smoothing
        % ============================================================
        %
        % The implementation below is primarily based on the methods
        % presented in the following publication. If you find this
        % useful as a part of your own research, please cite the papers.
        %
        %  [1] Simo Sarkka, Arno Solin, Jouni Hartikainen (2013).
        %      Spatiotemporal learning via infinite-dimensional Bayesian
        %      filtering and smoothing. IEEE Signal Processing Magazine,
        %      30(4):51-61.
        %
        %  [2] Simo Sarkka (2013). Bayesian filtering and smoothing.
        %      Cambridge University Press.
        %
        %  [3] Simo Sarkka (2006). Recursive Bayesian inference on stochastic
        %      differential equations. Doctoral dissertation, Helsinki
        %      University of Technology, Finland.
        %

        % Ensure that this is a purely temporal problem
        if size(x,2) > 1,
            error('The ''KALMAN'' option only supports one-dimensional data.')
        end

        % Extract the noise magnitude from the GP likelihood model
        R = gp.lik.sigma2;

        % Initialize model matrices
        F   = []; L     = []; Qc = [];
        H   = []; Pinf  = []; dF = [];
        dQc = []; dPinf = []; isstable = true;

        % For each covariance function
        for j=1:length(gp.cf)

            % Form correpsonding state space model for this covariance function
            try
                [jF,jL,jQc,jH,jPinf,jdF,jdQc,jdPinf,p] = gp.cf{j}.fh.cf2ss(gp.cf{j},x);
            catch
                gdata = nan*w; gprior = nan*w; g = nan*w;
                return
            end

            % Stack model
            F  = blkdiag(F,jF);
            L  = blkdiag(L,jL);
            Qc = blkdiag(Qc,jQc);
            H  = [H jH];
            Pinf = blkdiag(Pinf,jPinf);

            % Should the covariance parameters be inferred?
            if ~isempty(strfind(gp.infer_params, 'covariance'))

                % Add derivatives
                dF      = mblk(dF,jdF);
                dQc     = mblk(dQc,jdQc);
                dPinf   = mblk(dPinf, jdPinf);

            end

            % Set options
            isstable = isfield(p,'stationary') && (isstable && p.stationary);

        end

        % Number of partial derivatives (not including R)
        nparam = min(size(dF,3),numel(dF));

        % Gradient with respect to Gaussian likelihood function parameters
        if ~isempty(gp.lik.p.('sigma2')) && ...
                ~isempty(strfind(gp.infer_params, 'likelihood')) && ...
                isfield(gp.lik.fh,'trcov')

            % Include noise magnitude R into parameters
            nparam = nparam + 1;

            % Derivative of noise magnitude w.r.t. itself (1)
            dR = zeros(1,1,nparam);
            dR(end) = 1;

            % Derivatives of model matrices w.r.t. noise magnitude (0)
            dF(:,:,nparam)    = zeros(size(F));
            dQc(:,:,nparam)   = zeros(size(Qc));
            dPinf(:,:,nparam) = zeros(size(Pinf));

        else

            % Noise magnitude is not optimized
            dR = zeros(1,1,nparam);

        end

        % Sort values
        [x,ind] = sort(x(:));
        y = y(ind);

        % State dimension, number of data points and number of parameters
        n      = size(F,1);
        steps  = numel(y);

        % Allocate for results
        % edata  = 0;
        gdata = zeros(1,nparam);

        % Set up
        Z  = zeros(n);
        m  = zeros(n,1);
        P  = Pinf;
        dm = zeros(n,nparam);
        dP = dPinf;
        dt = -inf;
        QC = L*Qc*L';

        % Allocate space for expm results
        AA  = zeros(2*n,2*n,nparam);
        AAA = zeros(4*n,4*n,nparam);

        % Loop over all observations
        for k=1:steps

            % The previous time step
            dt_old = dt;

            % The time discretization step length
            if (k>1)
                dt = x(k)-x(k-1);
            else
                dt = 0;
            end

            % Loop through all parameters (Kalman filter prediction step)
            for j=1:nparam

                % Should we recalculate the matrix exponential?
                if abs(dt-dt_old) > 1e-9

                    % The first matrix for the matrix factor decomposition
                    FF = [ F        Z;
                        dF(:,:,j) F];

                    % Solve the matrix exponential
                    AA(:,:,j) = expm2(FF*dt);

                end

                % Solve the differential equation
                foo     = AA(:,:,j)*[m; dm(:,j)];
                mm      = foo(1:n,:);
                dm(:,j) = foo(n+(1:n),:);

                if isstable

                    % For stable systems we can use the method by Davison,
                    % which simplifies everything (and speeds things up).

                    % The discrete-time dynamical model
                    if (j==1)
                        A  = AA(1:n,1:n,j);
                        Q  = Pinf - A*Pinf*A';
                        PP = A*P*A' + Q;
                    end

                    % The derivatives of A and Q
                    dA = AA(n+1:end,1:n,j);
                    %dQ = dPinf(:,:,j) - dA*Pinf*A' - A*dPinf(:,:,j)*A' - A*Pinf*dA';
                    dAPinfAt = dA*Pinf*A';
                    dQ = dPinf(:,:,j) - dAPinfAt - A*dPinf(:,:,j)*A' - dAPinfAt';

                    % The derivatives of P
                    %dP(:,:,j) = dA*P*A' + A*dP(:,:,j)*A' + A*P*dA' + dQ;
                    dAPAt = dA*P*A';
                    dP(:,:,j) = dAPAt + A*dP(:,:,j)*A' + dAPAt' + dQ;

                else

                    % The more general way for closed-form integration of
                    % the covariance by matrix fraction decomposition.

                    % Should we recalculate the matrix exponential?
                    if abs(dt-dt_old) > 1e-9

                        % Define W and G
                        W = L*dQc(:,:,j)*L';
                        G = dF(:,:,j);

                        % The second matrix for the matrix factor decomposition
                        FFF = [F  QC   Z   Z;
                            Z  -F'  Z   Z;
                            G  W    F   QC;
                            Z  -G'  Z   -F'];

                        % Solve the matrix exponential
                        AAA(:,:,j) = expm2(FFF*dt);

                    end

                    % Solve using matrix fraction decomposition
                    foo = AAA(:,:,j)*[P; eye(size(P)); dP(:,:,j); Z];

                    % Pick the parts
                    C  = foo(    (1:n),:);
                    D  = foo(  n+(1:n),:);
                    dC = foo(2*n+(1:n),:);
                    dD = foo(3*n+(1:n),:);

                    % The prediction step covariance
                    if j==1, PP = C/D; end

                    % Sove dP for j (C/D == P_{k|k-1})
                    dP(:,:,j) = (dC - PP*dD)/D;

                end
            end

            % Set predicted m and P
            m = mm;
            P = PP;

            % Start the Kalman filter update step and precalculate variables
            S = H*P*H' + R;
            [LS,notposdef] = chol(S,'lower');

            % If matrix is not positive definite, add jitter
            if notposdef>0,
                jitter = gp.jitterSigma2*diag(rand(size(S,1),1));
                [LS,notposdef] = chol(S+jitter,'lower');

                if notposdef>0,
                    gdata = nan; gprior = nan; g = nan;
                    return;
                end
            end

            % Continue update
            HtiS = H'/LS/LS';
            iS   = eye(size(S))/LS/LS';
            K    = P*HtiS;
            v    = y(k) - H*m;
            vtiS = v'/LS/LS';

            % Loop through all parameters (Kalman filter update step derivative)
            for j=1:nparam

                % Innovation covariance derivative
                dS = H*dP(:,:,j)*H' + dR(:,:,j);

                % Evaluate the energy derivative for j (optimized from above)
                gdata(j) = gdata(j) ...
                    + .5*sum(iS(:).*dS(:)) ...
                    - .5*(H*dm(:,j))*vtiS' ...
                    - .5*vtiS*dS*vtiS'     ...
                    - .5*vtiS*(H*dm(:,j));

                % Kalman filter update step derivatives
                dK        = dP(:,:,j)*HtiS - P*HtiS*dS/LS/LS';
                dm(:,j)   = dm(:,j) + dK*v - K*H*dm(:,j);
                dKSKt     = dK*S*K';
                dP(:,:,j) = dP(:,:,j) - dKSKt - K*dS*K' - dKSKt';

            end

            % Evaluate the energy (but only gradient required)
            %edata = edata + .5*size(S,1)*log(2*pi) + sum(log(diag(LS))) + .5*vtiS*v;

            % Finish Kalman filter update step
            m = m + K*v;
            P = P - K*S*K';

        end

        % Take all priors into account in the gradient
        gprior = [];
        if ~isempty(strfind(gp.infer_params, 'covariance'))
            for i=1:length(gp.cf)
                gprior = [gprior, -gp.cf{i}.fh.lpg(gp.cf{i})];
            end
        end

        gprior_lik = -gp.lik.fh.lpg(gp.lik);
        if ~isempty(gp.lik.p.('sigma2')) && ...
                ~isempty(strfind(gp.infer_params, 'likelihood')) && ...
                isfield(gp.lik.fh,'trcov')
            gprior = [gprior gprior_lik];
        end

        % Account for log transform
        w = gp_pak(gp);

        % Take correct values from w
        if length(gdata) < length(w)
            if any(hier==0)
                w = w([hier(1:find(hier==0,1)-1)==1 ... % Covariance function
                    hier(find(hier==0,1):find(hier==0,1)+...
                    length(gprior_lik)-1)==0]);      % Likelihood function
            else
                w = w(hier==1);
            end
        end

        % Log-transformation
        [w,ws] = gp_pak(gp);
        ind = strncmpi(ws,'log(',4) & ~strncmpi(ws,'log(log(',8);
        gdata(ind) = gdata(ind).*exp(w(ind));

        % Log-log-transformation
        ind = strncmpi(ws,'log(log(',8);
        gdata(ind) = gdata(ind).*exp(w(ind)+exp(w(ind)));

    otherwise
        error('Unknown type of Gaussian process!')
end

% If ther parameters of the model (covariance function parameters,
% likelihood function parameters, inducing inputs, mean function
% parameters) have additional hyperparameters that are not fixed,
% set the gradients in correct order
if length(gprior) > length(gdata)
    %gdata(gdata==0)=[];
    tmp=gdata;
    gdata = zeros(size(gprior));
    % Set the gradients to right place
    if any(hier==0)
        gdata([hier(1:find(hier==0,1)-1)==1 ...  % Covariance function
            hier(find(hier==0,1):find(hier==0,1)+length(gprior_lik)-1)==0 ... % Likelihood function
            hier(find(hier==0,1)+length(gprior_lik):end)==1 | ... % Inducing inputs or ...
            hier(find(hier==0,1)+length(gprior_lik):end)==-1]) = tmp; % Mean function parameters
    else
        if any(hier<0)
            hier(hier==-1)=1;
        end
        gdata(hier==1) = tmp;
    end
end
g = gdata + gprior;

function C = mblk(A,B)
%% mblk - Stack matrices by third dimension

% Get sizes
sA=size(A); sB=size(B);

% Check if A or B is empty
Ae = ~any(sA==0); Be = ~any(sB==0);

% Numel of sizes to 3
sA = [sA Ae]; sA = sA(1:3);
sB = [sB Be]; sB = sB(1:3);

% Assign space for C
C = zeros(sA+sB);

% Set values of A if there is any
if Ae
    C(1:sA(1), 1:sA(2), 1:sA(3)) = A;
end

% Set values of B if there is any
if Be
    C(sA(1)+(1:sB(1)), ...
        sA(2)+(1:sB(2)), ...
        sA(3)+(1:sB(3))) = B;
end