function [gamma,phase,dta,alpha,gamma_xmesh,gamma_ymesh,gamma_zmesh]=dicomrt_GAMMAcal2DVol(evalm,refm,dose_xmesh,dose_ymesh,dose_zmesh,resf,range,dta_criteria,dd_criteria,voi,voiselect,voilookup)
% dicomrt_GAMMAcal2DVol(evalm,refm,dose_xmesh,dose_ymesh,dose_zmesh,resf,range,dta_criteria,dd_criteria,voi,voiselect,voilookup)
%
% Calculates 2D gamma distribution for a 3D dataset using a 2D algorithm.
% Calculates 2D gamma maps on every slice of a user selected voi and stack 2D maps onto a 3D array.
%
% evalm is the 2D matrix to evaluate.
% refm is the reference 2D matrix.
%   Both eval and ref can be a TPS generated dataset or a MC generated dataset.
%   Doses are normalised to the prescribed target dose whenever is possible.
%   If no normalization dose is provided within the data it is assumed that matrices are already normalised.
% dose_xmesh, dose_ymesh, are the coordinates of the center of the pixel for eval and ref.
% slice is the number of the slice quantities should be calculated for.
% resf is the "resolution factor". Each voxel is divided resf times to allow dose to be interpolated
%   and quantities calculated. The higher resf the more accurate the calculation is, the slower the
%   function will be.
% range is the "search range". Range is the number of pixel about (j,i) that is considered for calculation.
%   If a dta or a dd match is not found within the search range gamma and all the other quantities in that point
%   will be NaN (Not a Number).
% dta_criteria is the Distance-to-agreement criteria in cm (e.g. 0.3).
% dd_criteria:
%   1) IF matrices ARE NOT NORMALIZED before calling the function
%      dta_criteria is the percentage dose difference (e.g. =0.03);
%   2) IF matrices ARE already NORMALIZED before calling the function
%      dd_criteria must be given accordingly to the normalization applyed
%      (e.g. =0.03 for 3% over dose norm =1Gy, or =1.98 for 3% over dose norm=66Gy).
% voi and voiselect are the vois' cell array and the # of the voi to be used for the gamma calculation respectively.
%   They have to be specified together. Both matrices will be masked and reduced in size accordingly with the selected
%   voi's dimensions. This reduce calculation time especially for the 3D algorithm.
%
% NOTE: the use of tyhis function is equivalent to looping calls to dicomrt_GAMMAcal2D, storing
% results in a 3D matrix. Useful and not mandatory parameter that this function returns is zmesh, which
% is needed by dicomrt_gvhcal.
%
% Example:
%
% [gamma1_2,phase1_2,dta1_2,alpha1_2,xmesh,ymesh,zmesh]=dicomrt_GAMMAcal2DVol(image_eval,image_ref, ...
%                                                             xmesh_red,ymesh_red,40,2,4,0.3,0.03);
%
% returns the calculated gamma function for image_eval vs image_ref at slice # 40 in gamma1_2.
% Gamma is calculated with 3%-3mm DD-DTA criteria.
% The phase is returned in phase1_2, the dta matrix in dta1_2.
% The function return also the direction cosines in alpha1_2. These are the
% direction cosines on the gamma vector in the two dimensional space xy. The direction
% cosines may provide useful information in detecting systematic spatial displacements between eval and ref.
% Coordinates of the xyz location where gamma is defined are returned in xmesh, ymesh, and zmesh.
%
% The concept of gamma function was developed by Low et al Med. Phys. 25 656-661.
%
% See also dicomrt_GAMMAcal3DP, dicomrt_loaddose, dicomrt_loadmcdose, dicomrt_gvhcal
%
% Copyright (C) 2002 Emiliano Spezi (emiliano.spezi@physics.org)

% Check number of argument and set-up some parameters and variables
error(nargchk(11,12,nargin))

if exist('voilookup')~=1
    voilookup=1; % assume VOI number 1 is patient outline
end

% Check case and set-up some parameters and variables
[evalm_temp,evalm_type,evalm_label,evalm_PatientPosition]=dicomrt_checkinput(evalm);
evalm=dicomrt_varfilter(evalm_temp);
[refm_temp,refm_type,refm_label,refm_PatientPosition]=dicomrt_checkinput(refm);
refm=dicomrt_varfilter(refm_temp);
[voi_temp]=dicomrt_checkinput(voi);
voi=dicomrt_varfilter(voi_temp);
voitype=dicomrt_checkvoitype(voi_temp);

% Retrieve
%voi_start=dicomrt_findslice(voi_temp,voiselect,1,voilookup);
%voi_stop=voi_start+size(voi{voiselect,2},1)-1;
%bias=voi_start-1;

gamma=[];
phase=[];
dta=[];
alpha=[];
gamma_zmesh=[];

% Mask the matrix using the selected VOI
voiZ=dicomrt_makevertical(dicomrt_getvoiz(voi_temp,voiselect));

for i=1:length(voiZ)
    [locate_slice]=dicomrt_findpointVECT(dose_zmesh,voiZ(i),evalm_PatientPosition);
    disp(['Working on ',voi{voiselect,1}, ' slice: ', num2str(i)]);
    [gamma(:,:,i),phase(:,:,i),dta(:,:,i),alpha(:,:,i),gamma_xmesh,gamma_ymesh,zmesh_temp]=...
        dicomrt_GAMMAcal2D(evalm_temp,refm_temp,dose_xmesh,dose_ymesh,dose_zmesh,...
        locate_slice,resf,range,dta_criteria,dd_criteria,voi_temp,voiselect);
    gamma_zmesh=[gamma_zmesh zmesh_temp];
end

gamma=dicomrt_restorevarformat(evalm_temp,gamma);

% Label info and update time of creation
gamma{1,1}{1}.RTPlanLabel=[gamma{1,1}{1}.RTPlanLabel,'-GAMMA'];
gamma{1,1}{1}.RTPlanDate=date;
time=fix(clock);
creationtime=[num2str(time(4)),':',num2str(time(5))];
gamma{1,1}{1}.RTPlanTime=creationtime;


function [granger, v, n] = ft_connectivity_granger(H, Z, S, varargin)

% FT_CONNECTIVITY_GRANGER computes spectrally resolved granger causality. This
% implementation is loosely based on the code used in Brovelli, et. al., PNAS 101,
% 9849-9854 (2004).
%
% Use as
%   [GRANGER, V, N] = FT_CONNECTIVITY_GRANGER(H, Z, S, ...)
%
% The input data should be
%   H = spectral transfer matrix, Nrpt x Nchan x Nchan x Nfreq (x Ntime),
%       or Nrpt x Nchancmb x Nfreq (x Ntime). Nrpt can be 1.
%   Z = the covariance matrix of the noise, Nrpt x Nchan x Nchan (x Ntime),
%       or Nrpt x Nchancmb (x Ntime).
%   S = the cross-spectral density matrix with the same dimensionality as H.
%
% Additional optional input arguments come as key-value pairs:
%   'dimord'  = required string specifying how to interpret the input data
%               supported values are 'rpt_chan_chan_freq(_time) and
%               'rpt_chan_freq(_time), 'rpt_pos_pos_XXX' and 'rpt_pos_XXX'
%   'method'  = 'granger' (default), or 'instantaneous', or 'total'.
%   'hasjack' = 0 (default) is a boolean specifying whether the input
%               contains leave-one-outs, required for correct variance
%               estimate
%   'powindx' = is a variable determining the exact computation, see below
%
% If the inputdata is such that the channel-pairs are linearly indexed, granger
% causality is computed per quadruplet of consecutive entries, where the convention
% is as follows:
%
%  H(:, (k-1)*4 + 1, :, :, :) -> 'chan1-chan1'
%  H(:, (k-1)*4 + 2, :, :, :) -> 'chan1->chan2'
%  H(:, (k-1)*4 + 3, :, :, :) -> 'chan2->chan1'
%  H(:, (k-1)*4 + 4, :, :, :) -> 'chan2->chan2'
%
% The same holds for the Z and S matrices.
%
% Pairwise block-granger causality can be computed when the inputdata has
% dimensionality Nchan x Nchan. In that case powindx should be specified, as a 1x2
% cell-array indexing the individual channels that go into each 'block'.
%
% See also FT_CONNECTIVITYANALYSIS

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Undocumented option: powindx can be a struct. In that case, blockwise
% conditional granger can be computed.
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% Copyright (C) 2009-2017, Jan-Mathijs Schoffelen
%
% 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$

method  = ft_getopt(varargin, 'method',  'granger');
hasjack = ft_getopt(varargin, 'hasjack', 0);
powindx = ft_getopt(varargin, 'powindx', []);
dimord  = ft_getopt(varargin, 'dimord',  []);

%FIXME speed up code and check
siz = size(H);
if numel(siz)==4
  siz(5) = 1;
end
n   = siz(1);
Nc  = siz(2);

outsum = zeros(siz(2:end));
outssq = zeros(siz(2:end));

% crossterms are described by chan_chan_therest
issquare = length(strfind(dimord, 'chan'))==2 || length(strfind(dimord, 'pos'))==2;

