function field=meg_forward(dip_par,forwpar)
% calculates the magnetic field of n dipoles
% in a realistic volume conductor
% usage: field=meg_forward(dip_par,forwpar)
%
% input:
% dip_par nx6 matrix where each row contains location (first 3 numbers)
%         and moment (second 3 numbers) of a dipole
% forwpar structure containing all information relevant for this
%         calculation; forwpar is calculated with meg_ini
%         You have here an option to include linear transformations in
%         the forward model by specifying forpwar.lintrafo=A
%         where A is an NxM matrix. Then field -> A field
%         You can use that, e.g., if you can write the forward model
%         with M magnetometer-channels plus a matrix multiplication
%         transforming this to a (eventually higher order) gradiometer.
%
% output:
% field  mxn matrix where the i.th column is the field in m channels
%        of the i.th dipole
%
% note:  No assumptions about units are made (i.e. no scaling factors)
%
% Copyright (C) 2003, Guido Nolte
%
% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org
% for the documentation and details.
%
%    FieldTrip is free software: you can redistribute it and/or modify
%    it under the terms of the GNU General Public License as published by
%    the Free Software Foundation, either version 3 of the License, or
%    (at your option) any later version.
%
%    FieldTrip is distributed in the hope that it will be useful,
%    but WITHOUT ANY WARRANTY; without even the implied warranty of
%    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
%    GNU General Public License for more details.
%
%    You should have received a copy of the GNU General Public License
%    along with FieldTrip. If not, see <http://www.gnu.org/licenses/>.
%
% $Id$

device_sens=forwpar.device_sens;

field_sens_sphere=getfield_sphere(dip_par,forwpar.device_sens,forwpar.center);
field=field_sens_sphere;clear field_sens_sphere;
if isfield(forwpar,'device_ref')
  field=field-getfield_sphere(dip_par,forwpar.device_ref,forwpar.center);
end
if isfield(forwpar,'device_weights')
  field=field+forwpar.weights*getfield_sphere(dip_par,forwpar.device_weights,forwpar.center);
end

if forwpar.order>0
  coeff=forwpar.coeff_sens;
  if isfield(forwpar,'device_ref')
    coeff=coeff-forwpar.coeff_ref;
  end
  if isfield(forwpar,'device_weights')
    coeff=coeff+forwpar.coeff_weights*forwpar.weights';
  end
  field=field+getfield_corr(dip_par,coeff,forwpar.center,forwpar.order);
end

if isfield(forwpar,'lintrafo');
  field=forwpar.lintrafo*field;
end

return % main function

