function [tf,te,to,sc,I,up] = tsurf_cad(F,V,varargin)
  % [tf,te,to,sc,I,up] = tsurf_cad(F,V,varargin)
  %
  % TSURF_CAD Display a triangle mesh in a CAD-like rendering style: edges,
  % shadow, floor.
  %
  % Inputs:
  %   F  #F by 3 list of face indices
  %   V  #V by 3 list of vertex positions
  % Outputs:
  %   tf  plot handle to mesh
  %   te  plot handle to sharp edges
  %   to  plot handle to boundary edges
  %   sc  plot handle to shadow
  %   I  indices into V to cut mesh along sharp edges
  %   up  callback function to reset lines based on rotation
  %

  function set_hold(v)
    if v
      hold on;
    else
      hold off;
    end
  end
  old_hold = ishold;
  %V = V*axisangle2matrix([1 0 0],-pi/2);
  N = normals(V,F);
  BC = barycenter(V,F);

  E = sharp_edges(V,F);
  E = union(sort(E,2),sort(outline(F),2),'rows');

  % cut mesh at sharp edges to get crisp normals
  [G,I] = cut_edges(F,E);
  W = V(I,:);

  if ~old_hold
    clf;
  end
  hold on;
  tf = tsurf(G,W, ...
    'FaceVertexCData',repmat(blue,size(W,1),1), ...
    'SpecularStrength',0, ...
    'DiffuseStrength',0.1, ...
    'AmbientStrength',1.0, ...
    'EdgeColor','none','FaceAlpha',0.9,fphong, ...
    varargin{:});
  te = tsurf(E(:,[1 2 2]),V,'EdgeColor',blue*0.75);
  to = tsurf([1 1 1],V,'LineWidth',2,'EdgeColor',blue*0.5);
  view(-9,34);
  axis equal;
  l = light('Position',[3 -4 5.0],'Style','infinite');
  [h,~,M,g] = add_shadow(tf,l,'Ground',[0 0 -1 min(V(:,3))-2e-3],'Fade','local','Color',[0.8 0.8 0.8]);
  % faint amient occlusion
  AO = ambient_occlusion(W,G,W,per_vertex_normals(W,G),1000);
  AO = AO*0.27;
  tf.FaceVertexCData = bsxfun(@times,tf.FaceVertexCData,1-AO);
  %set_hold(old_hold);

  % Hack so that axis doesn't change if V contains unreferenced points and
  % doesn't change.
  [BB,BF] = bounding_box(V);
  hold on;
  bf = trisurf(BF,BB(:,1),BB(:,2),BB(:,3),'CData',nan*BB(:,1),'EdgeColor','none');
  set_hold(old_hold);

  % floor board
  SV = [V ones(size(V,1),1)]*M';
  SV = bsxfun(@rdivide,SV(:,1:3),SV(:,4));

  [BB,BF] = bounding_box(SV(:,1:2));
  M = mean(bounding_box(V(:,1:2)));
  BBmM = bsxfun(@minus,BB,M);
  BB = bsxfun(@plus,sign(BBmM)*max(abs(BBmM(:))),M);
  BB = bsxfun(@plus,bsxfun(@minus,BB,mean(BB))*1.1,mean(BB));
  BB(:,3) = min(V(:,3))-4e-3;
  BB = reshape(permute(BB,[1 3 2]),[2 2 3]);
  % checkboard texture
  nx = 16;
  extent = @(BB) max(max(BB)) - min(min(BB));
  ny = ceil(extent(BB(:,:,2))/ extent(BB(:,:,1))*nx/2)*2;
  ny(isinf(ny)) = nx;
  ch = repmat(0.99-0.15*xor((mod(repmat(0:8*nx-1,8*ny,1),8*2)>7), ...
    (mod(repmat((0:8*ny-1)',1,8*nx),8*2)>7)),[1 1 3])*0.5 + 0.5;
  hold on;
  sc = surf(BB(:,:,1),BB(:,:,2),BB(:,:,3), ...
    'CData',ch,'FaceColor','texturemap', ...
    'SpecularStrength',0, 'DiffuseStrength',0, 'AmbientStrength',1);
  set_hold(old_hold);

  axis vis3d;
  camproj('persp');
  set(gca,'Visible','off');
  T = get(gca,'tightinset');
  set(gca,'position',[T(1) T(2) 1-T(1)-T(3) 1-T(2)-T(4)]);

  % Set up rotatation callbacks to hide view-dependent effects during drag
  up = @() ...
    set(to,'Faces', ...
      outline(F((sum(N.*bsxfun(@minus,BC,campos),2)<=0),:))*[1 0 0;0 1 1]) | ...
    cell2mat(cellfun(@(h) set(h,'FaceAlpha',0.5*(g*[campos 1]'<0)),h,'UniformOutput',false)) | ...
    set(sc,'FaceAlpha',1.0*(g*[campos 1]'<0));
  up();
  down = @() ...
    (exist('to','var') && ishandle(to) && isempty(set(to,'Faces',[]))) || ...
    isempty(set(rotate3d,'ActionPostCallback',[],'ActionPreCallback',[]));
  set(rotate3d,'ActionPostCallback',@(src,obj) up());
  set(rotate3d,'ActionPreCallback',@(src,obj) down());

%   for t = linspace(0,-360,60)
%    view(64+t,20);
%    up();
%    drawnow;
%    filename = 'tsurf-cad.gif';
%    figgif(filename,'nodither');
%   end
end


function k = rbfperiodicKernDiagCompute(kern, x)

% RBFPERIODICKERNDIAGCOMPUTE Compute diagonal of RBFPERIODIC kernel.
% FORMAT
% DESC computes the diagonal of the kernel matrix for the RBF derived periodic kernel given a design matrix of inputs.
% ARG kern : the kernel structure for which the matrix is computed.
% ARG x : input data matrix in the form of a design matrix.
% RETURN k : a vector containing the diagonal of the kernel matrix
% computed at the given points.
%
% SEEALSO : rbfperiodicKernParamInit, kernDiagCompute, kernCreate, rbfperiodicKernCompute
%
% COPYRIGHT : Neil D. Lawrence, 2007

% KERN

k = repmat(kern.variance, size(x, 1), 1);


function [matched,only1,only2] = Match(list1,list2,varargin)

%Match - Replace values in one list with closest values in a second list.
%
%  USAGE
%
%    [matched,only1,only2] = Match(list1,list2,<options>)
%
%    list1          first list
%    list2          second list
%    <options>      optional list of property-value pairs (see table below)
%
%    =========================================================================
%     Properties    Values
%    -------------------------------------------------------------------------
%     'match'       optional matching criterion: 'up', 'down' (default) or
%                   'closest' (see below)
%     'error'       matched values for which the difference exceeds this
%                   threshold are set to NaN (default = inf)
%    =========================================================================
%
%  OUTPUT
%
%    matched        values in list2 that matched those of list1
%    only1          elements in list1 that were not matched by any element in list2
%    only2          elements in list2 that did not match any element in list1
%
%    only1 and only2 are lists of logical indices.
%
%  NOTE
%
%    Pairs are matched in the following way. Assume the lists are sorted in
%    ascending order and let list1 = [a(1)...a(n)] and list2 = [b(1)...b(m)].
%    We are trying to match each a(i) with one of the b(1..m). Suppose
%
%       b(1) <= ... <= b(j) <= a(i) <= b(j+1) <= ... <= b(m)
%
%    Then a(i) will be matched with either b(j) or b(j+1), depending on the
%    'match' option:
%
%       1) 'down' (default) => b(j)
%       2) 'up' => b(j+1)
%       3) 'closest' => the one that minimizes the distance
%          (b(j) if the distances are equal)
%
%    If however the distance between a(i) and b(j) (or b(j+1)) is too large
%    (see option 'error'), then none of the b(1..m) matches a(i).
%
%    Note that this function is generally asymmetrical.

% Copyright (C) 2004-2012 by Michaël Zugaro
%
% This program is free software; you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation; either version 3 of the License, or
% (at your option) any later version.

% Default values
match = 'down';
err = inf;

% Check parameters
if nargin < 2 | mod(length(varargin),2) ~= 0,
  error('Incorrect number of parameters (type ''help Match'' for details).');
end
if ~isdvector(list1) | ~isdvector(list2),
	error('Incorrect sizes: ''list1'' and ''list2'' must be vectors (type ''help Match'' for details).');
end

% Parse parameter list
for i = 1:2:length(varargin),
  if ~isa(varargin{i},'char'),
    error(['Parameter ' num2str(i+2) ' is not a property (type ''help Match'' for details).']);
  end
  switch(lower(varargin{i})),

    case 'match',
		match = lower(varargin{i+1});
		if ~isstring_FMAT(match,'up','down','closest'),
			error('Incorrect value for property ''match'' (type ''help Match'' for details).');
      end

	case 'error',
		err = varargin{i+1};
		if ~isdscalar(err,'>=0'),
			error('Incorrect value for property ''error'' (type ''help Match'' for details).');
		end

    otherwise,
      error(['Unknown property ''' num2str(varargin{i}) ''' (type ''help Match'' for details).']);

  end
end

% Sort elements in both list so that list1 = [a1 a2 a3 ... an] and list2 = [b1 b2 b3 ... bm]
[list1,idx1] = sort(list1(:));
[list2,idx2] = sort(list2(:));

% Find 'up' and 'down' matching indices
% (We need both, because this will allow us to compare matches if the criterion is 'closest')
n = length(list2);
up = MatchUpIndices(list1,list2);
down = up-1;

% List corresponding values in list2
% Special cases: when matching down (resp. up), values in list1 lesser (resp. greater)
% than all values in list2 cannot be matched; set them to NaN
goodUp = up>0 & up<=n;
matchedUp = nan(size(up));
matchedUp(goodUp) = list2(up(goodUp));
goodDown = down>0 & down<=n;
matchedDown = nan(size(down));
matchedDown(goodDown) = list2(down(goodDown));

% Use match criterion
switch(match),
	case 'up',
		matched = matchedUp;
	case 'down',
		matched = matchedDown;
	case 'closest',
		[unused,idx] = min(abs([matchedUp matchedDown]-[list1 list1]),[],2);
		matched = matchedUp;
		matched(idx==2) = matchedDown(idx==2);
end

% Check error threshold
bad = abs(matched-list1) > err+eps; % eps is to compensate for inevitable numerical rounding errors such as 1.1-1.0>0.1
matched(bad) = nan;

% Output
only1 = isnan(matched);
matched = matched(~only1);
only1(idx1) = only1;
only2 = logical(zeros(size(list2)));
m = unique(matched);
[unused,i] = setdiff(list2,m);
only2(i) = 1;
only2(idx2) = only2;


function params = spm_normalise(VG,VF,matname,VWG,VWF,flags)
% Spatial (stereotactic) normalization
%
% FORMAT params = spm_normalise(VG,VF,matname,VWG,VWF,flags)
% VG        - template handle(s)
% VF        - handle of image to estimate params from
% matname   - name of file to store deformation definitions
% VWG       - template weighting image
% VWF       - source weighting image
% flags     - flags.  If any field is not passed, then defaults are assumed.
%             smosrc - smoothing of source image (FWHM of Gaussian in mm).
%                      Defaults to 8.
%             smoref - smoothing of template image (defaults to 0).
%             regtype - regularisation type for affine registration
%                       See spm_affreg.m (default = 'mni').
%             cutoff  - Cutoff of the DCT bases.  Lower values mean more
%                       basis functions are used (default = 30mm).
%             nits    - number of nonlinear iterations (default=16).
%             reg     - amount of regularisation (default=0.1)
% _________________________________________________________________________
%
% This module spatially (stereotactically) normalizes MRI, PET or SPECT
% images into a standard space defined by some ideal model or template
% image[s].  The template images supplied with SPM conform to the space
% defined by the ICBM, NIH P-20 project, and approximate that of the
% the space described in the atlas of Talairach and Tournoux (1988).
% The transformation can also be applied to any other image that has
% been coregistered with these scans.
%
%
% Mechanism
% Generally, the algorithms work by minimising the sum of squares
% difference between the image which is to be normalised, and a linear
% combination of one or more template images.  For the least squares
% registration to produce an unbiased estimate of the spatial
% transformation, the image contrast in the templates (or linear
% combination of templates) should be similar to that of the image from
% which the spatial normalization is derived.  The registration simply
% searches for an optimum solution.  If the starting estimates are not
% good, then the optimum it finds may not find the global optimum.
%
% The first step of the normalization is to determine the optimum
% 12-parameter affine transformation.  Initially, the registration is
% performed by matching the whole of the head (including the scalp) to
% the template.  Following this, the registration proceeded by only
% matching the brains together, by appropriate weighting of the template
% voxels.  This is a completely automated procedure (that does not
% require ``scalp editing'') that discounts the confounding effects of
% skull and scalp differences.   A Bayesian framework is used, such that
% the registration searches for the solution that maximizes the a
% posteriori probability of it being correct.  i.e., it maximizes the
% product of the likelihood function (derived from the residual squared
% difference) and the prior function (which is based on the probability
% of obtaining a particular set of zooms and shears).
%
% The affine registration is followed by estimating nonlinear deformations,
% whereby the deformations are defined by a linear combination of three
% dimensional discrete cosine transform (DCT) basis functions.
% The parameters represent coefficients of the deformations in
% three orthogonal directions.  The matching involved simultaneously
% minimizing the bending energies of the deformation fields and the
% residual squared difference between the images and template(s).
%
% An option is provided for allowing weighting images (consisting of pixel
% values between the range of zero to one) to be used for registering
% abnormal or lesioned brains.  These images should match the dimensions
% of the image from which the parameters are estimated, and should contain
% zeros corresponding to regions of abnormal tissue.
%
%
% Uses
% Primarily for stereotactic normalization to facilitate inter-subject
% averaging and precise characterization of functional anatomy.  It is
% not necessary to spatially normalise the data (this is only a
% pre-requisite  for  intersubject averaging or reporting in the
% Talairach space).
%
% Inputs
% The first input is the image which is to be normalised. This image
% should be of the same modality (and MRI sequence etc) as the template
% which is specified. The same spatial transformation can then be
% applied to any other images of the same subject.  These files should
% conform to the SPM data format (See 'Data Format'). Many subjects can
% be entered at once, and there is no restriction on image dimensions
% or voxel size.
%
% Providing that the images have a correct ".mat" file associated with
% them, which describes the spatial relationship between them, it is
% possible to spatially normalise the images without having first
% resliced them all into the same space. The ".mat" files are generated
% by "spm_realign" or "spm_coregister".
%
% Default values of parameters pertaining to the extent and sampling of
% the standard space can be changed, including the model or template
% image[s].
%
%
% Outputs
% All normalized *.img scans are written to the same subdirectory as
% the original *.img, prefixed with a 'n' (i.e. n*.img).  The details
% of the transformations are displayed in the results window, and the
% parameters are saved in the "*_sn.mat" file.
%
%__________________________________________________________________________
%
% References:
% K.J. Friston, J. Ashburner, C.D. Frith, J.-B. Poline, J.D. Heather,
% and R.S.J. Frackowiak
% Spatial Registration and Normalization of Images.
% Human Brain Mapping 2:165-189, 1995.
%
% J. Ashburner, P. Neelin, D.L. Collins, A.C. Evans and K.J. Friston
% Incorporating Prior Knowledge into Image Registration.
% NeuroImage 6:344-352, 1997.
%
% J. Ashburner and K.J. Friston
% Nonlinear spatial normalization using basis functions.
% Human Brain Mapping, 7(4):254-266, 1999.
%__________________________________________________________________________
% Copyright (C) 2002-2013 Wellcome Trust Centre for Neuroimaging

% John Ashburner
% $Id: spm_normalise.m 5745 2013-11-14 12:46:49Z guillaume $

if nargin<2, error('Incorrect usage.'); end;
if ischar(VF), VF = spm_vol(VF); end;
if ischar(VG), VG = spm_vol(VG); end;
if nargin<3,
    if nargout==0,
        [pth,nm]  = spm_fileparts(deblank(VF(1).fname));
        matname   = fullfile(pth,[nm '_sn.mat']);
    else
        matname   = '';
    end;
end;
if nargin<4, VWG  = ''; end;
if nargin<5, VWF  = ''; end;
if ischar(VWG), VWG=spm_vol(VWG); end;
if ischar(VWF), VWF=spm_vol(VWF); end;

def_flags          = spm_get_defaults('old.normalise.estimate');
def_flags.graphics = 1;
if nargin < 6,
    flags = def_flags;
else
    fnms  = fieldnames(def_flags);
    for i=1:length(fnms),
        if ~isfield(flags,fnms{i}),
            flags.(fnms{i}) = def_flags.(fnms{i});
        end;
    end;
end;

fprintf('Smoothing by %g & %gmm..\n', flags.smoref, flags.smosrc);
VF1 = spm_smoothto8bit(VF,flags.smosrc);

% Rescale images so that globals are better conditioned
VF1.pinfo(1:2,:) = VF1.pinfo(1:2,:)/spm_global(VF1);
for i=1:numel(VG),
    VG1(i) = spm_smoothto8bit(VG(i),flags.smoref);
    VG1(i).pinfo(1:2,:) = VG1(i).pinfo(1:2,:)/spm_global(VG(i));
end;

% Affine Normalisation
%--------------------------------------------------------------------------
fprintf('Coarse Affine Registration..\n');
aflags    = struct('sep',max(flags.smoref,flags.smosrc), 'regtype',flags.regtype,...
    'WG',[],'WF',[],'globnorm',0);
aflags.sep = max(aflags.sep,max(sqrt(sum(VG(1).mat(1:3,1:3).^2))));
aflags.sep = max(aflags.sep,max(sqrt(sum(VF(1).mat(1:3,1:3).^2))));

M         = eye(4); %spm_matrix(prms');
spm_plot_convergence('Init','Affine Registration','Mean squared difference','Iteration');
[M,scal]  = spm_affreg(VG1, VF1, aflags, M);

fprintf('Fine Affine Registration..\n');
aflags.WG  = VWG;
aflags.WF  = VWF;
aflags.sep = aflags.sep/2;
[M,scal]   = spm_affreg(VG1, VF1, aflags, M,scal);
Affine     = inv(VG(1).mat\M*VF1(1).mat);
spm_plot_convergence('Clear');

% Basis function Normalisation
%--------------------------------------------------------------------------
fov = VF1(1).dim(1:3).*sqrt(sum(VF1(1).mat(1:3,1:3).^2));
if any(fov<15*flags.smosrc/2 & VF1(1).dim(1:3)<15),
    fprintf('Field of view too small for nonlinear registration\n');
    Tr = [];
elseif isfinite(flags.cutoff) && flags.nits && ~isinf(flags.reg),
        fprintf('3D CT Norm...\n');
    Tr = snbasis(VG1,VF1,VWG,VWF,Affine,...
        max(flags.smoref,flags.smosrc),flags.cutoff,flags.nits,flags.reg);
else
    Tr = [];
end;
clear VF1 VG1

flags.version = '$Rev: 5745 $';
flags.date    = date;

params = struct('Affine',Affine, 'Tr',Tr, 'VF',VF, 'VG',VG, 'flags',flags);

if flags.graphics, spm_normalise_disp(params,VF); end;

% Remove dat fields before saving
%--------------------------------------------------------------------------
if isfield(VF,'dat'), VF = rmfield(VF,'dat'); end;
if isfield(VG,'dat'), VG = rmfield(VG,'dat'); end;
if ~isempty(matname),
    fprintf('Saving Parameters..\n');
    save(matname,'Affine','Tr','VF','VG','flags', spm_get_defaults('mat.format'));
end;
return;

%==========================================================================
% function Tr = snbasis(VG,VF,VWG,VWF,Affine,fwhm,cutoff,nits,reg)
%==========================================================================
function Tr = snbasis(VG,VF,VWG,VWF,Affine,fwhm,cutoff,nits,reg)
% 3D Basis Function Normalization
% FORMAT Tr = snbasis(VG,VF,VWG,VWF,Affine,fwhm,cutoff,nits,reg)
% VG        - Template volumes (see spm_vol).
% VF        - Volume to normalize.
% VWG       - weighting Volume - for template.
% VWF       - weighting Volume - for object.
% Affine    - A 4x4 transformation (in voxel space).
% fwhm      - smoothness of images.
% cutoff    - frequency cutoff of basis functions.
% nits      - number of iterations.
% reg       - regularisation.
% Tr - Discrete cosine transform of the warps in X, Y & Z.
%
% snbasis performs a spatial normalization based upon a 3D
% discrete cosine transform.
%__________________________________________________________________________

fwhm    = [fwhm 30];

% Number of basis functions for x, y & z
%--------------------------------------------------------------------------
tmp  = sqrt(sum(VG(1).mat(1:3,1:3).^2));
k    = max(round((VG(1).dim(1:3).*tmp)/cutoff),[1 1 1]);

% Scaling is to improve stability.
%--------------------------------------------------------------------------
stabilise = 8;
basX = spm_dctmtx(VG(1).dim(1),k(1))*stabilise;
basY = spm_dctmtx(VG(1).dim(2),k(2))*stabilise;
basZ = spm_dctmtx(VG(1).dim(3),k(3))*stabilise;

dbasX = spm_dctmtx(VG(1).dim(1),k(1),'diff')*stabilise;
dbasY = spm_dctmtx(VG(1).dim(2),k(2),'diff')*stabilise;
dbasZ = spm_dctmtx(VG(1).dim(3),k(3),'diff')*stabilise;

vx1 = sqrt(sum(VG(1).mat(1:3,1:3).^2));
vx2 = vx1;
kx = (pi*((1:k(1))'-1)/VG(1).dim(1)/vx1(1)).^2; ox=ones(k(1),1);
ky = (pi*((1:k(2))'-1)/VG(1).dim(2)/vx1(2)).^2; oy=ones(k(2),1);
kz = (pi*((1:k(3))'-1)/VG(1).dim(3)/vx1(3)).^2; oz=ones(k(3),1);

if 1,
    % BENDING ENERGY REGULARIZATION
    % Estimate a suitable sparse diagonal inverse covariance matrix for
    % the parameters (IC0).
    %----------------------------------------------------------------------
    IC0 = (1*kron(kz.^2,kron(ky.^0,kx.^0)) +...
        1*kron(kz.^0,kron(ky.^2,kx.^0)) +...
        1*kron(kz.^0,kron(ky.^0,kx.^2)) +...
        2*kron(kz.^1,kron(ky.^1,kx.^0)) +...
        2*kron(kz.^1,kron(ky.^0,kx.^1)) +...
        2*kron(kz.^0,kron(ky.^1,kx.^1)) );
    IC0 = reg*IC0*stabilise^6;
    IC0 = [IC0*vx2(1)^4 ; IC0*vx2(2)^4 ; IC0*vx2(3)^4 ; zeros(prod(size(VG))*4,1)];
    IC0 = sparse(1:length(IC0),1:length(IC0),IC0,length(IC0),length(IC0));
else
    % MEMBRANE ENERGY (LAPLACIAN) REGULARIZATION
    %----------------------------------------------------------------------
    IC0 = kron(kron(oz,oy),kx) + kron(kron(oz,ky),ox) + kron(kron(kz,oy),ox);
    IC0 = reg*IC0*stabilise^6;
    IC0 = [IC0*vx2(1)^2 ; IC0*vx2(2)^2 ; IC0*vx2(3)^2 ; zeros(prod(size(VG))*4,1)];
    IC0 = sparse(1:length(IC0),1:length(IC0),IC0,length(IC0),length(IC0));
end;

% Generate starting estimates.
%--------------------------------------------------------------------------
s1 = 3*prod(k);
s2 = s1 + numel(VG)*4;
T  = zeros(s2,1);
T(s1+(1:4:numel(VG)*4)) = 1;

pVar = Inf;
for iter=1:nits,
    fprintf(' iteration %2d: ', iter);
    [Alpha,Beta,Var,fw] = spm_brainwarp(VG,VF,Affine,basX,basY,basZ,dbasX,dbasY,dbasZ,T,fwhm,VWG, VWF);
    if Var>pVar, scal = pVar/Var ; Var = pVar; else scal = 1; end;
    pVar = Var;
    T = (Alpha + IC0*scal)\(Alpha*T + Beta);
    fwhm(2) = min([fw fwhm(2)]);
    fprintf(' FWHM = %6.4g Var = %g\n', fw,Var);
end;

% Values of the 3D-DCT - for some bizarre reason, this needs to be done
% as two separate statements in Matlab 6.5...
%--------------------------------------------------------------------------
Tr = reshape(T(1:s1),[k 3]);
drawnow;
Tr = Tr*stabilise.^3;
return;


function prediction = VGG8(varargin)
%VGG8 Returns a VGG-S/M/F (VGG-8) model for ImageNet
%   M = models.VGG8() returns the model VGG-F proposed in:
%
%     Chatfield et al., "Return of the Devil in the Details: Delving Deep
%     into Convolutional Networks", BMVC 2014.
%
%   models.VGG8(..., 'option', value, ...) accepts the following options:
%
%   `variant`:: 'f'
%     Model variant: s, m or f (slow/medium/fast).
%
%   `pretrained`:: false
%     If true, returns a model pre-trained on ImageNet (using the
%     MatConvNet example code).
%
%   `input`:: default input
%     Specifies an input (images) layer for the network. If unspecified, a
%     new one is created.
%
%   `numClasses`:: 1000
%     Number of output classes.
%
%   `batchNorm`:: true
%     Whether to use batch normalization.
%
%   `normalization`:: [5 1 0.0001/5 0.75]
%     Parameters for vl_nnnormalize layer (only used without batch-norm).
%
%   Any other options will be passed to models.ConvBlock(), and can be used
%   to change the activation function, weight initialization, etc.
%
%   Suggested SGD training options are also returned in the struct M.meta.

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

  % parse options. unknown arguments will be passed to ConvBlock (e.g.
  % activation).
  opts.pretrained = false ;  % whether to fetch a pre-trained model
  opts.input = Input('name', 'images', 'gpu', true) ;  % default input layer
  opts.numClasses = 1000 ;  % number of predicted classes
  opts.variant = 'f' ;  % choose between variants s/m/f (slow/medium/fast)
  opts.batchNorm = true ;  % whether to use batch normalization
  opts.normalization = [5 1 0.0001/5 0.75] ;  % for LRN layer (vl_nnnormalize)
  [opts, convBlockArgs] = vl_argparse(opts, varargin, 'nonrecursive') ;

  % default training options for this network (returned as output.meta)
  switch lower(opts.variant)
  case 's'
    meta.batchSize = 128 ;
  case 'm'
    meta.batchSize = 196 ;
  case 'f'
    meta.batchSize = 256 ;
  otherwise
    error('Unknown variant.') ;
  end
  meta.imageSize = [224, 224, 3] ;
  meta.augmentation.crop = 224 / 256;
  meta.augmentation.location = true ;
  meta.augmentation.flip = true ;
  meta.augmentation.brightness = 0.1 ;
  meta.augmentation.aspect = [2/3, 3/2] ;
  meta.weightDecay = 0.0005 ;

  % the default learning rate schedule
  if ~opts.pretrained
    if ~opts.batchNorm
      meta.learningRate = logspace(-2, -4, 60) ;
    else
      meta.learningRate = logspace(-1, -4, 20) ;
    end
    meta.numEpochs = numel(meta.learningRate) ;
  else  % fine-tuning has lower LR
    meta.learningRate = 1e-5 ;
    meta.numEpochs = 20 ;
  end

  % return a pre-trained model
  if opts.pretrained
    if opts.batchNorm
      warning('The pre-trained model does not include batch-norm (set batchNorm to false).') ;
    end
    if opts.numClasses ~= 1000
      warning('Model options are ignored when loading a pre-trained model.') ;
    end
    prediction = models.pretrained(['imagenet-vgg-' opts.variant]) ;

    % return prediction layer (not softmax)
    assert(isequal(prediction{1}.func, @vl_nnsoftmax)) ;
    prediction = prediction{1}.inputs{1} ;

    % replace input layer with the given one
    input = prediction.find('Input', 1) ;
    prediction.replace(input, opts.input) ;

    prediction.meta = meta ;
    return
  end

  % get conv block generator with the given options. default activation is
  % ReLU, with pre-activation batch normalization (can be overriden).
  conv = models.ConvBlock('batchNorm', opts.batchNorm, ...
    'preActivationBatchNorm', true, convBlockArgs{:}) ;

  % build network
  images = opts.input ;

  % implement the 3 variants: S, M and F (from paper)
  switch lower(opts.variant)
  case 's'
    % first conv block
    x = conv(images, 'size', [7, 7, 3, 96], 'stride', 2) ;
    if ~opts.batchNorm
      x = vl_nnnormalize(x, opts.normalization) ;
    end
    x = vl_nnpool(x, 3, 'stride', 3, 'pad', [0 2 0 2]) ;

    % second conv block
    x = conv(x, 'size', [5, 5, 96, 256]) ;
    if ~opts.batchNorm
      x = vl_nnnormalize(x, opts.normalization) ;
    end
    x = vl_nnpool(x, 2, 'stride', 2, 'pad', [0 1 0 1]) ;

    % conv blocks 3-5
    x = conv(x, 'size', [3, 3, 256, 512], 'pad', 1) ;
    x = conv(x, 'size', [3, 3, 512, 512], 'pad', 1) ;
    x = conv(x, 'size', [3, 3, 512, 512], 'pad', 1) ;
    x = vl_nnpool(x, 3, 'stride', 3, 'pad', [0 1 0 1]) ;

    % first fully-connected block
    x = conv(x, 'size', [6, 6, 512, 4096]) ;

  case 'm'
    % first conv block
    x = conv(images, 'size', [7, 7, 3, 96], 'stride', 2) ;
    if ~opts.batchNorm
      x = vl_nnnormalize(x, opts.normalization) ;
    end
    x = vl_nnpool(x, 3, 'stride', 2) ;

    % second conv block
    x = conv(x, 'size', [5, 5, 96, 256], 'stride', 2, 'pad', 1) ;
    if ~opts.batchNorm
      x = vl_nnnormalize(x, opts.normalization) ;
    end
    x = vl_nnpool(x, 3, 'stride', 2, 'pad', [0 1 0 1]) ;

    % conv blocks 3-5
    x = conv(x, 'size', [3, 3, 256, 512], 'pad', 1) ;
    x = conv(x, 'size', [3, 3, 512, 512], 'pad', 1) ;
    x = conv(x, 'size', [3, 3, 512, 512], 'pad', 1) ;
    x = vl_nnpool(x, 3, 'stride', 2) ;

    % first fully-connected block
    x = conv(x, 'size', [6, 6, 512, 4096]) ;

  case 'f'
    % first conv block
    x = conv(images, 'size', [11, 11, 3, 64], 'stride', 4) ;
    if ~opts.batchNorm
      x = vl_nnnormalize(x, opts.normalization) ;
    end
    x = vl_nnpool(x, 3, 'stride', 2, 'pad', [0 1 0 1]) ;

    % second conv block
    x = conv(x, 'size', [5, 5, 64, 256], 'pad', 2) ;
    if ~opts.batchNorm
      x = vl_nnnormalize(x, opts.normalization) ;
    end
    x = vl_nnpool(x, 3, 'stride', 2) ;

    % conv blocks 3-5
    x = conv(x, 'size', [3, 3, 256, 256], 'pad', 1) ;
    x = conv(x, 'size', [3, 3, 256, 256], 'pad', 1) ;
    x = conv(x, 'size', [3, 3, 256, 256], 'pad', 1) ;
    x = vl_nnpool(x, 3, 'stride', 2) ;

    % first fully-connected block
    x = conv(x, 'size', [6, 6, 256, 4096]) ;
  end

  % finish first fully-connected block
  if ~opts.batchNorm
    x = vl_nndropout(x) ;
  end

  % second fully-connected block
  x = conv(x, 'size', [1, 1, 4096, 4096]) ;
  if ~opts.batchNorm
    x = vl_nndropout(x) ;
  end

  % prediction layer
  prediction = conv(x, 'size', [1, 1, 4096, opts.numClasses], ...
    'batchNorm', false, 'activation', 'none') ;

  prediction.meta = meta ;

end


function mertens_values_test ( )

%*****************************************************************************80
%
%% MERTENS_VALUES demonstrates the use of MERTENS_VALUES.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    17 October 2007
%
%  Author:
%
%    John Burkardt
%
  fprintf ( 1, '\n' );
  fprintf ( 1, 'MERTENS_VALUES:\n' );
  fprintf ( 1, '  MERTENS_VALUES returns values of \n' );
  fprintf ( 1, '  the Mertens function.\n' );
  fprintf ( 1, '\n' );
  fprintf ( 1, '     N         MERTENS(N)\n' );
  fprintf ( 1, '\n' );

  n_data = 0;

  while ( 1 )

    [ n_data, n, fn ] = mertens_values ( n_data );

    if ( n_data == 0 )
      break
    end

    fprintf ( 1, '  %4d  %10d\n', n, fn );

  end

  return
end


% VL_DEMO_KMEANS_VS_BUILTIN

clear elaps energy ;

dimension  = 128 ;
numData    = 1000 ;
numCenters = 10 ;
numTrials  = 10 ;

for trial = 1:numTrials
  X = rand(dimension, numData) ;

  tic ;
  [idx, C, e] = kmeans(X', numCenters) ;
  e = sum(e) ;
  elaps.builtin(trial) = toc ;
  energy.builtin(trial) = e ;

  tic ;
  [idx_, C_, e] = vl_kmeans(X, numCenters, 'verbose') ;
  elaps.vl(trial) = toc ;
  energy.vl(trial) = e ;

  tic ;
  [idx_, C_, e] = vl_kmeans(X, numCenters, 'initialization', 'plusplus') ;
  elaps.vlpp(trial) = toc ;
  energy.vlpp(trial) = e ;
end

figure(1) ; clf ;
subplot(1,2,1) ; title('Energy') ;
mu = [mean(elaps.builtin) mean(elaps.vl) mean(elaps.vlpp)] ;
st = [std(elaps.builtin) std(elaps.vl) std(elaps.vlpp)] ;
bar(mu) ; hold on ;
errorbar(mu, st, 'linestyle', 'none', 'color', 'r', 'linewidth', 4) ;

subplot(1,2,2) ;
mu = [mean(energy.builtin) mean(energy.vl) mean(energy.vlpp)] ;
st = [std(energy.builtin) std(energy.vl) std(energy.vlpp)] ;
bar(mu) ; hold on ;
errorbar(mu, st, 'linestyle', 'none', 'color', 'r', 'linewidth', 4) ;


% This file is part of the project NILM-Eval (https://github.com/beckel/nilm-eval).
% Licence: GPL 2.0 (http://www.gnu.org/licenses/gpl-2.0.html)
% Copyright: ETH Zurich, 2014
% Author: Romano Cicchetti

function snippets = generateSnippetsFromPlevels(plevel)

    % generate snippets using power levels

    snippets = struct;
    snippets.duration = {};
    snippets.mean = {};
    snippets.std = {};
    snippets.start = [];
    snippets.end = [];
    snippetStart = 1;
    while snippetStart < length(plevel.startidx) - 4
        snippetFound = 0;
        for snippetEnd = snippetStart + 1: snippetStart + 3
           if min(plevel.mean(snippetStart:snippetEnd)) + 10 < plevel.mean(snippetStart)
               break;
           end
           if abs(plevel.mean(snippetStart) - plevel.mean(snippetEnd+1)) < 10
               snippets.mean{end+1} = plevel.mean(snippetStart+1:snippetEnd) - plevel.mean(snippetStart);
               snippets.std{end+1} = plevel.std(snippetStart+1:snippetEnd);
               snippets.duration{end+1} = plevel.duration(snippetStart+1:snippetEnd);
               snippets.start(end+1) = snippetStart+1;
               snippets.end(end+1) = snippetEnd;
               snippetFound = 1;
               snippetStart = snippetEnd + 1;
               break;
           end
        end

        if snippetFound == 0
           snippetStart = snippetStart + 1;
        end
    end

end


% Copyright 2016 Google Inc.
%
% Licensed under the Apache License, Version 2.0 (the "License");
% you may not use this file except in compliance with the License.
% You may obtain a copy of the License at
%
% http ://www.apache.org/licenses/LICENSE-2.0
%
% Unless required by applicable law or agreed to in writing, software
% distributed under the License is distributed on an "AS IS" BASIS,
% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
% See the License for the specific language governing permissions and
% limitations under the License.

% Bilateral Guided Upsampling fits an affine bilateral grid gamma for an
%   operator f: input_image --> output_image.
%
% function [gamma, A, b, lambda_spatial, intensity_options] =
%   bguFit(input_image, edge_image, ...
%   output_image, output_weight, grid_size, lambda_spatial, intensity_options)
%
% Inputs:
%
% input_image is a double tensor: the input to the operator f.
%   Dimensions: height x width x num_input_channels
% edge_image is a double matrix: the "edges" we try to respect. For
%   example, the input luminance (try rgb2luminance or rgb2gray).
%   Dimensions: height x width (x 1)
% output_image is a double tensor: the output f(input_image).
%   Dimensions: height x width x num_output_channels
%
% [Optional] weight_image is a double tensor: whether f is defined at each
%   pixel. Weights need only be non-negative (>= 0) and can be any double.
%   Dimensions: height x width x num_output_channels
%   (same size as output_image)
%
% [Optional] grid_size is:
%   [height width depth num_output_channels num_input_channels]
%   If not specified or empty, defaults to:
%   [round(input_height / 16), round(input_width / 16), 8, ...
%    output_channels, input_channels + 1]
%
% [Optional] lambda_spatial controls spatial smoothness, and defaults to 1.
%   lambda_spatial must be positive.
%
% [Optional] intensity_options is a struct:
%   .type:
%     The type of constraint you want on the grid in the intensity
%     direction. Must be one of:
%       'none': no constraints
%       'first': constrain the first derivative dg/dz
%       'second': constrain the second derivative d2g/dz2
%     Default: 'second'
%   .value:
%     The value towards which the first or second derivative should be.
%     Note that this value is in *absolute* output units and is independent
%     of the grid spacing. I.e., if you want your derivatives to be close
%     to 1 in each bin, then set type to 'first' and value to 1, not
%     1/grid_size(3).
%       type = 'none': ignored.
%       type = 'first' or 'second': any double.
%     Default: 0
%   .lambda:
%     The strength of the constraint relative to the other terms.
%     Default: 4e-6 for first derivative, 4e-7 for second derivative.
%
% Outputs:
%
% A, b are the optional output sparse matrix and right hand side vector
% used to solve for gamma. gamma = reshape(A \ b, grid_size).
%
% lambda_spatial and intensity_options are echoed back as optional outputs
% so the test harness can ask for default parameters then retrieve them.
function [gamma, A, b, lambda_spatial, intensity_options] = ...
    bguFit(input_image, edge_image, ...
    output_image, output_weight, grid_size, lambda_spatial, intensity_options)

DEFAULT_LAMBDA_SPATIAL = 1;

DEFAULT_FIRST_DERIVATIVE_LAMBDA_Z = 4e-6; % about 0.01 for default bin sizes.
%DEFAULT_FIRST_DERIVATIVE_LAMBDA_Z = 4e-7; % about 0.001 for default bin sizes.

DEFAULT_SECOND_DERIVATIVE_LAMBDA_Z = 4e-7; % about 0.01 for default bin sizes.
%DEFAULT_SECOND_DERIVATIVE_LAMBDA_Z = 4e-8; % about 0.001 for default bin sizes.

if ~isa(input_image, 'double')
    error('input_image must be double.');
end

if ~isa(output_image, 'double')
    error('model_image must be double.');
end

if ~exist('edge_image', 'var') || isempty(edge_image) || ...
        ~ismatrix(edge_image) || ~isa(edge_image, 'double')
    error('edge_image must be a double matrix (one channel).');
end

if isempty(output_weight)
    output_weight = ones(size(output_image));
end

if ~isa(output_weight, 'double')
    error('weight must be double matrix');
end

if ndims(input_image) < 2
    error('input_image must be at least two-dimensional.');
end

if ndims(output_image) < 2
    error('output_image must be at least two-dimensional.');
end

if ~isequal(size(input_image, 1), size(output_image, 1)) || ...
    ~isequal(size(input_image, 2), size(output_image, 2))
    error('input_image and output_image must have the same width and height.');
end

if ~exist('grid_size', 'var') || isempty(grid_size)
    grid_size = getDefaultAffineGridSize(input_image, output_image);
end

if ~exist('lambda_spatial', 'var') || isempty(lambda_spatial)
    lambda_spatial = DEFAULT_LAMBDA_SPATIAL;
end

if lambda_spatial <= 0
    error('lambda_spatial must be positive.');
end

% If you pass in nothing, default to second derivative.
if ~exist('intensity_options', 'var') || isempty(intensity_options)
    intensity_options.type = 'second';
    intensity_options.value = 0;
    intensity_options.lambda = DEFAULT_SECOND_DERIVATIVE_LAMBDA_Z;
    intensity_options.enforce_monotonic = false;
end

% If you ask for a constraint but are missing some of the parameters.
if strcmp(intensity_options.type, 'first')
    if ~isfield(intensity_options, 'lambda')
        intensity_options.lambda = DEFAULT_FIRST_DERIVATIVE_LAMBDA_Z;
    end

    if ~isfield(intensity_options, 'value')
        intensity_options.value = 0;
    end

    if ~isfield(intensity_options, 'enforce_monotonic')
        intensity_options.enforce_monotonic = false;
    end
elseif strcmp(intensity_options.type, 'second')
    if ~isfield(intensity_options, 'lambda')

        intensity_options.lambda = DEFAULT_SECOND_DERIVATIVE_LAMBDA_Z;
    end

    if ~isfield(intensity_options, 'value')
        intensity_options.value = 0;
    end

    if ~isfield(intensity_options, 'enforce_monotonic')
        intensity_options.enforce_monotonic = false;
    end
else
    if ~isfield(intensity_options, 'enforce_monotonic')
        intensity_options.enforce_monotonic = false;
    end
end

input_height = size(input_image, 1);
input_width = size(input_image, 2);
grid_height = grid_size(1);
grid_width = grid_size(2);
grid_depth = grid_size(3);
affine_output_size = grid_size(4);
affine_input_size = grid_size(5);

% Size of each grid cell in pixels (# pixels per bin).
bin_size_x = input_width / grid_width;
bin_size_y = input_height / grid_height;
bin_size_z = 1 / grid_depth;

num_deriv_y_rows = (grid_height - 1) * grid_width * grid_depth ...
    * affine_output_size * affine_input_size;
num_deriv_x_rows = grid_height * (grid_width - 1) * grid_depth ...
    * affine_output_size * affine_input_size;

% Set up data term Ax = b.
%
% x is the bilateral grid. It is vectorized as a column vector of size n,
% where n = grid_height * grid_width * grid_depth * ...
%   affine_output_size * affine_input_size.
%
% The right hand side b is the *output* image, vectorized as output_image(:).
% I.e., it's a m x 1 column vector:
% [ red0 red1 ... redN green0 green1 ... greenN blue0 blue1 ... blueN ]'
%
% A is an m x n (sparse) matrix.
% It slices into the bilateral grid with linear interpolation at edge_image
% to get an affine model, then applies it.

% TODO: vectorize
% Build slice matrices for each (i,j) entry of the affine model.
weight_matrices = cell(affine_output_size, affine_input_size);
slice_matrices = cell(affine_output_size, affine_input_size);
for j = 1:affine_input_size
    for i = 1:affine_output_size
        %fprintf('Building weight and slice matrices, i = %d, j = %d\n', i, j);
        [weight_matrices{i, j}, slice_matrices{i, j}] = ...
            buildAffineSliceMatrix(input_image, edge_image, grid_size, i, j);
    end
end

% Concat them together.
slice_matrix = sparse(0, 0);
weight_matrix = sparse(0, 0);
for j = 1:affine_input_size
    for i = 1:affine_output_size
        %fprintf('Concatenating affine slice matrices, i = %d, j = %d\n', i, j);
        slice_matrix = [slice_matrix; slice_matrices{i, j}];
        weight_matrix = blkdiag(weight_matrix, weight_matrices{i, j});
    end
end

%fprintf('Building apply affine model matrix\n');
apply_affine_model_matrix = buildApplyAffineModelMatrix(...
    input_image, affine_output_size);

% Complete slicing matrix.
%fprintf('Building full slice matrix\n');
sqrt_w = sqrt(output_weight(:)); % weighted least squares takes sqrt(w).
W_data = spdiags(sqrt_w, 0, numel(output_weight), numel(output_weight));
A_data = W_data * apply_affine_model_matrix * weight_matrix * slice_matrix;
b_data = output_image(:) .* sqrt_w;

% ----- Add deriv_y constraints -----
%fprintf('Building d/dy matrix\n');
A_deriv_y = (bin_size_x * bin_size_z / bin_size_y) * lambda_spatial * ...
    buildDerivYMatrix(grid_size);
b_deriv_y = zeros(num_deriv_y_rows, 1);

% ----- Add deriv_x constraints -----
%fprintf('Building d/dx matrix\n');
A_deriv_x = (bin_size_y * bin_size_z / bin_size_x) * lambda_spatial * ...
    buildDerivXMatrix(grid_size);
b_deriv_x = zeros(num_deriv_x_rows, 1);

% ----- Add intensity constraints -----
%fprintf('Building d/dz matrix\n');
if strcmp(intensity_options.type, 'first')
    A_intensity = (bin_size_x * bin_size_y / bin_size_z) * ...
        intensity_options.lambda * buildDerivZMatrix(grid_size);
    value = intensity_options.lambda * intensity_options.value;
    m = size(A_intensity, 1);
    b_intensity = value * ones(m, 1);
elseif strcmp(intensity_options.type, 'second')
    A_intensity = (bin_size_x * bin_size_y) / (bin_size_z * bin_size_z) * ...
        intensity_options.lambda * ...
        buildSecondDerivZMatrix(grid_size);
    value = intensity_options.lambda * intensity_options.value;
    m = size(A_intensity, 1);
    b_intensity = value * ones(m, 1);
end

% ----- Assemble final A matrix -----
%fprintf('Assembling final sparse system\n');
if ~strcmp(intensity_options.type, 'none')
    A = [A_data; A_deriv_y; A_deriv_x; A_intensity];
    b = [b_data; b_deriv_y; b_deriv_x; b_intensity];
else
    A = [A_data; A_deriv_y; A_deriv_x];
    b = [b_data; b_deriv_y; b_deriv_x];
end

% ----- Solve -----
%fprintf('Solving system\n');
gamma = A \ b;
gamma = reshape(gamma, grid_size);


function [orderedPoints,biggest]=orderMeshPerimeterPointsAll(mesh,perimeterEdges)
% Take a set of mesh points on a perimeter and order them by their connections
%
%  orderedPoints=orderMeshPerimeterPointsAll(perimeterEdges)
%
% If more than one (unlinked) perimeter exists, return a structure
% containing all the separate ordered perimeters.
%
% N.B.  Each vertex should appear in exactly two rows.
% Starting with an initial position on the perimeter, startNode, the routine finds
% the other node in the row. By selectively deleting entries in the connection matrix
% as we progress, we can use a simple find operation each time to give us the next node.
%
% AUTHOR:  Wade
% DATE: 200-06-21

foundAllPerimeters=0; % Flag to say we've finished
nVerts=length(mesh.connectionMatrix); % Total number of vertices in the full connection matrix

edgePoints=unique(perimeterEdges(:)); % List of nodes that are on the perimeter
[y x]=size(mesh.connectionMatrix); % = nVerts

% Generate a connection matrix using only the edges - make sure it's symmetric
ec=sparse(perimeterEdges(:,1),perimeterEdges(:,2),1,nVerts,nVerts);
ec=ec+ec';
edgeConMat=(ec~=0);
%nnz(edgeConMat)

% Used for checking which perimeter is biggest
biggest=-Inf;
biggestSize=-Inf;

thisPerim=1; % Index of the current perimeter
nextRow=-99999; % Dummy

while (~foundAllPerimeters)
    counter=1;
    [startRow startCol]=find(edgeConMat);
    if (~isempty(startRow)) % If there is one...

        startRow=startRow(1); % ...pick the first non-zero row in the edge connection matrix
        startCol=startCol(1); % Pick the first non-zero column.
        orderedPoints{thisPerim}.points=startRow;

        thisRow=startRow;
        deleteCol=startCol;
        nextRow=-Inf;
        foundEnd=0;
        edgeConMat(thisRow,deleteCol)=0; % Zero one of the entries in this row

        while ((~foundEnd)&(counter<10000))

            % Do the deletions
            edgeConMat(thisRow,deleteCol)=0; % Zero one of the entries in this row
            edgeConMat(deleteCol,thisRow)=0; % Zero one of the entries in this row

            [thisCol]=find(edgeConMat(thisRow,:));

            thisCol=unique(thisCol(:));
            if (length(thisCol)>1)
                disp ('Warning!! - thiscol has more than 1 entry:');
                disp (thisCol);
                disp ('Perim node is connected to many other perim nodes. Carrying on but expect trouble....');
                % Can we just ignore superfluous ones? We'll press ahead regardless...
                thisCol=thisCol(1);
            end

            if (~isempty(thisCol)) % If there >is< another point in this perimeter
                orderedPoints{thisPerim}.points=[orderedPoints{thisPerim}.points;thisCol];
                deleteCol=thisRow;
                thisRow=thisCol;

            else % We've found the end of the perimeter.
                % fprintf ('\n.');

                foundEnd=1;
                orderedPoints{thisPerim}.size=counter;

            end % endif
            counter=counter+1;

        end % end while not startRow

        if (orderedPoints{thisPerim}.size>biggestSize) % Have to return the index of the larges tperim
            biggestSize=orderedPoints{thisPerim}.size;
            biggest=thisPerim;
        end % end size check

        fprintf('%d points\n');

    end % End if anything found to start with

    %keyboard;
    %nnz(edgeConMat)
    foundAllPerimeters=(x*y)-nnz(edgeConMat); %(sum(sum(edgeConMat==0)));
    thisPerim=thisPerim+1;
    %     disp(thisPerim);
    %     disp(nnz(edgeConMat));

end % End while not all perims found

fprintf('Found %d perimeters\n',thisPerim-1);

return;


function [PDAGs, nodes] = mk_nbrs_of_pdag_add(cpdag,engine)
% MK_NBRS_OF_PDAG_ADD Make the superior inclusion boundary of CPDAG.
% [PDAGs, nodes] = mk_nbrs_of_pdag_add(CPDAG)
%
% PDAGs{i} is the i'th neighbor of CPDAG0 generated by INSERT(X,Y,T)  with
% nodes{i,1:2}=[X Y]
% nodes{i,3}=T
%
% See D.M. Chickering 2002 : "Optimal Structure Identification with Greedy Search".
%
% philippe.leray@univ-nantes.fr, francois.olivier.c.h@gmail.com
% 24 july 2003

compteur=0 ;
N=length(cpdag);
G=pdag_to_dag(cpdag);
if nargin==1,
 bnet_tmp=mk_bnet(pdag_to_dag(cpdag),2*ones(N,1));
 bnet_tmp=mk_bnet(G,2*ones(N,1));
 engine_tmp=struct(jtree_inf_engine(bnet_tmp));
 clear bnet_tmp
else
 engine_tmp=struct(engine);
end
cliques=engine_tmp.cliques;
nbcliques=length(cliques);
clear engine_tmp;

% find in the PDAG all the X Y not connected
[LX LY]=find((cpdag|cpdag')+eye(N)==0);
nlinks=length(LX); % here is a bug when nlinks is zeros, for i=1:nlinks fail 31-37 added by hanbin
if nlinks==0
   [i,j]=find(cpdag);k=unidrnd(size(i,1),1);
   cpdag(i(k),j(k))=0;
   [LX LY]=find((cpdag|cpdag')+eye(N)==0);
    nlinks=length(LX);
end

for i=1:nlinks
    X=LX(i);
    Y=LY(i);
    % Neighbors of Y
    NY = myintersect(find(cpdag(:,Y)), find(cpdag(Y,:)));
    % Adjacents of X
    AX =  myunion(find(cpdag(:,X)), find(cpdag(X,:)));
    % Neighbors of Y adjacent to X
    NAYX = myintersect(NY,AX);
    % Neighbors of Y NOT adjacent to X
    NNAYX = mysetdiff(NY, NAYX);

    % this function recursively "walks" (dfs) in the graph representation of NNA powerset
    liste=NNAYX;
    if ~isempty(liste)
        premier=liste(1);
        dernier=liste(end);
    end;

    current_set=[];
    fini=0;
    evite2 = 0 ;

    while ~fini
        isclique=0;
        %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Test 1
        % is [NAYX current_set] a clique ?
        if isempty(current_set)
            NAYXT=NAYX;
        elseif isempty(NAYX)
            NAYXT=current_set;
        else
            NAYXT = union(NAYX,current_set);
        end
        % if isempty(NAYXT),isclique=1;end

        isclique = isempty(NAYXT) | ismemberclique(NAYXT,cliques) ;
        %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% End Test 1

        %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Test 2
        % is there exist a partially directed path Y...X in PDAG \ NAYXT ?
        if isclique
            if evite2
                test2=1 ;
            else
                %%%% calcul test 2
                L2 = setdiff(1:N,NAYXT);
                test2=~partialconnected(cpdag(L2,L2),find(L2==Y),find(L2==X));

            end
            %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% End Test 2

            if test2
                evitet2=1;

                % avoid testing INSERT(X,Y,[]) and INSERT(Y,X,[]) if pa(Y)=pa(X)
                test0=1 ;
                if (X>Y)
                    if length(current_set)==0
                        PaX=setdiff(find(cpdag(X,:)),find(cpdag(:,X)));
                        PaY=setdiff(find(cpdag(Y,:)),find(cpdag(:,Y)));
                        test0=(length(setdiff(PaX,PaY))~=0);
                    end
                end
                if test0
                    %fprintf(' INSERT(%d,%d,',X,Y); fprintf('%d',current_set);
                    %fprintf(')\n');
                    compteur=compteur+1;
                    nodes{compteur,1}=X;
                    nodes{compteur,2}=Y;
                    nodes{compteur,3}=current_set;
                    ptmp=cpdag;
                    ptmp(X,Y)=1;
                    ptmp(current_set,Y)=1;
                    ptmp(Y,current_set)=0;
                    PDAGs{compteur}=ptmp;
                end
            end
        end

        %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Next Set to test ...
        % what is the net set in the powerset ?
        if length(liste)==0
            fini=1 ; next_set=[];
        elseif length(current_set)==0
            next_set=[premier];     % first node after the root []
        else
            actuel=current_set(end);
            if actuel==dernier
                if length(current_set)==1
                    fini=1;            % no more node ...
                else
                    ancien=current_set(end-1);  % new "branch"
                    next_set=[current_set(1:end-2) liste(find(liste==ancien)+1)];
                end
            else                            % new node in the "branch"
                if ~isclique
                    if length(current_set)==1
                        fini=1;            % no more node ...
                    else
                        ancien=current_set(end-1);  % new "branch"
                        next_set=[current_set(1:end-2) liste(find(liste==ancien)+1)];
                        evite2=0;
                    end
                else
                    next_set=[current_set liste(find(liste==actuel)+1)];
                end
            end
        end
        current_set=next_set;
    end
end

%%%%%%%%%
function resu = ismemberclique(v,cliques)
finiclique = 0 ; resu=0 ;
cl=1;  ncl=length(cliques) ;
while (~finiclique) & (cl<=ncl);
    if ismember(v,cliques{cl})
        resu=1;
        finiclique=1 ;
    end
    cl=cl+1;
end

%%%%%%%%%
function resu = partialconnected(G,Y,X)
tmp=expm(G);
resu = (tmp(Y,X)~=0);


function [ b_labels ] = ml_2binary(labels)
%ML_2BINARY Transforms class labels to binary {-1,+1} class consisting of
%positive and negative class labels. This is needed for instance for binary
% class adaboost
%
%   input -----------------------------------------------------------------
%
%       o labels    : (N x 1), set of N labels with K classes
%
%   ouput -----------------------------------------------------------------
%
%       o b_lables  : (N x 1), set of N binary lables {-1,+1}
%
%

% find unique labels

N        = size(labels(:),1);
classes  = unique(labels);
b_lables = zeros(N,1);

if length(classes) == 2

    b_labels(labels == classes(1)) = -1;
    b_labels(labels == classes(2)) =  1;

else
    error('not yet implemented');

end

end


function [Jul1,Jul2]=TT2TDB(Jul1,Jul2,deltaTTUT1,clockLoc)
%%TT2TDB Convert from terrestrial time (TT) to barycentric dynamical time
%        (TDB) to an accuracy of nanoseconds (if deltaT is accurate)
%        using the routines from the International Astronomical Union's
%        library that do not require external ephemeris data.
%
%INPUTS: Jul1,Jul2 Two parts of a Julian date given in TT. The units of
%                  the date are days. The full date is the sum of both
%                  terms. The date is broken into two parts to provide
%                  more bits of precision. It does not matter how the date
%                  is split.
%       deltaTTUT1 An optional parameter specifying the offset between TT
%                  and UT1 in seconds. If this parameter is omitted or an
%                  empty matrix is passed, then the value of the function
%                  getEOP will be used.
%         clockLoc An optional 3X1 vector specifying the location of the
%                  clock in the Terrestrial Intermediate Reference System
%                  (TIRS), though it would not make much of a difference
%                  if the International Terrestrial Reference System
%                  (ITRS) were used. The units are meters. Due to
%                  relativistic effects, clocks that are synchronized with
%                  respect to TT are not synchronized with respect to TDB.
%                  If this parameter is omitted, then a clock at the
%                  center of the Earth is used.
%
%OUTPUTS:Jul1,Jul2 Two parts of a Julian date given in TDB.
%
%This function relies on a number of functions in the International
%Astronomical Union's Standards of Fundamental Astronomy library. The
%implementation is essentially the same as TT2TCB except a final
%conversion step is omitted.
%
%The algorithm can be compiled for use in Matlab  using the
%CompileCLibraries function.
%
%The algorithm is run in Matlab using the command format
%[Jul1,Jul2]=TT2TDB(Jul1,Jul2,deltaTTUT1,clockLoc);
%or
%[Jul1,Jul2]=TT2TDB(Jul1,Jul2);
%
%Many temporal coordinate systems standards are compared in [1].
%
%REFERENCES:
%[1] D. F. Crouse, "An Overview of Major Terrestrial, Celestial, and
%    Temporal Coordinate Systems for Target Tracking," Formal Report,
%    Naval Research Laboratory, no. NRL/FR/5344--16-10,279, 10 Aug. 2016,
%    173 pages.
%
%March 2014 David F. Crouse, Naval Research Laboratory, Washington D.C.
%(UNCLASSIFIED) DISTRIBUTION STATEMENT A. Approved for public release.

error('This function is only implemented as a mexed C or C++ function. Please run CompileCLibraries.m to compile the function for use.')

end

%LICENSE:
%
%The source code is in the public domain and not licensed or under
%copyright. The information and software may be used freely by the public.
%As required by 17 U.S.C. 403, third parties producing copyrighted works
%consisting predominantly of the material produced by U.S. government
%agencies must provide notice with such work(s) identifying the U.S.
%Government material incorporated and stating that such material is not
%subject to copyright protection.
%
%Derived works shall not identify themselves in a manner that implies an
%endorsement by or an affiliation with the Naval Research Laboratory.
%
%RECIPIENT BEARS ALL RISK RELATING TO QUALITY AND PERFORMANCE OF THE
%SOFTWARE AND ANY RELATED MATERIALS, AND AGREES TO INDEMNIFY THE NAVAL
%RESEARCH LABORATORY FOR ALL THIRD-PARTY CLAIMS RESULTING FROM THE ACTIONS
%OF RECIPIENT IN THE USE OF THE SOFTWARE.


function [y]=lars(p,ind,mat,rhs,n,d,smin,h,i0,j0)
%The dimension is p^2 (thus w
k0=p*p;
x=zeros(k0,1);
pp=2.^(0:d-1);
for i=1:k0
   ix=ind(d*(i-1)+1:d*i);
   ix=dot((ix-1),pp);
   x(i)=smin+h*ix;
end
mm=x(1)*mat{1};
for i=2:numel(mat)
  mm=mm+x(i)*mat{i};
end
   sol=mm \ rhs;
   sol=reshape(sol,n,n);
y=sol(i0,j0);
return
end


classdef EdgesConnectivitiesComputer < handle

    properties (GetAccess = public, SetAccess = private)
        nodesInEdges
        edgesInElem
        nEdgeByElem
        nNodeByEdge
        localNodeByEdgeByElem
    end

    properties (Access = private)
        allToUnique
        uniqueToAll
        nodesInAllEdges
    end

    properties (Access = private)
        nodesByElem
        nElem
        nAllEdges
        localEdgesInElem
    end

    methods (Access = public)

        function obj = EdgesConnectivitiesComputer(cParams)
            obj.init(cParams)
        end

        function compute(obj)
            obj.computeNodesInAllEdges();
            obj.computeUniqueEdgesIndex();
            obj.computeNodesInUniqueEdges();
            obj.computeEdgesInElem();
            obj.computeLocalOrientedEdgeConnec();
        end

        function cV = computeConnectedVertex(obj,vertex)
            e  = obj.computeAllEdgesOfVertex(vertex);
            cV = obj.computeOtherVertexOfEdge(e,vertex);
        end

    end

    methods (Access = private)

        function init(obj,cParams)
            obj.nodesByElem = cParams.nodesByElem;
            nNodes = size(cParams.nodesByElem,2);
            switch nNodes
                case 3
                    obj.localEdgesInElem = nchoosek(1:nNodes,2);%[1 2; 2 3; 3 1];
                    obj.localEdgesInElem = [1 2; 2 3; 3 1];
                case 4
                    obj.localEdgesInElem =  nchoosek(1:nNodes,2);%[1 2; 2 3; 3 1];
            end
            obj.nElem = size(obj.nodesByElem,1);
            obj.nEdgeByElem = size(obj.localEdgesInElem,1);
            obj.nNodeByEdge = size(obj.localEdgesInElem,2);
            obj.nAllEdges = obj.nElem*obj.nEdgeByElem;
        end

        function computeNodesInAllEdges(obj)
            nodesInE = zeros(obj.nAllEdges,obj.nNodeByEdge);
            allElem = 1:obj.nElem;
            for inode = 1:obj.nNodeByEdge
                for iedge = 1:obj.nEdgeByElem
                    lNode = obj.localEdgesInElem(iedge,inode);
                    index = (allElem - 1)*obj.nEdgeByElem + iedge;
                    nodes = obj.nodesByElem(:,lNode);
                    nodesInE(index,inode) = nodes;
                end
            end
            obj.nodesInAllEdges = nodesInE;
        end

        function computeUniqueEdgesIndex(obj)
            nE = obj.nodesInAllEdges;
            [nE] = sort(nE,2);
            [~,a2u,u2a] = unique(nE,'rows');
            obj.allToUnique = a2u;
            obj.uniqueToAll = u2a;
        end

        function computeNodesInUniqueEdges(obj)
            a2u = obj.allToUnique;
            nodesInUniqueEdges = obj.nodesInAllEdges(a2u,:);
            obj.nodesInEdges = nodesInUniqueEdges;
        end

        function computeEdgesInElem(obj)
            uniqueEdgeByElem = obj.computeUniqueEdgesByElem();
            eInElem = zeros(obj.nElem,obj.nEdgeByElem);
            for iEdge = 1:obj.nEdgeByElem
                edge = uniqueEdgeByElem(:,iEdge);
                eInElem(:,iEdge) = obj.uniqueToAll(edge);
            end
            obj.edgesInElem = eInElem;
        end

        function edgeByElem = computeUniqueEdgesByElem(obj)
            edges(:,1) = 1:obj.nAllEdges;
            edgeByElem = reshape(edges,obj.nEdgeByElem,obj.nElem);
            edgeByElem = edgeByElem';
        end

        function computeLocalOrientedEdgeConnec(obj)
            edgeConnec = zeros(obj.nElem,obj.nEdgeByElem,obj.nNodeByEdge);
            for iedge = 1:obj.nEdgeByElem
                isOriented = obj.isEdgeOriented(iedge);
                nodeA = obj.localEdgesInElem(iedge,1);
                nodeB = obj.localEdgesInElem(iedge,2);
                edgeConnec(isOriented,iedge,1)  = nodeA;
                edgeConnec(isOriented,iedge,2)  = nodeB;
                edgeConnec(~isOriented,iedge,1) = nodeB;
                edgeConnec(~isOriented,iedge,2) = nodeA;
            end
            obj.localNodeByEdgeByElem = edgeConnec;
        end

        function itIs = isEdgeOriented(obj,iedge)
            globalNodes = obj.computeGlobalNode(iedge);
            localNodes  = obj.computeLocalNodes(iedge);
            itIs = obj.isEquallyOriented(globalNodes,localNodes);
        end

        function globalNodes = computeGlobalNode(obj,iedge)
            edges       = obj.edgesInElem(:,iedge);
            globalNodes = obj.nodesInEdges(edges,:);
        end

        function localNodes = computeLocalNodes(obj,iedge)
            localNode   = obj.localEdgesInElem(iedge,:);
            localNodes  = obj.nodesByElem(:,localNode);
        end

        function edges = computeAllEdgesOfVertex(obj,vertex)
            vertexInEdges = obj.nodesInEdges;
            isInEdge = vertexInEdges == vertex;
            allEdges  = 1:size(isInEdge,1);
            allEdgesA(:,1) = allEdges(isInEdge(:,1));
            allEdgesB(:,1) = allEdges(isInEdge(:,2));
            edges = [allEdgesA;allEdgesB];
        end

        function oV = computeOtherVertexOfEdge(obj,edge,vertex)
            vertexesInEdges = obj.nodesInEdges;
            vertexesOfEdge  = vertexesInEdges(edge,:);
            oVB = setdiff(vertexesOfEdge(:,2),vertex);
            oVA = setdiff(vertexesOfEdge(:,1),vertex);
            oV = [oVB;oVA];
        end

    end

    methods (Access = private, Static)

        function itIs = isEquallyOriented(gNode,lNode)
            itIs = sum(abs(gNode - lNode),2) < 1;
        end

    end
end


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Released under the MIT License.
% If you use this code, please cite the following paper:
% Mahmoud Afifi, Abdelrahman Abdelhamed, Abdullah Abuolaim, Abhijith
% Punnappurath, and Michael S Brown. CIE XYZ Net: Unprocessing Images for
% Low-Level Computer Vision Tasks. arXiv preprint, 2020.
%
% Author: Mahmoud Afifi | Email: mafifi@eecs.yorku.ca, m.3afifi@gmail.com
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

classdef resizeLayer_ < nnet.layer.Layer

    properties
        target_size
    end

    methods
        function layer = resizeLayer_(name, target_size)
            layer.Name = name;
            layer.Description = "Image resizing";
            layer.target_size = target_size;
        end

        function Z = predict(layer, X)
            sz = size(X);
            if length(sz) < 3
                sz = [sz ones(1, 3 - length(sz))];

            end
            poolsize_d1 = floor(sz(1)/layer.target_size);
            poolsize_d2 = floor(sz(2)/layer.target_size);

            if isempty(X) || sz(1) == 0
                Z = X;
            else
                L = length(X(:))/(sz(1) * sz(2) * sz(3));
                if L == 1
                    Z = zeros(layer.target_size, layer.target_size, ...
                        sz(3), 'like',X);
                    temp = avgpool(X,[poolsize_d1, poolsize_d2], ...
                        'Stride', [poolsize_d1, poolsize_d2],...
                        'DataFormat','SSCB');
                    Z = temp(1:layer.target_size,1:layer.target_size,:);
                    size(Z)
                else
                    Z = zeros(layer.target_size, layer.target_size, ...
                        sz(3), sz(4), 'like', X);
                    for i = 1: L
                        temp = avgpool(X(:,:,:,i),...
                            [poolsize_d1, poolsize_d2],...
                            'Stride', [poolsize_d1, poolsize_d2],...
                            'DataFormat','SSCB');
                        Z(:,:,:,i) = ...
                            temp(1:layer.target_size, ...
                            1:layer.target_size,:,i);
                    end
                end
            end
        end
    end
end


function out = subsref(x, s)
	%SUBSREF Subscripted reference for TTeMPS.
	%
	%   Examples:
	%   x([2,3,1]) computes and returns the element (2,3,1) of x.
	%   x( ind ) computes and returns all elements x(ind(i,:)) for i = 1:size(ind,1).
	%   x(:) returns the vectorization of full(x) (careful!)
    %   x{i} returns the i-th core of x, shorthand for x.U{i}
	%
	%   See also TTEMPS.

    %   TTeMPS Toolbox.
    %   Michael Steinlechner, 2013-2016
    %   Questions and contact: michael.steinlechner@epfl.ch
    %   BSD 2-clause license, see LICENSE.txt

	switch s(1).type

	case '.'

		prop = properties('TTeMPS');
		if( any(strcmp(s(1).subs, prop) ) ),
		    out = builtin('subsref', x, s);
		else
			ll = length(prop);
			proplist = repmat({', '}, 2*ll-1, 1);
			proplist(1:2:end) = prop;
			proplist = cat(2,proplist{:});
			error(['Object TTeMPS does not have field ' s(1).subs ...
			'. The following fields are available: ' proplist '.']);
		end

	case '()'
		% x(:)
		ind = s(1).subs{1};
		if(length(ind) == 1) && (ind == ':')
			out = full(x);
			out = out(:);
			% e.g. x( [1,2,3; 4 5 6; 7 8 9; 3 5 3]) for d = 3 tensor
		elseif(size(ind,2) == x.order)

			r = x.rank;
			%out = zeros(size(ind,1), 1);

			%C = cell(1,x.order);
			%for i=1:x.order
			%	C{i} = permute( x.U{i}, [1 3 2]);
			%end
			%for i = 1:size(ind,1)
			%	p = C{1}(:,:,ind(i,1));
			%	for j = 2:size(ind,2)
			%		p = p * C{j}(:,:,ind(i,j));
			%	end
			%	out(i) = p;
			%end
			n = x.size;

			C = cell(1,x.order);
			for i=1:x.order
				C{i} = permute( x.U{i}, [1 3 2]);
				C{i} = unfold( C{i}, 'right');
			end

            out = TTeMPS.subsref_mex( n, r, transpose(ind), C);

			%for i = 1:size(ind,1)
			%	p = C{1}(:, (ind(i,1)-1)*r(2)+1:ind(i,1)*r(2));
			%	for j = 2:size(ind,2)
			%		p = p * C{j}(: , (ind(i,j)-1)*r(j+1)+1:ind(i,j)*r(j+1));
			%	end
			%	out(i) = p;
			%end

		else
			error('Number of indices does not match order of TTeMPS tensor.');
		end

	case '{}'

		if(length(s(1).subs) ~= 1 || ~isnumeric(s(1).subs{1}) || ...
			s(1).subs{1} <= 0)
			error('{} only takes one positive integer.');
		end

		ii = s(1).subs{1};
		if(ii > x.order)
			error('Index exceeds number of dimensions');
		end
		out = builtin('subsref', x.U, s);

	end


% get_MBS_GCI_intervals.m
% Function to detect intervals to be used for searching the LP-residual to
% determine GCI locations using the method described in Drugman et al
% (2012).
%
% Octave compatible
%
% Description
% Function to detect intervals to be used for searching the LP-residual to
% determine GCI locations using the method described in Drugman et al
% (2012).
%
% Inputs
%  MBS             : [samples] [Nx1] Mean-based signal
%  fs              : [Hz]      [1x1] sampling frequency
%  T0mean          : [samples] [1x1] Glottal period
%  F0max           : [Hz]      [1x1] Maximum fundamental frequency
%
% Outputs
%  interval        : [samples] [Mx2] Search intervals for GCI estimation
%
% Example
%  interval = get_MBS_GCI_intervals(MBS,fs,T0mean,F0max)
%
% References
%  [1] Kane, J., Gobl, C., (2013) `Evaluation of glottal closure instant
%       detection in a range of voice qualities', Speech Communication
%       55(2), pp. 295-314.
%
% Copyright (c) 2013 Trinity College Dublin
%
% License
%  This code is a part of the Voice Analysis Toolkit with the following
%  licence:
%  The software product (and any modifications thereof) is distributed under
%  a dual licence: an open source license for individual, non-commercial
%  purposes, and a commercial license. The opensource licence under which
%  the product is distributed is GNU GPL v2. For individual users, this
%  licence suits their use as these are not distributing proprietary
%  modifications, additions to, or derivatives of the product and don't
%  require legal protection of a commercial licence. For commercial users,
%  where open source does not meet their requirements, we offer commercial
%  licensing of the product. A commercial license permits customers to
%  modify, add or produce derivative products without the obligation of
%  making the subsequent code open source. For more information regarding
%  our commercial licence, please contact john.whelan@tcd.ie
%
% This function is part of the Covarep project: http://covarep.github.io/covarep
%
% Author
%  John Kane kanejo@tcd.ie
%
% $Id <info set by the versioning system> $

function interval = get_MBS_GCI_intervals(MBS,fs,T0mean,F0max)

% Function to detect intervals to be used for searching the LP-residual to
% determine GCI locations using the method described in Drugman et al
% (2012).

%% Initial settings
if nargin < 4
    F0max=500;
end
F0max=F0max*2;
T0max=round(fs/F0max);
[idx,TMP]=v_findpeaks(MBS*-1,'minpeakdistance',T0max); % Find locations of negative peaks
N=length(idx);
search_rate=0.28;
search_left_rate=0.01;
interval=zeros(N,2);

%% Do processing
for n=1:N

    if length(T0mean)>1
        start=idx(n)-round(T0mean(idx(n))*search_left_rate);
        stop=idx(n)+round(T0mean(idx(n))*search_rate);
    else start=idx(n)-round(T0mean*search_left_rate);
        stop=idx(n)+round(T0mean*search_rate);
    end

    if start < 1
        start=1;
    end

    % Check start and end points of detected intervals
    if stop > length(MBS) && start < length(MBS)
        stop=length(MBS);
    elseif stop > length(MBS) && start >= length(MBS)
        break
    end

    interval(n,1)=start;
    interval(n,2)=stop;
end


function r8vec_permute_test ( )

%*****************************************************************************80
%
%% RR8VEC_PERMUTE_TEST tests R8VEC_PERMUTE.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    26 October 2014
%
%  Author:
%
%    John Burkardt
%
  n = 5;

  p = [ 2, 4, 5, 1, 3 ];
  x = [ 1.0, 2.0, 3.0, 4.0, 5.0 ];

  fprintf ( 1, '\n' );
  fprintf ( 1, 'R8VEC_PERMUTE_TEST\n' );
  fprintf ( 1, '  R8VEC_PERMUTE permutes an R8VEC.\n' );

  r8vec_print ( n, x, '  Original array X[]:' )

  i4vec_print ( n, p, '  Permutation vector P[]:' )

  x = r8vec_permute ( n, x, p );

  r8vec_print ( n, x, '  Permuted array X[P[]]:' );

  return
end


classdef cDPEA < ALGORITHM
% <multi> <real/integer/label/binary/permutation> <constrained>
% Constrained dual-population evolutionary algorithm

%------------------------------- Reference --------------------------------
% M. Ming, A. Trivedi, R. Wang, and D. Srinivasan, A dual-population based
% evolutionary algorithm for constrained multi-objective optimization, IEEE
% Transactions on Evolutionary Computation, 2021, 25(4): 739-753.
%------------------------------- Copyright --------------------------------
% Copyright (c) 2023 BIMK Group. You are free to use the PlatEMO for
% research purposes. All publications which use this platform or any code
% in the platform should acknowledge the use of "PlatEMO" and reference "Ye
% Tian, Ran Cheng, Xingyi Zhang, and Yaochu Jin, PlatEMO: A MATLAB platform
% for evolutionary multi-objective optimization [educational forum], IEEE
% Computational Intelligence Magazine, 2017, 12(4): 73-87".
%--------------------------------------------------------------------------

% This function is written by Mengjun Ming

    methods
        function main(Algorithm,Problem)
            %% Generate random population
            Population1 = Problem.Initialization();
            Population2 = Population1;
            alpha       = 2./(1+exp(1).^(-Problem.FE*10/Problem.maxFE))-1;
            para        = ceil(Problem.maxFE/Problem.N)/2 - ceil(Problem.FE/Problem.N);

            %% Optimization
            while Algorithm.NotTerminated(Population1)
                % Re-rank
                Population1 = Population1(randperm(Problem.N));
                Population2 = Population2(randperm(Problem.N));
                Lia   = ismember(Population2.objs,Population1.objs, 'rows');
                gamma = 1-sum(Lia)/Problem.N;
                [Population1,FrontNo1] = EnvironmentalSelection(Population1,Problem.N,alpha);
                [Population2,FrontNo2] = EnvironmentalSelection_noCon(Population2,Problem.N,alpha,gamma,para);

                % Offspring Reproduction
                Population_all   = [Population1,Population2];
                RankSolution_all = [FrontNo1,FrontNo2];
                MatingPool = TournamentSelection(2,2*Problem.N,RankSolution_all);
                Offspring  = OperatorGAhalf(Problem,Population_all(MatingPool));

                % Environmental Selection
                alpha = 2./(1+exp(1).^(-Problem.FE*10/Problem.maxFE)) - 1;
                para  = ceil(Problem.maxFE/Problem.N)/2 - ceil(Problem.FE/Problem.N);
                [Population1,~] = EnvironmentalSelection([Population1,Offspring],Problem.N,alpha);
                [Population2,~] = EnvironmentalSelection_noCon([Population2,Offspring],Problem.N,alpha,gamma,para);
            end
        end
    end
end


function f_x = ParFor2(in1)
%PARFOR2
%    F_X = PARFOR2(IN1)

%    This function was generated by the Symbolic Math Toolbox version 8.1.
%    16-Jul-2019 11:57:35

u = in1(:,1);
ux = in1(:,4);
uxx = in1(:,5);
uxxx = in1(:,6);
f_x = (u.*2.6068e4+ux.*7.4257e4+uxx.*2.6372e5+uxxx.*4.3251e5-u.*ux.*1.0e5+u.*uxx.*2.3836e4-u.*uxxx.*1.0264e6)./(u.*2.6078e4+2.6069e5);


function output = F_mean(input_layer, curr_layer)
input = input_layer.a;
if isfield(curr_layer, 'pool_idx')
    output = mean(input, curr_layer.pool_idx);
else
    output = mean(input,2);
end
end


function [foldChange,standardError,solControl,solCondition] = eFlux(model,controlExpression,conditionExpression,varargin)
% Calculate the objective fold change according to the eFlux approach for
% expression integration as described in:
% Interpreting Expression Data with Metabolic Flux Models: Predicting Mycobacterium tuberculosis Mycolic Acid Production
% Colijn C, Brandes A, Zucker J, Lun DS, Weiner B, et al. (2009)
% PLOS Computational Biology 5(8): e1000489. https://doi.org/10.1371/journal.pcbi.1000489
%
% USAGE:
%    [foldChange,standardError] = eFlux(model,controlExpression,conditionExpression,varargin)
%
% INPUTS:
%    model:                  The COBRA model struct to use
%    controlExpression:      struct for the control expression with two fields required and one optional field:
%                              * .target       - the names of the target (rxns or genes)
%                              * .value        - the value for the target. Positive values for all constraint reactions, negative values for unconstraint reactions.
%                              * .preprocessed - Indicator whether the provided targets are genes (false), or reactions (true) Default: false
%    conditionExpression:    struct for the condition expression (fields are the same as controlExpression)
% OPTIONAL INPUT:
%    varargin:               parameters given as struct or parameter/value pairs.
%                             * testNoise - indicator whether to run multiple calculations with added noise to get a significance of the fold change. Requires either a noise function and a standard deviation of noise ('noiseFun' and 'noiseStd' respectively) or a controlData struct and a noise function.
%                             * noiseCount - number of noisy controls to create if noise is tested (default: 10)
%                             * noiseFun - The noise function to use, has to be a function handle taking 2 arguments (mean and std)
%                             * noiseStd - The standard deviation(s) to use. Either a single value (used for all values, or a vector with length equal to controlExpression.value).
%                             * controlData - a struct (like controlExpression which has a value matrix with multiple values per controlExpression to determine the noise distribution. If provided with testNoise == false, the values from this struct will be used to determine the noise.
%                             * minSum:           Switch for the processing of Genetic data. If false, ORs in the GPR will be treated as min. If true(default), ORs will be treated as addition.
%                             * softBounds:       Whether to use soft bounds for the infered constraints or to add flexibility variables (default: false).
%                             * weightFactor:     The weight factor for soft bounds (default: 1)
% OUTPUTS:
%    foldChange:             The fold change between the objective of the
%                            condition and the objective of the control
%                            expression
%    standardError:          The error if noise is being used.
%    solControl:             The solution of the given Control expression;
%    solCondition:           The solution of the given Condition expression;
%
% ..Author:    Thomas Pfau OCt 2018
%
% NOTE:
%    This si an implementation of the eFlux concept as presented in:
%    Interpreting Expression Data with Metabolic Flux Models: Predicting Mycobacterium tuberculosis Mycolic Acid Production
%    Colijn C, Brandes A, Zucker J, Lun DS, Weiner B, et al. (2009)
%    PLOS Computational Biology 5(8): e1000489. https://doi.org/10.1371/journal.pcbi.1000489
%    Please note, that this code does not perform any preprocessing expcept
%    for that described in the above paper after array normalization.

normFun = @(mean,std) normrnd(mean,std);

if ~isfield(controlExpression,'preprocessed')
    controlExpression.preprocessed = true;
end

parser = inputParser();
parser.addRequired('model',@(x) verifyModel(x,'simpleCheck',true));
parser.addRequired('controlExpression',@(x) verifyEFluxExpressionStruct(model,x));
parser.addRequired('conditionExpression',@(x) verifyEFluxExpressionStruct(model,x));
parser.addParameter('testNoise',false, @(x) islogical(x) || (isnumeric(x) && (x==1 || x==0)));
parser.addParameter('noiseCount',10, @isnumeric);
parser.addParameter('noiseFun',normFun, @(x) isa(x, 'function_handle'));
parser.addParameter('noiseStd',[], @isnumeric);
parser.addParameter('controlData',[], @(x) verifyEFluxExpressionStruct(model,x));
parser.addParameter('minSum',false,@(x) islogical(x) || (isnumeric(x) && (x==1 || x==0)));
parser.addParameter('softBounds',false,@(x) islogical(x) || (isnumeric(x) && (x==1 || x==0)));
parser.addParameter('weightFactor',1,@isnumeric);

parser.parse(model,controlExpression,conditionExpression,varargin{:});
testNoise = parser.Results.testNoise;
noiseCount = parser.Results.noiseCount;
noiseStd = parser.Results.noiseStd;
noiseFun = parser.Results.noiseFun;
controlData = parser.Results.controlData;
noisyControl = [];
% remove enforcing bounds (contradict eFlux concept)
if(any(model.lb > 0 | model.ub < 0))
    warning('Enforcing bounds for the following fluxes have been removed:\n%s', strjoin(model.rxns((model.lb > 0 | model.ub < 0)),'\n'));
    model.lb(model.lb > 0) = 0;
    model.ub(model.ub < 0) = 0;
end

if testNoise
    noisyControl = repmat(columnVector(controlExpression.value),1,noiseCount);
    if isempty(controlData) && isempty(noiseStd)
        error('To test noise, either a standard deviation, or appropriate controlData has to be provided')
    end
    if ~isempty(controlData)
        if ~isempty(noiseStd)
            error('To test noise, either a standard deviation, or appropriate controlData has to be provided but not both.')
        end
        % ok, we use the controlData and create noise based on its
        % standarddeviations.
        noiseStd = std(controlData.values');
    else
        if numel(noiseStd) == 1
            noiseStd = repmat(noiseStd,size(noisyControl,1),1);
        end
        controlData.target = controlExpression.target;
        controlData.preprocessed = controlExpression.preprocessed;
    end
    for i = 1:noiseCount
            % add the noise.
            noise = arrayfun(@(x) noiseFun(0,x),columnVector(noiseStd));
            noisyControl(:,i) = noisyControl(:,i) + columnVector(noise);
    end
else
    if ~isempty(controlData)
        noisyControl = controlData.values;
    end
end

% calculate the condition solution
conditionModel = applyEFluxConstraints(model,conditionExpression,varargin{:});
solCondition = optimizeCbModel(conditionModel);
conditionValue =  solCondition.f;

% calculate the default solution (for controlExpression
defaultControl = applyEFluxConstraints(model,controlExpression,varargin{:});
solControl = optimizeCbModel(defaultControl);
controlValue = zeros(1+size(noisyControl,2),1);
controlValue(1) = solControl.f;

for noisy = 1:size(noisyControl,2)
    % calculcate the noisy solutions
    if isfield(controlData,'preprocessed')
        exprStruct.target = controlData.target;
        exprStruct.value = noisyControl(:,noisy);
        exprStruct.preprocessed = controlData.preprocessed;
    else
        exprStruct.target = controlData.target;
        exprStruct.value = noisyControl(:,noisy);
    end
    defaultControl = applyEFluxConstraints(model,exprStruct,varargin{:});
    controlSol = optimizeCbModel(defaultControl);
    controlValue(1+noisy) = controlSol.f;
end
% set the outputs
foldChanges = conditionValue./controlValue;
foldChange = foldChanges(1);
standardError = std(foldChanges) / sqrt(numel(foldChanges));

end


function anatIm = recomputeAnatImage(vw,displayMode,slice)
% anatIm = recomputeAnatImage(vw,[displayMode],[slice]);
%
% Return the anat image, clipped and or scaled
% according to the (non-hidden) vw's ui settings,
% for the specified display mode. displayMode
% defaults to the currently-selected mode (e.g.,
% 'anat' 'amp' 'map' etc). slice defaults to the
% current slice.
%
%
%
% ras 01/05. Added b/c I want to migrate to using
% brightness/contrast/possibly gamma, but also
% want back compatibility.
if isequal(viewGet(vw, 'Name'),'hidden')
    % hidden views: just return the anat
    % image w/o contrast adjustment
    %TODO: put slice error handling here
    anatIm = viewGet(view, 'anatomycurrentslice', slice);
    return
end

ui = viewGet(vw,'ui');

if ieNotDefined('slice')
	% Get curSlice from ui
	slice = viewGet(vw, 'curSlice');
end

if ieNotDefined('displayMode')
    displayMode = ui.displayMode;
end

modeStr = sprintf('%sMode',displayMode);
numGrays = ui.(modeStr).numGrays;

% Get anatomy image from vw (non-scaled)
anatIm = cropCurAnatSlice(vw,slice);

if isfield(ui,'brightness')
    % adjust img brightness/contrast
    brightness = get(ui.brightness.sliderHandle,'Value');
    contrast = get(ui.contrast.sliderHandle,'Value');

    % unlike the normal way contrast/brightness work,
    % I've found it's better to have 'contrast'
    % just change the upper bound of the anatClip,
    % and 'brightness' just shift the median value
    % up and down a bit:
    minVal = double(min(anatIm(:)));
	maxVal = (1-contrast)*double(max(anatIm(:)));
    % removed lines that turned off/on warnings...
	anatIm = (rescale2(double(anatIm),[minVal maxVal],[1 numGrays]));

    % brighten
    brightDelta = brightness - 0.5;
    if brightDelta ~= 0 % slowwww....
        anatIm = brighten(anatIm,brightDelta);
        anatIm = rescale2(anatIm,[],[1 numGrays]);
    end
else
    % do it the old way, using the
    % anat clip slider values

	% Get anatClip from sliders
	anatClip = getAnatClip(vw);

	% Rescale anatIm to [1:numGrays], anatClip determines the range
	% of anatomy values that gets mapped to the available grayscales.
	% If anatClip=[0,1] then there is no clipping and the entire
	% range of anatomy values is scaled to the range of available gray
	% scales.
    minVal = double(min(anatIm(:)));
	maxVal = double(max(anatIm(:)));
	anatClipMin = min(anatClip)*(maxVal-minVal) + minVal;
	anatClipMax = max(anatClip)*(maxVal-minVal) + minVal;
	warning off;
	anatIm = (rescale2(double(anatIm),[anatClipMin,anatClipMax],[1,numGrays]));
	warning backtrace;
end

return

% This way seemed sensible, but
% the images didn't look as great as
% I'd hoped:
%     % Will rescale the values in anatIm to be
%     % distributed with a median determined by
%     % the brightness param, and a range of values
%     % determined by contrast -- but it will all
%     % fit in the range 1:numGrays:
%     mu = round(brightness*numGrays);
%     sigma = (1-contrast) * (numGrays/2-1) + 1;
%     anatIm = rescale2(anatIm,[],[-sigma sigma]) + mu;
%
%     % need a 2nd clip step -- could set this in the
%     % rescale2 step, but it's the same # of cycles to
%     % do it this way, I think, and more legible:
%     anatIm(anatIm <= 1) = 1;
%     anatIm(anatIm >= numGrays) = numGrays;


function [res] = tracking_push_relabel(dres, c_en, c_ex, c_ij, betta, max_it)

dnum = length(dres.x);

dres.c = betta - dres.r; %% cost for each detection window

%%% The mex function works with large numbers.
dres.c  = dres.c  *1e6;
c_en    = c_en    *1e6;
c_ex    = c_ex    *1e6;
c_ij    = c_ij    *1e6;

n_nodes = 2*dnum+2; %% number of nodes in the graph
n_edges = 0;

dat_in = zeros(1e7,3); %% each row represents an edge from node in column 1 to node in column 2 with cost in column 3.
k_dat = 0;
for i = 1:dnum
  k_dat = k_dat+3;
  dat_in(k_dat-2,:) = [1      2*i     c_en      ];
  dat_in(k_dat-1,:) = [2*i    2*i+1   dres.c(i) ];
  dat_in(k_dat,:)   = [2*i+1  n_nodes c_ex      ];
end
for i=1:dnum
  f2 = dres.nei(i).inds;
  for j = 1:length(f2)
    k_dat = k_dat+1;
    dat_in(k_dat,:) = [2*f2(j)+1 2*i c_ij];
  end
end
dat_in = [dat_in repmat([0 1],size(dat_in,1),1)];  %% add two columns: 0 for min capacity in column 4 and 1 for max capacity in column 5 for all edges.

excess_node = [1 n_nodes];  %% push flow in the first node and collect it in the last node.

k = 0;
dat2_old = [0 0 0];

inds_all = [];
tic
lb=1;
ub=1e4;
tr_num_old = 1;

tic

while ub-lb > 1     %% bisection search for the optimum amount of flow. This can be implemented by Golden section search more efficiently.
  tr_num = round((lb+ub)/2);

  [cost_l, dat_l] = cs2_func(dat_in(1:k_dat,:), excess_node, [tr_num -tr_num]);      %% try flow = tr_num
  [cost_u, dat_u] = cs2_func(dat_in(1:k_dat,:), excess_node, [tr_num+1 -tr_num-1]);  %% try flow = tr_num+1
  if cost_u-cost_l > 0
    ub = tr_num;
  else
    lb = tr_num;
  end
  k=k+1;
end

if cost_u < cost_l
  dat1 = dat_u;
else
  cost1 = cost_l;
  dat1 = dat_l;
end

%%%% backtrack tracks to get ids
tmp   = find( dat1(:, 1) == 1);
start = dat1(tmp, 2);       %% starting nodes; is even

tmp   = find( ~mod(dat1(:, 1), 2) .* (dat1(:, 2)-dat1(:, 1) == 11) );
detcs = dat1(tmp, 1);       %% detection nodes; is even

tmp   = find( mod(dat1(:, 1), 2) .* ~mod(dat1(:, 2), 2) .* (dat1(:, 2)-dat1(:, 1) ~= 1) );
links = dat1(tmp, 1:2);     %% links; is [even  odd]

res_inds  = zeros(1, 1e5);
res_ids   = zeros(1, 1e5);

k = 0;
for i = 1:length(start);    %% for each track
  this1 = start(i);
  while this1 ~= n_nodes
    k = k+1;
    res_inds(k) = this1/2;
    res_ids(k) = i;
    this1 = links(find(links(:,1) == this1+1), 2);  %% should have only one value
    if mod(this1, 2) + (length(this1) ~= 1)         %% sanity check
      display('error in the output of solver');
    end
  end
end
res_inds  = res_inds(1:k);    %% only these detection windows are used in tracks.
res_ids   = res_ids(1:k);     %% track id for each detection window

%%% Calculate the cost value to sort tracks
this_cost = zeros(1, max(res_ids));
for i = 1:max(res_ids)  %% for each track
  inds = find(res_ids == i);
  len1= length(inds);
  track_cost(i) = sum(dres.c(res_inds(inds))) + (len1-1) * c_ij + c_en + c_ex;
end
[dummy sort_inds] = sort(track_cost);

for i = 1:length(sort_inds)
  res_ids_sorted(res_ids == sort_inds(i)) = i;
end

res = sub(dres, res_inds);
res.id = res_ids_sorted(:);

% [dummy tmp] = sort(res.id);
% res = sub(res,tmp);
% % res = sub(dres,inds_all_its(1).inds);
% % res.r = res.r/1e6;


function net = init_MSLapSRN_model(opts, mode)
% -------------------------------------------------------------------------
%   Description:
%       initialize MS-LapSRN model
%
%   Input:
%       - opts  : options generated from init_MSLapSRN_opts()
%
%   Output:
%       - net   : dagnn model
%
%   Citation:
%       Fast and Accurate Image Super-Resolution with Deep Laplacian Pyramid Networks
%       Wei-Sheng Lai, Jia-Bin Huang, Narendra Ahuja, and Ming-Hsuan Yang
%       arXiv, 2017
%
%   Contact:
%       Wei-Sheng Lai
%       wlai24@ucmerced.edu
%       University of California, Merced
% -------------------------------------------------------------------------

    %% parameters
    rng('default');
    rng(0) ;

    f   = opts.conv_f;
    n   = opts.conv_n;
    pad = floor(f/2);

    if( f == 3 )
        crop = [0, 1, 0, 1];
    elseif( f == 5 )
        crop = [1, 2, 1, 2];
    else
        error('Need to specify crop in deconvolution for f = %d\n', f);
    end

    %% initialize model
    net = dagnn.DagNN;

    %% multiscale training
    for s = 1:length(opts.scales)

        scale = opts.scales(s);
        level = ceil(log(scale) / log(2));

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    %% Feature extraction branch
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

        %% input conv
        inputs  = { sprintf('x%dSR_LR', scale) };
        outputs = { sprintf('x%dSR_input_conv', scale) };
        params  = { 'input_conv_f', 'input_conv_b' };

        net.addLayer(outputs{1}, ...
                     dagnn.Conv('size', [f, f, 1, n], ...
                                'pad', pad, ...
                                'stride', 1), ...
                     inputs, outputs, params);

        level_input = outputs{1};

        %% feature embedding sub-network
        for l = 1 : level

            block_input = {};
            block_output = {};

            for r = 1:opts.recursive

                if r == 1
                    block_input{r} = level_input;
                else
                    block_input{r} = block_output{r-1};
                end

                %% recursive block
                for d = 1:opts.depth

                    if d == 1
                        next_input = block_input{r};
                    end

                    % ReLU
                    inputs  = { next_input };
                    outputs = { sprintf('x%dSR_level%d_R%d_relu%d', scale, l, r, d) };

                    net.addLayer(outputs{1}, ...
                                 dagnn.ReLU('leak', 0.2), ...
                             inputs, outputs);

                    next_input = outputs{1};

                    % conv
                    inputs  = { next_input };
                    outputs = { sprintf('x%dSR_level%d_R%d_conv%d', scale, l, r, d) };
                    params  = { sprintf('conv%d_f', d), ...
                                sprintf('conv%d_b', d)};

                    net.addLayer(outputs{1}, ...
                                 dagnn.Conv('size', [f, f, n, n], ...
                                            'pad', pad, ...
                                            'stride', 1), ...
                                 inputs, outputs, params);

                    next_input = outputs{1};

                end %% end of recursive block

                %% local skip connection
                if strcmp(opts.LRL, 'NS')

                    % no skip connection
                    block_output{r} = next_input;

                elseif strcmp(opts.LRL, 'DS')

                    % next_input + block_input
                    inputs  = { next_input, block_input{r} };
                    outputs = { sprintf('x%dSR_level%d_R%d_output', scale, l, r) };
                    net.addLayer(outputs{1}, ...
                        dagnn.Sum(), ...
                        inputs, outputs);

                    block_output{r} = outputs{1};

                elseif strcmp(opts.LRL, 'SS')

                    % next_input + level_input
                    inputs  = { next_input, level_input };
                    outputs = { sprintf('x%dSR_level%d_R%d_output', scale, l, r) };
                    net.addLayer(outputs{1}, ...
                        dagnn.Sum(), ...
                        inputs, outputs);

                    block_output{r} = outputs{1};

                else

                    error('Unknown local skip connection %s.', opts.LRL);

                end %% end of local skip connection

            end %% end of recursive

            %% features upsample layers
            % ReLU
            inputs  = { block_output{opts.recursive} };
            outputs = { sprintf('x%dSR_level%d_uprelu', scale, l) };

            net.addLayer(outputs{1}, ...
                         dagnn.ReLU('leak', 0.2), ...
                         inputs, outputs);

            next_input = outputs{1};

            % conv
            inputs  = { next_input };
            outputs = { sprintf('x%dSR_level%d_upconv', scale, l) };
            params  = { 'upconv_f', 'upconv_b' };

            net.addLayer(outputs{1}, ...
                         dagnn.ConvTranspose(...
                             'size', [f, f, n, n], ...
                             'upsample', 2, ...
                             'crop', crop, ...
                             'numGroups', 1, ...
                             'hasBias', true), ...
                         inputs, outputs, params) ;

            level_input = outputs{1};

        end %% end of level (feature extraction)

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    %% Image reconstruction branch
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

        for l = 1 : level

            if l == 1
                next_input = sprintf('x%dSR_LR', scale);
            else
                next_input = sprintf('x%dSR_%dx_output', scale, 2^(l-1));
            end

            %% image upsample layer
            inputs  = { next_input };
            outputs = { sprintf('x%dSR_level%d_img_up', scale, l) };
            params  = { 'img_up_f' };

            net.addLayer(outputs{1}, ...
                dagnn.ConvTranspose(...
                    'size', [4, 4, 1, 1], ...
                    'upsample', 2, ...
                    'crop', 1, ...
                    'numGroups', 1, ...
                    'hasBias', false), ...
                inputs, outputs, params) ;

            %% residual prediction layer (f x f x n x 1)
            inputs  = { sprintf('x%dSR_level%d_upconv', scale, l) };
            outputs = { sprintf('x%dSR_level%d_residual', scale, l) };
            params  = { 'residual_conv_f', 'residual_conv_b' };

            net.addLayer(outputs{1}, ...
                dagnn.Conv('size', [f, f, n, 1], ...
                           'pad', pad, ...
                           'stride', 1), ...
                inputs, outputs, params);

            %% addition layer
            inputs  = { sprintf('x%dSR_level%d_img_up', scale, l), ...
                        sprintf('x%dSR_level%d_residual', scale, l) };
            outputs = { sprintf('x%dSR_%dx_output', scale, 2^l) };
            net.addLayer(outputs{1}, ...
                dagnn.Sum(), ...
                inputs, outputs);

            next_input = outputs{1};

            %% Loss layer
            inputs  = { next_input, ...
                        sprintf('x%dSR_%dx_HR', scale, 2^l) };
            outputs = { sprintf('x%dSR_%dx_%s_loss', scale, 2^l, opts.loss) };

            net.addLayer(outputs{1}, ...
                     dagnn.vllab_dag_loss(...
                        'loss_type', opts.loss), ...
                     inputs, outputs);

        end %% end of level (image reconstruction)

    end %% end of multiscale

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    %% initialize parameters
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

    if strcmp(mode, 'train')

        %% input conv
        params  = { 'input_conv_f', 'input_conv_b' };

        sigma   = opts.init_sigma;
        filters = sigma * randn(f, f, 1, n, 'single');
        biases  = zeros(1, n, 'single');

        idx = net.getParamIndex(params{1});
        net.params(idx).value         = filters;
        net.params(idx).learningRate  = 1;
        net.params(idx).weightDecay   = 1;

        idx = net.getParamIndex(params{2});
        net.params(idx).value         = biases;
        net.params(idx).learningRate  = 0.1;
        net.params(idx).weightDecay   = 1;

        %% deep conv
        for d = 1:opts.depth

            params  = { sprintf('conv%d_f', d), ...
                        sprintf('conv%d_b', d)};

            sigma   = sqrt( 2 / (f * f * n) );
            filters = sigma * randn(f, f, n, n, 'single');
            biases  = zeros(1, n, 'single');

            idx = net.getParamIndex(params{1});
            net.params(idx).value         = filters;
            net.params(idx).learningRate  = 1;
            net.params(idx).weightDecay   = 1;

            idx = net.getParamIndex(params{2});
            net.params(idx).value         = biases;
            net.params(idx).learningRate  = 0.1;
            net.params(idx).weightDecay   = 1;

        end

        %% feature upsample
        params  = { 'upconv_f', 'upconv_b' };

        sigma   = sqrt( 2 / (f * f * n) );
        filters = sigma * randn(f, f, n, n, 'single');
        biases  = zeros(1, n, 'single');

        idx = net.getParamIndex(params{1});
        net.params(idx).value         = filters;
        net.params(idx).learningRate  = 1;
        net.params(idx).weightDecay   = 1;

        idx = net.getParamIndex(params{2});
        net.params(idx).value         = biases;
        net.params(idx).learningRate  = 0.1;
        net.params(idx).weightDecay   = 1;

        %% image upsample
        params  = { 'img_up_f' };

        filters = single(bilinear_kernel(4, 1, 1));

        idx = net.getParamIndex(params{1});
        net.params(idx).value         = filters;
        net.params(idx).learningRate  = 1;
        net.params(idx).weightDecay   = 1;

        %% residual prediction
        params  = { 'residual_conv_f', 'residual_conv_b' };

        sigma   = sqrt(2 / (f * f * n));
        filters = sigma * randn(f, f, n, 1, 'single');
        biases  = zeros(1, 1, 'single');

        idx = net.getParamIndex(params{1});
        net.params(idx).value         = filters;
        net.params(idx).learningRate  = 1;
        net.params(idx).weightDecay   = 1;

        idx = net.getParamIndex(params{2});
        net.params(idx).value         = biases;
        net.params(idx).learningRate  = 0.1;
        net.params(idx).weightDecay   = 1;

    end

end


function value = r8_mop ( i )

%*****************************************************************************80
%
%% R8_MOP returns the I-th power of -1 as an R8 value.
%
%  Modified:
%
%    07 November 2007
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer I, the power of -1.
%
%    Output, real R8_MOP, the I-th power of -1.
%
  if ( mod ( i, 2 ) == 0 )
    value = + 1.0;
  else
    value = - 1.0;
  end

  return
end