switch method
  case 'granger'

    if issquare && isempty(powindx)
      %%%%%%%%%%%%%%%%%%%%%%%%%%%%
      % data are chan_chan_therest
      %%%%%%%%%%%%%%%%%%%%%%%%%%%%

      for kk = 1:n
        for ii = 1:Nc
          for jj = 1:Nc
            if ii ~=jj
              zc     = reshape(Z(kk,jj,jj,:) - Z(kk,ii,jj,:).^2./Z(kk,ii,ii,:),[1 1 1 1 siz(5)]);
              zc     = repmat(zc,[1 1 1 siz(4) 1]);
              numer  = reshape(abs(S(kk,ii,ii,:,:)),[1 1 siz(4:end)]);
              denom  = reshape(abs(S(kk,ii,ii,:,:)-zc.*abs(H(kk,ii,jj,:,:)).^2),[1 1 siz(4:end)]);
              outsum(jj,ii,:,:) = outsum(jj,ii,:,:) + log(numer./denom);
              outssq(jj,ii,:,:) = outssq(jj,ii,:,:) + (log(numer./denom)).^2;
            end
          end
          outsum(ii,ii,:,:) = 0; %self-granger set to zero
        end
      end

    elseif ~issquare && isempty(powindx)
      %%%%%%%%%%%%%%%%%%%%%%%%%%
      %data are linearly indexed
      %%%%%%%%%%%%%%%%%%%%%%%%%%

      for j = 1:n
        for k = 1:Nc
          %FIXME powindx is not used here anymore
          %iauto1  = sum(powindx==powindx(k,1),2)==2;
          %iauto2  = sum(powindx==powindx(k,2),2)==2;
          %icross1 = k;
          %icross2 = sum(powindx==powindx(ones(Nc,1)*k,[2 1]),2)==2;

          % The following is based on hard-coded assumptions: which is fair
          % to do if the order of the labelcmb is according to the output of
          % ft_connectivity_csd2transfer
          if mod(k-1, 4)==0
            continue; % auto granger set to 0
            %iauto1=k;iauto2=k;icross1=k;icross2=k;
          elseif mod(k-1, 4)==1
            iauto1=k+2;iauto2=k-1;icross1=k;icross2=k+1;
          elseif mod(k-1, 4)==2
            iauto1=k-2;iauto2=k+1;icross1=k;icross2=k-1;
          elseif mod(k-1, 4)==3
            continue; % auto granger set to 0
            %iauto1=k;iauto2=k;icross1=k;icross2=k;
          end

          zc      = Z(j,iauto2,:,:) - Z(j,icross1,:,:).^2./Z(j,iauto1,:,:);
          numer   = abs(S(j,iauto1,:,:));
          denom   = abs(S(j,iauto1,:,:)-zc(:,:,ones(1,size(H,3)),:).*abs(H(j,icross1,:,:)).^2);
          outsum(icross2,:,:) = outsum(icross2,:,:) + reshape(log(numer./denom), [1 siz(3:end)]);
          outssq(icross2,:,:) = outssq(icross2,:,:) + reshape((log(numer./denom)).^2, [1 siz(3:end)]);
        end
      end

    elseif issquare && iscell(powindx)
      %%%%%%%%%%%%%%%%%%%
      % blockwise granger
      %%%%%%%%%%%%%%%%%%%

      % H = transfer function nchan x nchan x nfreq
      % Z = noise covariance  nchan x nchan
      % S = crosspectrum      nchan x nchan x nfreq
      % powindx{k} is a list of indices for block k

      nblock = numel(powindx);
      n      = size(H,1);
      nfreq  = size(H,4);

      outsum = zeros(nblock,nblock,nfreq);
      outssq = zeros(nblock,nblock,nfreq);

      for k = 1:nblock
        for m = (k+1):nblock
          indx  = [powindx{k}(:);powindx{m}(:)];
          indx  = cat(1,indx,setdiff((1:size(Z,2))',indx));
          n1    = numel(powindx{k});
          n2    = numel(powindx{m});
          ntot  = numel(indx);
          indx1 = 1:n1;
          indx1_  = (1:ntot)'; indx1_(indx1) = [];
          indx2   = n1+(1:n2);
          indx12_ = (1:ntot)'; indx12_([indx1(:);indx2(:)]) = [];

          for kk = 1:n
            tmpZ = reshape(Z(kk,indx,indx), [ntot ntot]);

            % projection matrix for therest+block2 -> block1
            P1 = [eye(n1)                                zeros(n1,ntot-n1);
              -tmpZ(indx1_,indx1)/tmpZ(indx1,indx1)      eye(ntot-n1)];

            % projection matrix for therest+block1 -> block2
            P2 = [  eye(n1)    -tmpZ(indx1,indx2)/tmpZ(indx2,indx2) zeros(n1,ntot-n1-n2);
                  zeros(n2,n1)   eye(n2) zeros(n2, ntot-n1-n2);
                  zeros(ntot-n1-n2,n1) -tmpZ(indx12_,indx2)/tmpZ(indx2,indx2) eye(ntot-n1-n2)];

            % invert only once
            for jj = 1:nfreq
              % post multiply transfer matrix with the inverse of the projection matrix
              % this is equivalent to time domain pre multiplication with P
              Sj = reshape(S(kk,indx,indx,jj), [ntot ntot]);
              Zj = tmpZ; %(:,:);
              H1 = reshape(H(kk,indx,indx,jj), [ntot ntot])/P1;
              H2 = reshape(H(kk,indx,indx,jj), [ntot ntot])/P2;
              num1 = abs(det(Sj(indx1,indx1))); % numerical round off leads to tiny imaginary components
              num2 = abs(det(Sj(indx2,indx2))); % numerical round off leads to tiny imaginary components
              denom1 = abs(det(H1(indx1,indx1)*Zj(indx1,indx1)*H1(indx1,indx1)'));
              denom2 = abs(det(H2(indx2,indx2)*Zj(indx2,indx2)*H2(indx2,indx2)'));
              %rH1 = real(H1(indx1,indx1));
              %rH2 = real(H2(indx2,indx2));
              %iH1 = imag(H1(indx1,indx1));
              %iH2 = imag(H2(indx2,indx2));
              %h1 = rH1*Zj(indx1,indx1)*rH1' + iH1*Zj(indx1,indx1)*iH1';
              %h2 = rH2*Zj(indx2,indx2)*rH2' + iH2*Zj(indx2,indx2)*iH2';
              %denom1 = abs(det(h1));
              %denom2 = abs(det(h2));

              outsum(m,k,jj) = log( num1./denom1 )    + outsum(m,k,jj);
              outsum(k,m,jj) = log( num2./denom2 )    + outsum(k,m,jj);
              outssq(m,k,jj) = log( num1./denom1 ).^2 + outssq(m,k,jj);
              outssq(k,m,jj) = log( num2./denom2 ).^2 + outssq(k,m,jj);
            end
          end

        end
      end

    elseif ~issquare && isstruct(powindx) && isfield(powindx, 'n')
      %%%%%%%%%%%%%%%%%%%%%%
      %blockwise conditional
      %%%%%%%%%%%%%%%%%%%%%%

      n     = size(H,1);
      ncmb  = size(H,2);
      nfreq = size(H,3);
      ncnd  = size(powindx.cmbindx,1);

      outsum = zeros(ncnd, nfreq);
      outssq = zeros(ncnd, nfreq);
      for k = 1:n
        tmpS = reshape(S, [ncmb nfreq]);
        tmpH = reshape(H, [ncmb nfreq]);
        tmpZ = reshape(Z, [ncmb 1]);
        tmp  = blockwise_conditionalgranger(tmpS,tmpH,tmpZ,powindx.cmbindx,powindx.n);

        outsum = outsum + tmp;
        outssq = outssq + tmp.^2;
      end

    elseif ~issquare && isstruct(powindx)
      %%%%%%%%%%%%%%%%%%%%%%
      %triplet conditional
      %%%%%%%%%%%%%%%%%%%%%%

      % decode from the powindx struct which rows in the data correspond with
      % the triplets, and which correspond with the duplets
      ublockindx = unique(powindx.blockindx);
      nperblock  = zeros(size(ublockindx));
      for k = 1:numel(ublockindx)
        nperblock(k,1) = sum(powindx.blockindx==ublockindx(k));
      end
      if ~all(ismember(nperblock,[4 9]))
        error('the data should be a mixture of trivariate and bivariate decompositions');
      end
      indx_triplets = ismember(powindx.blockindx, ublockindx(nperblock==9)); ntriplets = sum(indx_triplets)./9;
      indx_duplets  = ismember(powindx.blockindx, ublockindx(nperblock==4)); nduplets  = sum(indx_duplets)./4;

      % this assumes well-behaved powindx.cmbindx
      cmbindx2 = reshape(powindx.cmbindx(indx_duplets, 1), 2, [])';
      cmbindx3 = reshape(powindx.cmbindx(indx_triplets,1), 3, [])';

      cmbindx2 = cmbindx2(1:2:end,:);
      cmbindx3 = cmbindx3(1:3:end,:);

      cmbindx  = powindx.outindx;

      n   = size(H,1);
      siz = size(H);

      outsum = zeros(size(cmbindx,1), size(H,3));
      outssq = outsum;

      % call the low-level function
      for k = 1:n
        H3 = reshape(H(k,indx_triplets,:,:), [3 3 ntriplets siz(3:end)]);
        Z3 = reshape(Z(k,indx_triplets),     [3 3 ntriplets]);

        H2 = reshape(H(k,indx_duplets,:,:), [2 2 nduplets siz(3:end)]);
        Z2 = reshape(Z(k,indx_duplets),     [2 2 nduplets]);

        tmp  = triplet_conditionalgranger(H3,Z3,cmbindx3,H2,Z2,cmbindx2,cmbindx);

        outsum = outsum + tmp;
        outssq = outssq + tmp.^2;
      end

    end

  case 'instantaneous'

    if issquare && isempty(powindx)
      % data are chan_chan_therest
      for kk = 1:n
        for ii = 1:Nc
          for jj = 1:Nc
            if ii ~=jj
              zc1    = reshape(Z(kk,jj,jj,:) - Z(kk,ii,jj,:).^2./Z(kk,ii,ii,:),[1 1 1 1 siz(5)]);
              zc2    = reshape(Z(kk,ii,ii,:) - Z(kk,jj,ii,:).^2./Z(kk,jj,jj,:),[1 1 1 1 siz(5)]);
              zc1    = repmat(zc1,[1 1 1 siz(4) 1]);
              zc2    = repmat(zc2,[1 1 1 siz(4) 1]);
              term1  = abs(S(kk,ii,ii,:,:)) - zc1.*abs(H(kk,ii,jj,:,:)).^2;
              term2  = abs(S(kk,jj,jj,:,:)) - zc2.*abs(H(kk,jj,ii,:,:)).^2;
              numer  = term1.*term2;
              denom  = abs(S(kk,ii,ii,:,:).*S(kk,jj,jj,:,:) - S(kk,ii,jj,:,:).*S(kk,jj,ii,:,:));
              outsum(jj,ii,:,:) = outsum(jj,ii,:,:) + reshape(log(numer./denom),  [1 1 siz(4:end)]);
              outssq(jj,ii,:,:) = outssq(jj,ii,:,:) + reshape((log(numer./denom)).^2, [1 1 siz(4:end)]);
            end
          end
          outsum(ii,ii,:,:) = 0; %self-granger set to zero
        end
      end
    elseif ~issquare && isempty(powindx)
      % data are linearly indexed
      for j = 1:n
        for k = 1:Nc
          %iauto1  = sum(powindx==powindx(k,1),2)==2;
          %iauto2  = sum(powindx==powindx(k,2),2)==2;
          %icross1 = k;
          %icross2 = sum(powindx==powindx(ones(Nc,1)*k,[2 1]),2)==2;
          if mod(k-1, 4)==0
            continue; % auto granger set to 0
            %iauto1=k;iauto2=k;icross1=k;icross2=k;
          elseif mod(k-1, 4)==1
            iauto1=k+2;iauto2=k-1;icross1=k;icross2=k+1;
          elseif mod(k-1, 4)==2
            iauto1=k-2;iauto2=k+1;icross1=k;icross2=k-1;
          elseif mod(k-1, 4)==3
            continue; % auto granger set to 0
            %iauto1=k;iauto2=k;icross1=k;icross2=k;
          end

          zc1     = Z(j,iauto1,:, :) - Z(j,icross2,:, :).^2./Z(j,iauto2,:, :);
          zc2     = Z(j,iauto2,:, :) - Z(j,icross1,:, :).^2./Z(j,iauto1,:, :);
          term1   = abs(S(j,iauto2,:,:)) - zc1(:,:,ones(1,size(H,3)),:).*abs(H(j,icross2,:,:)).^2;
          term2   = abs(S(j,iauto1,:,:)) - zc2(:,:,ones(1,size(H,3)),:).*abs(H(j,icross1,:,:)).^2;
          numer   = term1.*term2;
          denom   = abs(S(j,iauto1,:,:).*S(j,iauto2,:,:) - S(j,icross1,:,:).*S(j,icross2,:,:));

          outsum(icross2,:,:) = outsum(icross2,:,:) + reshape(log(numer./denom), [1 siz(3:end)]);
          outssq(icross2,:,:) = outssq(icross2,:,:) + reshape((log(numer./denom)).^2, [1 siz(3:end)]);
        end
      end
    elseif issquare && iscell(powindx)
      % blockwise granger
      % H = transfer function nchan x nchan x nfreq
      % Z = noise covariance  nchan x nchan
      % S = crosspectrum      nchan x nchan x nfreq
      % powindx{1} is a list of indices for block1
      % powindx{2} is a list of indices for block2
      ft_error('instantaneous causality is not implemented for blockwise factorizations');
    elseif isstruct(powindx)
      %blockwise conditional
      ft_error('blockwise conditional instantaneous causality is not implemented');
    else
      ft_error('not implemented');
    end

  case 'total'

    if issquare && isempty(powindx)
      % data are chan_chan_therest
      for kk = 1:n
        for ii = 1:Nc
          for jj = 1:Nc
            if ii ~=jj
              numer  = abs(S(kk,ii,ii,:,:).*S(kk,jj,jj,:,:));
              denom  = abs(S(kk,ii,ii,:,:).*S(kk,jj,jj,:,:) - S(kk,ii,jj,:,:).*S(kk,jj,ii,:,:));
              outsum(jj,ii,:,:) = outsum(jj,ii,:,:) + reshape(log(numer./denom), [1 1 siz(4:end)]);
              outssq(jj,ii,:,:) = outssq(jj,ii,:,:) + reshape((log(numer./denom)).^2, [1 1 siz(4:end)]);
            end
          end
          outsum(ii,ii,:,:) = 0; %self-granger set to zero
        end
      end
    elseif ~issquare && isempty(powindx)
      % data are linearly indexed
      for j = 1:n
        for k = 1:Nc
          %iauto1  = sum(powindx==powindx(k,1),2)==2;
          %iauto2  = sum(powindx==powindx(k,2),2)==2;
          %icross1 = k;
          %icross2 = sum(powindx==powindx(ones(Nc,1)*k,[2 1]),2)==2;
          if mod(k-1, 4)==0
            continue; % auto granger set to 0
            %iauto1=k;iauto2=k;icross1=k;icross2=k;
          elseif mod(k-1, 4)==1
            iauto1=k+2;iauto2=k-1;icross1=k;icross2=k+1;
          elseif mod(k-1, 4)==2
            iauto1=k-2;iauto2=k+1;icross1=k;icross2=k-1;
          elseif mod(k-1, 4)==3
            continue; % auto granger set to 0
            %iauto1=k;iauto2=k;icross1=k;icross2=k;
          end

          numer   = abs(S(j,iauto1,:,:).*S(j,iauto2,:,:));
          denom   = abs(S(j,iauto1,:,:).*S(j,iauto2,:,:) - S(j,icross1,:,:).*S(j,icross2,:,:));
          outsum(icross2,:,:) = outsum(icross2,:,:) + reshape(log(numer./denom), [1 siz(3:end)]);
          outssq(icross2,:,:) = outssq(icross2,:,:) + reshape((log(numer./denom)).^2, [1 siz(3:end)]);
        end
      end
    elseif issquare && iscell(powindx)
      % blockwise total interdependence

      % S = crosspectrum      nchan x nchan x nfreq
      % powindx{k} is a list of indices for block k

      nblock = numel(powindx);
      n      = size(S,1);
      nfreq  = size(S,4);

      outsum = zeros(nblock,nblock,nfreq);
      outssq = zeros(nblock,nblock,nfreq);

      for k = 1:nblock
        for m = (k+1):nblock
          indx  = [powindx{k}(:);powindx{m}(:)];
          n1    = numel(powindx{k});
          n2    = numel(powindx{m});
          ntot  = n1+n2;
          indx1 = 1:n1;
          indx2 = (n1+1):ntot;

          for kk = 1:n
            for jj = 1:nfreq
              Sj = reshape(S(kk,indx,indx,jj), [ntot ntot]);
              num1 = abs(det(Sj(indx1,indx1))); % numerical round off leads to tiny imaginary components
              num2 = abs(det(Sj(indx2,indx2))); % numerical round off leads to tiny imaginary components
              num  = num1.*num2;
              denom = abs(det(Sj));

              outsum(m,k,jj) = log( num./denom )    + outsum(m,k,jj);
              outsum(k,m,jj) = log( num./denom )    + outsum(k,m,jj);
              outssq(m,k,jj) = log( num./denom ).^2 + outssq(m,k,jj);
              outssq(k,m,jj) = log( num./denom ).^2 + outssq(k,m,jj);
            end
          end

        end
      end

    elseif issquare && isstruct(powindx)
      %blockwise conditional
      ft_error('blockwise conditional total interdependence is not implemented');
    end

  case 'iis'
    ft_warning('THIS IS EXPERIMENTAL CODE, USE AT YOUR OWN RISK!');
    % this is experimental
    if ~issquare && isempty(powindx)
      A = transfer2coeffs(shiftdim(H),(0:size(H,3)-1));
      ncmb = size(A,1)./4;
      iis = coeffs2iis(reshape(A,[2 2 ncmb size(A,2)]),reshape(Z,[2 2 ncmb]));
      iis = repmat(iis(:),[1 4])';
      outsum = iis(:);
      outssq = nan(size(outsum));
    else
      ft_error('iis can only be computed when the input contains sets of bivariate factorizations');
    end

  otherwise
    ft_error('unsupported output requested');
end

granger = outsum./n;
if n>1
  if hasjack
    bias = (n-1).^2;
  else
    bias = 1;
  end
  v = bias*(outssq - (outsum.^2)./n)./(n - 1);
else
  v = [];
end


Network Resnet50 {
	Layer CONV1 {
		Type: CONV
		Stride { X: 2, Y: 2 }
		Dimensions { K: 64, C: 3, R: 7, S: 7, Y:224, X:224 }
		Dataflow {
			TemporalMap (1,1) K;
			TemporalMap (1,1) C;
			TemporalMap (Sz(R),1) Y;
			SpatialMap (Sz(S),1) X;
			TemporalMap (Sz(R),Sz(R)) R;
			TemporalMap (Sz(S),Sz(S)) S;
		}

	}

	Layer CONV2_1_1 {
		Type: CONV
		Stride { X: 1, Y: 1 }
		Dimensions { K: 64, C: 64, R: 1, S: 1, Y: 56, X: 56 }
		Dataflow {
			TemporalMap (1,1) K;
			TemporalMap (1,1) C;
			TemporalMap (Sz(R),1) Y;
			SpatialMap (Sz(S),1) X;
			TemporalMap (Sz(R),Sz(R)) R;
			TemporalMap (Sz(S),Sz(S)) S;
		}

	}

	Layer CONV2_1_2 {
		Type: CONV
		Stride { X: 1, Y: 1 }
		Dimensions { K: 64, C: 64, R: 3, S: 3, Y: 56, X: 56 }
		Dataflow {
			TemporalMap (1,1) K;
			TemporalMap (1,1) C;
			TemporalMap (Sz(R),1) Y;
			SpatialMap (Sz(S),1) X;
			TemporalMap (Sz(R),Sz(R)) R;
			TemporalMap (Sz(S),Sz(S)) S;
		}

	}

	Layer CONV2_1_3 {
		Type: CONV
		Stride { X: 1, Y: 1 }
		Dimensions { K: 256, C: 64, R: 1, S: 1, Y: 56, X: 56 }
		Dataflow {
			TemporalMap (1,1) K;
			TemporalMap (1,1) C;
			TemporalMap (Sz(R),1) Y;
			SpatialMap (Sz(S),1) X;
			TemporalMap (Sz(R),Sz(R)) R;
			TemporalMap (Sz(S),Sz(S)) S;
		}

	}

	Layer CONV2_1_Residual {
		Type: DSCONV
		Stride { X: 1, Y: 1 }
		Dimensions { K: 1, C: 256, R: 1, S: 1, Y: 56, X: 56 }
		Dataflow {
			TemporalMap (1,1) C;
			TemporalMap (Sz(R),1) Y;
			SpatialMap (Sz(S),1) X;
			TemporalMap (Sz(R),Sz(R)) R;
			TemporalMap (Sz(S),Sz(S)) S;
		}

	}

	Layer CONV2_2_1 {
		Type: CONV
		Stride { X: 1, Y: 1 }
		Dimensions { K: 64, C: 256, R: 1, S: 1, Y: 56, X: 56 }
		Dataflow {
			TemporalMap (1,1) K;
			TemporalMap (1,1) C;
			TemporalMap (Sz(R),1) Y;
			SpatialMap (Sz(S),1) X;
			TemporalMap (Sz(R),Sz(R)) R;
			TemporalMap (Sz(S),Sz(S)) S;
		}

	}

	Layer CONV2_2_2 {
		Type: CONV
		Stride { X: 1, Y: 1 }
		Dimensions { K: 64, C: 64, R: 3, S: 3, Y: 56, X: 56 }
		Dataflow {
			TemporalMap (1,1) K;
			TemporalMap (1,1) C;
			TemporalMap (Sz(R),1) Y;
			SpatialMap (Sz(S),1) X;
			TemporalMap (Sz(R),Sz(R)) R;
			TemporalMap (Sz(S),Sz(S)) S;
		}

	}

	Layer CONV2_2_3 {
		Type: CONV
		Stride { X: 1, Y: 1 }
		Dimensions { K: 256, C: 64, R: 1, S: 1, Y: 56, X: 56 }
		Dataflow {
			TemporalMap (1,1) K;
			TemporalMap (1,1) C;
			TemporalMap (Sz(R),1) Y;
			SpatialMap (Sz(S),1) X;
			TemporalMap (Sz(R),Sz(R)) R;
			TemporalMap (Sz(S),Sz(S)) S;
		}

	}

	Layer CONV2_2_Residual {
		Type: DSCONV
		Stride { X: 1, Y: 1 }
		Dimensions { K: 1, C: 256, R: 1, S: 1, Y: 56, X: 56 }
		Dataflow {
			TemporalMap (1,1) C;
			TemporalMap (Sz(R),1) Y;
			SpatialMap (Sz(S),1) X;
			TemporalMap (Sz(R),Sz(R)) R;
			TemporalMap (Sz(S),Sz(S)) S;
		}

	}

	Layer CONV2_3_1 {
		Type: CONV
		Stride { X: 1, Y: 1 }
		Dimensions { K: 64, C: 256, R: 1, S: 1, Y: 56, X: 56 }
		Dataflow {
			TemporalMap (1,1) K;
			TemporalMap (1,1) C;
			TemporalMap (Sz(R),1) Y;
			SpatialMap (Sz(S),1) X;
			TemporalMap (Sz(R),Sz(R)) R;
			TemporalMap (Sz(S),Sz(S)) S;
		}

	}

	Layer CONV2_3_2 {
		Type: CONV
		Stride { X: 1, Y: 1 }
		Dimensions { K: 64, C: 64, R: 3, S: 3, Y: 56, X: 56 }
		Dataflow {
			TemporalMap (1,1) K;
			TemporalMap (1,1) C;
			TemporalMap (Sz(R),1) Y;
			SpatialMap (Sz(S),1) X;
			TemporalMap (Sz(R),Sz(R)) R;
			TemporalMap (Sz(S),Sz(S)) S;
		}

	}

	Layer CONV2_3_3 {
		Type: CONV
		Stride { X: 1, Y: 1 }
		Dimensions { K: 256, C: 64, R: 1, S: 1, Y: 56, X: 56 }
		Dataflow {
			TemporalMap (1,1) K;
			TemporalMap (1,1) C;
			TemporalMap (Sz(R),1) Y;
			SpatialMap (Sz(S),1) X;
			TemporalMap (Sz(R),Sz(R)) R;
			TemporalMap (Sz(S),Sz(S)) S;
		}

	}

	Layer CONV2_3_Residual {
		Type: DSCONV
		Stride { X: 1, Y: 1 }
		Dimensions { K: 1, C: 256, R: 1, S: 1, Y: 56, X: 56 }
		Dataflow {
			TemporalMap (1,1) C;
			TemporalMap (Sz(R),1) Y;
			SpatialMap (Sz(S),1) X;
			TemporalMap (Sz(R),Sz(R)) R;
			TemporalMap (Sz(S),Sz(S)) S;
		}

	}

	Layer CONV3_1_1 {
		Type: CONV
		Stride { X: 2, Y: 2 }
		Dimensions { K: 128, C: 256, R: 1, S: 1, Y: 56, X: 56 }
		Dataflow {
			TemporalMap (1,1) K;
			TemporalMap (1,1) C;
			TemporalMap (Sz(R),1) Y;
			SpatialMap (Sz(S),1) X;
			TemporalMap (Sz(R),Sz(R)) R;
			TemporalMap (Sz(S),Sz(S)) S;
		}

	}

	Layer CONV3_1_2 {
		Type: CONV
		Stride { X: 1, Y: 1 }
		Dimensions { K: 128, C: 128, R: 3, S: 3, Y: 28, X: 28 }
		Dataflow {
			TemporalMap (1,1) K;
			TemporalMap (1,1) C;
			TemporalMap (Sz(R),1) Y;
			SpatialMap (Sz(S),1) X;
			TemporalMap (Sz(R),Sz(R)) R;
			TemporalMap (Sz(S),Sz(S)) S;
		}

	}

	Layer CONV3_1_3 {
		Type: CONV
		Stride { X: 1, Y: 1 }
		Dimensions { K: 512, C: 128, R: 1, S: 1, Y: 28, X: 28 }
		Dataflow {
			TemporalMap (1,1) K;
			TemporalMap (1,1) C;
			TemporalMap (Sz(R),1) Y;
			SpatialMap (Sz(S),1) X;
			TemporalMap (Sz(R),Sz(R)) R;
			TemporalMap (Sz(S),Sz(S)) S;
		}

	}

	Layer CONV3_1_Residual {
		Type: DSCONV
		Stride { X: 1, Y: 1 }
		Dimensions { K: 1, C: 512, R: 1, S: 1, Y: 28, X: 28 }
		Dataflow {
			TemporalMap (1,1) C;
			TemporalMap (Sz(R),1) Y;
			SpatialMap (Sz(S),1) X;
			TemporalMap (Sz(R),Sz(R)) R;
			TemporalMap (Sz(S),Sz(S)) S;
		}

	}

	Layer CONV3_2_1 {
		Type: CONV
		Stride { X: 1, Y: 1 }
		Dimensions { K: 128, C: 256, R: 1, S: 1, Y: 28, X: 28 }
		Dataflow {
			TemporalMap (1,1) K;
			TemporalMap (1,1) C;
			TemporalMap (Sz(R),1) Y;
			SpatialMap (Sz(S),1) X;
			TemporalMap (Sz(R),Sz(R)) R;
			TemporalMap (Sz(S),Sz(S)) S;
		}

	}

	Layer CONV3_2_2 {
		Type: CONV
		Stride { X: 1, Y: 1 }
		Dimensions { K: 128, C: 128, R: 3, S: 3, Y: 28, X: 28 }
		Dataflow {
			TemporalMap (1,1) K;
			TemporalMap (1,1) C;
			TemporalMap (Sz(R),1) Y;
			SpatialMap (Sz(S),1) X;
			TemporalMap (Sz(R),Sz(R)) R;
			TemporalMap (Sz(S),Sz(S)) S;
		}

	}

	Layer CONV3_2_3 {
		Type: CONV
		Stride { X: 1, Y: 1 }
		Dimensions { K: 512, C: 128, R: 1, S: 1, Y: 28, X: 28 }
		Dataflow {
			TemporalMap (1,1) K;
			TemporalMap (1,1) C;
			TemporalMap (Sz(R),1) Y;
			SpatialMap (Sz(S),1) X;
			TemporalMap (Sz(R),Sz(R)) R;
			TemporalMap (Sz(S),Sz(S)) S;
		}

	}

	Layer CONV3_1_Residual {
		Type: DSCONV
		Stride { X: 1, Y: 1 }
		Dimensions { K: 1, C: 512, R: 1, S: 1, Y: 28, X: 28 }
		Dataflow {
			TemporalMap (1,1) C;
			TemporalMap (Sz(R),1) Y;
			SpatialMap (Sz(S),1) X;
			TemporalMap (Sz(R),Sz(R)) R;
			TemporalMap (Sz(S),Sz(S)) S;
		}

	}

	Layer CONV3_3_1 {
		Type: CONV
		Stride { X: 1, Y: 1 }
		Dimensions { K: 128, C: 256, R: 1, S: 1, Y: 28, X: 28 }
		Dataflow {
			TemporalMap (1,1) K;
			TemporalMap (1,1) C;
			TemporalMap (Sz(R),1) Y;
			SpatialMap (Sz(S),1) X;
			TemporalMap (Sz(R),Sz(R)) R;
			TemporalMap (Sz(S),Sz(S)) S;
		}

	}

	Layer CONV3_3_2 {
		Type: CONV
		Stride { X: 1, Y: 1 }
		Dimensions { K: 128, C: 128, R: 3, S: 3, Y: 28, X: 28 }
		Dataflow {
			TemporalMap (1,1) K;
			TemporalMap (1,1) C;
			TemporalMap (Sz(R),1) Y;
			SpatialMap (Sz(S),1) X;
			TemporalMap (Sz(R),Sz(R)) R;
			TemporalMap (Sz(S),Sz(S)) S;
		}

	}

	Layer CONV3_3_3 {
		Type: CONV
		Stride { X: 1, Y: 1 }
		Dimensions { K: 512, C: 128, R: 1, S: 1, Y: 28, X: 28 }
		Dataflow {
			TemporalMap (1,1) K;
			TemporalMap (1,1) C;
			TemporalMap (Sz(R),1) Y;
			SpatialMap (Sz(S),1) X;
			TemporalMap (Sz(R),Sz(R)) R;
			TemporalMap (Sz(S),Sz(S)) S;
		}

	}

	Layer CONV3_3_Residual {
		Type: DSCONV
		Stride { X: 1, Y: 1 }
		Dimensions { K: 1, C: 512, R: 1, S: 1, Y: 28, X: 28 }
		Dataflow {
			TemporalMap (1,1) C;
			TemporalMap (Sz(R),1) Y;
			SpatialMap (Sz(S),1) X;
			TemporalMap (Sz(R),Sz(R)) R;
			TemporalMap (Sz(S),Sz(S)) S;
		}

	}

	Layer CONV3_4_1 {
		Type: CONV
		Stride { X: 1, Y: 1 }
		Dimensions { K: 128, C: 256, R: 1, S: 1, Y: 28, X: 28 }
		Dataflow {
			TemporalMap (1,1) K;
			TemporalMap (1,1) C;
			TemporalMap (Sz(R),1) Y;
			SpatialMap (Sz(S),1) X;
			TemporalMap (Sz(R),Sz(R)) R;
			TemporalMap (Sz(S),Sz(S)) S;
		}

	}

	Layer CONV3_4_2 {
		Type: CONV
		Stride { X: 1, Y: 1 }
		Dimensions { K: 128, C: 128, R: 3, S: 3, Y: 28, X: 28 }
		Dataflow {
			TemporalMap (1,1) K;
			TemporalMap (1,1) C;
			TemporalMap (Sz(R),1) Y;
			SpatialMap (Sz(S),1) X;
			TemporalMap (Sz(R),Sz(R)) R;
			TemporalMap (Sz(S),Sz(S)) S;
		}

	}

	Layer CONV3_4_3 {
		Type: CONV
		Stride { X: 1, Y: 1 }
		Dimensions { K: 512, C: 128, R: 1, S: 1, Y: 28, X: 28 }
		Dataflow {
			TemporalMap (1,1) K;
			TemporalMap (1,1) C;
			TemporalMap (Sz(R),1) Y;
			SpatialMap (Sz(S),1) X;
			TemporalMap (Sz(R),Sz(R)) R;
			TemporalMap (Sz(S),Sz(S)) S;
		}

	}

	Layer CONV3_4_Residual {
		Type: DSCONV
		Stride { X: 1, Y: 1 }
		Dimensions { K: 1, C: 512, R: 1, S: 1, Y: 28, X: 28 }
		Dataflow {
			TemporalMap (1,1) C;
			TemporalMap (Sz(R),1) Y;
			SpatialMap (Sz(S),1) X;
			TemporalMap (Sz(R),Sz(R)) R;
			TemporalMap (Sz(S),Sz(S)) S;
		}

	}

	Layer CONV4_1_1 {
		Type: CONV
		Stride { X: 2, Y: 2 }
		Dimensions { K: 256, C: 512, R: 1, S: 1, Y: 28, X: 28 }
		Dataflow {
			TemporalMap (1,1) K;
			TemporalMap (1,1) C;
			TemporalMap (Sz(R),1) Y;
			SpatialMap (Sz(S),1) X;
			TemporalMap (Sz(R),Sz(R)) R;
			TemporalMap (Sz(S),Sz(S)) S;
		}

	}

	Layer CONV4_1_2 {
		Type: CONV
		Stride { X: 1, Y: 1 }
		Dimensions { K: 256, C: 256, R: 3, S: 3, Y: 14, X: 14 }
		Dataflow {
			TemporalMap (1,1) K;
			TemporalMap (1,1) C;
			TemporalMap (Sz(R),1) Y;
			SpatialMap (Sz(S),1) X;
			TemporalMap (Sz(R),Sz(R)) R;
			TemporalMap (Sz(S),Sz(S)) S;
		}

	}

	Layer CONV4_1_3 {
		Type: CONV
		Stride { X: 1, Y: 1 }
		Dimensions { K: 1024, C: 256, R: 1, S: 1, Y: 14, X: 14 }
		Dataflow {
			TemporalMap (1,1) K;
			TemporalMap (1,1) C;
			TemporalMap (Sz(R),1) Y;
			SpatialMap (Sz(S),1) X;
			TemporalMap (Sz(R),Sz(R)) R;
			TemporalMap (Sz(S),Sz(S)) S;
		}

	}

	Layer CONV4_1_Residual {
		Type: DSCONV
		Stride { X: 1, Y: 1 }
		Dimensions { K: 1, C: 1024, R: 1, S: 1, Y: 14, X: 14 }
		Dataflow {
			TemporalMap (1,1) C;
			TemporalMap (Sz(R),1) Y;
			SpatialMap (Sz(S),1) X;
			TemporalMap (Sz(R),Sz(R)) R;
			TemporalMap (Sz(S),Sz(S)) S;
		}

	}

	Layer CONV4_2_1 {
		Type: CONV
		Stride { X: 1, Y: 1 }
		Dimensions { K: 256, C: 1024, R: 1, S: 1, Y: 14, X: 14 }
		Dataflow {
			TemporalMap (1,1) K;
			TemporalMap (1,1) C;
			TemporalMap (Sz(R),1) Y;
			SpatialMap (Sz(S),1) X;
			TemporalMap (Sz(R),Sz(R)) R;
			TemporalMap (Sz(S),Sz(S)) S;
		}

	}

	Layer CONV4_2_2 {
		Type: CONV
		Stride { X: 1, Y: 1 }
		Dimensions { K: 256, C: 256, R: 3, S: 3, Y: 14, X: 14 }
		Dataflow {
			TemporalMap (1,1) K;
			TemporalMap (1,1) C;
			TemporalMap (Sz(R),1) Y;
			SpatialMap (Sz(S),1) X;
			TemporalMap (Sz(R),Sz(R)) R;
			TemporalMap (Sz(S),Sz(S)) S;
		}

	}

	Layer CONV4_2_3 {
		Type: CONV
		Stride { X: 1, Y: 1 }
		Dimensions { K: 1024, C: 256, R: 1, S: 1, Y: 14, X: 14 }
		Dataflow {
			TemporalMap (1,1) K;
			TemporalMap (1,1) C;
			TemporalMap (Sz(R),1) Y;
			SpatialMap (Sz(S),1) X;
			TemporalMap (Sz(R),Sz(R)) R;
			TemporalMap (Sz(S),Sz(S)) S;
		}

	}

	Layer CONV4_2_Residual {
		Type: DSCONV
		Stride { X: 1, Y: 1 }
		Dimensions { K: 1, C: 1024, R: 1, S: 1, Y: 14, X: 14 }
		Dataflow {
			TemporalMap (1,1) C;
			TemporalMap (Sz(R),1) Y;
			SpatialMap (Sz(S),1) X;
			TemporalMap (Sz(R),Sz(R)) R;
			TemporalMap (Sz(S),Sz(S)) S;
		}

	}

	Layer CONV4_3_1 {
		Type: CONV
		Stride { X: 1, Y: 1 }
		Dimensions { K: 256, C: 1024, R: 1, S: 1, Y: 14, X: 14 }
		Dataflow {
			TemporalMap (1,1) K;
			TemporalMap (1,1) C;
			TemporalMap (Sz(R),1) Y;
			SpatialMap (Sz(S),1) X;
			TemporalMap (Sz(R),Sz(R)) R;
			TemporalMap (Sz(S),Sz(S)) S;
		}

	}

	Layer CONV4_3_2 {
		Type: CONV
		Stride { X: 1, Y: 1 }
		Dimensions { K: 256, C: 256, R: 3, S: 3, Y: 14, X: 14 }
		Dataflow {
			TemporalMap (1,1) K;
			TemporalMap (1,1) C;
			TemporalMap (Sz(R),1) Y;
			SpatialMap (Sz(S),1) X;
			TemporalMap (Sz(R),Sz(R)) R;
			TemporalMap (Sz(S),Sz(S)) S;
		}

	}

	Layer CONV4_3_3 {
		Type: CONV
		Stride { X: 1, Y: 1 }
		Dimensions { K: 1024, C: 256, R: 1, S: 1, Y: 14, X: 14 }
		Dataflow {
			TemporalMap (1,1) K;
			TemporalMap (1,1) C;
			TemporalMap (Sz(R),1) Y;
			SpatialMap (Sz(S),1) X;
			TemporalMap (Sz(R),Sz(R)) R;
			TemporalMap (Sz(S),Sz(S)) S;
		}

	}

	Layer CONV4_3_Residual {
		Type: DSCONV
		Stride { X: 1, Y: 1 }
		Dimensions { K: 1, C: 1024, R: 1, S: 1, Y: 14, X: 14 }
		Dataflow {
			TemporalMap (1,1) C;
			TemporalMap (Sz(R),1) Y;
			SpatialMap (Sz(S),1) X;
			TemporalMap (Sz(R),Sz(R)) R;
			TemporalMap (Sz(S),Sz(S)) S;
		}
	}

	Layer CONV4_4_1 {
		Type: CONV
		Stride { X: 1, Y: 1 }
		Dimensions { K: 256, C: 1024, R: 1, S: 1, Y: 14, X: 14 }
		Dataflow {
			TemporalMap (1,1) K;
			TemporalMap (1,1) C;
			TemporalMap (Sz(R),1) Y;
			SpatialMap (Sz(S),1) X;
			TemporalMap (Sz(R),Sz(R)) R;
			TemporalMap (Sz(S),Sz(S)) S;
		}

	}

	Layer CONV4_4_2 {
		Type: CONV
		Stride { X: 1, Y: 1 }
		Dimensions { K: 256, C: 256, R: 3, S: 3, Y: 14, X: 14 }
		Dataflow {
			TemporalMap (1,1) K;
			TemporalMap (1,1) C;
			TemporalMap (Sz(R),1) Y;
			SpatialMap (Sz(S),1) X;
			TemporalMap (Sz(R),Sz(R)) R;
			TemporalMap (Sz(S),Sz(S)) S;
		}

	}

	Layer CONV4_4_3 {
		Type: CONV
		Stride { X: 1, Y: 1 }
		Dimensions { K: 1024, C: 256, R: 1, S: 1, Y: 14, X: 14 }
		Dataflow {
			TemporalMap (1,1) K;
			TemporalMap (1,1) C;
			TemporalMap (Sz(R),1) Y;
			SpatialMap (Sz(S),1) X;
			TemporalMap (Sz(R),Sz(R)) R;
			TemporalMap (Sz(S),Sz(S)) S;
		}

	}

	Layer CONV4_4_Residual {
		Type: DSCONV
		Stride { X: 1, Y: 1 }
		Dimensions { K: 1, C: 1024, R: 1, S: 1, Y: 14, X: 14 }
		Dataflow {
			TemporalMap (1,1) C;
			TemporalMap (Sz(R),1) Y;
			SpatialMap (Sz(S),1) X;
			TemporalMap (Sz(R),Sz(R)) R;
			TemporalMap (Sz(S),Sz(S)) S;
		}

	}

	Layer CONV4_5_1 {
		Type: CONV
		Stride { X: 1, Y: 1 }
		Dimensions { K: 256, C: 1024, R: 1, S: 1, Y: 14, X: 14 }
		Dataflow {
			TemporalMap (1,1) K;
			TemporalMap (1,1) C;
			TemporalMap (Sz(R),1) Y;
			SpatialMap (Sz(S),1) X;
			TemporalMap (Sz(R),Sz(R)) R;
			TemporalMap (Sz(S),Sz(S)) S;
		}

	}

	Layer CONV4_5_2 {
		Type: CONV
		Stride { X: 1, Y: 1 }
		Dimensions { K: 256, C: 256, R: 3, S: 3, Y: 14, X: 14 }
		Dataflow {
			TemporalMap (1,1) K;
			TemporalMap (1,1) C;
			TemporalMap (Sz(R),1) Y;
			SpatialMap (Sz(S),1) X;
			TemporalMap (Sz(R),Sz(R)) R;
			TemporalMap (Sz(S),Sz(S)) S;
		}

	}

	Layer CONV4_5_3 {
		Type: CONV
		Stride { X: 1, Y: 1 }
		Dimensions { K: 1024, C: 256, R: 1, S: 1, Y: 14, X: 14 }
		Dataflow {
			TemporalMap (1,1) K;
			TemporalMap (1,1) C;
			TemporalMap (Sz(R),1) Y;
			SpatialMap (Sz(S),1) X;
			TemporalMap (Sz(R),Sz(R)) R;
			TemporalMap (Sz(S),Sz(S)) S;
		}

	}

	Layer CONV4_5_Residual {
		Type: DSCONV
		Stride { X: 1, Y: 1 }
		Dimensions { K: 1, C: 1024, R: 1, S: 1, Y: 14, X: 14 }
		Dataflow {
			TemporalMap (1,1) C;
			TemporalMap (Sz(R),1) Y;
			SpatialMap (Sz(S),1) X;
			TemporalMap (Sz(R),Sz(R)) R;
			TemporalMap (Sz(S),Sz(S)) S;
		}

	}

	Layer CONV4_6_1 {
		Type: CONV
		Stride { X: 1, Y: 1 }
		Dimensions { K: 256, C: 1024, R: 1, S: 1, Y: 14, X: 14 }
		Dataflow {
			TemporalMap (1,1) K;
			TemporalMap (1,1) C;
			TemporalMap (Sz(R),1) Y;
			SpatialMap (Sz(S),1) X;
			TemporalMap (Sz(R),Sz(R)) R;
			TemporalMap (Sz(S),Sz(S)) S;
		}

	}

	Layer CONV4_6_2 {
		Type: CONV
		Stride { X: 1, Y: 1 }
		Dimensions { K: 256, C: 256, R: 3, S: 3, Y: 14, X: 14 }
		Dataflow {
			TemporalMap (1,1) K;
			TemporalMap (1,1) C;
			TemporalMap (Sz(R),1) Y;
			SpatialMap (Sz(S),1) X;
			TemporalMap (Sz(R),Sz(R)) R;
			TemporalMap (Sz(S),Sz(S)) S;
		}

	}

	Layer CONV4_6_3 {
		Type: CONV
		Stride { X: 1, Y: 1 }
		Dimensions { K: 1024, C: 256, R: 1, S: 1, Y: 14, X: 14 }
		Dataflow {
			TemporalMap (1,1) K;
			TemporalMap (1,1) C;
			TemporalMap (Sz(R),1) Y;
			SpatialMap (Sz(S),1) X;
			TemporalMap (Sz(R),Sz(R)) R;
			TemporalMap (Sz(S),Sz(S)) S;
		}

	}

	Layer CONV4_6_Residual {
		Type: DSCONV
		Stride { X: 1, Y: 1 }
		Dimensions { K: 1, C: 1024, R: 1, S: 1, Y: 14, X: 14 }
		Dataflow {
			TemporalMap (1,1) C;
			TemporalMap (Sz(R),1) Y;
			SpatialMap (Sz(S),1) X;
			TemporalMap (Sz(R),Sz(R)) R;
			TemporalMap (Sz(S),Sz(S)) S;
		}

	}

	Layer CONV5_1_1 {
		Type: CONV
		Stride { X: 2, Y: 2 }
		Dimensions { K: 512, C: 1024, R: 1, S: 1, Y: 14, X: 14 }
		Dataflow {
			TemporalMap (1,1) K;
			TemporalMap (1,1) C;
			TemporalMap (Sz(R),1) Y;
			SpatialMap (Sz(S),1) X;
			TemporalMap (Sz(R),Sz(R)) R;
			TemporalMap (Sz(S),Sz(S)) S;
		}

	}

	Layer CONV5_1_2 {
		Type: CONV
		Stride { X: 1, Y: 1 }
		Dimensions { K: 512, C: 512, R: 3, S: 3, Y: 7, X: 7 }
		Dataflow {
			TemporalMap (1,1) K;
			TemporalMap (1,1) C;
			TemporalMap (Sz(R),1) Y;
			SpatialMap (Sz(S),1) X;
			TemporalMap (Sz(R),Sz(R)) R;
			TemporalMap (Sz(S),Sz(S)) S;
		}

	}

	Layer CONV5_1_3 {
		Type: CONV
		Stride { X: 1, Y: 1 }
		Dimensions { K: 2048, C: 512, R: 1, S: 1, Y: 7, X: 7 }
		Dataflow {
			TemporalMap (1,1) K;
			TemporalMap (1,1) C;
			TemporalMap (Sz(R),1) Y;
			SpatialMap (Sz(S),1) X;
			TemporalMap (Sz(R),Sz(R)) R;
			TemporalMap (Sz(S),Sz(S)) S;
		}

	}

	Layer CONV5_1_Residual {
		Type: DSCONV
		Stride { X: 1, Y: 1 }
		Dimensions { K: 1, C: 2048, R: 1, S: 1, Y: 7, X: 7 }
		Dataflow {
			TemporalMap (1,1) C;
			TemporalMap (Sz(R),1) Y;
			SpatialMap (Sz(S),1) X;
			TemporalMap (Sz(R),Sz(R)) R;
			TemporalMap (Sz(S),Sz(S)) S;
		}

	}

	Layer CONV5_2_1 {
		Type: CONV
		Stride { X: 1, Y: 1 }
		Dimensions { K: 512, C: 2048, R: 1, S: 1, Y: 7, X: 7 }
		Dataflow {
			TemporalMap (1,1) K;
			TemporalMap (1,1) C;
			TemporalMap (Sz(R),1) Y;
			SpatialMap (Sz(S),1) X;
			TemporalMap (Sz(R),Sz(R)) R;
			TemporalMap (Sz(S),Sz(S)) S;
		}

	}

	Layer CONV5_2_2 {
		Type: CONV
		Stride { X: 1, Y: 1 }
		Dimensions { K: 512, C: 512, R: 3, S: 3, Y: 7, X: 7 }
		Dataflow {
			TemporalMap (1,1) K;
			TemporalMap (1,1) C;
			TemporalMap (Sz(R),1) Y;
			SpatialMap (Sz(S),1) X;
			TemporalMap (Sz(R),Sz(R)) R;
			TemporalMap (Sz(S),Sz(S)) S;
		}

	}

	Layer CONV5_2_3 {
		Type: CONV
		Stride { X: 1, Y: 1 }
		Dimensions { K: 2048, C: 512, R: 1, S: 1, Y: 7, X: 7 }
		Dataflow {
			TemporalMap (1,1) K;
			TemporalMap (1,1) C;
			TemporalMap (Sz(R),1) Y;
			SpatialMap (Sz(S),1) X;
			TemporalMap (Sz(R),Sz(R)) R;
			TemporalMap (Sz(S),Sz(S)) S;
		}

	}

	Layer CONV5_2_Residual {
		Type: DSCONV
		Stride { X: 1, Y: 1 }
		Dimensions { K: 1, C: 2048, R: 1, S: 1, Y: 7, X: 7 }
		Dataflow {
			TemporalMap (1,1) C;
			TemporalMap (Sz(R),1) Y;
			SpatialMap (Sz(S),1) X;
			TemporalMap (Sz(R),Sz(R)) R;
			TemporalMap (Sz(S),Sz(S)) S;
		}

	}

	Layer CONV5_3_1 {
		Type: CONV
		Stride { X: 1, Y: 1 }
		Dimensions { K: 512, C: 2048, R: 1, S: 1, Y: 7, X: 7 }
		Dataflow {
			TemporalMap (1,1) K;
			TemporalMap (1,1) C;
			TemporalMap (Sz(R),1) Y;
			SpatialMap (Sz(S),1) X;
			TemporalMap (Sz(R),Sz(R)) R;
			TemporalMap (Sz(S),Sz(S)) S;
		}

	}

	Layer CONV5_3_2 {
		Type: CONV
		Stride { X: 1, Y: 1 }
		Dimensions { K: 512, C: 512, R: 3, S: 3, Y: 7, X: 7 }
		Dataflow {
			TemporalMap (1,1) K;
			TemporalMap (1,1) C;
			TemporalMap (Sz(R),1) Y;
			SpatialMap (Sz(S),1) X;
			TemporalMap (Sz(R),Sz(R)) R;
			TemporalMap (Sz(S),Sz(S)) S;
		}

	}

	Layer CONV5_3_3 {
		Type: CONV
		Stride { X: 1, Y: 1 }
		Dimensions { K: 2048, C: 512, R: 1, S: 1, Y: 7, X: 7 }
		Dataflow {
			TemporalMap (1,1) K;
			TemporalMap (1,1) C;
			TemporalMap (Sz(R),1) Y;
			SpatialMap (Sz(S),1) X;
			TemporalMap (Sz(R),Sz(R)) R;
			TemporalMap (Sz(S),Sz(S)) S;
		}

	}

	Layer CONV5_3_Residual {
		Type: DSCONV
		Stride { X: 1, Y: 1 }
		Dimensions { K: 1, C: 2048, R: 1, S: 1, Y: 7, X: 7 }
		Dataflow {
			TemporalMap (1,1) C;
			TemporalMap (Sz(R),1) Y;
			SpatialMap (Sz(S),1) X;
			TemporalMap (Sz(R),Sz(R)) R;
			TemporalMap (Sz(S),Sz(S)) S;
		}

	}

	Layer FC1000 {
		Type: CONV
		Stride { X: 1, Y: 1 }
		Dimensions { K: 1000, C: 2048, R: 7, S: 7, Y: 7, X: 7 }
		Dataflow {
			TemporalMap (1,1) K;
			TemporalMap (1,1) C;
			TemporalMap (Sz(R),1) Y;
			SpatialMap (Sz(S),1) X;
			TemporalMap (Sz(R),Sz(R)) R;
			TemporalMap (Sz(S),Sz(S)) S;
		}

	}

}


function Data = loadsamples(imgpathlistfile, exc_setlabel)
%LOADSAMPLES Summary of this function goes here
%   Function: load samples from dbname database
%   Detailed explanation goes here
%   Input:
%        dbname: the name of one database
%        exc_setlabel: excluded set label
%   Output:
%        Data: loaded data from the database
imgpathlist = textread(imgpathlistfile, '%s', 'delimiter', '\n');

Data = cell(length(imgpathlist), 1);

setnames = {'train' 'test'};

% Create a cascade detector object.
% faceDetector = vision.CascadeObjectDetector();
% bboxes_facedet = zeros(length(imgpathlist), 4);
% bboxes_gt      = zeros(length(imgpathlist), 4);
% isdetected = zeros(length(imgpathlist), 1);

parfor i = 1:length(imgpathlist)
    img = im2uint8(imread(imgpathlist{i}));
    Data{i}.width_orig    = size(img, 2);
    Data{i}.height_orig   = size(img, 1);

    % Data{i}.img      = img
    % shapepath = strrep(imgpathlist{i}, 'png', 'pts');
    shapepath = strcat(imgpathlist{i}(1:end-3), 'pts');
    Data{i}.shape_gt = double(loadshape(shapepath));
    % Data{i}.shape_gt = Data{i}.shape_gt(params.ind_usedpts, :);
    % bbox     =  bounding_boxes_allsamples{i}.bb_detector; %
    Data{i}.bbox_gt = getbbox(Data{i}.shape_gt); % [bbox(1) bbox(2) bbox(3)-bbox(1) bbox(4)-bbox(2)];

    % cut original image to a region which is a bit larger than the face
    % bounding box
    region = enlargingbbox(Data{i}.bbox_gt, 2.0);

    region(2) = double(max(region(2), 1));
    region(1) = double(max(region(1), 1));

    bottom_y = double(min(region(2) + region(4) - 1, Data{i}.height_orig));
    right_x = double(min(region(1) + region(3) - 1, Data{i}.width_orig));

    img_region = img(region(2):bottom_y, region(1):right_x, :);
    Data{i}.shape_gt = bsxfun(@minus, Data{i}.shape_gt, double([region(1) region(2)]));

    % to save memory cost during training
    if exc_setlabel == 2
        ratio = min(1, sqrt(single(150 * 150) / single(size(img_region, 1) * size(img_region, 2))));
        img_region = imresize(img_region, ratio);
        Data{i}.shape_gt = Data{i}.shape_gt .* ratio;
    end
    Data{i}.bbox_gt = getbbox(Data{i}.shape_gt);

    Data{i}.bbox_facedet = getbbox(Data{i}.shape_gt);
    % perform face detection using matlab face detector
    %{
    bbox = step(faceDetector, img_region);
    if isempty(bbox)
        % if face detection is failed
        isdetected(i) = 1;
        Data{i}.bbox_facedet = getbbox(Data{i}.shape_gt);
    else
        int_ratios = zeros(1, size(bbox, 1));
        for b = 1:size(bbox, 1)
            area = rectint(Data{i}.bbox_gt, bbox(b, :));
            int_ratios(b) = (area)/(bbox(b, 3)*bbox(b, 4) + Data{i}.bbox_gt(3)*Data{i}.bbox_gt(4) - area);
        end
        [max_ratio, max_ind] = max(int_ratios);

        if max_ratio < 0.4  % detection fail
            isdetected(i) = 0;
        else
            Data{i}.bbox_facedet = bbox(max_ind, 1:4);
            isdetected(i) = 1;
            % imgOut = insertObjectAnnotation(img_region,'rectangle',Data{i}.bbox_facedet,'Face');
            % imshow(imgOut);
        end
    end
    %}
    % recalculate the location of groundtruth shape and bounding box
    % Data{i}.shape_gt = bsxfun(@minus, Data{i}.shape_gt, double([region(1) region(2)]));
    % Data{i}.bbox_gt = getbbox(Data{i}.shape_gt);

    if size(img_region, 3) == 1
        Data{i}.img_gray = img_region;
    else
        % hsv = rgb2hsv(img_region);
        Data{i}.img_gray = rgb2gray(img_region);
    end

    Data{i}.width    = size(img_region, 2);
    Data{i}.height   = size(img_region, 1);
end

ind_valid = ones(1, length(imgpathlist));
parfor i = 1:length(imgpathlist)
    if ~isempty(exc_setlabel)
        ind = strfind(imgpathlist{i}, setnames{exc_setlabel});
        if ~isempty(ind) % | ~isdetected(i)
            ind_valid(i) = 0;
        end
    end
end

% learn the linear transformation from detected bboxes to groundtruth bboxes
% bboxes = [bboxes_gt bboxes_facedet];
% bboxes = bboxes(ind_valid == 1, :);

Data = Data(ind_valid == 1);

end

function shape = loadshape(path)
% function: load shape from pts file
file = fopen(path);

if ~isempty(strfind(path, 'COFW'))
    shape = textscan(file, '%d16 %d16 %d8', 'HeaderLines', 3, 'CollectOutput', 3);
else
    shape = textscan(file, '%d16 %d16', 'HeaderLines', 3, 'CollectOutput', 2);
end
fclose(file);

shape = shape{1};
end

function region = enlargingbbox(bbox, scale)

region(1) = floor(bbox(1) - (scale - 1)/2*bbox(3));
region(2) = floor(bbox(2) - (scale - 1)/2*bbox(4));

region(3) = floor(scale*bbox(3));
region(4) = floor(scale*bbox(4));

% region.right_x = floor(region.left_x + region.width - 1);
% region.bottom_y = floor(region.top_y + region.height - 1);

end


function coinWrite(prob,filename,type)
%COINWRITE  Write a Mathematical File From Matlab Values
%
% coinWrite(prob,filename,type) writes the problem prob to the file specified
% by filename and problem type specified by type. If you do not specify a
% file extension it will default to the type specified.
%
%   Available File Types:
%       - MPS  [.mps]
%       - QPS  [.qps]
%       - LP   [.lp]  (Appears to be CPLEX LP version - Simplified)
%
% You may specify a full path to the file, or if you specify a filename
% only, it will be written to the current MATLAB directory.
%
% The routines underneath use COIN-OR utilities for File IO. See
% attached EPL License.

%   Copyright (C) 2011 Jonathan Currie (IPL)

%Quick Checks
if(~exist('type','var') || isempty(type))
    %See if contained within file name
    dots = strfind(filename,'.');
    if(isempty(dots))
        error('You must supply the file type to this function');
    else %hopefully got a good ext, will check below
        type = filename(dots(end)+1:end);
    end
end
if(~ischar(filename))
    error('Filename must be a char array!');
end

%Problem Checks
if(~isempty(prob.fun) || ~isempty(prob.nlcon))
    error('You cannot write Nonlinear problems using this interface');
end
if(isa(prob.f,'function_handle') || isa(prob.H,'function_handle'))
    error('f and H must be vectors / matrices!');
end
if(~isempty(prob.Q))
    error('You cannot write quadratic constraints using this interface');
end
if(prob.sense == -1)
    error('You cannot explicity write maximization problems using this interface'); %fix up later
end

%Determine file type
switch(lower(type))
    case 'mps'
        filetype = 'mps';
    case 'qps'
        filetype = 'qps';
    case 'lp'
        filetype = 'lp';
    otherwise
        error('Unknown file type %s',type);
end

%Check problem type vs file type
if(isfield(prob,'type') && ~isempty(prob.type))
    switch(lower(prob.type))
        case {'lp','bilp','milp'}
            if(strcmpi(filetype,'qps'))
                error('LP/BILP/MILPs should be written to MPS or LP files');
            end
        case {'qp','miqp'}
            if(~strcmpi(filetype,'qps'))
                error('QP/MIQPs should be written to QPS files');
            end
        otherwise
            error('Problem type %s is not supported by this interface',upper(prob.type));
    end
end

% Make sure we have file extension
if(isempty(strfind(filename,['.' filetype])))
    filename = [filename '.' filetype];
end

% If filename includes absolute path, we can skip which
if(~isempty(strfind(filename,':')))
    p = filename;
else %Locate the full path to the file
    p = [cd filesep filename];
end

%Convert to coin problem
if(~isempty(prob.b) || ~isempty(prob.beq))
    [prob.A,prob.rl,prob.ru] = gen2row(prob.A,prob.b,prob.Aeq,prob.beq);
end
if(isempty(prob.A))
    prob.A = spalloc(0,length(prob.f),0); %error thrown internally if not ok
end

%Ensure sparsity
prob.A = sparse(prob.A);
prob.H = sparse(prob.H);

%Ensure Bounds
if(isempty(prob.lb))
    prob.lb = -Inf(size(prob.f));
end
if(isempty(prob.ub))
    prob.ub = Inf(size(prob.f));
end

%Setup Integer Vars
if(isstruct(prob.int))
    prob.int = prob.int.str;
end
if(isempty(prob.int))
    prob.int = zeros(size(prob.f));
elseif(ischar(prob.int)) %char string
    prob.int = lower(prob.int);
    i = zeros(size(prob.f));
    int = prob.int == 'i';
    bin = prob.int == 'b';
    i(int) = 1;
    i(bin) = 1;
    %Some problems have fixed binary vars (i.e. 0 <= bvar_n <= 0), can't
    %just blanket modify the bounds
    lb_idx = prob.lb(bin) ~= 0 & prob.lb(bin) ~= 1;
    ub_idx = prob.ub(bin) ~= 0 & prob.ub(bin) ~= 1;
    if(any(lb_idx))
        prob.lb(lb_idx) = 0;
    end
    if(any(ub_idx))
        prob.ub(ub_idx) = 1;
    end
    prob.int = i;
elseif(length(prob.int) ~= length(prob.f) || any(prob.int > 1)) %find indicies (might be wrong though!)
    i = zeros(size(prob.f));
    i(prob.int) = 1;
    prob.int = i;
end
prob.int = int8(prob.int);

%Setup SOS
if(~isempty(prob.sos))
    [r,c] = size(prob.sos.type);
    if(r > c)
        prob.sos_type = str2num(prob.sos.type); %#ok<ST2NM>
    else
        prob.sos_type = str2num(prob.sos.type'); %#ok<ST2NM>
    end
    if(length(prob.sos.type) == 1)
        prob.sos_index = {prob.sos.index};
        prob.sos_weight = {prob.sos.weight};
    else
        prob.sos_index = prob.sos.index;
        prob.sos_weight = prob.sos.weight;
    end
else
    prob.sos_type = [];
    prob.sos_index = [];
    prob.sos_weight = [];
end
%Setup Objc
if(~isempty(prob.objbias) && prob.objbias == 0)
    prob.objbias = 0;
end

%Call coin-or mex
coinW(prob,p,filetype);


function im = loadImage(filename)
%LOADIMAGE      Load an image file.
%
% DESCRIPTION:
%       loadImage loads an external image file using imread and returns a
%       two-dimensional image matrix scaled between 0 and 1. If a colour
%       image is loaded, the colour channels are summed before scaling. If
%       no input is given for filename, a selection dialog is invoked.
%
% USAGE:
%       im = loadImage()
%       im = loadImage(filename)
%
% OPTIONAL INPUTS:
%       filename    - filename (and pathname if not in the same directory)
%                     of the image to load
%
% OUTPUTS:
%       im          - scaled image matrix
%
% ABOUT:
%       author      - Bradley Treeby
%       date        - 30th June 2009
%       last update - 25th 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
%
% See also imread, resize

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

% load the file if not selected
if nargin == 0
    [filename, pathname] = getFilename();
    filename = strcat(pathname, filename);
end

% extract the filetype
split_filename = regexp(filename, '\.', 'split');
filetype = lower(split_filename{2});

% check the filetype
if ~strcmp(filetype, 'jpg') && ~strcmp(filetype, 'bmp') && ~strcmp(filetype, 'gif')  && ~strcmp(filetype, 'png')
    disp('WARNING: image filetype may not be supported - see loadImage.m');
end

% load the image
im = imread(filename);

% if there are multiple colour channels, sum them up
if numDim(im) > 2
    im = squeeze(double(im(:, :, 1)) + double(im(:, :, 2)) + double(im(:, :, 3)));
else
    im = double(im);
end

% scale pixel values from 0 -> 1
im = max(im(:)) - im;
im = im .* (1/max(im(:)));


function h= testmotion
h = [];
h.plotData = @plotData;
h.setRotation = @setRotation;
h.setStepSize = @setStepSize;
h.onerot = @onerot;
h.ccrot = @ccrot;
h.cwrot = @cwrot;
h.readcell = @readcell;
% ---
rotationdirection = 1;
%1 for clockwise
%-1 for counter clockwise
%--
stepsize = 1; % in degrees

hMotor = makeMotor(stepsize,h);

    function onerot()
        hMotor.moveHand(rotationdirection);
    end

    function ccrot()
       rotationdirection = 1;
    end

    function cwrot()
       rotationdirection = -1;
    end

    function result = readcell(lednumber)
        result = hMotor.readcell(lednumber);
    end
end

function hMotor = makeMotor(setstepsize,hmotion)
hMotor = [];

stepsize = setstepsize;
hMotor.moveHand = @moveHand;
hMotor.plotData = @plotData;
hMotor.readcell = @readcell;
currentTheta = 0;
% Initiliaze the clock face
clockface = figure('name','Stepper Motor Simulator');% Figure clockface named "scotts clock"
set(clockface,'NumberTitle','off');                 %
set(clockface,'MenuBar','none');                    % Disable the menu bar
set(clockface,'color','w');                         % Set the backround color to white
set(clockface,'visible','on');                      % Set the clock to visible
set(clockface,'closerequestfcn',@closeRequestFcn);   % Specify closefunc at the bottom of the screen
set(clockface,'Resize','off');
set(clockface,'DoubleBuffer','on');
%set(clockface,'Renderer','OpenGL');
set(clockface,'UserData',hmotion);

%Draw the perimeter of the clock and position the figure
R=linspace(0,2*pi,1000);
x1=9*cos(R);
y1=9*sin(R);
plot(x1,y1,'b','linewidth',8,'color','k') %Draws a thick black circle
hold on
axis off
axis([-10 10 -10 10]) %Draws the figure with +/-10 for [Xmin Xmin Ymin Ymax]
axis equal
%== Tic Marks ==
% Define the Length of the Tic marks
TLenStart = 8.1;    % Start of the Tick mark (distance from origin)
TLenStop = 8.5;     % End of the Tick mark (distance from origin)
[STX,STY,TTX,TTY] = ticMark(TLenStart,TLenStop);
% Plot Skinny and Thick Tick marks on the clock face
plot(STX,STY, 'linewidth',1,'color','k');
%plot(TTX,TTY, 'linewidth',5,'color','k');
[HpDx,HpDy] = GetPolyData(0);

hourhand = fill(HpDx,HpDy,'k');

% Plot the numbers 1-12 on the screen
% --Declare variables to be used in the plotting the clock numbers
Clk_fSize = 12;                                  % controls the font size of the numbers of the clock
Clk_fTheta = (0:pi/4:((2*pi)-(pi/4)))';           % sets the Theta for each number position
Clk_fRad = 7;                                 % sets the Raduis for each number position
Clk_numbas = (0:45:315)';
Clk_nData = [Clk_fRad*cos(Clk_fTheta) Clk_fRad*sin(Clk_fTheta) Clk_numbas];
text(Clk_nData(:,1),Clk_nData(:,2),num2str(Clk_nData(:,3)),...
    'horizontalAlignment','center','verticalAlignment','middle','FontSize',Clk_fSize);

led1 = makeLED(5,0,0);
led2 = makeLED(0,-5,270);
led1.cursorPosition(0);
led2.cursorPosition(0);

hFace = makeFace(stepsize,hMotor,led1,led2);
firststep = hFace.getFirstStep();
currentstep = firststep;

    function result = readcell(cellnumber)
        result = 0;
        if cellnumber ==1
           result =  led1.getState();
        elseif cellnumber ==2
            result = led2.getState();
        end

    end

    function moveHand(direction)
        if direction>0
            currentstep = currentstep.getNext();
            currentstep.landedon();
        else
            currentstep = currentstep.getPrev();
            currentstep.landedon();
        end
    end

    function setRotation(rotdir)
        rotationdirection = rotdir;
    end

    function setStepSize(stepsizedeg)
        stepsize = stepsizedeg;
    end

    function onerot()
        plotData(currentTheta+stepsize*rotationdirection);
        currentTheta = currentTheta+stepsize*rotationdirection;
    end

    function plotData(angleOfArm)
        angleOfArm = angleOfArm*(pi/180);
[HpDx,HpDy] = GetPolyData(angleOfArm);
set(hourhand,'xdata',HpDx,'ydata',HpDy);

%         for idx = 1:.5:abs((angleOfArm*(180/pi))-stateTheta)
%             if currentTheta >=360
%                 currentTheta = 0;
%             elseif currentTheta<=0
%                 currentTheta = 360;
%             end
%             [HpDx,HpDy] = GetPolyData((pi/180)*(currentTheta+(rotationdirection*idx)));
%             set(hourhand,'xdata',HpDx,'ydata',HpDy);
%             led1.cursorPosition((currentTheta+(rotationdirection*idx)));
%             led2.cursorPosition((currentTheta+(rotationdirection*idx)));
%             pause(.01);
%         end
    end

    function [HpDx,HpDy] = GetPolyData(theta)

        HourHandData = [5 2/3 .2 .4];
        [HpDx,HpDy] = PolyEngine(theta,HourHandData);
    end

    function [STX,STY,TTX,TTY] = ticMark(TLenStart,TLenStop)
        %ticMark is given the distance from center to start the tick
        % marks (TLenStart) and the distance from origin to stop the
        % tick marks (TLenStop).
        %STTTheta 60 point array going clockwise skinny ticmarks
        STTheta = pi/2:-2*pi/180:-3*pi/2;
        %Calculates X Y coordinates for all 60 skinny tick marks
        STX = [TLenStart*cos(STTheta') TLenStop*cos(STTheta')]';
        STY = [TLenStart*sin(STTheta') TLenStop*sin(STTheta')]';
        %TTTheta 12 point array going around clockwise thick tic marks
        TTTheta = pi/2:-2*pi/12:-3*pi/2;
        %Calculates X Y coordinates for all 12 thick tic marks
        TTX = [TLenStart*cos(TTTheta') TLenStop*cos(TTTheta')]';
        TTY = [TLenStart*sin(TTTheta') TLenStop*sin(TTTheta')]';
    end %end ticmark function

    function [pDx,pDy] = PolyEngine(Theta,HanData)
        %PolyEngine is given the initial angle and specifications for each
        % polygon it is to generate. It then calculates the data points
        % that will makeup the polygon and passes it back in two variables
        % making up a set of X and Y coordinates that corelate to the
        % current angle. This makes it easy to plot the points as well as
        % easy to change the polygons shape. At this time it is specified
        % with 4 data points but a 5th could be added easily with only
        % changing the data in this PolyEngine function.

        %-Hand Polygon Equations
        %==================================================================
        %-Calculate the length from origin to points A and B.
        oA = sqrt(HanData(1)^2+HanData(3)^2);
        %-Calculate the length from origin to points C and D.
        oB = sqrt(HanData(2)^2+HanData(4)^2);
        %-Calculate X Y coordinates of points A B C and D.
        %-Prepare the X Y data points to be easily passed back and plotted.
        pDx = [oA*cos(Theta+atan(HanData(3)/HanData(1))), ...
            (HanData(1)+HanData(3)*5)*cos(Theta),...
            oA*cos(Theta-atan(HanData(3)/HanData(1))),...
            oB*cos(Theta+atan(HanData(4)/HanData(2))+pi),...
            oB*cos(Theta-atan(HanData(4)/HanData(2))+pi)];
        pDy = [oA*sin(Theta+atan(HanData(3)/HanData(1))) (HanData(1)+HanData(3)*5)*sin(Theta) oA*sin(Theta-atan(HanData(3)/HanData(1))) oB*sin(Theta+atan(HanData(4)/HanData(2))+pi) oB*sin(Theta-atan(HanData(4)/HanData(2))+pi)];
    end%end PolyEngine function

    function closeRequestFcn(varargin)
        closereq
    end%end closerequestfcn

end%end of motor

function hplace = makePlaceHolder(setposition,sethand)
hplace = [];
position = setposition;
hand = sethand;
hplace.setNext = @setNext;
hplace.getNext = @getNext;
hplace.setPrev = @setPrev;
hplace.getPrev = @getPrev;
hplace.addListener = @addListener;
hplace.landedon = @landedon;

theNextPlace = [];
thePrevPlace =[];
listeners = [];

    function landedon()

        hand.plotData(position);
        if ~isempty(listeners)
            listeners.turnOn()
        end
        pause(.1);
    end

    function addListener(listener)
        listeners = listener;
    end

    function setNext(nextplace)
        theNextPlace = nextplace;
    end

    function setPrev(prevplace)
        thePrevPlace = prevplace;
    end

    function result = getNext()

        result = theNextPlace;
        if ~isempty(listeners)
            listeners.turnOff()
        end
    end

    function result = getPrev()

        result = thePrevPlace;
        if ~isempty(listeners)
            listeners.turnOff()
        end
    end

end

function hface = makeFace(stepsize,hand,led1,led2)
hface = [];
hface.getFirstStep = @getFirstStep;

startstep = makePlaceHolder(0,hand);
endstep = makePlaceHolder((360-stepsize),hand);
startstep.setPrev(endstep);
startstep.addListener(led1);
endstep.setNext(startstep);
laststep = [];
beforebeforeendstep= [];
for step = (0+stepsize):stepsize:(360-(2*stepsize))
    newplace = makePlaceHolder(step,hand);
    if step==(0+stepsize)
        startstep.setPrev(endstep);
        startstep.setNext(newplace);
        newplace.setPrev(startstep);
    elseif step==(360-(2*stepsize))
        newplace.setNext(endstep);
        newplace.setPrev(laststep);
        endstep.setPrev(newplace);
        beforebeforeendstep.setNext(newplace);
    else
        laststep.setNext(newplace);
        newplace.setPrev(laststep);
    end

    if step==(360-(3*stepsize))
        beforebeforeendstep = newplace;
    end

    if step==270
       newplace.addListener(led2);
    end
    laststep = newplace;
end

    function result = getFirstStep()
        result = startstep;
    end

end

function hled = makeLED(x,y,angleOfLED)
%sensors
plot(x,y,'*k') %270 degrees
plot(x*2,y*2,'O','color','black') %the circle around the light
led = plot(x*2,y*2,'*k','color',[.75 .75 .75]); %gray it out

hled = [];
hled.cursorPosition = @cursorPosition;
hled.led = led;
hled.turnOn = @turnOn;
hled.turnOff = @turnOff;
hled.getState = @getState;
fov = 4;

if (angleOfLED==0)
    angleofled = [360:-.01:(360-fov),0:.01:(0+fov)];
else
    angleofled = (angleOfLED-fov):(angleOfLED+fov);
end

    function cursorPosition(angleOfArm)
        if any(angleOfArm==angleofled)
            turnOn();
        else
            turnOff();
        end
    end

    function result = getState()
        result = [];
        currentcolor = get(hled.led,'Color');
        if sum(currentcolor)==1
           result = 5;
        else
            result = 0;
        end
    end

    function turnOn
        set(hled.led,'Color','r');
    end

    function turnOff
        set(hled.led,'Color',[.75 .75 .75]);
    end
end


function [offset, Nx, Ny] = RegdoBlockMatch(FixedImage, RegisterImage, Nx, Ny)
%
% Copyright 2010, Joseph O. Deasy, on behalf of the CERR development team.
%
% This file is part of The Computational Environment for Radiotherapy Research (CERR).
%
% CERR development has been led by:  Aditya Apte, Divya Khullar, James Alaly, and Joseph O. Deasy.
%
% CERR has been financially supported by the US National Institutes of Health under multiple grants.
%
% CERR is distributed under the terms of the Lesser GNU Public License.
%
%     This version of CERR 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.
%
% CERR 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 CERR.  If not, see <http://www.gnu.org/licenses/>.

global planC
global stateS

indexS = planC{end};

dim = size(FixedImage);

subFixed = cell(Nx, Ny); subMoving = cell(Nx, Ny);
Lx = ceil(dim(2)/Nx); Ly = ceil(dim(1)/Ny);

offset = cell(Nx, Ny); subOrigin = cell(Nx, Ny);
out = cell(Nx, Ny);

for i = 0 : Nx - 1
    for j = 0 : Ny - 1

        x1 = i*Lx + 1; x2 = min( dim(2), (i+1)*Lx );
        y1 = j*Ly + 1; y2 = min( dim(1), (j+1)*Ly );

        subFixed{j+1, i+1} = FixedImage(y1:y2, x1:x2);
        subMoving{j+1, i+1} = RegisterImage(y1:y2, x1:x2);

        %MImg = planC{indexS.scan}(stateS.imageRegistrationBaseDataset).scanArray;
        centerValue = RegisterImage(y1+ceil(abs(y2-y1)/2), x1+ceil(abs(x2-x1)/2));
        [n,x] = hist(double(RegisterImage(:)), 40);
        ind = find(x>=centerValue, 1, 'first');
        if ind>3

            %             switch get(handles.BMMetric,'Value')
            %                 case 1
            %
            %                     [out{j+1, i+1}, rotation, os] = MeanSquare2D(int16(subFixed{j+1, i+1}), [0 0 0], [1 1 1], ...
            %                                                                int16(subMoving{j+1, i+1}), [0 0 0], [1 1 1], ...
            %                                                                0.01, 4, 200);
            %                 case 2
            try
                [out{j+1, i+1}, rotation, os] = NormalizedCorrelation2D(int16(subFixed{j+1, i+1}), [0 0 0], [1 1 1], ...
                    int16(subMoving{j+1, i+1}), [0 0 0], [1 1 1], ...
                    0.01, 4, 200);
            catch
                [out{j+1, i+1}, rotation, os] = MeanSquare2D_64(int16(subFixed{j+1, i+1}), [0 0 0], [1 1 1], ...
                    int16(subMoving{j+1, i+1}), [0 0 0], [1 1 1], ...
                    0.01, 4, 200);
            end
            %             end

            os(2) = -os(2);
            offset{j+1, i+1} = os;
            subOrigin{j+1, i+1} = [(x2+x1)/2 (y2+y1)/2];
        else
            offset{j+1, i+1} = [0 0];
        end
    end

end

end


function gX = lmcKernDiagGradX(kern, X)

% LMCKERNDIAGGRADX Gradient of LMC kernel's diagonal with respect to X.
% FORMAT
% DESC computes the gradient of the diagonal of the LMC kernel matrix with
% respect to the elements of the design matrix given in X.
% RETURN gX : the gradients of the diagonal with respect to each element of
% X. The returned matrix has the same dimensions as X.
% ARG kern : the kernel structure for which gradients are being
% computed.
% ARG X : the input data in the form of a design matrix.
%
% SEEALSO : lmcKernParamInit, kernDiagGradX
%
% COPYRIGHT : Mauricio A. Alvarez, 2010

% KERN

gX = zeros([kern.nout*size(X, 1) size(X,2)]);


classdef SubCellConnecComputer < handle

    properties (GetAccess = public, SetAccess = private)
        connec
    end

    properties (Access = private)
        coord
        nElem
        isEdgeCutInElem
        levelSet
        vertexNodesInElem

        allNodesInElem

        firstCutEdge
        finalNode
        cutNodesInElem

        isSubCellInterior
        triangleSubMesher
    end

    methods (Access = public)

        function obj = SubCellConnecComputer(cParams)
            obj.init(cParams);
            obj.compute();
        end

    end

    methods (Access = private)

        function init(obj,cParams)
            obj.firstCutEdge      = cParams.firstCutEdge;
            obj.vertexNodesInElem = cParams.backgroundConnec;
            obj.nElem             = cParams.nElem;
            obj.isEdgeCutInElem   = cParams.isEdgeCutInElem;
            obj.coord             = cParams.coord;
            obj.levelSet          = cParams.levelSet;
            obj.finalNode         = cParams.finalNode;

        end

        function compute(obj)
            obj.computeCutNodePerElem();
            obj.computeAllNodesInElem();
            obj.computeNodesInAllSubCells();
            obj.computeIsSubCellsInterior();
            obj.computeConnec();
        end

        function computeAllNodesInElem(obj)
            vertexNodes = obj.vertexNodesInElem;
            cutNodes    = obj.cutNodesInElem;
            allNodes    = [vertexNodes,cutNodes];
            obj.allNodesInElem = allNodes;
        end

        function computeNodesInAllSubCells(obj)
            s.nodesInElem     = obj.allNodesInElem;
            s.isEdgeCutInElem = obj.isEdgeCutInElem;
            s.nElem           = obj.nElem;
            s.coord           = obj.coord;
            tComputer = TriangleSubCellNodesComputer(s);
            tComputer.compute();
            obj.triangleSubMesher = tComputer;
        end

        function computeIsSubCellsInterior(obj)
            isTriInt  = obj.computeIsSubCellTriangleInterior();
            nSubCells = obj.triangleSubMesher.nSubCellsByElem;
            itIs = false(nSubCells,obj.nElem);
            itIs(1,isTriInt)  = true;
            itIs(2,~isTriInt) = true;
            itIs(3,~isTriInt) = true;
            obj.isSubCellInterior = itIs;
        end

        function itIs = computeIsSubCellTriangleInterior(obj)
            isoNode = obj.triangleSubMesher.isoNode;
            isoNodeIsFull = obj.levelSet(isoNode) < 0;
            itIs = isoNodeIsFull;
        end

        function computeConnec(obj)
            allConnec  = obj.triangleSubMesher.allSubCellsConnec;
            isInterior = obj.isSubCellInterior(:);
            obj.connec = allConnec(isInterior,:);
        end

        function cutNodePerElemen = computeCutNodePerElem(obj)
            firstCutEdgePerElem = obj.firstCutEdge;
            cutNodePerElemen = firstCutEdgePerElem + obj.finalNode;
            obj.cutNodesInElem = cutNodePerElemen;
        end

    end

end


function [t,u]=fpreadh(fname)
% FPREADH reads the unformatted data exported by FlexPDE HISTORY plot command.
%
% Description:
% This utility function reads the unformatted
% data EXPORTED by FlexPDE using the HISTORY
% plot command. This code has been tested under
% Matlb R13, R14, and FlexPDE 4.x.
%
% Syntax:
%       [sv,u]=fpreadh(fname)
%              fpreadh(fname)
%
% Input arguments:
% fname: a string contains the data file name (and its path).
%
% Output arguments:
% t: vector of time data points.
% u: matrix of values of u at exported grid points
%
% If fpreadh is called without output arguments it simply
% will draw data in a 2D plot.
%
% Limitaions:
%	1. Only one variable can be exported to each file.
%   2. Supported FlexPDE output files limited to unformatted data file.
%
%
% See also FPREADE, FPREADSC

%{

___________________________________________________
File Properties:
Name:		fpreadh.m
Subject:	Reading History plot data from FlexPDE
Category:	FPMat: FlexPDE Interface
Author:		Mohammad Rahmani
Created:	Dec. 3, 2004
Version: 	1.0
Comments:	See FPMat Interface.pdf for more information
______________________________________________________

Mohammad Rahmani
Chemical Eng. Dep.
Amir-Kabir Uni. of Technology
Tehran, IRAN
Mohammad.Rahmani@Gmail.com

%}

%% 1 - Check for input/output arguments
if nargin<1
    error('Not enough input arguments')
end
if nargout==0
    outputfcn='Plot';
elseif nargout~=2
    error('Wrong number of output arguments')
else
    outputfcn='SendOut';
end

%% 2- Open the Data file and catch the error
[fid,errmsg] = fopen(fname,'r');
if fid == -1 % An error has been occured
    error([fname, '!??  ',errmsg])
end;

%% 3- Bypass the header of data file
% This header normally is a FlexPDE header enclosed in {}
while (feof(fid) ~= 1)	% ignore lines till }
  tline = fgetl(fid);
  if (tline == '}')
    break
    end
end

%% 4- Read the rest of data  and close the file
svar=textscan(fid,'%f','headerLines',2);
nc=0;
while (feof(fid) ~=1)
    nc=nc+1;
    data(nc)=textscan(fid,'%f','headerLines',1);
end
fclose(fid);

%% 5- Convert cell data to double data matrix
svar=svar{1};
data=cell2mat(data);

%% 6 - Prepare output arguments
switch outputfcn
    case 'Plot'
        figure('Name',['History Plot for: ',fname]);
        plot(svar,data);
        return
    case 'SendOut'
        t=svar;
        u=data;
        return
end


% Test file for chebtech/minus.m

function pass = test_minus(pref)

% Get preferences.
if ( nargin < 1 )
    pref = chebtech.techPref();
end

% Generate a few random points to use as test values.
seedRNG(6178);
x = 2 * rand(100, 1) - 1;

% A random number to use as an arbitrary additive constant.
alpha = randn() + 1i*randn();

for n = 1:2
    if ( n == 1 )
        testclass = chebtech1();
    else
        testclass = chebtech2();
    end

    %%
    % Check operation in the face of empty arguments.

    f = testclass.make();
    g = testclass.make(@(x) x, [], pref);
    pass(n, 1) = (isempty(f - f) && isempty(f - g) && isempty(g - f));

    %%
    % Check subtraction with scalars.

    f_op = @(x) sin(x);
    f = testclass.make(f_op, [], pref);
    pass(n, 2:3) = test_sub_function_and_scalar(f, f_op, alpha, x);

    %%
    % Check subtraction of two chebtech objects.

    f_op = @(x) zeros(size(x));
    f = testclass.make(f_op, [], pref);
    pass(n, 4:5) = test_sub_function_and_function(f, f_op, f, f_op, x);

    f_op = @(x) exp(x) - 1;
    f = testclass.make(f_op, [], pref);

    g_op = @(x) 1./(1 + x.^2);
    g = testclass.make(g_op, [], pref);
    pass(n, 6:7) = test_sub_function_and_function(f, f_op, g, g_op, x);

    g_op = @(x) cos(1e4*x);
    g = testclass.make(g_op, [], pref);
    pass(n, 8:9) = test_sub_function_and_function(f, f_op, g, g_op, x);

    g_op = @(t) sinh(t*exp(2*pi*1i/6));
    g = testclass.make(g_op, [], pref);
    pass(n, 10:11) = test_sub_function_and_function(f, f_op, g, g_op, x);

    %%
    % Check operation for array-valued chebtech objects.

    f_op = @(x) [zeros(size(x)) zeros(size(x)) zeros(size(x))];
    f = testclass.make(f_op, [], pref);
    pass(n, 12:13) = test_sub_function_and_function(f, f_op, f, f_op, x);

    f_op = @(x) [sin(x) cos(x) exp(x)];
    f = testclass.make(f_op, [], pref);
    pass(n, 14:15) = test_sub_function_and_scalar(f, f_op, alpha, x);

    g_op = @(x) [cosh(x) airy(1i*x) sinh(x)];
    g = testclass.make(g_op, [], pref);
    pass(n, 16:17) = test_sub_function_and_function(f, f_op, g, g_op, x);

    % This should fail with a dimension mismatch error.
    % (Actually, we can do this in R2016a anda above!)
    g_op = @(x) sin(x);
    g = testclass.make(g_op, [], pref);
    try
        h = f - g; %#ok<NASGU>
        pass(n, 18) = false;
        if ( verLessThan('matlab', '9.1') )
            pass(n, 18) = false;
        else
            pass(n, 18) = true;
        end
    catch ME
        if ( verLessThan('matlab', '9.1') )
            pass(n, 18) = strcmp(ME.identifier, 'MATLAB:dimagree');
        else
            pass(n, 18) = false;
        end
    end

    %%
    % Check that direct construction and MINUS give comparable results.

    tol = 10*eps;
    f = testclass.make(@(x) x, [], pref);
    g = testclass.make(@(x) cos(x) - 1, [], pref);
    h1 = f - g;
    h2 = testclass.make(@(x) x - (cos(x) - 1), [], pref);
    h3 = h1 - h2;
    pass(n, 19) = norm(h3.coeffs, inf) < tol;

    %%
    % Check that subtracting a CHEBTECH and an unhappy CHEBTECH gives an
    % unhappy result.

    f = testclass.make(@(x) cos(x+1));    % Happy
    g = testclass.make(@(x) sqrt(x+1));   % Unhappy
    h = f - g;  % Subtract unhappy from happy.
    pass(n, 20) = (~g.ishappy) && (~h.ishappy);
    h = g - f;  % Subtract happy from unhappy.
    pass(n, 21) = (~g.ishappy) && (~h.ishappy);
end

end

% Test the subtraction of a CHEBTECH F, specified by F_OP, to and from a scalar
% ALPHA using a grid of points X in [-1  1] for testing samples.
function result = test_sub_function_and_scalar(f, f_op, alpha, x)
    g1 = f - alpha;
    g2 = alpha - f;
    result(1) = isequal(g1, -g2);
    g_exact = @(x) f_op(x) - alpha;
    result(2) = norm(feval(g1, x) - g_exact(x), inf) <= ...
        10*max(vscale(g1)*eps);
end

% Test the subraction of two CHEBTECH objects F and G, specified by F_OP and
% G_OP, using a grid of points X in [-1  1] for testing samples.
function result = test_sub_function_and_function(f, f_op, g, g_op, x)
    h1 = f - g;
    h2 = g - f;
    result(1) = isequal(h1, -h2);
    h_exact = @(x) f_op(x) - g_op(x);
    norm(feval(h1, x) - h_exact(x), inf);
    result(2) = norm(feval(h1, x) - h_exact(x), inf) <= ...
        1e4*max(vscale(h1)*eps);

end