function field=getfield_sphere(source,device,center)
[ndip,ndum]=size(source);
[nchan,ndum]=size(device);
x1=source(:,1:3)-repmat(center',ndip,1);
n1=source(:,4:6);
x2=device(:,1:3)-repmat(center',nchan,1);
n2=device(:,4:6);
%spherical
bt=leadsphere_all(x1',x2',n2');
n1rep=reshape(repmat(n1',1,nchan),3,ndip,nchan);
b=dotproduct(n1rep,bt);
field=b';
return

function field=getfield_corr(source,coeffs,center,order)
[ndip,ndum]=size(source);
x1=source(:,1:3)-repmat(center',ndip,1);
n1=source(:,4:6);
%correction
if order>0
  scale=10;
  [bas,gradbas]=legs(x1,n1,order,scale);
  nbasis=(order+1)^2-1;
  coeffs=coeffs(1:nbasis,:);
  field=-(gradbas*coeffs)';
end
return

function out=crossproduct(x,y)
% usage: out=testprog(x,y)
% testprog calculates the cross-product of vector x and y
[n,m,k]=size(x);
out=zeros(3,m,k);
out(1,:,:)=x(2,:,:).*y(3,:,:)-x(3,:,:).*y(2,:,:);
out(2,:,:)=x(3,:,:).*y(1,:,:)-x(1,:,:).*y(3,:,:);
out(3,:,:)=x(1,:,:).*y(2,:,:)-x(2,:,:).*y(1,:,:);
return

function out=dotproduct(x,y)
% usage: out=dotproduct(x,y)
% testprog calculates the dotproduct of vector x and y
[n,m,k]=size(x);
outb=x(1,:,:).*y(1,:,:)+x(2,:,:).*y(2,:,:)+x(3,:,:).*y(3,:,:);
out=reshape(outb,m,k);
return

function result=norms(x)
[n,m,k]=size(x);
resultb=sqrt(x(1,:,:).^2+x(2,:,:).^2+x(3,:,:).^2);
result=reshape(resultb,m,k);
return


function [W,dist_H_V] = shepard(V,C,P,E,CE,p)
  % SHEPARD Compute shepard weights for a list of vertices, given a list of
  % samples and optionally a denominator power value.
  %
  % W = shepard(V,C)
  %
  % Inputs:
  %  V  list of vertex positions
  %  C  list of control vertices
  %  P  list of indices into C for point controls, { 1:size(C,1) }
  %  E  list of bones, pairs of indices into C, connecting control vertices,
  %    { [] }
  %  CE  list of "cage edges", pairs of indices into ***P***, connecting
  %    control ***points***. A "cage edge" just tells point boundary conditions
  %    to vary linearly along straight lines between the end points, and to be
  %    zero for all other handles. { [] }
  %  p  (optional) power for denominator, scalar or list same size as C {2}
  %
  % Outputs:
  %  W  weights, # vertices by # handles matrix of weights
  %
  % Example:
  %  % "Shape-aware" shepard weights
  %  D = permute(sum((repmat(V,[1,1,size(C,1)]) - ...
  %    permute(repmat(C,[1,1,n]),[3,2,1])).^2,2),[1,3,2]);
  %  [minD,b] = min(D);
  %  snap_C = V(b,:);
  %  [BV,ev,ed] = biharmonic_embedding(V,F,6);
  %  [W] = shepard(BV,BV(b,:));
  %
  %

  % check if either are 3D but really all z's are 0
  V_flat = size(V,2) == 3 && (sqrt(sum(V(:,3).^2)) < 1e-10);
  C_flat = size(C,2) == 3 && (sqrt(sum(C(:,3).^2)) < 1e-10);
  % is both are essentially 2D then ignore z-coords
  if((size(C,2) == 2 || C_flat) && (size(V,2) == 2 || V_flat))
    % ignore z coordinate
    V = V(:,1:2);
    C = C(:,1:2);
  end

  assert(size(C,2) == size(V,2));
  dim = size(C,2);

  % default p value
  if(~exist('p','var'))
    p = 2;
  end

  if(~exist('P','var'))
    P = 1:size(C,1);
  end

  if(~exist('E','var'))
    E = [];
  end

  if(~exist('CE','var'))
    CE = [];
  end

  assert(prod(size(P)) == max(size(P)));
  assert(isempty(E) || size(E,2) == 2);
  assert(isempty(CE) || size(CE,2) == 2);
  assert(isempty(E) || max(E(:))<=size(C,1));
  assert(isempty(CE) || max(CE(:))<=numel(P));
  assert(isempty(P) || max(P(:))<=size(C,1));
  assert(isempty(E) || min(E(:)) >= 1);
  assert(isempty(CE) || min(CE(:)) >= 1);
  assert(isempty(P) || min(P(:)) >= 1);

  % number of point controls
  np = numel(P);
  nb = size(E,1);
  % number of domain vertices
  n = size(V,1);

  % if p is a scalar convert it now to a list the same size as C
  if(prod(size(p)) == 1)
    p = repmat(p,np+nb,1);
  elseif ~isempty(CE)
    if 0 ~= std(p(CE(:)))
      error('Variable powers not supported with cage edges');
    end
  end

  dist_P_V = zeros(np,n);
  if(np > 0)
    % vectors from V to every P, where PmV(i,j,:) is the vector from domain
    % vertex j to handle i
    PmV = ...
      permute( ...
        permute(repmat(C(P,:),[1,1,n]),[3,2,1]) - ...
        repmat(V,[1,1,np]),[3,1,2]);
    % distance from V to every P, where dist_P_V(i,j) is the distance from domain
    % vertex j to point handle i
    dist_P_V = sqrt(sum(PmV.^2,3));
    % distance from each corner in P to the next corner so that edge_length(i)
  end

  dist_B_V = zeros(nb,n);
  if(nb > 0)
    % loop over bones
    for( ii = 1:nb )
      % project to line
      [t,sqr_d] = project_to_lines(V,C(E(ii,1),:),C(E(ii,2),:));
      d = sqrt(sqr_d);
      % if projected point falls outside of line segment take distance to
      % closest end point
      d(t<0) = sqrt(sum((V(t<0,:)-repmat(C(E(ii,1),:),size(V(t<0,:),1),1)).^2,2));
      d(t>1) = sqrt(sum((V(t>1,:)-repmat(C(E(ii,2),:),size(V(t>1,:),1),1)).^2,2));
      dist_B_V(ii,:) = d';
    end
  end

  dist_H_V = [dist_P_V ; dist_B_V];
  % power of each control point seen by each vertex in domain
  pp = repmat(p,1,n);
  W = 1.0./((dist_H_V).^pp);

  if(size(CE,1) > 0)
    % zero out previous entries in points with incident cage edges
    W(CE(:),:) = 0;
    % compute projection of each point to each line segment
    [T,sqrD] = project_to_lines(V,C(P(CE(:,1)),:),C(P(CE(:,2)),:));
    % compute weights for each cage edge as if it were a bone
    pce = (p(CE(:,1))+p(CE(:,2)))/2;
    [~,dist_CE_V] = shepard(V,C,[],P(CE),[],pce);
    % compute "bone weights" for each cage edge as they were before
    % normalization
    ppce = repmat(pce,1,n);
    WCE = 1.0./((dist_CE_V).^ppce)';
    % clamp distances
    T(T<0) = 0;
    T(T>1) = 1;
    % multiply "bone weight" by projection parameter and sum up
    II = repmat(1:n,1,2*size(CE,1))';
    JJ = reshape(repmat(CE(:)',n,1),[],1);
    VV = [WCE(:).*(1-T(:)); WCE(:).*T(:)];
    W = W+sparse(II,JJ,VV,n,np+nb)';
    % cage edges on-samples
    on_sample = dist_CE_V < eps;
    [I,J] = find(on_sample);
    W(:,any(on_sample)) = 0;
    % grab T transpose
    TT = T';
    W(sub2ind(size(W),CE(I,1),J)) = 1-TT(on_sample);
    W(sub2ind(size(W),CE(I,2),J)) = TT(on_sample);
  end

  % Handle degenrate case that a control point is on a mesh vertex
  % snap vertices close to corners
  on_sample = dist_H_V < eps;
  W(:,any(on_sample,1)) = 0;
  W(on_sample) = 1;

  % normalize W
  W = W./repmat(sum(W,1),np+nb,1);

  % we've made W transpose
  W = W';

end


classdef prtClassMilVbDpGmm < prtClass
    % prtClassMilVbDpGmm VBDPGMM MIL Classifier
    %
    %  A variational bayes classifier for multiple instance data.  There
    %  are a number of parameters available for pruning/training.
    %  Performnace should (in theory) be somewhat invariant to these.  See
    %  the text of Manandhar et al., for descriptions.
    %
    %  You may need to reduce the dimensionality of the data being used via
    %  PCA, PLS, or some other pre-processing prior to using
    %  prtClassMilVbDpGmm.  We also recommend making sure the data is
    %  approximately zero-mean, unit-variance in each of the columns.
    %
    %  dsTrain = prtDataGenMultipleInstance;
    %  dsTest = prtDataGenMultipleInstance;
    %  class = prtClassMilVbDpGmm;
    %  class = class.train(dsTrain);
    %  yOutTrain = class.run(dsTrain);
    %  yOutTest = class.run(dsTest);
    %
    %  [pfTrain,pdTrain] = prtScoreRoc(yOutTrain);
    %  [pfTest,pdTest] = prtScoreRoc(yOutTest);
    %  subplot(1,1,1);
    %  plot(pfTrain,pdTrain,pfTest,pdTest);
    %

    properties (SetAccess=private)
        name = 'MilVbDpGmm'
        nameAbbreviation = 'MilVbDpGmm'
        isNativeMary = false;  % False
    end

    properties
        maxVbIter = 300;

        K0 = 5;
        K1 = 5;
        gamma_0_1_m0 = 1;
        gamma_0_2_m0 = .001;
        gamma_0_1_m1 = 1;
        gamma_0_2_m1 = .001;
        alpha = [10 10];

        rvH1
        rvH0
    end

    properties (Hidden)

        initialFit = [];
        %Dependent on "d"
        v_0_m
        v_1_m
        beta_1_m
        beta_0_m

        rho_0_1
        rho_0_0
        Phi_0_1
        Phi_0_0
    end

	methods
		function self = prtClassMilVbDpGmm(varargin)

			self = prtUtilAssignStringValuePairs(self,varargin{:});

            self.classTrain = 'prtDataSetClassMultipleInstance';
            self.classRun = 'prtDataSetClassMultipleInstance';
            self.classRunRetained = false;
        end

    end

    methods (Access=protected, Hidden = true)
		function self = trainAction(self,dsMil)

            milStruct = dsMil.data;
            x = cat(1,milStruct.data);

            bagLabels = dsMil.targets;
            expandedBagLabels = dsMil.expandedTargets;

            bagIndices = dsMil.getBagInds;
            uniqueBagIndices = unique(bagIndices);

            d = size(x,2);

            self.v_0_m = d+2;
            self.v_1_m = d+2;
            self.beta_1_m = d;
            self.beta_0_m = d;
            self.rho_0_0 = zeros(1,d);
            self.rho_0_1 = zeros(1,d);

            self.Phi_0_0 = eye(d)*d;
            self.Phi_0_1 = eye(d)*d;

%             params = struct('K0',5,'K1',5,'gamma_0_1_m0',1,'gamma_0_2_m0',.001,'gamma_0_1_m1',1,'gamma_0_2_m1',.001,...
%                 'v_0_m',d+2,'v_1_m',d+2,'beta_1_m',d,'beta_0_m',d, ...
%                 'rho_0_1',zeros(1,d),'rho_0_0',zeros(1,d),'Phi_0_1',eye(d)*d,'Phi_0_0',eye(d)*d,'alpha',[4 1]*10000);

            [rvH0,rvH1,xH0,xH1] = initialize(self,{milStruct.data},bagLabels);
            %self.initialFit = self.calculateDataLogLikelihood(self,xH0,xH1);
            ll0 = sum(rvH0.logPdf(xH0));
            ll1 = rvH0.pdf(xH1).*self.alpha(1)/sum(self.alpha) + rvH1.pdf(xH1).*self.alpha(2)/sum(self.alpha);
            self.initialFit = ll0 + sum(log(ll1));

            [p0,pp] = rvH0.pdf(x);
            phi_0_i = bsxfun(@rdivide,pp,sum(pp,2));

            [p1,pp] = rvH1.pdf(x);
            phi_1_i = bsxfun(@rdivide,pp,sum(pp,2));
            phi_1_i(expandedBagLabels == 0,:) = 0;
            p1(expandedBagLabels ==0) = 0;

            phi_1_M = p1./(p0 + p1);
            phi_0_M = 1-phi_1_M;

            locEps = 1e-6;

            for vbIter = 1:self.maxVbIter
                %Equations 67 and 68; note, these are different from 20-21 (!)
                %     gamma_i_1_m0 = sum(bsxfun(@times,phi_0_M,phi_0_i)) + self.gamma_0_1_m0;
                %     gamma_i_2_m0 = cat(2,0,cumsum(gamma_i_1_m0(1:end-1),2)) + self.gamma_0_2_m0;
                %
                %     gamma_i_1_m1 = sum(bsxfun(@times,phi_1_M,phi_1_i)) + self.gamma_0_1_m1;
                %     gamma_i_2_m1 = cat(2,0,cumsum(gamma_i_1_m1(1:end-1),2)) + self.gamma_0_2_m1;

                %Equations 20-21
                gamma_i_1_m0 = sum(bsxfun(@times,phi_0_M,phi_0_i)) + self.gamma_0_1_m0;
                gamma_i_2_m0 = fliplr(cumsum(fliplr(gamma_i_1_m0))) - gamma_i_1_m0 + self.gamma_0_2_m0;

                gamma_i_1_m1 = sum(bsxfun(@times,phi_1_M,phi_1_i)) + self.gamma_0_1_m1;
                gamma_i_2_m1 = fliplr(cumsum(fliplr(gamma_i_1_m1))) - gamma_i_1_m1 + self.gamma_0_2_m1;

                %Update priors... H0 Gaussian; Equations 77-79 (H0)
                respMat = bsxfun(@times,phi_0_M,phi_0_i);
                nBar0 = sum(respMat)+locEps;  %avoid 1./0 problems
                for cluster = 1:self.K0
                    mu_i_0(cluster,:) = 1/nBar0(cluster)*sum(bsxfun(@times,respMat(:,cluster),x));

                    xx = bsxfun(@times,sqrt(respMat(:,cluster)),bsxfun(@minus,x,mu_i_0(cluster,:)));
                    c = 1/nBar0(cluster)*xx'*xx;
                    cov_i_0(cluster,:) = c(:);
                end

                %Update priors... H1 Gaussian; Equations 77-79
                respMat = bsxfun(@times,phi_1_M,phi_1_i);
                nBar1 = sum(respMat)+locEps;
                for cluster = 1:self.K1
                    mu_i_1(cluster,:) = 1/nBar1(cluster)*sum(bsxfun(@times,respMat(:,cluster),x));

                    xx = bsxfun(@times,sqrt(respMat(:,cluster)),bsxfun(@minus,x,mu_i_1(cluster,:)));
                    c = 1/nBar1(cluster)*xx'*xx;
                    cov_i_1(cluster,:) = c(:);
                end

                %Updating hyper-parameters; eqns 81-84
                nu_0 = self.v_0_m + nBar0;
                nu_1 = self.v_1_m + nBar1;

                beta_0 = self.beta_0_m + nBar0;
                beta_1 = self.beta_1_m + nBar1;

                for cluster = 1:self.K0
                    rho_0(cluster,:) = self.beta_0_m*self.rho_0_0 + nBar0(cluster)*mu_i_0(cluster,:);
                    rho_0(cluster,:) = rho_0(cluster,:)./beta_0(cluster);

                    %See equation 84; Note: this is different from Equation 84 - 84 is
                    %wrong
                    %covPart = nBar0(cluster) * reshape(cov_i_0(cluster,:),d,d) * ((mu_i_0(cluster,:) - rho_0(cluster,:))'*(mu_i_0(cluster,:) - rho_0(cluster,:)));
                    covPart = nBar0(cluster) * self.beta_0_m * ((mu_i_0(cluster,:) - self.rho_0_0)'*(mu_i_0(cluster,:) - self.rho_0_0));
                    covPart = covPart ./ beta_0(cluster);

                    phiCov = self.Phi_0_0 + nBar0(cluster)*reshape(cov_i_0(cluster,:),d,d) + covPart;
                    Phi_0(cluster,:) = phiCov(:)';
                end

                for cluster = 1:self.K1
                    rho_1(cluster,:) = self.beta_1_m*self.rho_0_1 + nBar1(cluster)*mu_i_1(cluster,:);
                    rho_1(cluster,:) = rho_1(cluster,:)./beta_1(cluster);

                    %See equation 84; Note: this is different from Equation 84 - 84 is
                    %wrong
                    %covPart = nBar1(cluster) * reshape(cov_i_1(cluster,:),d,d) * ((mu_i_1(cluster,:) - rho_1(cluster,:))'*(mu_i_1(cluster,:) - rho_1(cluster,:)));
                    %         covPart = nBar1(cluster) * reshape(cov_i_1(cluster,:),d,d) * ((mu_i_1(cluster,:) - self.rho_0_0)'*(mu_i_1(cluster,:) - self.rho_0_0));
                    covPart = nBar1(cluster) * self.beta_1_m * ((mu_i_1(cluster,:) - self.rho_0_1)'*(mu_i_1(cluster,:) - self.rho_0_1));
                    covPart = covPart ./ beta_1(cluster);

                    phiCov = self.Phi_0_1 + nBar1(cluster)*reshape(cov_i_1(cluster,:),d,d) + covPart;
                    Phi_1(cluster,:) = phiCov(:)';
                end

                % <log_pi>  Equation 102
                matGamma = psi(gamma_i_1_m0) - psi(gamma_i_1_m0 + gamma_i_2_m0);
                matGamma2 = psi(gamma_i_2_m0) - psi(gamma_i_1_m0 + gamma_i_2_m0);
                log_pi_0 = matGamma + cat(2,0,cumsum(matGamma2(1:end-1)));

                matGamma = psi(gamma_i_1_m1) - psi(gamma_i_1_m1 + gamma_i_2_m1);
                matGamma2 = psi(gamma_i_2_m1) - psi(gamma_i_1_m1 + gamma_i_2_m1);
                log_pi_1 = matGamma + cat(2,0,cumsum(matGamma2(1:end-1)));

                % <Log \eta_m>  Equation 105
                nBarM0 = self.alpha(1) +  sum(phi_0_M(expandedBagLabels == 1,:));
                nBarM1 = self.alpha(2) +  sum(phi_1_M(expandedBagLabels == 1,:));
                log_eta = psi([nBarM0,nBarM1]) - psi(nBarM1+nBarM0);

                % <log det ||>
                for cluster = 1:self.K0
                    temp_det0 = sum(psi((nu_0(cluster) + 1 - (1:d))/2));
                    temp_det0 = temp_det0 + d*log(2);
                    log_det_0(cluster) = temp_det0 + log(det(reshape(Phi_0(cluster,:),d,d)^-1));
                end
                for cluster = 1:self.K1
                    temp_det1 = sum(psi((nu_1(cluster) + 1 - (1:d))/2));
                    temp_det1 = temp_det1 + d*log(2);
                    log_det_1(cluster) = temp_det1 + log(det(reshape(Phi_1(cluster,:),d,d)^-1));
                end

                %Equatioin 113
                for cluster = 1:self.K0
                    c = reshape(Phi_0(cluster,:),d,d);
                    %         v = prtUtilCalcDiagXcInvXT(bsxfun(@minus,x,mu_i_0(cluster,:)),c);
                    v = prtUtilCalcDiagXcInvXT(bsxfun(@minus,x,rho_0(cluster,:)),c);
                    inner_0_n(:,cluster) = d/beta_0(cluster) + nu_0(cluster).*v;
                end

                for cluster = 1:self.K1
                    c = reshape(Phi_1(cluster,:),d,d);
                    %         v = prtUtilCalcDiagXcInvXT(bsxfun(@minus,x,mu_i_1(cluster,:)),c);
                    v = prtUtilCalcDiagXcInvXT(bsxfun(@minus,x,rho_1(cluster,:)),c);
                    inner_1_n(:,cluster) = d/beta_1(cluster) + nu_1(cluster).*v;
                end

                for cluster = 1:self.K0
                    eLogPdf0(:,cluster) = -d*log(2*pi) + log_det_0(cluster) - inner_0_n(:,cluster);
                end
                eLogPdf0 = eLogPdf0*1/2;

                for cluster = 1:self.K1
                    eLogPdf1(:,cluster) = -d*log(2*pi) + log_det_1(cluster) - inner_1_n(:,cluster);
                end
                eLogPdf1 = eLogPdf1*1/2;

                phiHat0 = bsxfun(@plus,eLogPdf0,log_pi_0);
                phiHat1 = bsxfun(@plus,eLogPdf1,log_pi_1);
                phiHat1(expandedBagLabels == 0,:) = -inf;

                phi_1_i = bsxfun(@rdivide,exp(phiHat1),sum(exp(phiHat1),2));
                phi_0_i = bsxfun(@rdivide,exp(phiHat0),sum(exp(phiHat0),2));

                phi_1_i(exp(phiHat1) == 0) = 0;
                phi_0_i(exp(phiHat0) == 0) = 0;

                %
                n1 = (log(sum(exp(phiHat1),2))+log_eta(2));
                n0 = (log(sum(exp(phiHat0),2))+log_eta(1));
                phi_1_M = exp(n1)./(exp(n1)+exp(n0));
                phi_1_M(expandedBagLabels == 0) = 0;

                phi_0_M = 1-phi_1_M;

                for i = 1:size(rho_0,1)
                    c = Phi_0(i,:)./nu_0(i);
                    c = reshape(c,d,d);
                    if vbIter == 1
                        mm0(i) = prtRvMvn('mu',rho_0(i,:),'sigma',c);
                    else
                        mm0(i).mu = rho_0(i,:);
                        mm0(i).sigma = c;
                    end
                end
                for i = 1:size(rho_1,1)
                    c = Phi_1(i,:)./nu_1(i);
                    c = reshape(c,d,d);
                    if vbIter == 1
                        mm1(i) = prtRvMvn('mu',rho_1(i,:),'sigma',c);
                    else
                        mm1(i).mu = rho_1(i,:);
                        mm1(i).sigma = c;
                    end
                end

                pi0 = exp(log_pi_0); pi0 = pi0./sum(pi0);
                rv0 = prtRvGmm('nComponents',size(mu_i_0,1),'mixingProportions',pi0,'components',mm0);

                pi1 = exp(log_pi_1); pi1 = pi1./sum(pi1);
                rv1 = prtRvGmm('nComponents',size(mu_i_1,1),'mixingProportions',pi1,'components',mm1);

                self.rvH1 = rv1;
                self.rvH0 = rv0;

                drawnow;
                if ~mod(vbIter,100);
                    disp(vbIter)
                    subplot(2,4,1);
                    stem(exp(log_pi_0));
                    subplot(2,4,2);
                    stem(exp(log_eta))
                    subplot(2,4,3);
                    stem(exp(log_pi_1));
                    fprintf('exp(log_pi_0): %.2f\n',exp(log_pi_0))
                    fprintf('exp(log_pi_1): %.2f\n',exp(log_pi_1))

                    subplot(2,4,4);
                    self.isTrained = true;
                    yOut = self.run(dsMil);
                    prtScoreRoc(yOut);

                    subplot(2,4,5:8);
                    if d > 1
                        plot(x(expandedBagLabels == 0,1),x(expandedBagLabels == 0,2),'b.');
                        hold on;
                        plot(x(expandedBagLabels == 1,1),x(expandedBagLabels == 1,2),'r.');

                        for i = 1:size(rho_0,1)
                            c = Phi_0(i,:)./nu_0(i);
                            c = reshape(c,d,d);
                            if d > 1
                                %                                 plotMvnEllipse(rho_0(i,1:2),c(1:2,1:2),1);
                            end
                            mm0(i) = prtRvMvn('mu',rho_0(i,:),'sigma',c);
                        end
                        for i = 1:size(rho_1,1)
                            c = Phi_1(i,:)./nu_1(i);
                            c = reshape(c,d,d);
                            if d > 1
                                %                                 plotMvnEllipse(rho_1(i,1:2),c(1:2,1:2),1);
                            end
                            mm1(i) = prtRvMvn('mu',rho_1(i,:),'sigma',c);
                        end
                        hold on; h = plot(rho_0(:,1),rho_0(:,2),'ko'); set(h,'MarkerFaceColor','k');
                        hold on; h = plot(rho_1(:,1),rho_1(:,2),'go'); set(h,'MarkerFaceColor','g');

                        hold off;
                    end
                    drawnow;
                end

            end
        end

        function yOut = runAction(self,dsMil)

            for n = 1:dsMil.nObservations
                milStruct = dsMil.data(n);
                data = milStruct.data;

                h1 = self.rvH1.logPdf(data);
                h0 = self.rvH0.logPdf(data);
                l0 = h0'-prtUtilSumExp([h0';h1']);

                h0LogProbabilityBag = sum(l0);
                %exp(h0LogProbabilityBag)

                y(n,1) = 1-exp(h0LogProbabilityBag);
            end

            yOut = prtDataSetClass(y,dsMil.targets);
        end

        function [h0Mix,h1Mix,xH0,xH1] = initialize(self,xBag,bagLabels)

            %H0 Kmeans
            xH0 = cat(1,xBag{bagLabels == 0});
            idx0 = kmeans(xH0,self.K0,'Replicates',1,'EmptyAction','singleton','MaxIter',100);
            uIds = unique(idx0);

            for idx = 1:length(uIds)
                rvH0struct(idx).mean = mean(xH0(idx0 == idx,:),1);

                try
                    rvH0struct(idx).cov = cov(xH0(idx0 == idx,:));
                    chol(rvH0struct(idx).cov);
                    assert(size(xH0,2) == size(rvH0struct(idx).cov,2));
                catch ME
                    rvH0struct(idx).cov = diag(var(xH0(idx0 == idx,:))) + eye(size(xH0,2));
                end

                rvH0(idx) = prtRvMvn('mu',rvH0struct(idx).mean,'sigma',rvH0struct(idx).cov);
                pi(idx) = sum(idx0 == idx)./length(idx0);

            end
            pi = pi./sum(pi);
            h0Mix = prtRvGmm('nComponents',length(rvH0),'mixingProportions',pi,'components',rvH0);

            %Least likely H1
            h1Bags = xBag(bagLabels == 1);
            for h1Ind = 1:length(h1Bags)
                ll = h0Mix.logPdf(h1Bags{h1Ind});
                [~,ind] = min(ll);
                xH1(h1Ind,:) = h1Bags{h1Ind}(ind,:);
            end

            %H1 Kmeans
            idx1 = kmeans(xH1,self.K1,'Replicates',1,'EmptyAction','singleton','MaxIter',100);
            uIds = unique(idx1);

            pi = [];
            for idx = 1:length(uIds)
                rvH1struct(idx).mean = mean(xH1(idx1 == idx,:),1);

                try
                    rvH1struct(idx).cov = cov(xH1(idx0 == idx,:));
                    chol(rvH1struct(idx).cov);
                catch ME
                    rvH1struct(idx).cov = diag(var(xH1)) + eye(size(xH1,2));
                end

                rvH1(idx) = prtRvMvn('mu',rvH1struct(idx).mean,'sigma',rvH1struct(idx).cov);
                pi(idx) = sum(idx1 == idx)./length(idx1);

            end
            pi = pi./sum(pi);
            h1Mix = prtRvGmm('nComponents',length(rvH1),'mixingProportions',pi,'components',rvH1);
        end
    end
end


function [cl,cd] = bpolar(BladeState,AC)
% To make your own bpolar:
% BladeState is the state of the blade element with matrix components:
% BladeState =
%            M: Mach number
%        alpha: angle of attack (radians)
%            r: radius location (nondimensional)
%          psi: azimuth location (radians)
%     alphadot: rate of change of angle of attack (rad/s)
%           Re: Reynolds number
% all components on BladeState inputs are m x n, where n is the number of
% blade elements, and m is the uniformly distributed number of azimuth
% elements.
%
% NB: this function should run VERY fast, so avoid loops and extensive
% interpolation.
% see also lininterp1f
%
% AC is a structure with AC data (see documentation)
%
% cl and cd are the m x n arrays containing each blade element's lift and
% drag coefficients.
% note that output coefficients will be non-dimensionalized by a constant
% blade chord, typically the average blade chord.
%
% The blade polar function is responsible for capturing all important rotor
% phenomena, including reverse flow, retreating blade stall, and advancing
% tip critical mach number.
%
% to capture reverse flow effects, the drag polar should be able to handle
% alphas +/- 360 deg
%
% critical mach drag rise and stall should also be modeled.

% This file is an example of using wind tunnel data (NACA 0012), very fast
% interpolation, and a critical mach drag rise approximation.

% Drag Coefficient data
%from http://www.cyberiad.net
% -------------------------------- REYNOLDS NUMBER -----------------------

% RE = [5000000] ;

DragData = [    0.0064
    0.0064
    0.0066
    0.0068
    0.0072
    0.0076
    0.0081
    0.0086
    0.0092
    0.0098
    0.0106
    0.0118
    0.0130
    0.0143
    0.0159
    0.0177
    0.0198
    0.0229
    0.1480
    0.2740
    0.2970
    0.3200
    0.3440
    0.3690
    0.3940
    0.4200
    0.4460
    0.4730
    0.5700
    0.7450
    0.9200
    1.0750
    1.2150
    1.3450
    1.4700
    1.5750
    1.6650
    1.7350
    1.7800
    1.8000
    1.8000
    1.7800
    1.7500
    1.7000
    1.6350
    1.5550
    1.4650
    1.3500
    1.2250
    1.0850
    0.9250
    0.7550
    0.5750
    0.4200
    0.3200
    0.2300
    0.1400
    0.0550
    0.0250
    0.0550
    0.1400
    0.2300
    0.3200
    0.4200
    0.5750
    0.7550
    0.9250
    1.0850
    1.2250
    1.3500
    1.4650
    1.5550
    1.6350
    1.7000
    1.7500
    1.7800
    1.8000
    1.8000
    1.7800
    1.7350
    1.6650
    1.5750
    1.4700
    1.3450
    1.2150
    1.0750
    0.9200
    0.7450
    0.5700
    0.4730
    0.4460
    0.4200
    0.3940
    0.3690
    0.3440
    0.3200
    0.2970
    0.2740
    0.1480
    0.0229
    0.0198
    0.0177
    0.0159
    0.0143
    0.0130
    0.0118
    0.0106
    0.0098
    0.0092
    0.0086
    0.0081
    0.0076
    0.0072
    0.0068
    0.0066
    0.0064
    0.0064];
LiftData =[          0
    0.1100
    0.2200
    0.3300
    0.4400
    0.5500
    0.6600
    0.7700
    0.8800
    0.9900
    1.1000
    1.1842
    1.2673
    1.3242
    1.3423
    1.3093
    1.2195
    1.0365
    0.9054
    0.8412
    0.8233
    0.8327
    0.8563
    0.8903
    0.9295
    0.9718
    1.0193
    1.0680
    0.9150
    1.0200
    1.0750
    1.0850
    1.0400
    0.9650
    0.8750
    0.7650
    0.6500
    0.5150
    0.3700
    0.2200
    0.0700
    -0.0700
    -0.2200
    -0.3700
    -0.5100
    -0.6250
    -0.7350
    -0.8400
    -0.9100
    -0.9450
    -0.9450
    -0.9100
    -0.8500
    -0.7400
    -0.6600
    -0.6750
    -0.8500
    -0.6900
    0
    0.6900
    0.8500
    0.6750
    0.6600
    0.7400
    0.8500
    0.9100
    0.9450
    0.9450
    0.9100
    0.8400
    0.7350
    0.6250
    0.5100
    0.3700
    0.2200
    0.0700
    -0.0700
    -0.2200
    -0.3700
    -0.5150
    -0.6500
    -0.7650
    -0.8750
    -0.9650
    -1.0400
    -1.0850
    -1.0750
    -1.0200
    -0.9150
    -1.0680
    -1.0193
    -0.9718
    -0.9295
    -0.8903
    -0.8563
    -0.8327
    -0.8233
    -0.8412
    -0.9054
    -1.0365
    -1.2195
    -1.3093
    -1.3423
    -1.3242
    -1.2673
    -1.1842
    -1.1000
    -0.9900
    -0.8800
    -0.7700
    -0.6600
    -0.5500
    -0.4400
    -0.3300
    -0.2200
    -0.1100
    0];
Alpha =[0
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    30
    35
    40
    45
    50
    55
    60
    65
    70
    75
    80
    85
    90
    95
    100
    105
    110
    115
    120
    125
    130
    135
    140
    145
    150
    155
    160
    165
    170
    175
    180
    185
    190
    195
    200
    205
    210
    215
    220
    225
    230
    235
    240
    245
    250
    255
    260
    265
    270
    275
    280
    285
    290
    295
    300
    305
    310
    315
    320
    325
    330
    333
    334
    335
    336
    337
    338
    339
    340
    341
    342
    343
    344
    345
    346
    347
    348
    349
    350
    351
    352
    353
    354
    355
    356
    357
    358
    359
    360]*pi/180;
modinpcount = 0;
while any(BladeState.alpha(:)<0)
    ind = BladeState.alpha<0;
    BladeState.alpha(ind) = BladeState.alpha(ind)+2*pi;
    modinpcount=modinpcount+1;
end
if modinpcount>2
    disp('crazy stuff going on in bpolar - very very negative alphas')
end
modinpcount = 0;
while any(BladeState.alpha(:)>2*pi)
    ind = BladeState.alpha>2*pi;
    BladeState.alpha(ind) = BladeState.alpha(ind)-2*pi;
    modinpcount=modinpcount+1;
end
if modinpcount>1
    disp('crazy stuff going on in bpolar - very very positive alphas')
end
cd = mylerp1(Alpha,DragData,BladeState.alpha);%,'linear');
cl = mylerp1(Alpha,LiftData,BladeState.alpha);%,'linear');
if any(isnan([cd(:);cl(:)]));
    disp('    check this out')
end
% Data from http://adg.stanford.edu/aa241/drag/dragrise.html
% at Cl of .3
% M-Mcrit:
Mdiff0 =[ -1
    -0.15
    -0.125
    -0.1
    -0.075
    -0.05
    -0.025
    0
    0.025
    0.05
    0.075
    0.1
    0.125
    0.15
    0.175
    0.2
    0.225
    10000.225];

Cdinvisc =[0
    0
    0
    0.000025
    0.00005
    0.00015
    0.0005
    0.002
    0.0052
    0.0103
    0.0173
    0.026
    0.037
    0.048
    0.068
    0.1
    0.148
    20000];

Mcrit = max(.55,-.02002*abs(BladeState.alpha)+.765);
% from ESDU AERO W.00.03.01
% Critical Mach number for high speed aerofoil sections.

cd = cd + mylerp1(Mdiff0,Cdinvisc,abs(BladeState.M)-Mcrit);

%tip loss
ind = BladeState.r>.97;
cl(ind)=cl(ind).*(1-(BladeState.r(ind)-.97)/.03);

%% root cutout
cl(BladeState.r<.15) = 0;
cd(BladeState.r<.15) = .1;

if any([isnan(cl(:)) isnan(cd(:))])
    disp('NaN in bpolar function')
    cd(isnan(cd)) = 1;
    cl(isnan(cl)) = 1;
end

end

function yi = mylerp1(x,y,xi)
% Y = interp1(x,y,X,'linear');

toreshape = size(xi);
xi = xi(:);
yi = lininterp1f(x,y,xi,NaN); %mex .dll function
yi = reshape(yi,toreshape);

end


% Scrip: mcgrid.m
% Calculates Magnitude shift map for two specific time periods
% Uses view_mcgrid to plot the results
%
% J. Woessner
% last update: 22.01.04

report_this_filefun(mfilename('fullpath'));

global no1 bo1 inb1 inb2 valeg valeg2 CO valm1

valeg = 1;
valm1 = min(a.Magnitude);
prf = NaN;
if sel == 'in'
    % Set the grid parameter
    %Initial values
    dx = 1;
    dy = 1;
    ni = 150;
    Nmin = 150;
    ra = 50;
    fMaxRadius = 5;
    fSplitTime = 2000.4;

    % cut catalog at mainshock time:
%     l = a.Date > maepi(1,3);
%     a = a.subset(l);

    % cat at selecte magnitude threshold
    l = a.Magnitude < valm1;
    a(l,:) = [];
    newt2 = a;

    ho2=true;
    timeplot
    ho2=false;

    %The definitions in the following line were present in the initial bvalgrid.m file.
    %stan2 = NaN; stan = NaN; prf = NaN; av = NaN;

    % make the interface
    % creates a dialog box to input grid parameters
    %
    figure_w_normalized_uicontrolunits(...
        'Name','Grid Input Parameter',...
        'NumberTitle','off', ...
        'MenuBar','none', ...
        'NextPlot','new', ...
        'units','points',...
        'Visible','off', ...
        'Position',[ wex+200 wey-200 650 250]);
    axis off
%     labelList2=[' Automatic Mcomp (max curvature) | Fixed Mc (Mc = Mmin) | Automatic Mcomp (90% probability) | Automatic Mcomp (95% probability) | Best (?) combination (Mc95 - Mc90 - max curvature) | Constant Mc'];
%     labelPos = [0.2 0.8  0.6  0.08];
%     hndl2=uicontrol(...
%         'Style','popup',...
%         'Position',labelPos,...
%         'Units','normalized',...
%         'String',labelList2,...
%         'Callback','inb2 =get(hndl2,''Value''); ');
%
%     set(hndl2,'value',5);

    % creates a dialog box to input grid parameters
    %

    oldfig_button = uicontrol('BackGroundColor',[.60 .92 .84], ...
        'Style','checkbox','string','Plot in Current Figure',...
        'Position',[.78 .7 .20 .08],...
        'Units','normalized');

    set(oldfig_button,'value',1);

    freq_field=uicontrol('Style','edit',...
        'Position',[.30 .60 .12 .08],...
        'Units','normalized','String',num2str(ni),...
        'Callback','ni=str2double(get(freq_field,''String'')); set(freq_field,''String'',num2str(ni));set(tgl2,''value'',0); set(tgl1,''value'',1)');

    freq_field0=uicontrol('Style','edit',...
        'Position',[.30 .50 .12 .08],...
        'Units','normalized','String',num2str(ra),...
        'Callback','ra=str2double(get(freq_field0,''String'')); set(freq_field0,''String'',num2str(ra)) ; set(tgl2,''value'',1); set(tgl1,''value'',0)');

    freq_field2=uicontrol('Style','edit',...
        'Position',[.30 .40 .12 .08],...
        'Units','normalized','String',num2str(dx),...
        'Callback','dx=str2double(get(freq_field2,''String'')); set(freq_field2,''String'',num2str(dx));');

    freq_field3=uicontrol('Style','edit',...
        'Position',[.30 .30 .12 .080],...
        'Units','normalized','String',num2str(dy),...
        'Callback','dy=str2double(get(freq_field3,''String'')); set(freq_field3,''String'',num2str(dy));');

    freq_field4=uicontrol('Style','edit',...
        'Position',[.6 .30 .12 .080],...
        'Units','normalized','String',num2str(fSplitTime),...
        'Callback','fSplitTime=str2double(get(freq_field4,''String'')); set(freq_field4,''String'',num2str(fSplitTime));');

    freq_field7=uicontrol('Style','edit',...
        'Position',[.30 .20 .12 .080],...
        'Units','normalized','String',num2str(Nmin),...
        'Callback','Nmin=str2double(get(freq_field7,''String'')); set(freq_field7,''String'',num2str(Nmin));');

    freq_field8=uicontrol('Style','edit',...
        'Position',[.6 .60 .12 .080],...
        'Units','normalized','String',num2str(fMaxRadius),...
        'Callback','fMaxRadius=str2double(get(freq_field8,''String'')); set(freq_field8,''String'',num2str(fMaxRadius));');

    tgl1 = uicontrol('Style','radiobutton',...
        'string','Number of Events:',...
        'Position',[.05 .60 .2 .0800], 'Callback','set(tgl2,''value'',0)',...
        'Units','normalized');

    set(tgl1,'value',0);

    tgl2 =  uicontrol('Style','radiobutton',...
        'string','OR: Constant Radius',...
        'Position',[.05 .50 .2 .080], 'Callback','set(tgl1,''value'',0)',...
        'Units','normalized');
    set(tgl2,'value',1);

    create_grid =  uicontrol('Style','radiobutton',...
        'string','Calculate a new grid', 'Callback','set(load_grid,''value'',0), set(prev_grid,''value'',0)','Position',[.78 .55 .2 .080],...
        'Units','normalized');

    set(create_grid,'value',1);

    prev_grid =  uicontrol('Style','radiobutton',...
        'string','Reuse the previous grid', 'Callback','set(load_grid,''value'',0),set(create_grid,''value'',0)','Position',[.78 .45 .2 .080],...
        'Units','normalized');

    load_grid =  uicontrol('Style','radiobutton',...
        'string','Load a previously saved grid', 'Callback','set(prev_grid,''value'',0),set(create_grid,''value'',0)','Position',[.78 .35 .2 .080],...
        'Units','normalized');

    save_grid =  uicontrol('Style','checkbox',...
        'string','Save selected grid to file',...
        'Position',[.78 .22 .2 .080],...
        'Units','normalized');

    close_button=uicontrol('Style','Pushbutton',...
        'Position',[.60 .05 .15 .12 ],...
        'Units','normalized','Callback','close;done','String','Cancel');

    go_button1=uicontrol('Style','Pushbutton',...
        'Position',[.20 .05 .15 .12 ],...
        'Units','normalized',...
        'Callback','tgl1 =get(tgl1,''Value'');tgl2 =get(tgl2,''Value'');prev_grid = get(prev_grid,''Value'');create_grid = get(create_grid,''Value''); load_grid = get(load_grid,''Value''); save_grid = get(save_grid,''Value''); oldfig_button = get(oldfig_button,''Value''); close,sel =''ca'', mc_grid',...
        'String','Go');

    text(...
        'Color',[0 0 0 ],...
        'EraseMode','normal',...
        'Position',[0.10 0.98 0 ],...
        'Rotation',0 ,...
        'FontSize',ZmapGlobal.Data.fontsz.l ,...
        'FontWeight','bold',...
        'String','Please choose an Mc estimation option   ');
    txt3 = text(...
        'Color',[0 0 0 ],...
        'EraseMode','normal',...
        'Position',[0.30 0.75 0 ],...
        'Rotation',0 ,...
        'FontSize',ZmapGlobal.Data.fontsz.l ,...
        'FontWeight','bold',...
        'String',' Grid Parameter');
    txt5 = text(...
        'Color',[0 0 0 ],...
        'EraseMode','normal',...
        'Position',[-0.1 0.4 0 ],...
        'Rotation',0 ,...
        'FontSize',ZmapGlobal.Data.fontsz.m ,...
        'FontWeight','bold',...
        'String','Spacing in x (dx) in deg:');

    txt6 = text(...
        'Color',[0 0 0 ],...
        'EraseMode','normal',...
        'Position',[-0.1 0.3 0 ],...
        'Rotation',0 ,...
        'FontSize',ZmapGlobal.Data.fontsz.m ,...
        'FontWeight','bold',...
        'String','Spacing in y (dy) in deg:');

    txt7 = text(...
        'Color',[0 0 0 ],...
        'EraseMode','normal',...
        'Position',[-0.1 0.18 0 ],...
        'Rotation',0 ,...
        'FontSize',ZmapGlobal.Data.fontsz.m ,...
        'FontWeight','bold',...
        'String','Min. No. of events > Mc:');

    txt9 = text(...
        'Color',[0 0 0 ],...
        'EraseMode','normal',...
        'Position',[0.42 0.28 0 ],...
        'Rotation',0 ,...
        'FontSize',ZmapGlobal.Data.fontsz.s ,...
        'FontWeight','bold',...
        'String','Time window:');

    txt11 = text(...
        'Color',[0 0 0 ],...
        'EraseMode','normal',...
        'Position',[0.42 0.62 0 ],...
        'Rotation',0 ,...
        'FontSize',ZmapGlobal.Data.fontsz.s ,...
        'FontWeight','bold',...
        'String','Max. Radius /[km]:');

    set(gcf,'visible','on');
    watchoff

end   % if nargin ==0

% get the grid-size interactively and
% calculate the b-value in the grid by sorting
% thge seimicity and selectiong the ni neighbors
% to each grid point

if sel == 'ca'

    %In the following line, the program .m is called, which creates a rectangular grid from which then selects,
    %on the basis of the vector ll, the points within the selected poligon.

    % get new grid if needed
    if load_grid == 1
        [file1,path1] = uigetfile(['*.mat'],'previously saved grid');
        if length(path1) > 1
            think
            load([path1 file1])
        end
        plot(newgri(:,1),newgri(:,2),'k+')
    elseif load_grid ==0  &&  prev_grid == 0
        selgp
        if length(gx) < 2  ||  length(gy) < 2
            errordlg('Selection too small! (Dx and Dy are in degreees! ');
            return
        end
    elseif prev_grid == 1
        plot(newgri(:,1),newgri(:,2),'k+')
    end
    %   end

    gll = ll;

    if save_grid == 1

        sFile = ['*.mat'];
        sPath = pwd;
        [file1,path1] = uiputfile([sFile], 'Grid File Name?');
        sSaveFile = [path1 file1];
        save(sSaveFile, 'newgri', 'dx', 'dy', 'gx', 'gy', 'xvect', 'yvect', 'tmpgri', 'll', 'fSplitTime', 'Nmin', 'ra', 'a');
    end

    %   selgp
    itotal = length(newgri(:,1));
%     if length(gx) < 4 | length(gy) < 4
%         errordlg('Selection too small! (Dx and Dy are in degreees! ');
%         return
%     end

    zmap_message_center.set_info(' ','Running... ');think
    %  make grid, calculate start- endtime etc.  ...
    %
    t0b = min(a.Date)  ;
    n = a.Count;
    teb = a(n,3) ;
    tdiff = round((teb - t0b)*365/par1);
    loc = zeros(3, length(gx)*length(gy));

    % loop over  all points
    %
    i1 = 0.;
    mRes =[];
    mResult = [];
    allcount = 0.;
    wai = waitbar(0,' Please Wait ...  ');
    set(wai,'NumberTitle','off','Name','Rate change grid - percent done');
    drawnow

    % loop over all points
    for i= 1:length(newgri(:,1))
        i/length(newgri(:,1));
        % Grid node point
        x = newgri(i,1);y = newgri(i,2);
        allcount = allcount + 1.;

        % calculate distance from center point and sort with distance
        l = sqrt(((a.Longitude-x)*cos(pi/180*y)*111).^2 + ((a.Latitude-y)*111).^2) ;
        [s,is] = sort(l);
        mCat = a(is(:,1),:) ;       % re-orders matrix to agree row-wise
        % Use Radius to determine grid node catalogs
        l3 = l <= ra;
        mCat = a.subset(l3);      % new data per grid point (mCat) is sorted in distance

        %         % Select earthquakes in non-overlapping rectangles
        %         vSel = (a.Longitude >= (newgri(i,1)-dx/2)) & (a.Longitude < (newgri(i,1)+dx/2)) &...
        %             (a.Latitude >= (newgri(i,2)-dy/2)) & (a.Latitude < (newgri(i,2)+dy/2));
        %         % Select earthquakes in overlapping rectangles
        %         vSel = (a.Longitude >= (newgri(i,1)-dx)) & (a.Longitude < (newgri(i,1)+dx)) &...
        %             (a.Latitude >= (newgri(i,2)-dy)) & (a.Latitude < (newgri(i,2)+dy));
        %         mCat = a.subset(vSel);

        % Initialize
        fMinTime = min(mCat(:,3));
        fMaxTime = max(mCat(:,3));

        % Select data from 2 time periods
        vSelT = mCat(:,3) < fSplitTime;
        mCat1 = mCat(vSelT,:);
        mCat2 = mCat(~vSelT,:);
        % Length of catalog (resolution)
        nNumevents1 = length(mCat1(:,1));
        nNumevents2 = length(mCat2(:,1));

        % Minimum bin: essential for computing difference in FMD
        fMinBin = roundn(min(mCat(:,6)),-1);
        fMaxBin = roundn(max(mCat(:,6)),-1);

        if (length(mCat1(:,1)) >=Nmin & length(mCat2(:,1)) >= Nmin)
            % Compute change in FMD normalized by time period
            % Time periods
            fPeriod1 = max(mCat1(:,3))-min(mCat1(:,3));
            fPeriod2 = max(mCat2(:,3))-min(mCat2(:,3));
            [vFMD1, vBin1] = hist(mCat1(:,6),fMinBin:0.1:fMaxBin);
            [vFMD2, vBin2] = hist(mCat2(:,6),fMinBin:0.1:fMaxBin);
            fChFMD = max(cumsum(abs(vFMD2./fPeriod2-vFMD1./fPeriod1)));
            % Calculate shift
            [fMshift, fProbability, fAICc, mProblikelihood, bH] = calc_loglikelihood_dM2(mCat1, mCat2);
            % Check for validity of model using KS-Test result to produce validated result of magnitude shift
            if (bH == 1 | fMshift == 0)
                fMshift_valid = NaN;
            else
                fMshift_valid = fMshift;
            end
            % Calculate Mc
            [mResult1, fMls1, fMc1, fMu1, fSigma1, mDatPredBest1,...
                    vPredBest1, fBvalue1, fAvalue1, bH1] = calc_McEMR_kstest(mCat1, 0.1);
            [mResult2, fMls2, fMc2, fMu2, fSigma2, mDatPredBest2,...
                    vPredBest2, fBvalue2, fAvalue2, bH2] = calc_McEMR_kstest(mCat2, 0.1);
            % Mc change
            fdMc = fMc2-fMc1;
            if (bH1 ==0  &&  bH2 == 0)
                fdMc_val = fdMc;
            else
                fdMc_val = NaN;
            end
            % Calculate Utsu-Test
            [dA, fProbEqual, fProbDifferent] = calc_Utsu(fBvalue1, fBvalue2, nNumevents1, nNumevents2);
            % Create result matrix
            mResult = [mResult; i fMshift fProbability fAICc bH nNumevents1 nNumevents2 fMshift_valid fChFMD fMc1 fMc2 fdMc fdMc_val dA fProbEqual fProbDifferent bH1 fBvalue1 fAvalue1 bH2 fBvalue2 fAvalue2];
        else
            % Create result matrix
            mResult = [mResult; i NaN NaN NaN NaN nNumevents1 nNumevents2 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN];
        end
        waitbar(allcount/itotal)
    end  % for newgr

    % Save the data to rcval_grid.mat
    save  mcgrid.mat mResult gx gy dx dy par1 tdiff t0b teb a main faults mainfault coastline yvect xvect tmpgri ll bo1 newgri gll ra maepi fSplitTime
    disp('Saving data to mcgrid.mat in current directory')

    close(wai)
    watchoff

    normlap2=ones(length(tmpgri(:,1)),1)*nan;
    % Magnitude shift
    normlap2(ll)= mResult(:,2);
    mMagShift = reshape(normlap2,length(yvect),length(xvect));

    % KS-Test-Value
    normlap2(ll)= mResult(:,5);
    mHkstest = reshape(normlap2,length(yvect),length(xvect));

    %%% Resolution parameters
    % Number of events per grid node in first time period
    normlap2(ll)= mResult(:,6);
    mNumevents1 = reshape(normlap2,length(yvect),length(xvect));

    % Number of events per grid node in first time period
    normlap2(ll)= mResult(:,7);
    mNumevents2 = reshape(normlap2,length(yvect),length(xvect));

    % Validated magnitude shift
    normlap2(ll)= mResult(:,8);
    mMagShift_valid = reshape(normlap2,length(yvect),length(xvect));

    % Absolute FMD difference
    normlap2(ll)= mResult(:,9);
    mChFMD = reshape(normlap2,length(yvect),length(xvect));

    % Mc Periode 1
    normlap2(ll)= mResult(:,10);
    mMc1 = reshape(normlap2,length(yvect),length(xvect));

    % Mc Periode 2
    normlap2(ll)= mResult(:,11);
    mMc2 = reshape(normlap2,length(yvect),length(xvect));

    % Mc change
    normlap2(ll)= mResult(:,12);
    mdMc = reshape(normlap2,length(yvect),length(xvect));

    % Mc change validated by KS-Test
    normlap2(ll)= mResult(:,13);
    mdMc_val = reshape(normlap2,length(yvect),length(xvect));

    % Difference of AIC for Utsu-Test
    normlap2(ll)= mResult(:,14);
    mdAIC_Utsu = reshape(normlap2,length(yvect),length(xvect));

    % Probability of stationarity (favoring stationarity)
    normlap2(ll)= mResult(:,15);
    mStationary1_Utsu = reshape(normlap2,length(yvect),length(xvect));

    % Probability of stationarity (favoring non-stationarity)
    normlap2(ll)= mResult(:,16);
    mStationary2_Utsu = reshape(normlap2,length(yvect),length(xvect));

    % Data to plot first map
    re3 = mMagShift;
    lab1 = 'Magnitude shift';

    % View the map
    view_mcgrid

end   % if sel = na

% Load exist b-grid
if sel == 'lo'
    [file1,path1] = uigetfile(['*.mat'],'b-value gridfile');
    if length(path1) > 1
        think
        load([path1 file1])

        normlap2=ones(length(tmpgri(:,1)),1)*nan;

        % Magnitude shift
        normlap2(ll)= mResult(:,2);
        mMagShift = reshape(normlap2,length(yvect),length(xvect));

        % KS-Test-Value
        normlap2(ll)= mResult(:,5);
        mHkstest = reshape(normlap2,length(yvect),length(xvect));

        %%% Resolution parameters
        % Number of events per grid node in first time period
        normlap2(ll)= mResult(:,6);
        mNumevents1 = reshape(normlap2,length(yvect),length(xvect));

        % Number of events per grid node in first time period
        normlap2(ll)= mResult(:,7);
        mNumevents2 = reshape(normlap2,length(yvect),length(xvect));

        try
            % Validated magnitude shift
            normlap2(ll)= mResult(:,8);
            mMagShift_valid = reshape(normlap2,length(yvect),length(xvect));

            % Absolute FMD difference
            normlap2(ll)= mResult(:,9);
            mChFMD = reshape(normlap2,length(yvect),length(xvect));
            % Mc Periode 1
            normlap2(ll)= mResult(:,10);
            mMc1 = reshape(normlap2,length(yvect),length(xvect));

            % Mc Periode 2
            normlap2(ll)= mResult(:,11);
            mMc2 = reshape(normlap2,length(yvect),length(xvect));

            % Mc change
            normlap2(ll)= mResult(:,12);
            mdMc = reshape(normlap2,length(yvect),length(xvect));

            % Mc change
            normlap2(ll)= mResult(:,13);
            mdMc_val = reshape(normlap2,length(yvect),length(xvect));

            % Difference of AIC for Utsu-Test
            normlap2(ll)= mResult(:,14);
            mdAIC_Utsu = reshape(normlap2,length(yvect),length(xvect));

            % Probability of stationarity (favoring stationarity)
            normlap2(ll)= mResult(:,15);
            mStationary1_Utsu = reshape(normlap2,length(yvect),length(xvect));

            % Probability of stationarity (favoring non-stationarity)
            normlap2(ll)= mResult(:,16);
            mStationary2_Utsu = reshape(normlap2,length(yvect),length(xvect));

        catch
            disp('Validated magnitude shift map not available');
        end
        % Initial map set to relative rate change
        re3 = mMagShift;
        lab1 = 'Magnitude shift';

        old = re3;
        % Plot
        view_mcgrid;
    else
        return
    end
end


function [lf] = eeg_halfspace_dipole(dippos, elc, vol)

% EEG_HALFSPACE_DIPOLE calculate the leadfield on electrode positions elc
% for a dipole at position dippos. The halfspace solution requires a plane dividing a
% conductive zone (cond > 0), from a non-coductive zone (cond = 0).
%
% Use as
%   [lf] = eeg_halfspace_dipole(dippos, elc, vol)
%
% See also EEG_INFINITE_DIPOLE, EEG_INFINITE_MONOPOLE, EEG_HALFSPACE_MONOPOLE

% Copyright (C) 2011, Cristiano Micheli and Robert Oostenveld
%
% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org
% for the documentation and details.
%
%    FieldTrip is free software: you can redistribute it and/or modify
%    it under the terms of the GNU General Public License as published by
%    the Free Software Foundation, either version 3 of the License, or
%    (at your option) any later version.
%
%    FieldTrip is distributed in the hope that it will be useful,
%    but WITHOUT ANY WARRANTY; without even the implied warranty of
%    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
%    GNU General Public License for more details.
%
%    You should have received a copy of the GNU General Public License
%    along with FieldTrip. If not, see <http://www.gnu.org/licenses/>.
%
% $Id$

if isfield(vol, 'pos')
  % this is for forward/backward compatibility
  vol.pnt = vol.pos;
  vol = rmfield(vol, 'pos');
end

siz = size(dippos);
if any(siz==1)
  % positions are specified as a single vector
  Ndipoles = prod(siz)/3;
  dippos = dippos(:)'; % ensure that it is a row vector
elseif siz(2)==3
  % positions are specified as a Nx3 matrix -> reformat to a single vector
  Ndipoles = siz(1);
  dippos = dippos';
  dippos = dippos(:)'; % ensure that it is a row vector
else
  ft_error('incorrect specification of dipole locations');
end

Nelc     = size(elc,1);
lf       = zeros(Nelc,3*Ndipoles);

for i=1:Ndipoles
  % this is the position of dipole "i"
  dip1 = dippos((1:3) + 3*(i-1));

  % find the position of a mirror dipole symmetric to the plane
  dip2 = get_mirror_pos(dip1, vol);

  % compute the potential of the original and the mirror dipole
  lf1 = eeg_infinite_dipole(dip1, elc, vol);
  lf2 = eeg_infinite_dipole(dip2, elc, vol);

  % the z-direction of the mirror dipole should be swapped
  lf2(:,3) = -lf2(:,3);

  % take the sum of the two dipoles
  lf = lf1 + lf2;
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% SUBFUNCTION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

function P2 = get_mirror_pos(P1,vol)
% calculates the position of a point symmetric to pnt with respect to a plane

% define the plane
pnt = vol.pnt;
ori = vol.ori; % already normalized

if abs(dot(P1-pnt,ori))<eps
  ft_warning(sprintf ('point %f %f %f lies in the symmetry plane',P1(1),P1(2),P1(3)))
  P2 = P1;
else
  % define the plane in parametric form
  % define a non colinear vector vc with respect to the plane normal
  vc = [1 0 0];
  if abs(cross(ori, vc, 2))<eps
    vc = [0 1 0];
  end
  % define plane's direction vectors
  v1 = cross(ori, vc, 2);  v1 = v1/norm(v1);
  v2 = cross(pnt, ori, 2); v2 = v2/norm(v2);
  plane = [pnt v1 v2];

  % distance plane-point P1
  d = abs(dot(ori, plane(:,1:3)-P1(:,1:3), 2));

  % symmetric point
  P2 = P1 + 2*d*ori;
end


% Image Reconstruction With Directional Sensors Example
%
% This example demonstrates how the directionality of sensor elements can
% give rise to artefacts in time reversal photoacoustic image
% reconstruction. It builds on the Sensor Element Directivity in 2D and 2D
% Time Reversal Reconstruction For A Line Sensor examples.
%
% author: Bradley Treeby & Ben Cox
% date: 18th January 2010
% last update: 24th August 2014
%
% This function is part of the k-Wave Toolbox (http://www.k-wave.org)
% Copyright (C) 2009-2014 Bradley Treeby and Ben Cox

% This file is part of k-Wave. k-Wave is free software: you can
% redistribute it and/or modify it under the terms of the GNU Lesser
% General Public License as published by the Free Software Foundation,
% either version 3 of the License, or (at your option) any later version.
%
% k-Wave is distributed in the hope that it will be useful, but WITHOUT ANY
% WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
% FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
% more details.
%
% You should have received a copy of the GNU Lesser General Public License
% along with k-Wave. If not, see <http://www.gnu.org/licenses/>.

clear all

% =========================================================================
% SIMULATION
% =========================================================================

% create the computational grid
PML_size = 20;          % size of the PML in grid points
Nx = 128 - 2*PML_size;  % number of grid points in the x (row) direction
Ny = 256 - 2*PML_size;  % number of grid points in the y (column) direction
dx = 0.1e-3;            % grid point spacing in the x direction [m]
dy = 0.1e-3;            % grid point spacing in the y direction [m]
kgrid = makeGrid(Nx, dx, Ny, dy);

% define the properties of the propagation medium
medium.sound_speed = 1500;	% [m/s]

% create initial pressure distribution using makeDisc
disc_magnitude = 5; % [au]
disc_x_pos = 60;    % [grid points]
disc_y_pos = 140;  	% [grid points]
disc_radius = 5;    % [grid points]
disc_2 = disc_magnitude*makeDisc(Nx, Ny, disc_x_pos, disc_y_pos, disc_radius);

disc_x_pos = 30;    % [grid points]
disc_y_pos = 110; 	% [grid points]
disc_radius = 8;    % [grid points]
disc_1 = disc_magnitude*makeDisc(Nx, Ny, disc_x_pos, disc_y_pos, disc_radius);

% smooth the initial pressure distribution and restore the magnitude
p0 = smooth(kgrid, disc_1 + disc_2, true);

% assign to the source structure
source.p0 = p0;

% define a four-sided, square sensor
sensor.mask = zeros(kgrid.Nx, kgrid.Ny);
sensor.mask(1, :) = 1;
sensor.mask(end, :) = 1;
sensor.mask(:, 1) = 1;
sensor.mask(:, end) = 1;

% create the time array
[kgrid.t_array, dt] = makeTime(kgrid, medium.sound_speed);

% set the input arguements
input_args = {'PMLInside', false, 'PMLSize', PML_size, 'PlotPML', false, 'Smooth', false};

% run the simulation for omnidirectional detector elements
sensor_data = kspaceFirstOrder2D(kgrid, medium, source, sensor, input_args{:});

% define the directionality of the sensor elements
sensor.directivity_angle = zeros(kgrid.Nx, kgrid.Ny);
sensor.directivity_angle(1, :) = 0;    	 % max sensitivity in x direction
sensor.directivity_angle(end, :) = 0;  	 % max sensitivity in x direction
sensor.directivity_angle(:, 1) = pi/2;   % max sensitivity in y direction
sensor.directivity_angle(:, end) = pi/2; % max sensitivity in y direction

% define the directivity size
sensor.directivity_size = 20*kgrid.dx;

% run the simulation with directional elements
sensor_data_directional = kspaceFirstOrder2D(kgrid, medium, source, sensor, input_args{:});

% reset the initial pressure
source.p0 = 0;

% assign the time reversal data for the omnidirectional case
sensor.time_reversal_boundary_data = sensor_data;

% run the time reversal reconstruction
p0_recon = kspaceFirstOrder2D(kgrid, medium, source, sensor, input_args{:});

% assign the time reversal data for the directional case
sensor.time_reversal_boundary_data = sensor_data_directional;

% run the time reversal reconstruction with directional elements
p0_recon_directional = kspaceFirstOrder2D(kgrid, medium, source, sensor, input_args{:});

% =========================================================================
% VISUALISATION
% =========================================================================

% plot the initial pressure and sensor distribution
figure;
imagesc(kgrid.y_vec*1e3, kgrid.x_vec*1e3, p0 + sensor.mask*disc_magnitude, [-disc_magnitude disc_magnitude]);
colormap(getColorMap);
ylabel('x-position [mm]');
xlabel('y-position [mm]');
axis image;
colorbar;

% plot the reconstructed initial pressure
figure;
imagesc(kgrid.y_vec*1e3, kgrid.x_vec*1e3, p0_recon, [-disc_magnitude disc_magnitude]);
colormap(getColorMap);
ylabel('x-position [mm]');
xlabel('y-position [mm]');
axis image;
colorbar;

% plot the reconstructed initial pressure with directivity
figure;
imagesc(kgrid.y_vec*1e3, kgrid.x_vec*1e3, p0_recon_directional, [-disc_magnitude disc_magnitude]);
colormap(getColorMap);
ylabel('x-position [mm]');
xlabel('y-position [mm]');
axis image;
colorbar;

% plot a profile for comparison
figure;
plot(kgrid.y_vec*1e3, p0(disc_x_pos, :), 'k-',...
    kgrid.y_vec*1e3, p0_recon(disc_x_pos, :), 'r--',...
    kgrid.y_vec*1e3, p0_recon_directional(disc_x_pos, :), 'b:');
xlabel('y-position [mm]');
ylabel('Pressure');
legend('True', 'Omnidirectional','Directional');
axis tight;
set(gca, 'YLim', [0 5.1]);


function gpcf = gpcf_squared(varargin)
%GPCF_SQUARED  Create a squared (dot product) covariance function
%
%  Description
%    GPCF = GPCF_SQUARED('PARAM1',VALUE1,'PARAM2,VALUE2,...) creates
%    a squared (dot product) covariance function structure in which
%    the named parameters have the specified values. Any unspecified
%    parameters are set to default values. The squared covariance function
%    corresponds to x.^2 mean function and the respective covariance matrix
%    is given as C = x.^2*diag(gpcf.coeffSigma2)*(x'.^2);
%
%    GPCF = GPCF_SQUARED(GPCF,'PARAM1',VALUE1,'PARAM2,VALUE2,...)
%    modify a covariance function structure with the named
%    parameters altered with the specified values.
%
%    Parameters for squared (dot product) covariance function
%      interactions      - twoway interactions (default off)
%      coeffSigma2       - prior variance for regressor coefficients [10]
%                          This can be either scalar corresponding
%                          to a common prior variance or vector
%                          defining own prior variance for each
%                          coefficient.
%      coeffSigma2_prior - prior structure for coeffSigma2 [prior_logunif]
%      selectedVariables - vector defining which inputs are used [all]
%
%    Note! If the prior is 'prior_fixed' then the parameter in
%    question is considered fixed and it is not handled in
%    optimization, grid integration, MCMC etc.
%
%  See also
%    GP_SET, GPCF_*, PRIOR_*, MEAN_*
%
% Copyright (c) 2007-2016 Jarno Vanhatalo
% Copyright (c) 2008-2010 Jaakko Riihimäki
% Copyright (c) 2010 Aki Vehtari
% Copyright (c) 2014 Arno Solin

% 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.

  ip=inputParser;
  ip.FunctionName = 'GPCF_SQUARED';
  ip.addOptional('gpcf', [], @isstruct);
  ip.addParamValue('interactions', 'off', @(x) ismember(x,{'on' 'off'}))
  ip.addParamValue('coeffSigma2',10, @(x) isvector(x) && all(x>0));
  ip.addParamValue('coeffSigma2_prior',prior_logunif, @(x) isstruct(x) || isempty(x));
  ip.addParamValue('selectedVariables',[], @(x) isvector(x) && all(x>0));
  ip.parse(varargin{:});
  gpcf=ip.Results.gpcf;

  if isempty(gpcf)
    init=true;
    gpcf.type = 'gpcf_squared';
  else
    if ~isfield(gpcf,'type') && ~isequal(gpcf.type,'gpcf_squared')
      error('First argument does not seem to be a valid covariance function structure')
    end
    init=false;
  end

  if init || ~ismember('interactions',ip.UsingDefaults)
    gpcf.interactions=ip.Results.interactions;
  end

  % Initialize parameter
  if init || ~ismember('coeffSigma2',ip.UsingDefaults)
    gpcf.coeffSigma2=ip.Results.coeffSigma2;
  end

  % Initialize prior structure
  if init
    gpcf.p=[];
  end
  if init || ~ismember('coeffSigma2_prior',ip.UsingDefaults)
    gpcf.p.coeffSigma2=ip.Results.coeffSigma2_prior;
  end
  if ~ismember('selectedVariables',ip.UsingDefaults)
    selectedVariables=ip.Results.selectedVariables;
    if ~isempty(selectedVariables)
      gpcf.selectedVariables = selectedVariables;
    end
  end

  if init
    % Set the function handles to the subfunctions
    gpcf.fh.pak = @gpcf_squared_pak;
    gpcf.fh.unpak = @gpcf_squared_unpak;
    gpcf.fh.lp = @gpcf_squared_lp;
    gpcf.fh.lpg = @gpcf_squared_lpg;
    gpcf.fh.cfg = @gpcf_squared_cfg;
    gpcf.fh.cfdg = @gpcf_squared_cfdg;
    gpcf.fh.cfdg2 = @gpcf_squared_cfdg2;
    gpcf.fh.ginput = @gpcf_squared_ginput;
    gpcf.fh.ginput2 = @gpcf_squared_ginput2;
    gpcf.fh.ginput3 = @gpcf_squared_ginput3;
    gpcf.fh.ginput4 = @gpcf_squared_ginput4;
    gpcf.fh.cov = @gpcf_squared_cov;
    gpcf.fh.trcov  = @gpcf_squared_trcov;
    gpcf.fh.trvar  = @gpcf_squared_trvar;
    gpcf.fh.recappend = @gpcf_squared_recappend;
    gpcf.fh.cf2ss = @gpcf_squared_cf2ss;
  end

end

function [w, s, h] = gpcf_squared_pak(gpcf, w)
%GPCF_squared_PAK  Combine GP covariance function parameters into one vector
%
%  Description
%    W = GPCF_squared_PAK(GPCF) takes a covariance function
%    structure GPCF and combines the covariance function
%    parameters and their hyperparameters into a single row
%    vector W. This is a mandatory subfunction used for
%    example in energy and gradient computations.
%
%       w = [ log(gpcf.coeffSigma2)
%             (hyperparameters of gpcf.coeffSigma2)]'
%
%  See also
%    GPCF_squared_UNPAK

  w = []; s = {}; h =[];
  if ~isempty(gpcf.p.coeffSigma2)
    w = log(gpcf.coeffSigma2);
    if numel(gpcf.coeffSigma2)>1
      s = [s; sprintf('log(squared.coeffSigma2 x %d)',numel(gpcf.coeffSigma2))];
    else
      s = [s; 'log(squared.coeffSigma2)'];
    end
    h = [h ones(1, numel(gpcf.coeffSigma2))];
    % Hyperparameters of coeffSigma2
    [wh, sh, hh] = gpcf.p.coeffSigma2.fh.pak(gpcf.p.coeffSigma2);
    sh=strcat(repmat('prior-', size(sh,1),1),sh);
    w = [w wh];
    s = [s; sh];
    h = [h 1+hh];
  end
end

function [gpcf, w] = gpcf_squared_unpak(gpcf, w)
%GPCF_squared_UNPAK  Sets the covariance function parameters
%                   into the structure
%
%  Description
%    [GPCF, W] = GPCF_squared_UNPAK(GPCF, W) takes a covariance
%    function structure GPCF and a hyper-parameter vector W, and
%    returns a covariance function structure identical to the
%    input, except that the covariance hyper-parameters have been
%    set to the values in W. Deletes the values set to GPCF from
%    W and returns the modified W. This is a mandatory subfunction
%    used for example in energy and gradient computations.
%
%    Assignment is inverse of
%       w = [ log(gpcf.coeffSigma2)
%             (hyperparameters of gpcf.coeffSigma2)]'
%
%  See also
%   GPCF_squared_PAK

  gpp=gpcf.p;

  if ~isempty(gpp.coeffSigma2)
    i2=length(gpcf.coeffSigma2);
    i1=1;
    gpcf.coeffSigma2 = exp(w(i1:i2));
    w = w(i2+1:end);

    % Hyperparameters of coeffSigma2
    [p, w] = gpcf.p.coeffSigma2.fh.unpak(gpcf.p.coeffSigma2, w);
    gpcf.p.coeffSigma2 = p;
  end
end

function lp = gpcf_squared_lp(gpcf)
%GPCF_squared_LP  Evaluate the log prior of covariance function parameters
%
%  Description
%    LP = GPCF_squared_LP(GPCF) takes a covariance function
%    structure GPCF and returns log(p(th)), where th collects the
%    parameters. This is a mandatory subfunction used for example
%    in energy computations.
%
%  See also
%   GPCF_squared_PAK, GPCF_squared_UNPAK, GPCF_squared_LPG, GP_E

% Evaluate the prior contribution to the error. The parameters that
% are sampled are from space W = log(w) where w is all the "real" samples.
% On the other hand errors are evaluated in the W-space so we need take
% into account also the  Jacobian of transformation W -> w = exp(W).
% See Gelman et al. (2013), Bayesian Data Analysis, third edition, p. 21.
  lp = 0;
  gpp=gpcf.p;

  if ~isempty(gpp.coeffSigma2)
    lp = gpp.coeffSigma2.fh.lp(gpcf.coeffSigma2, gpp.coeffSigma2) + sum(log(gpcf.coeffSigma2));
  end
end

function lpg = gpcf_squared_lpg(gpcf)
%GPCF_squared_LPG  Evaluate gradient of the log prior with respect
%                 to the parameters.
%
%  Description
%    LPG = GPCF_squared_LPG(GPCF) takes a covariance function
%    structure GPCF and returns LPG = d log (p(th))/dth, where th
%    is the vector of parameters. This is a mandatory subfunction
%    used for example in gradient computations.
%
%  See also
%    GPCF_squared_PAK, GPCF_SQUARED_UNPAK, GPCF_SQUARED_LP, GP_G

  lpg = [];
  gpp=gpcf.p;

  if ~isempty(gpcf.p.coeffSigma2)
    lll=length(gpcf.coeffSigma2);
    lpgs = gpp.coeffSigma2.fh.lpg(gpcf.coeffSigma2, gpp.coeffSigma2);
    lpg = [lpg lpgs(1:lll).*gpcf.coeffSigma2+1 lpgs(lll+1:end)];
  end
end

function DKff = gpcf_squared_cfg(gpcf, x, x2, mask, i1)
%GPCF_SQUARED_CFG  Evaluate gradient of covariance function
%                 with respect to the parameters
%
%  Description
%    DKff = GPCF_SQUARED_CFG(GPCF, X) takes a covariance function
%    structure GPCF, a matrix X of input vectors and returns
%    DKff, the gradients of covariance matrix Kff = k(X,X) with
%    respect to th (cell array with matrix elements). This is a
%    mandatory subfunction used in gradient computations.
%
%    DKff = GPCF_SQUARED_CFG(GPCF, X, X2) takes a covariance
%    function structure GPCF, a matrix X of input vectors and
%    returns DKff, the gradients of covariance matrix Kff =
%    k(X,X2) with respect to th (cell array with matrix
%    elements). This subfunction is needed when using sparse
%    approximations (e.g. FIC).
%
%    DKff = GPCF_SQUARED_CFG(GPCF, X, [], MASK) takes a covariance
%    function structure GPCF, a matrix X of input vectors and
%    returns DKff, the diagonal of gradients of covariance matrix
%    Kff = k(X,X2) with respect to th (cell array with matrix
%    elements). This subfunction is needed when using sparse
%    approximations (e.g. FIC).
%
%    DKff = GPCF_SQUARED_CFG(GPCF,X,X2,MASK,i) takes a covariance
%    function structure GPCF, a matrix X of input vectors and
%    returns DKff, the gradient of covariance matrix Kff =
%    k(X,X2), or k(X,X) if X2 is empty, with respect to ith
%    hyperparameter. This subfunction is needed when using
%    memory save option in gp_set.
%
%  See also
%   GPCF_SQUARED_PAK, GPCF_SQUARED_UNPAK, GPCF_SQUARED_LP, GP_G

  if nargin>2 && ~isempty(x2)
      if size(x,2) ~= size(x2,2)
          error('gpcf_squared -> _cfg: the number of columns of X1 and X2 has to be same')
      end
  end

  if isfield(gpcf, 'selectedVariables')
      x = x(:,gpcf.selectedVariables);
  end
  [n, m] =size(x);
  h = x.^2;
  if isequal(gpcf.interactions,'on')
      for xi1=1:m
          for xi2=xi1+1:m
              h = [h x(:,xi1).*x(:,xi2)];
          end
      end
  end

  DKff = {};

  if nargin==5
    % Use memory save option
    savememory=1;
    if i1==0
      % Return number of hyperparameters
      DKff=0;
      if ~isempty(gpcf.p.coeffSigma2)
        DKff=length(gpcf.coeffSigma2);
      end
      return
    end
  else
    savememory=0;
  end

  % Evaluate: DKff{1} = d Kff / d coeffSigma2
  % NOTE! Here we have already taken into account that the parameters are transformed
  % through log() and thus dK/dlog(p) = p * dK/dp

  % evaluate the gradient for training covariance
  if nargin == 2 || (isempty(x2) && isempty(mask))

      if ~isempty(gpcf.p.coeffSigma2)
          if length(gpcf.coeffSigma2) == 1
              DKff{1}=gpcf.coeffSigma2*h*(h');
          else
              if isa(gpcf.coeffSigma2,'single')
                  epsi=eps('single');
              else
                  epsi=eps;
              end
              if ~savememory
                  i1=1:length(gpcf.coeffSigma2);
              end
              DKff=cell(1,length(i1));
              for ii1=i1
                  DD = gpcf.coeffSigma2(ii1)*h(:,ii1)*(h(:,ii1)');
                  DD(abs(DD)<=epsi) = 0;
                  DKff{ii1}= (DD+DD')./2;
              end
          end
      end

    % Evaluate the gradient of non-symmetric covariance (e.g. K_fu)
  elseif nargin == 3 || isempty(mask)

      error('this part of the subfunction has not been tested')

      if isfield(gpcf, 'selectedVariables')
          x2 = x2(:,gpcf.selectedVariables);
      end
      h2 = x2.^2;
      if isequal(gpcf.interactions,'on')
          for xi1=1:m
              for xi2=xi1+1:m
                  h2 = [h2 x2(:,xi1).*x2(:,xi2)];
              end
          end
      end

      if ~isempty(gpcf.p.coeffSigma2)
          if length(gpcf.coeffSigma2) == 1
              DKff{1}=gpcf.coeffSigma2*h*(h2');
          else
              if ~savememory
                  i1=1:m;
              end
              for ii1=i1
                  DKff{ii1}=gpcf.coeffSigma2(ii1)*h(:,ii1)*(h2(:,ii1)');
              end
          end
      end
      % Evaluate: DKff{1}    = d mask(Kff,I) / d coeffSigma2
    %           DKff{2...} = d mask(Kff,I) / d coeffSigma2
  elseif nargin == 4 || nargin == 5

      error('this part of the subfunction has not been tested')

      if ~isempty(gpcf.p.coeffSigma2)
        if length(gpcf.coeffSigma2) == 1
          DKff{1}=gpcf.coeffSigma2*sum(h.^2,2); % d mask(Kff,I) / d coeffSigma2
        else
          if ~savememory
            i1=1:m;
          end
          for ii1=i1
            DKff{ii1}=gpcf.coeffSigma2(ii1)*(h(:,ii1).^2); % d mask(Kff,I) / d coeffSigma2
          end
        end
      end
  end
  if savememory
    DKff=DKff{i1};
  end
end

function DKff = gpcf_squared_cfdg(gpcf, x, x2, dims)
%GPCF_SQUARED_CFDG  Evaluate gradient of covariance function, of
%                  which has been taken partial derivative with
%                  respect to x, with respect to parameters.
%
%  Description
%    DKff = GPCF_SQUARED_CFDG(GPCF, X) takes a covariance function
%    structure GPCF, a matrix X of input vectors and returns
%    DKff, the gradients of derivatived covariance matrix
%    dK(df,f)/dhyp = d(d k(X,X)/dx)/dhyp, with respect to the
%    parameters
%
%    Evaluate: DKff{1:m} = d Kff / d coeffSigma2
%    m is the dimension of inputs. This subfunction is needed when using
%    derivative observations.
%
%    Note! When coding the derivatives of the covariance function, remember
%    to double check them. See gp_cov for lines of code to check the
%    matrices
%
%  See also
%    GPCF_SQUARED_GINPUT

[~,m]=size(x);
if nargin<3
    x2=x;
end
if nargin < 4 || isempty(dims)
    dims = 1:m;
end
ii1=0;
DKff={};
if isfield(gpcf,'selectedVariables')
    selVars = gpcf.selectedVariables;
else
    selVars = 1:m;
end
c = gpcf.coeffSigma2;

h = x.^2;
h2 = x2.^2;

ii1=0;
DKff={};
if ~isempty(gpcf.p.coeffSigma2)
  if length(gpcf.coeffSigma2)==1
    % One coeffSigma2
    for i1=dims
      if isfield(gpcf, 'selectedVariables') && sum(gpcf.selectedVariables==i1)==0
        DK{i1}=zeros(size(x,1),size(x2,1));
      else
        DK{i1}=c(1).*2*x(:,i1)*h2(:,i1)';
        if isequal(gpcf.interactions,'on')
            for xi2=selVars
                if xi2~=i1
                    DK{i1} = DK{i1} + c(1)*x(:,xi2)*(x2(:,i1).*x2(:,xi2))';
                end
            end
        end
      end
    end
    ii1=ii1+1;
    DKff{ii1}=cat(1,DK{1:end});
  else
      % vector of coeffSigma2s
      for i1=1:length(selVars)
          for j=dims
              if selVars(i1)~=j %|| (isfield(gpcf, 'selectedVariables') && sum(gpcf.selectedVariables==i1)==0)
                  DK{j}=zeros(size(x,1),size(x2,1));
              else
                  DK{j}=c(i1).*2*x(:,j)*h2(:,j)';
              end
          end
          ii1=ii1+1;
          DKff{ii1}=cat(1,DK{1:end});
      end
      if isequal(gpcf.interactions,'on')
          for xi1=1:length(selVars)
              for xi2=xi1+1:length(selVars)
                  i1=i1+1;
                  for j=dims
                      if j==xi1
                          DK{j} = c(i1)*x(:,xi2)*(x2(:,xi1).*x2(:,xi2))';
                      elseif j==xi2
                          DK{j} = c(i1)*x(:,xi1)*(x2(:,xi1).*x2(:,xi2))';
                      else
                          DK{j}=zeros(size(x,1),size(x2,1));
                      end
                  end
                  ii1=ii1+1;
                  DKff{ii1}=cat(1,DK{1:end});
              end
          end
      end
  end
end

end

function DKff = gpcf_squared_cfdg2(gpcf, x, x2, dims1, dims2)
%GPCF_SQUARED_CFDG2  Evaluate gradient of covariance function, of which has
%                    been taken partial derivatives with respect to both
%                    input variables x, with respect to parameters.
%
%  Description
%    DKff = GPCF_SQUARED_CFDG2(GPCF, X) takes a covariance
%    function structure GPCF, a matrix X of input vectors and
%    returns DKff, the gradients of derivative covariance matrix
%    dK(df,df)/dhyp = d(d^2 k(X1,X2)/dX1dX2)/dhyp with respect to
%    the parameters
%
%    Evaluate: DKff{1:m} = d K(df,df) / d coeffSigma
%    m is the dimension of inputs.  This subfunction is needed when using
%    derivative observations.
%
%    Note! When coding the derivatives of the covariance function, remember
%    to double check them. See gp_cov for lines of code to check the
%    matrices
%
%  See also
%   GPCF_SQUARED_GINPUT, GPCF_SQUARED_GINPUT2

[~, m] =size(x);
if nargin <3 || isempty(x2)
    x2=x;
end
if nargin < 4 || isempty(dims1)
    %dims1 = 1:m;
    error('dims1 needs to be given')
end
if nargin < 5 || isempty(dims2)
    %dims2 = 1:m;
    error('dims2 needs to be given')
end
if isfield(gpcf,'selectedVariables')
    selVars = gpcf.selectedVariables;
else
    selVars = 1:m;
end
% NOTICE. AS OF NOW we assume that dims1 and dims2 are scalars

ii1=0;
if length(gpcf.coeffSigma2)==1
    c=repmat(gpcf.coeffSigma2,1,(1+length(selVars))*length(selVars)/2);
else
    c=gpcf.coeffSigma2;
end
if length(gpcf.coeffSigma2)==1
    % One coeffSigma2
    if dims1~=dims2 %|| (isfield(gpcf, 'selectedVariables') && (sum(gpcf.selectedVariables==j)==0 || sum(gpcf.selectedVariables==k)==0))
        DK=zeros(size(x,1),size(x2,1));
        if isequal(gpcf.interactions,'on') && (sum(selVars==dims1)>0 || sum(selVars==dims2)>0)
            DK = c(1)*x(:,dims1)*x2(:,dims2)';
        end
    else
        if sum(selVars==dims1)==0
            DK=zeros(size(x,1),size(x2,1));
        else
            DK=c(1).*4.*x(:,dims1)*x2(:,dims2)';
        end
        if isequal(gpcf.interactions,'on') && sum(selVars==dims1)>0
            for xi2=selVars
                if xi2~=dims1
                    DK = DK + c(1)*x(:,xi2)*x2(:,xi2)';
                end
            end
        end
    end
    ii1=ii1+1;
    DKff{ii1}=DK;
else
    % vector of coeffSigma2s
    for i1=1:length(selVars)
        if dims1~=dims2 || dims2~=selVars(i1)
            DK=zeros(size(x,1),size(x2,1));
        else
            DK=c(i1).*4.*x(:,dims1)*x2(:,dims2)';
        end
        ii1=ii1+1;
        DKff{ii1}=DK;
    end
    if isequal(gpcf.interactions,'on')
        for xi1=1:length(selVars)
            for xi2=xi1+1:length(selVars)
                i1=i1+1;
                %if k==xi1 && j==xi2
                if dims1==xi1 && dims2==xi2
                    DK = c(i1)*x(:,xi2)*x2(:,xi1)';
                %elseif k==xi2 && j==xi1
                elseif dims1==xi2 && dims2==xi1
                    DK = c(i1)*x(:,xi1)*x2(:,xi2)';
                %elseif k==j && k==xi1
                elseif dims1==dims2 && dims1==xi1
                    DK = c(i1)*x(:,xi2)*x2(:,xi2)';
                %elseif k==j && k==xi2
                elseif dims1==dims2 && dims1==xi2
                    DK = c(i1)*x(:,xi1)*x2(:,xi1)';
                else
                    DK=zeros(size(x,1),size(x2,1));
                end
                ii1=ii1+1;
                DKff{ii1}=DK;
            end
        end
    end
end

end

function DKff = gpcf_squared_ginput(gpcf, x, x2, i1)
%GPCF_SQUARED_GINPUT  Evaluate gradient of covariance function with
%                    respect to x.
%
%  Description
%    DKff = GPCF_SQUARED_GINPUT(GPCF, X, X2) takes a covariance function
%    structure GPCF, a matrix X of input vectors and returns DKff, the
%    gradients of covariance matrix Kff = k(X,X2) with respect to X (cell
%    array with matrix elements). If called with only two inputs
%    GPCF_SQUARED_GINPUT(GPCF, X), X2=X. This subfunction is needed when
%    computing gradients with respect to inducing inputs in sparse
%    approximations.
%
%    DKff = GPCF_SQUARED_GINPUT(GPCF, X, X2, i) takes a covariance
%    function structure GPCF, a matrix X of input vectors and
%    returns DKff, the gradients of covariance matrix Kff =
%    k(X,X2) with respect to ith covariate in X (matrix).
%    This subfunction is needed when using memory save option
%    in gp_set.
%
%    Note! When coding the derivatives of the covariance function, remember
%    to double check them. See gp_cov for lines of code to check the
%    matrices
%
%  See also
%   GPCF_SQUARED_PAK, GPCF_SQUARED_UNPAK, GPCF_SQUARED_LP, GP_G

if isfield(gpcf, 'selectedVariables')
    error('The selectedVariables option has not yet been implemented for gpcf_squared with derivobs=''on'' ')
    % notice, some parts of the code already take into account the
    % selectedVariables but the code has not been checked
end

[n, m] =size(x);

if nargin==4
    % Use memory save option
    savememory=1;
    if i1==0
        % Return number of covariates
        DKff=m;
        return
    end
else
    savememory=0;
end

if nargin == 2 || isempty(x2)

    xx = x.^2;
    if length(gpcf.coeffSigma2)==1
        s=gpcf.coeffSigma2.*ones(1,(1+m)*m/2);
    else
        s=gpcf.coeffSigma2;
    end
    ii1 = 0;
    if nargin<4
        i1=1:m;
    end
    for j = 1:n
        for i=i1
            DK = zeros(n);
            DK(j,:)=2*s(i)*x(j,i)*xx(:,i)';
            if isequal(gpcf.interactions,'on')
                for xi2=i1
                    if xi2~=i
                        DK(j,:) = DK(j,:) + s(i)*x(j,xi2).*(x(:,i).*x(:,xi2))';
                    end
                end
            end
            DK = DK + DK';
            ii1 = ii1 + 1;
            DKff{ii1} = DK;
        end
    end

elseif nargin == 3 || nargin == 4

    xx2 = x2.^2;
    if length(gpcf.coeffSigma2)==1
        s=gpcf.coeffSigma2.*ones(1,(1+m)*m/2);
    else
        s=gpcf.coeffSigma2;
    end
    ii1 = 0;
    if ~savememory
        i1=1:m;
    end
    for j = 1:n
        for i=i1
            DK = zeros(n, size(x2,1));
            DK(j,:)=2*s(i)*x(j,i)*xx2(:,i)';
            if isequal(gpcf.interactions,'on')
                for xi2=i1
                    if xi2~=i
                        DK(j,:) = DK(j,:) + s(i)*x(j,xi2).*(x2(:,i).*x2(:,xi2))';
                    end
                end
            end
            ii1 = ii1 + 1;
            DKff{ii1} = DK;
        end
    end
end

end

function DKff = gpcf_squared_ginput2(gpcf, x, x2, dims,takeOnlyDiag)
%GPCF_SQUARED_GINPUT2  Evaluate gradient of covariance function with
%                   respect to both input variables x and x2 in
%                   same dimension.
%
%  Description
%    DKff = GPCF_SQUARED_GINPUT2(GPCF, X, X2) takes a covariance
%    function structure GPCF, a matrix X of input vectors and
%    returns DKff, the gradients of twice derivatived covariance
%    matrix K(df,df) = dk(X1,X2)/dX1dX2 (cell array with matrix
%    elements). Input variable's dimensions are expected to be
%    same.  This subfunction is needed when using derivative
%    observations.
%
%    Note! When coding the derivatives of the covariance function, remember
%    to double check them. See gp_cov for lines of code to check the
%    matrices
%
%  See also
%    GPCF_SQUARED_GINPUT, GPCF_SQUARED_GINPUT2, GPCF_SQUARED_CFDG2

[~,m]=size(x);
ii1=0;
if nargin<4 || isempty(dims)
    dims=1:m;
end
if length(gpcf.coeffSigma2)==1
    c=repmat(gpcf.coeffSigma2,1,(1+m)*m/2);
else
    c=gpcf.coeffSigma2;
end
if isfield(gpcf, 'selectedVariables')
    sv = gpcf.selectedVariables;
else
    sv = 1:m;
end

if nargin==5 && isequal(takeOnlyDiag,'takeOnlyDiag')
    for i1=dims
        if ~any(sv==i1)%isfield(gpcf, 'selectedVariables') && sum(gpcf.selectedVariables==i1)==0
            DK=zeros(size(x,1),1);
        else
            DK=c(i1).*4.*(x(:,i1).*x2(:,i1));
            if isequal(gpcf.interactions,'on')
                i2=length(sv);
                for xi1=1:length(sv)
                    for xi2=xi1+1:length(sv)
                        if any(sv==xi1) && any(sv==xi2)
                            i2=i2+1;
                            if i1==xi1
                                DK = DK + c(i2)*x(:,xi2).*x2(:,xi2);
                            elseif i1==xi2
                                DK = DK + c(i2)*x(:,xi1).*x2(:,xi1);
                            end
                        end
                    end
                end
            end
        end
        ii1=ii1+1;
        DKff{ii1}=DK;
    end
else
    for i1=dims
        if ~any(sv==i1)%isfield(gpcf, 'selectedVariables') && sum(gpcf.selectedVariables==i1)==0
            DK=zeros(size(x,1),size(x2,1));
        else
            DK=c(i1).*4.*(x(:,i1)*x2(:,i1)');
            if isequal(gpcf.interactions,'on')
                i2=length(sv);
                for xi1=1:length(sv)
                    for xi2=xi1+1:length(sv)
                        if any(sv==xi1) && any(sv==xi2)
                            i2=i2+1;
                            if i1==xi1
                                DK = DK + c(i2)*x(:,xi2)*x2(:,xi2)';
                            elseif i1==xi2
                                DK = DK + c(i2)*x(:,xi1)*x2(:,xi1)';
                            end
                        end
                    end
                end
            end
        end
        ii1=ii1+1;
        DKff{ii1}=DK;
    end
end
end

function DKff = gpcf_squared_ginput3(gpcf, x, x2, dims1, dims2)
%GPCF_SQUARED_GINPUT3  Evaluate gradient of covariance function with
%                   respect to both input variables x and x2 (in
%                   different dimensions).
%
%  Description
%    DKff = GPCF_SQUARED_GINPUT3(GPCF, X, X2) takes a covariance
%    function structure GPCF, a matrix X of input vectors and
%    returns DKff, the gradients of twice derivatived covariance
%    matrix K(df,df) = dk(X1,X2)/dX1dX2 (cell array with matrix
%    elements). The derivative is calculated in multidimensional
%    problem between input's observation dimensions which are not
%    same. This subfunction is needed when using derivative
%    observations.
%
%    DKff is a cell array with the following elements:
%      DKff{1} = dk(X1,X2)/dX1_1dX2_2
%      DKff{2} = dk(X1,X2)/dX1_1dX2_3
%       ...
%      DKff{m-1} = dk(X1,X2)/dX1_1dX2_m
%      DKff{m} = dk(X1,X2)/dX1_2dX2_3
%       ...
%      DKff{m} = dk(X1,X2)/dX1_(m-1)dX2_m
%    where _m denotes the input dimension with respect to which the
%    gradient is calculated.
%
%    Note! When coding the derivatives of the covariance function, remember
%    to double check them. See gp_cov for lines of code to check the
%    matrices
%
%  See also
%    GPCF_SQUARED_GINPUT, GPCF_SQUARED_GINPUT2, GPCF_SQUARED_CFDG2

[~,m]=size(x);
if nargin < 3
    error('Needs at least 3 input arguments')
end
if nargin<4 || isempty(dims1)
    dims1=1:m;
end
if nargin<5 || isempty(dims2)
    dims2=1:m;
end
if isfield(gpcf, 'selectedVariables')
    sv = gpcf.selectedVariables;
else
    sv = 1:m;
end

ii1=0;
if length(gpcf.coeffSigma2)==1
  c=repmat(gpcf.coeffSigma2,1,(1+m)*m/2);
else
  c=gpcf.coeffSigma2;
end
DK=zeros(size(x,1),size(x2,1));
i2=m;
for i=dims1
    for j=dims2
        ii1=ii1+1;
        if isequal(gpcf.interactions,'on') && any(sv==i) && any(sv==j)
            i2 = min(i,j)*length(sv) - 0.5*min(i,j)*(min(i,j)-1) + max(i,j)-min(i,j);
            DKff{ii1} = c(i2)*x(:,j)*x2(:,i)';
        else
            DKff{ii1}=DK;
        end
    end
end

% i2=m;
% for i=1:m-1
%     for j=i+1:m
%         i2=i2+1;
%         if any(dims1==i) && any(dims2==j)
%             ii1=ii1+1;
%             if isequal(gpcf.interactions,'on') && any(sv==i) && any(sv==j)
% %             if isequal(gpcf.interactions,'on') &&...
% %                     ~(isfield(gpcf, 'selectedVariables') && (sum(gpcf.selectedVariables==i)==0 || sum(gpcf.selectedVariables==j)==0))
%                 DKff{ii1} = c(i2)*x(:,j)*x2(:,i)';
%             else
%                 DKff{ii1}=DK;
%             end
%         end
%     end
% end

end

function DKff = gpcf_squared_ginput4(gpcf, x, x2, dims)
%GPCF_SQUARED_GINPUT  Evaluate gradient of covariance function with respect
%                    to x. Simplified and faster version of squared_ginput,
%                    returns full matrices.
%
%  Description
%    DKff = GPCF_SQUARED_GINPUT4(GPCF, X, X2) takes a covariance function
%    structure GPCF, matrices X and X2 of input vectors and returns DKff,
%    the gradients of covariance matrix Kff = k(X,X2) with respect to X
%    (whole matrix); that is d k(X,X2)/dX. If called with only two inputs
%    GPCF_SQUARED_GINPUT4(GPCF, X), X2=X.
%
%    This subfunction is needed when using derivative observations.
%
%    Note! When coding the derivatives of the covariance function, remember
%    to double check them. See gp_cov for lines of code to check the
%    matrices
%
%  See also
%    GPCF_SQUARED_PAK, GPCF_SQUARED_UNPAK, GPCF_SQUARED_LP, GP_G

% if isfield(gpcf, 'selectedVariables')
%     error('The selectedVariables option has not yet been implemented for gpcf_squared with derivobs=''on'' ')
%     % notice, some parts of the code already take into account the
%     % selectedVariables but the code has not been checked
% end
%
% if isfield(gpcf, 'selectedVariables')
%     m = length(gpcf.selectedVariables);
%     sv = gpcf.selectedVariables;
% else
%     [~,m]=size(x);
%     sv = 1:m;
% end
% if nargin==2
%     x2=x;
% end
% if nargin<4
%     dims=1:size(x,2);
% end
% if length(gpcf.coeffSigma2)==1
%     c=repmat(gpcf.coeffSigma2,1,(1+m)*m/2);
% else
%     % If coeffSigma is vector, we trust it is of rigth length
%     c=gpcf.coeffSigma2;
% end
% h2=x2.^2;
% ii1=0;
% for i=dims
%     if ~any(sv==i) %isfield(gpcf, 'selectedVariables') && sum(gpcf.selectedVariables==i)==0
%         DK=zeros(size(x,1),size(x2,1));
%     else
%         DK=c(i).*2.*x(:,i)*h2(:,i)';
%         if isequal(gpcf.interactions,'on')
%             i2=m;
%             for xi1=sv
%                 sv2 = sv(sv>xi1);
%                 for xi2=sv2 %xi1+1:m
%                     i2 = i2+1;
%                     if i==xi1
%                         DK = DK + c(i2)*x(:,xi2)*(x2(:,i).*x2(:,xi2))';
%                     elseif i==xi2
%                         DK = DK + c(i2)*x(:,xi1)*(x2(:,i).*x2(:,xi1))';
%                     end
%                 end
%             end
%         end
%     end
%     ii1=ii1+1;
%     DKff{ii1}=DK;
% end

[n,m]=size(x);
if nargin<4
    dims=1:m;
end
if isfield(gpcf, 'selectedVariables')
    sv = gpcf.selectedVariables;
else
    sv = 1:m;
end
ii1=0;
if nargin==2
    x2=x;
end
h2=x2.^2;
if length(gpcf.coeffSigma2)==1
    c=repmat(gpcf.coeffSigma2,1,(1+m)*m/2);
else
    c=gpcf.coeffSigma2;
end
for i=dims
    if ~any(sv==i) % isfield(gpcf, 'selectedVariables') && sum(gpcf.selectedVariables==i)==0
        DK=zeros(size(x,1),size(x2,1));
    else
        DK=c(i).*2.*x(:,i)*h2(:,i)';
        if isequal(gpcf.interactions,'on')
            i2=length(sv);
            for xi1=1:length(sv)
                for xi2=xi1+1:length(sv)
                    if any(sv==xi1) && any(sv==xi2)
                        i2=i2+1;
%                        i2 = min(i,j)*length(sv) - 0.5*min(i,j)*(min(i,j)-1) + max(i,j)-min(i,j)
                        if i==xi1
                            DK = DK + c(i2)*x(:,xi2)*(x2(:,i).*x2(:,xi2))';
                        elseif i==xi2
                            DK = DK + c(i2)*x(:,xi1)*(x2(:,i).*x2(:,xi1))';
                        end
                    end
                end
            end
        end
    end
    ii1=ii1+1;
    DKff{ii1}=DK;
end
end

function C = gpcf_squared_cov(gpcf, x1, x2, varargin)
%GP_SQUARED_COV  Evaluate covariance matrix between two input vectors
%
%  Description
%    C = GP_SQUARED_COV(GP, TX, X) takes in covariance function of
%    a Gaussian process GP and two matrixes TX and X that contain
%    input vectors to GP. Returns covariance matrix C. Every
%    element ij of C contains covariance between inputs i in TX
%    and j in X. This is a mandatory subfunction used for example in
%    prediction and energy computations.
%
%  See also
%    GPCF_SQUARED_TRCOV, GPCF_SQUARED_TRVAR, GP_COV, GP_TRCOV

  if isempty(x2)
    x2=x1;
  end
  [n1,m1]=size(x1);
  [n2,m2]=size(x2);
  if m1~=m2
    error('the number of columns of X1 and X2 has to be same')
  end
  if isfield(gpcf, 'selectedVariables')
      x1 = x1(:,gpcf.selectedVariables);
      x2 = x2(:,gpcf.selectedVariables);
  end
  h1 = x1.^2;
  h2 = x2.^2;
  if isequal(gpcf.interactions,'on')
      m=size(x1,2);
      for xi1=1:m
          for xi2=xi1+1:m
              h1 = [h1 x1(:,xi1).*x1(:,xi2)];
              h2 = [h2 x2(:,xi1).*x2(:,xi2)];
          end
      end
  end
  C = h1*diag(gpcf.coeffSigma2)*(h2');
  C(abs(C)<=eps) = 0;

end

function C = gpcf_squared_trcov(gpcf, x)
%GP_SQUARED_TRCOV  Evaluate training covariance matrix of inputs
%
%  Description
%    C = GP_SQUARED_TRCOV(GP, TX) takes in covariance function of
%    a Gaussian process GP and matrix TX that contains training
%    input vectors. Returns covariance matrix C. Every element ij
%    of C contains covariance between inputs i and j in TX. This
%    is a mandatory subfunction used for example in prediction and
%    energy computations.
%
%  See also
%    GPCF_SQUARED_COV, GPCF_SQUARED_TRVAR, GP_COV, GP_TRCOV

  if isfield(gpcf, 'selectedVariables')
      x = x(:,gpcf.selectedVariables);
  end
  h = x.^2;
  if isequal(gpcf.interactions,'on')
      m=size(x,2);
      for xi1=1:m
          for xi2=xi1+1:m
              h = [h x(:,xi1).*x(:,xi2)];
          end
      end
  end
  C = h*diag(gpcf.coeffSigma2)*(h');
  C(abs(C)<=eps) = 0;
  C = (C+C')./2;

end

function C = gpcf_squared_trvar(gpcf, x)
%GP_SQUARED_TRVAR  Evaluate training variance vector
%
%  Description
%    C = GP_SQUARED_TRVAR(GPCF, TX) takes in covariance function
%    of a Gaussian process GPCF and matrix TX that contains
%    training inputs. Returns variance vector C. Every element i
%    of C contains variance of input i in TX. This is a mandatory
%    subfunction used for example in prediction and energy computations.
%
%
%  See also
%    GPCF_SQUARED_COV, GP_COV, GP_TRCOV

  if isfield(gpcf, 'selectedVariables')
      x = x(:,gpcf.selectedVariables);
  end
  h = x.^2;
  if isequal(gpcf.interactions,'on')
      m=size(x,2);
      for xi1=1:m
          for xi2=xi1+1:m
              h = [h x(:,xi1).*x(:,xi2)];
          end
      end
  end
  if length(gpcf.coeffSigma2) == 1
      C=gpcf.coeffSigma2.*sum(h.^2,2);
  else
      C=sum(repmat(gpcf.coeffSigma2, size(x,1), 1).*h.^2,2);
  end
  C(abs(C)<eps)=0;

end

function reccf = gpcf_squared_recappend(reccf, ri, gpcf)
%RECAPPEND Record append
%
%  Description
%    RECCF = GPCF_SQUARED_RECAPPEND(RECCF, RI, GPCF) takes a
%    covariance function record structure RECCF, record index RI
%    and covariance function structure GPCF with the current MCMC
%    samples of the parameters. Returns RECCF which contains all
%    the old samples and the current samples from GPCF. This
%    subfunction is needed when using MCMC sampling (gp_mc).
%
%  See also
%    GP_MC and GP_MC -> RECAPPEND

  if nargin == 2
    % Initialize the record
    reccf.type = 'gpcf_squared';

    % Initialize parameters
    reccf.coeffSigma2= [];

    % Set the function handles
    reccf.fh.pak = @gpcf_squared_pak;
    reccf.fh.unpak = @gpcf_squared_unpak;
    reccf.fh.lp = @gpcf_squared_lp;
    reccf.fh.lpg = @gpcf_squared_lpg;
    reccf.fh.cfg = @gpcf_squared_cfg;
    reccf.fh.cfdg = @gpcf_squared_cfdg;
    reccf.fh.cfdg2 = @gpcf_squared_cfdg2;
    reccf.fh.ginput = @gpcf_squared_ginput;
    reccf.fh.ginput2 = @gpcf_squared_ginput2;
    reccf.fh.ginput3 = @gpcf_squared_ginput3;
    reccf.fh.ginput4 = @gpcf_squared_ginput4;
    reccf.fh.cov = @gpcf_squared_cov;
    reccf.fh.trcov  = @gpcf_squared_trcov;
    reccf.fh.trvar  = @gpcf_squared_trvar;
    reccf.fh.recappend = @gpcf_squared_recappend;
    reccf.p=[];
    reccf.p.coeffSigma2=[];
    if ~isempty(ri.p.coeffSigma2)
      reccf.p.coeffSigma2 = ri.p.coeffSigma2;
    end

  else
    % Append to the record
    gpp = gpcf.p;

    reccf.interactions = gpcf.interactions;

    % record coeffSigma2
    reccf.coeffSigma2(ri,:)=gpcf.coeffSigma2;
    if isfield(gpp,'coeffSigma2') && ~isempty(gpp.coeffSigma2)
      reccf.p.coeffSigma2 = gpp.coeffSigma2.fh.recappend(reccf.p.coeffSigma2, ri, gpcf.p.coeffSigma2);
    end

    if isfield(gpcf, 'selectedVariables')
      reccf.selectedVariables = gpcf.selectedVariables;
    end
  end
end

function [F,L,Qc,H,Pinf,dF,dQc,dPinf,params] = gpcf_squared_cf2ss(gpcf,x)
%GPCF_SQUARED_CF2SS Convert the covariance function to state space form
%
%  Description
%    Convert the covariance function to state space form such that
%    the process can be described by the stochastic differential equation
%    of the form:
%      df(t)/dt = F f(t) + L w(t),
%    where w(t) is a white noise process. The observation model now
%    corresponds to y_k = H f(t_k) + r_k, where r_k ~ N(0,sigma2).
%
%

  % Check arguments
  if nargin < 2 || isempty(x), x = 0; end

  % Scaling
  x0 = min(x);

  % Define the model
  F      = [0 1; 0 0];
  L      = [0; 1];
  Qc     = 0;
  H      = [1 0];
  Pinf   = [x0^2 x0; x0 1]*gpcf.coeffSigma2;
  dF     = zeros(2,2,1);
  dQc    = zeros(1,1,1);
  dPinf  = [x0^2 x0; x0 1];
  params = {};

  % Set params
  params.stationary = false;

  % Check which parameters are optimized
  if isempty(gpcf.p.coeffSigma2), ind(1) = false; else ind(1) = true; end

  % Return only those derivatives that are needed
  dF    = dF(:,:,ind);
  dQc   = dQc(:,:,ind);
  dPinf = dPinf(:,:,ind);

end