%kvgef 'First Derivative Operator for Symmetric Exponential Filter (K1)'
% This MatLab function was automatically generated by a converter (KhorosToMatLab) from the Khoros vgef.pane file
%
% Parameters:
% InputFile: i 'Input Image', required: 'input image'
% Double: a0 ' a0 ', default: 0.45: 'Exponential Filter Parameter for GEF'
% Integer: t1 ' T1 ', default: 3: '1st Hysteresis Threshold'
% Integer: t2 ' T2 ', default: 4: '2nd Hysteresis Threshold'
% Integer: l 'Length', default: 5: 'Minimum Number of Pixels in a Segment'
% OutputFile: o 'Output Image', required: 'output image'
%
% Example: o = kvgef(i, {'i','';'a0',0.45;'t1',3;'t2',4;'l',5;'o',''})
%
% Khoros helpfile follows below:
%
%  PROGRAM
% vgef - First Derivative Operator for Symmetric Exponential Filter  (K1)
%
%  DESCRIPTION
%
% This Algorithm has been written by the team of:
%
%
% Professor SERGE CASTAN
%
%
% IRIT, URA 1399
%
%
% 118, route de Narbonne  31062 Toulouse  FRANCE
%
% OPTIMAL FILTER(ISEF) FOR EDGE DETECTION
%
% PRINCIPLE :
% I. Introduction
% Edge detection is one of the most important subjects in image processing,
% which finds wide applications in the pattern recognition, the scene analysis and
% the 3-D vision, because the edges correspond in general to the important changes
% of physical or geometrical properties of objects in the scene and they are widely
% used as primitives in the pattern recognition, the image matching etc.
%
% The edges coincide, generally speaking, grey level transition, they can
% be detected by maxima of gradient or the zero-crossing of the second
% derivatives calculated by some differential operators. Because the
% differential operators are sensitive to noise, a preprocessing such as
% smoothing is often necessary to eliminate the noise. A well-known smoothing
% filter is Gaussian filter and the edges can therefore be detected by a
% Laplacian-Gaussian filter. But there is an essential difficulty of
% the Laplacian-Gaussian filter which is the contradiction between the
% smoothing effect and the precision of edge localization. To overcome this
% difficulty,  We proposed the optimal linear filter based on one step model
% (a step edge and the white noise) and the multi-edge model [9][10][11].
% This optimal smoothing filter is a symmetric exponential filter of an
% infinitely large window size and can be realized by very simple recursive
% algorithm. It  is proved that the band limited Laplacian of an input image
% filtered by this filter can be calculated from the Difference between the input
% and the output of this Recursive Filter (DRF). The edges detected by DRF
% method are less noisy and with a much better precision of localization.
%
% The maxima of gradient or zeros of the second directional derivative
% along the gradient are a natural definition of intensity edges. Zeros of the
% Laplacian are only extensively used for their computational convenience.
% However, we must stress here that the zeros crossing of the Laplacian are
% not always coincided with the maxima of gradient, for example, the zeros
% of the Laplacian are farther apart than the maxima of gradient for circularly
% symmetric patterns, this lack of localization by the Laplacian can also be
% seen in the fact that zeros of Laplacian "swing wide" of corners. Therefore,
% it had better to detect the edges by maxima of gradient or zeros of the second
% directional derivative along the gradient .
%
% So we propose two methods for edges detection, one uses maxima of Gradient
% (GEF), another uses the zeros crossing of Second directional Derivative along
% the gradient (SDEF).
%
% II. The First and the Second Directional Derivative Operators for Symmetric
% Exponential Filter
% A normalized symmetric exponential filter on 1-D can be written :
% fL(x)=C*a0*(1-a0)**|x|=f1(x)#f2(x)=C*(f1(x)+f2(x)-a0*d(x)) (1)
%
% where:
% C=1/(2-a0), # means the convolution, d(x) is dirac function.
% f1(x)={0 if x<0  and a0*(1-a0)**x   if x>= 0  (2)
% f2(x)={0 if x>0  and a0*(1-a0)**-x  if x<= 0  (2)
% we can write the first derivative operator of exponential filter:
%
% fL'(x)=f2(x)-f1(x)         (3)
% And we can obtain the normalized second derivative operator of
% exponential filter :
% fL"(x)=f1(x)+f2(x)-2*d(x)  (4)
%
% Because the exponential function is separable, we can write out 2-D
% exponential filter:
% f(x,y) = fL(x)*fL(y)                                            (5)
%
% From the equations (18),(19) and (20),  the first and the second
% directional derivative operators for symmetric exponential filter can be
% written like this:
%
%
% fx(x,y) = fL(y)*(f2(x)-f1(x))          (6)
%
%
% fy(x,y) = fL(x)*(f2(y)-f1(y))          (7)
%
%
% fxx(x,y) = fL(y)*(f1(x)+f2(x)-2*d(x))  (8)
%
%
% fyy(x,y) = fL(x)*(f1(y)+f2(y)-2*d(y))  (9)
%
% III. The Recursive Algorithm for realizing the These Directional Derivative
% Operators of Symmetric Exponential Filter
%
% The exponential filter is an IIR filter corresponding to an infinite
% window size, so we should realize the functions f1(x) and f2(x) (see formula
% (2)) by a recursive algorithm.
%
% Supposing I(x,y) is the input image,
% I1(x,y)=I(x,y)#f1(x) and I2(x,y)=I(x,y)#f2(x),
% we have the recursive algorithm :
% I1(x,y)=I1(x-1,y)+a0*(I(x,y)-I1(x-1,y))    (10)
% I2(x,y)=I2(x+1,y)+a0*(I(x,y)-I2(x+1,y))
%
% From the equations (1),(6),(7),(8) and (9), the band-limited first
% and second directional derivative of input image can be calculated by the
% recursive algorithm f1 and f2 as follows
% Ix(x,y)=I(x,y)#f1(y)#f2(y)#(f2(x)-f1(x))   (11)
% Ixx(x,y)=I(x,y)#f1(y)#f2(y)#(f2(x)+f1(x))-2*I(x,y)#f1(y)#f2(y)  (12)
% Iy(x,y) = I(x,y)#f1(x)#f2(x)#(f2(y)-f1(y))                        (13)
% Iyy(x,y) = I(x,y)#f1(x)#f2(x)#(f2(y)+f1(y))-2*I(x,y)#f1(x)#f2(x)  (14)
%
% With this algorithm, we can calculate at the same time the band-limited
% first and second directional derivative Ix and Ixx ( or Iy and Iyy ) of
% input image.
%
% IV. Edges Detection
% The band-limited first and second directional derivative of input image
% can be obtained by the algorithms as stated above. Using them, we can then
% realize the edge detection for an image.
%
% The maxima of gradient or zeros of the second directional derivative
% along the gradient are a natural way of characterizing and localizing intensity
% edges,  so we present here to detect the edges from the maxima of gradient or
% zeros of the second directional derivative along the gradient by using the
% differential operators of exponential filter.
%
%
% 1. Edges from the maxima of gradient
% Using the first directional derivative operator of the exponential filter,
% the two band-limited first directional derivatives Ix and Iy can be calculated,
% and the gradient vector can be therefore determined approximatively for every
% point in image. The gradient magnitude image is then non maxima suppressed in
% the gradient direction and thresholded with hysteresis, i.e. if the entire
% segment of the contour lies above a low threshold T1, and at least one of part of
% which is above a high threshold T2, that contour is output. The non maxima
% suppression scheme requires three points, one of which will be the current
% point, and the other two should be estimated of the gradient magnitude at
% points displaced from the current point by vector normal to the edge direction.
%
%
% 2. Edges from the zero-crossings of the second directional derivative along the
% gradient direction
% Because edges detected from local gradient maxima can not be a pixel
% width (less good precision of localization), we propose an other method which
% detect the edges from the zeros crossing of the second directional derivative
% along the gradient direction.
%
% We can calculate Ix,Iy,Ixx and Iyy by using the method shown in
% paragraph III, and therefore obtain approximatively the gradient vector
% and the second derivative in the gradient direction for every point in image.
% We extract at first the zero crossing of second derivative along the gradient
% direction on which the gradient magnitude must be above a low threshold, so an
% edges image is obtained. To this image, the entire segment of the contour will
% be kept, if the gradient magnitude on at least one part of this contour is above
% a high threshold.
% V. Comparison of Performance of the Filters
% Filtering is a problem of estimation from noisy signal, and edge
% detection is a problem of estimating the position of maximal local signal
% change. Up to now, many works are done for edges detection in image, and
% different filters are proposed, for example, Gaussian filter, Canny filter,
% exponential filter, Deriche filter etc...
%
% We appreciate the performance of the filters as follows :
%
%
% (1) Precision of edge localization
% According to our analysis [10], we can calculate the average
% localization error xe for Gaussian filter, Canny filter [15], Deriche
% filter [16] and the exponential filter :
%
%
% xeG = (4*(2*e*3.14)**0.5)/a
%
%
% xec = 0.81/a
%
%
% xeD = 4*exp(-1)/a = 1.47/ a
%
%
% xeE = 0
%
%
% i.e. xeG > xeD > xec > xeE = 0.
%
% So,  we can see that the exponential filter localizes edge points with
% the best precision.
%
%
% (2) Signal/Noise ratio on the edge point detected
% Because xe is the average estimation for the position of the edge point
% detected, we propose to calculate Signal/Noise ratio (Eq.(7)) at the point xe.
%
% And the signal/noise ratio for the Gaussian filter, Canny filter,
% Deriche filter and the exponential filter is :
%
%
% SNRG = 2*s*exp(-32*s)/ ((3.14)**0.5)
%
%
% SNRc = 0.39/ a
%
%
% SNRD = 0.64/ a
%
%
% SNRE =1/ a
%
%
% i.e. SNRE > SNRD > SNRc > SNRG.
%
% Then, we see that the exponential filter has the best noise eliminating
% effect among the above four.
%
%
% (3) Complexity of calculation
% For the complexity of calculation, we only tell the difference from
% exponential filter and Deriche filter [16], because they are implemented by
% recursive algorithms which have a simpler calculation.
%
% Because ISEF can be realized by first order recursive filter, the
% ISEF algorithms are much simpler than that of Deriche filter. Besides, the
% ISEF algorithms can be implemented independently to every line and every column,
% it can be easily realized by a parallel system.
%
% According to the analysis results above, the ISEF filter is superior
% to the others at the 3 principal aspects of the performance of the filter.
%
% VI. Conclusion
% The symmetric exponential filter of an infinite large window size is
% an optimal linear filter deduced from one step edge model and the multi-edge
% model, now we further prove that the symmetric exponential filter is the optimal
% edge detection filter in the criteria of the signal to noise ratio, the
% localization precision and unique maximum. Obviously, the real images will be
% still more complicated than these models, however DRF method has already provided
% good results for different type of images. The results obtained through the
% two new methods further show the superior performance of this filter.
% The theoretical analysis for the performance of the filters shows also that
% the exponential filter is superior to the other current filters.
%
% The first and second directional derivative operators can be realized by
% recursive algorithm and calculated at the same time. The new algorithms
% are therefore very simple as well as DRF algorithm, and they are also easy
% to implemented in a parallel way.
%
%
%
%  EXAMPLES
% vgef -i cross.xv -o output.xv
%
% will compute a edge extraction by using the first directional
% derivative operator for symmetric exponential filter on
% image cross.xv and write the result in output.xv
%
% For the GEF edge extraction the filter parameter value is generally
% a0 between 0.40 and 0.9 (0.45  [default] is a good value)
%
% The hysteresis Thresholds T1 and T2 can be set to 12 and 15.
%
% The minimum Length of segment is up to the user. It is set by default to 10.
%
%
%  "SEE ALSO"
%
%  RESTRICTIONS
% works only with single band byte images.
%
% Note that this routine was converted directly from Khoros 1.0.  Due to
% the mixing of unsigned versus signed char, and the difference in the rules
% that resolve the mixing of these data types between ANSI C and the original
% K&R C that the code was written in, the results of running this program
% on an image will not be the same as with vdrf in Khoros 1.0.  There is
% really no way to resolve which version is more correct, but the ANSI C version
% should at least be more consistent across machine architectures.
%
%  REFERENCES
%
% [1] W.K. PRATT, Digital Image Processing, New York, 1978.
%
% [2] J. PREWITT, Object Enhancement and Extraction,Picture Processing and
% Psychopictories, Etd. by B. Lipkin and A. Rosenfeld, New York, pp.75-149, 1970.
%
% [3] M. HUECKEL, An Operator Which Locates Edges in Digitized Pictures.
% J.A.C.M., Vol. 18, pp 113-125, 1971.
%
% [4] R.O. DUDA and P.E. HART, Pattern Classification and Scene Analysis.
% Wiley, New York, 1973.
%
% [5] R. HARALICK,Edge and Region Analysis for Digital Image Data. C.G.I.P.,
% Vol. 12, pp 60-73, 1980.
%
% [6] R.HARALICK and L.WATSON, A Facet Model for Image Data. C.G.I.P., Vol. 15,
% pp 113-129, 1981.
%
% [8] D. MARR and E.C. HILDRETH, Theory of Edge Detection. Proc. R. Soc.
% Lond. B, Vol. 207, pp 187-217, 1980.
%
% [9] J. SHEN and S. CASTAN, Un nouvel algorithme de detection de contours,
% proceedings of 5th Conf. on P.R.&.A.I. (in French), Grenoble, 1985.
%
% [10] J. SHEN and S. CASTAN, An Optimal Linear Operator for Edge Detection.
% Proc. CVPR'86,  Miami,1986.
%
% [11] J. SHEN and S. CASTAN, Edge Detection Based on Multi-Edge Models.Proc.
% SPIE'87, Cannes, 1987.
%
% [12] J. SHEN and S. CASTAN, Further Results on DRF Method for Edge Detection.
% 9th I.C.P.R., ROME, 1988.
%
% [13] V.TORRE and T.A.POGGIO, On Edge Detection IEEE Transaction on Pattern
% Analysis and    Machine Intelligence, Vol. Pami-8, N 2, March 1986.
%
% [14] J.S.CHEN and G.MEDIONI, Detection, Localization, and Estimation of Edges.
% IEEE  Transaction on Pattern  Analysis and Machine Intelligence, Vol. 11, N 2,
% February  1989.
%
% [15] J.F.CANNY, Finding Edges And Lines in Images.
% MIT Technical Report N 720, 1983.
%
% [16] R. DERICHE, Optimal Edge Detection Using Recursive Filtering.
% In proc. First International Conference on Computer Vision,
% London, June 8-12 1987.
%
%  COPYRIGHT
% Copyright (C) 1993 - 1997, Khoral Research, Inc. ("KRI")  All rights reserved.
%

function varargout = kvgef(varargin)
if nargin ==0
  Inputs={};arglist={'',''};
elseif nargin ==1
  Inputs=varargin{1};arglist={'',''};
elseif nargin ==2
  Inputs=varargin{1}; arglist=varargin{2};
else error('Usage: [out1,..] = kvgef(Inputs,arglist).');
end
if size(arglist,2)~=2
  error('arglist must be of form {''ParameterTag1'',value1;''ParameterTag2'',value2}')
 end
narglist={'i', '__input';'a0', 0.45;'t1', 3;'t2', 4;'l', 5;'o', '__output'};
maxval={0,1,255,255,100000,0};
minval={0,0,0,0,0,0};
istoggle=[0,1,1,1,1,0];
was_set=istoggle * 0;
paramtype={'InputFile','Double','Integer','Integer','Integer','OutputFile'};
% identify the input arrays and assign them to the arguments as stated by the user
if ~iscell(Inputs)
Inputs = {Inputs};
end
NumReqOutputs=1; nextinput=1; nextoutput=1;
  for ii=1:size(arglist,1)
  wasmatched=0;
  for jj=1:size(narglist,1)
   if strcmp(arglist{ii,1},narglist{jj,1})  % a given argument was matched to the possible arguments
     wasmatched = 1;
     was_set(jj) = 1;
     if strcmp(narglist{jj,2}, '__input')
      if (nextinput > length(Inputs))
        error(['Input ' narglist{jj,1} ' has no corresponding input!']);
      end
      narglist{jj,2} = 'OK_in';
      nextinput = nextinput + 1;
     elseif strcmp(narglist{jj,2}, '__output')
      if (nextoutput > nargout)
        error(['Output nr. ' narglist{jj,1} ' is not present in the assignment list of outputs !']);
      end
      if (isempty(arglist{ii,2}))
        narglist{jj,2} = 'OK_out';
      else
        narglist{jj,2} = arglist{ii,2};
      end

      nextoutput = nextoutput + 1;
      if (minval{jj} == 0)
         NumReqOutputs = NumReqOutputs - 1;
      end
     elseif isstr(arglist{ii,2})
      narglist{jj,2} = arglist{ii,2};
     else
        if strcmp(paramtype{jj}, 'Integer') & (round(arglist{ii,2}) ~= arglist{ii,2})
            error(['Argument ' arglist{ii,1} ' is of integer type but non-integer number ' arglist{ii,2} ' was supplied']);
        end
        if (minval{jj} ~= 0 | maxval{jj} ~= 0)
          if (minval{jj} == 1 & maxval{jj} == 1 & arglist{ii,2} < 0)
            error(['Argument ' arglist{ii,1} ' must be bigger or equal to zero!']);
          elseif (minval{jj} == -1 & maxval{jj} == -1 & arglist{ii,2} > 0)
            error(['Argument ' arglist{ii,1} ' must be smaller or equal to zero!']);
          elseif (minval{jj} == 2 & maxval{jj} == 2 & arglist{ii,2} <= 0)
            error(['Argument ' arglist{ii,1} ' must be bigger than zero!']);
          elseif (minval{jj} == -2 & maxval{jj} == -2 & arglist{ii,2} >= 0)
            error(['Argument ' arglist{ii,1} ' must be smaller than zero!']);
          elseif (minval{jj} ~= maxval{jj} & arglist{ii,2} < minval{jj})
            error(['Argument ' arglist{ii,1} ' must be bigger than ' num2str(minval{jj})]);
          elseif (minval{jj} ~= maxval{jj} & arglist{ii,2} > maxval{jj})
            error(['Argument ' arglist{ii,1} ' must be smaller than ' num2str(maxval{jj})]);
          end
        end
     end
     if ~strcmp(narglist{jj,2},'OK_out') &  ~strcmp(narglist{jj,2},'OK_in')
       narglist{jj,2} = arglist{ii,2};
     end
   end
   end
   if (wasmatched == 0 & ~strcmp(arglist{ii,1},''))
        error(['Argument ' arglist{ii,1} ' is not a valid argument for this function']);
   end
end
% match the remaining inputs/outputs to the unused arguments and test for missing required inputs
 for jj=1:size(narglist,1)
     if  strcmp(paramtype{jj}, 'Toggle')
        if (narglist{jj,2} ==0)
          narglist{jj,1} = '';
        end;
        narglist{jj,2} = '';
     end;
     if  ~strcmp(narglist{jj,2},'__input') && ~strcmp(narglist{jj,2},'__output') && istoggle(jj) && ~ was_set(jj)
          narglist{jj,1} = '';
          narglist{jj,2} = '';
     end;
     if strcmp(narglist{jj,2}, '__input')
      if (minval{jj} == 0)  % meaning this input is required
        if (nextinput > size(Inputs))
           error(['Required input ' narglist{jj,1} ' has no corresponding input in the list!']);
        else
          narglist{jj,2} = 'OK_in';
          nextinput = nextinput + 1;
        end
      else  % this is an optional input
        if (nextinput <= length(Inputs))
          narglist{jj,2} = 'OK_in';
          nextinput = nextinput + 1;
        else
          narglist{jj,1} = '';
          narglist{jj,2} = '';
        end;
      end;
     else
     if strcmp(narglist{jj,2}, '__output')
      if (minval{jj} == 0) % this is a required output
        if (nextoutput > nargout & nargout > 1)
           error(['Required output ' narglist{jj,1} ' is not stated in the assignment list!']);
        else
          narglist{jj,2} = 'OK_out';
          nextoutput = nextoutput + 1;
          NumReqOutputs = NumReqOutputs-1;
        end
      else % this is an optional output
        if (nargout - nextoutput >= NumReqOutputs)
          narglist{jj,2} = 'OK_out';
          nextoutput = nextoutput + 1;
        else
          narglist{jj,1} = '';
          narglist{jj,2} = '';
        end;
      end
     end
  end
end
if nargout
   varargout = cell(1,nargout);
else
  varargout = cell(1,1);
end
global KhorosRoot
if exist('KhorosRoot') && ~isempty(KhorosRoot)
w=['"' KhorosRoot];
else
if ispc
  w='"C:\Program Files\dip\khorosBin\';
else
[s,w] = system('which cantata');
w=['"' w(1:end-8)];
end
end
[varargout{:}]=callKhoros([w 'vgef"  '],Inputs,narglist);


function [im_alt] = altertxt(im_tx1,im_tx2)
% This function arrange the pixels alternatively from Text image 1 and 2, and
% returns the interleaved Text image
% im_tx1 - Input Text image 1,3,5 to be encoded
% im_tx2 - Input Text image 2,4,6 to be encoded
% im_alt - Output Interleaved Text image

% Locate Text images 1 and 2 in Odd pixel columns
im_alt(1:2:size(im_tx1,1)-1,1:2:size(im_tx1,2)-1,:) = im_tx1(1:2:size(im_tx1,1)-1,1:2:size(im_tx1,2)-1,:);
im_alt(2:2:size(im_tx1,1)  ,1:2:size(im_tx1,2)-1,:) = im_tx2(2:2:size(im_tx2,1)  ,1:2:size(im_tx2,2)-1,:);
% Locate Text images 1 and 2 in Even pixel columns
im_alt(1:2:size(im_tx1,1)-1,2:2:size(im_tx1,2)  ,:) = im_tx2(1:2:size(im_tx2,1)-1,2:2:size(im_tx2,2)  ,:);
im_alt(2:2:size(im_tx1,1)  ,2:2:size(im_tx1,2)  ,:) = im_tx1(2:2:size(im_tx1,1)  ,2:2:size(im_tx1,2)  ,:);


function [inpoly onboundary] = insidepoly(varargin)
% [inpoly onboundary] = insidepoly(X, Y, PX, PY)
%
% Check if (X,Y) are inside the interior of a 2D polygon delimited by the
% polygon vertices (PX,PY).
%
% INPUTS:
%   - X, Y: arrays of same size, coordinates of N data points
%   - PX, PY: arrays of same size, coordinates of M vertices
%   - Provide optionally EDGE: insidepoly(x, y, Px, Py, edges)
%   EDGES are (m x 2) linear indexes of (Px,Py) that will
%   be considered as vertices of the polygon. This allows user to provide
%   polygons with multiple connexed boundaries (e.g., having holes)
%
% Alternate calling:
%   >> inpoly = insidepoly(XY, P) % or
%   >> inpoly = insidepoly(XY, P, EDGES)
%   - XY: (n x 2) array of n points, ranged by row
%   - P: (m x 2) array of m vertices, ranged by row
%   - EDGES (if provided in third argument) correponds to the
%   first-dimension indexed of P: P(EDGES,:) is the polygone boundary
%
% Advanced control the algorithm:
%   Optionally, user can provide parameters to control the algorithm
%   >> insidepoly(..., 'property1', value1, ...)
% Valid properties (case sensitive) are
%   - 'tol': ['auto'] or positive value: eulidian distance tolerance to
%      detect boundary points. When 'tol' is is set to 'auto', the
%      tolerance value of 1e-9*max(dPx,dPy) is used, where dPx, dPy are
%      respectively the horizontal and vertical size of the polygon.
%   - 'presortflag':  ['auto'], 0, or 1:
%     For large number of vertices and points, INSIDEPOLY uses a sorting
%     strategy to reduce the data to be scanned by each polygonal edge.
%     This reduction comes at the cost of additional processing. The
%     complexity of the sorting step is O((N + M)*log(N+M)). If the numbers
%     of vertices and/or data are small, the sorting is relatively expensive
%     with respect to the complexity of O(M*N) needed for the calculation
%     for the polygonal region determitaion. It might be preferable to
%     disable this step. By default, the presorting is enabled when:
%                      M>32 and M*N>25000.
%     Set the presortflag to 0 or 1 to enable/disable the sorting step.
%
% OUTPUTS:
%   - INPOLY: logical array same size as (X,Y), TRUE if the point is
%   inside or on the boundary of the polygon, FALSE otherwise
%   - ONBOUNDARY: logical array same size as (X,Y) to determine points
%   on the boundary (within numercial tolerance)
%
% This functions has C-MEX engine to speed up the calculation. These Mex
% files must be compiled by lauching insidepoly_install.m
%
% See also: inpolygon, inpoly, insidepoly_dblengine, insidepoly_sglengine
%
% Acknowlegment: The idea of sorting coordinates is first implemented by
% Darren Engwirda, http://www.mathworks.com/matlabcentral/fileexchange/10391
%
% Author: Bruno Luong <brunoluong@yahoo.com>
% History:
%     Original: 06-Jun-2010

% Processing the argument list

% Default options
options = struct('tol', 'auto', ...
                 'presortflag', 'auto');

optionloc = cellfun('isclass',varargin,'char');
if any(optionloc)
    optionloc = find(optionloc,1,'first');
    mainargin = varargin(1:optionloc-1);
    % retreive property/value pairs
    for k=optionloc:2:nargin-1
        options.(varargin{k})= varargin{k+1};
    end
else
    mainargin = varargin;
end

edges = NaN;
if length(mainargin)<=3
    [xy P] = deal(varargin{1:2});
    x = xy(:,1);
    y = xy(:,2);
    if length(mainargin)==3 % edge provided
        edges = varargin{3};
    end
    Px = P(:,1);
    Py = P(:,2);
else
    [x y Px Py] = deal(varargin{1:4});
     if length(mainargin)==5 % edge provided
        edges = varargin{5};
     end
end

edgeprovided = ~isnan(edges);

% Orginal size of the data
sz = size(x);

% Reshape in columns
Px = Px(:);
Py = Py(:);
x = x(:);
y = y(:);

% Cast to double of one of them is (required by Mex)
isxdbl = isa(x,'double');
isydbl = isa(y,'double');
isPxdbl = isa(Px,'double');
isPydbl = isa(Py,'double');
doubleengine = isxdbl || isydbl || isPxdbl|| isPydbl;
if doubleengine
    if ~isxdbl, x = double(x); end
    if ~isydbl, y = double(y); end
    if ~isPxdbl, Px = double(Px); end
    if ~isPydbl, Py = double(Py); end
end

Pxmin = min(Px);
Pxmax = max(Px);
Pymin = min(Py);
Pymax = max(Py);

% Select the tolerance for determining on-boundary points
if isequal(options.tol,'auto')
    ontol = 1e-9*max(Pxmax-Pxmin,Pymax-Pymin);
else
    ontol = options.tol;
end

if ~edgeprovided
    % We don't want duplicate end vertices
    if Px(1)==Px(end) && Py(1)==Py(end)
        Px(end) = [];
        Py(end) = [];
    end
    % Linear wrap around the points
    Px1 = Px;
    Py1 = Py;
    Px2 = Px([2:end 1]);
    Py2 = Py([2:end 1]);
else
    Px1 = Px(edges(:,1),:);
    Py1 = Py(edges(:,1),:);
    Px2 = Px(edges(:,2),:);
    Py2 = Py(edges(:,2),:);
end

% Filter data outside the rectangular box
inpoly = x>=Pxmin-ontol & x<=Pxmax+ontol & ...
         y>=Pymin-ontol & y<=Pymax+ontol;
x = x(inpoly);
y = y(inpoly);

if isequal(options.presortflag,'auto')
    n = size(x,1);
    m = size(Px1,1);
    % We don't do presorting for small size polygon or small size data
    % Empirical law from experimental tests (Bruno)
    presortflag = (m>32) && (n*m > 25000);
else
    presortflag = options.presortflag;
end

if presortflag
    % Sort the array in x, and find the brackets. This is used to find
    % easily which data points have abscissa fall into the abcissa bracket
    % of each edge of the polygon.
    [x ix first last] = presort(Px1, Px2, x);
    y = y(ix); % arrange y in the same order

    % Call mex engine
    if doubleengine
        [in on] = insidepoly_dblengine(x, y, Px1, Py1, Px2, Py2, ontol, ...
                                       first, last);
    else % single arrays
        [in on] = insidepoly_sglengine(x, y, Px1, Py1, Px2, Py2, ontol, ...
                                       first, last);
    end

    % Restore the original order
    in(ix) = in;
    on(ix) = on;
else % No presorting
    % Call mex engine, without presorting
    if doubleengine
        [in on] = insidepoly_dblengine(x, y, Px1, Py1, Px2, Py2, ontol);
    else % single arrays
        [in on] = insidepoly_sglengine(x, y, Px1, Py1, Px2, Py2, ontol);
    end
end

in = in | on;

if nargout>=2
    onboundary = inpoly;
    onboundary(inpoly) = on;
    % Reshape to original size
    onboundary = reshape(onboundary, sz);
end

inpoly(inpoly) = in;
% Reshape to original size
inpoly = reshape(inpoly, sz);

end % insidepoly

%%
function [xsorted ix first last] = presort(Px1, Px2, x)
% (Px1, Px2) abscissa of vertices, x abscissa of data, they are supposed
% to be ranged in column.
% Return:
%   xsorted as sort(x) = x(ix)
%   "first" & "last" indexes such that, for each vertice point
%       min(Px1,Px2) <= xsorted(first:last) <= max(Px1,Px2)

% left and right brackets of the segment
Pmin = min(Px1,Px2);
Pmax = max(Px1,Px2);

nvertices = size(Px1,1);

% We seek to see how x interveaves with Pmin by sorting the ensemble
[trash is] = sort([Pmin; x],1); %#ok
isdata = is>nvertices; % tail index, i.e., belong to data abscissa x
anchor = find(~isdata);
% Get the sorted data alone
ix = is(isdata)-nvertices;
xsorted = x(ix); % sorted x
% Index of the first element in xsorted such that
%   xsorted(first)>=sort(Pmin)
first = anchor-(0:nvertices-1).';
% Rearrange first corresponds to the original order
ip = is(anchor);
first(ip) = first;

% determine how Pmax interleaves with xsorted, i.e.,
% index of the last element in xsorted such that xsorted(first)<=Pmax
% Note: in case of draw in binning edges, HISTC must return the last edge
[trash last] = histc(Pmax, [xsorted; inf]); %#ok

end % presort


function realimag(c)
%REALIMAG     Display real and imaginary part of interval hessians separately
%
%   realimag(c)
%

% written  04/04/04     S.M. Rump
% modified 04/06/05     S.M. Rump  rounding unchanged
% modified 02/11/06     S.M. Rump  SparseInfNanFlag removed
%

  loose = strcmp(get(0,'FormatSpacing'),'loose');

  name = inputname(1);
  if isempty(name)                    % happens for display(hessianinit(random))
    name = 'ans';
  end

  if isreal(c.x)
    display(c,name)
  else
    if loose, disp(' '); end
    display(real(c),['real(' name ')'])
    if loose, disp(' '); end

    if loose, disp(' '); end
    display(imag(c),['imag(' name ')'])
    if loose, disp(' '); end
  end


% Compute QCFC correlations and plot them (Parkes et al 2018, Ciric et al
% 2018, Power et al 2012). Computes edgewise correlations between
% connectivity estimates (stored in bs.connectivity.regions.r) and a QC metric (typically head
% motion) passed in by user
function qcfc(bs, qc_metric)

    flat_conn_mat = flatten_conn_matrices(bs);
    corrs = corr(flat_conn_mat, qc_metric);
    figure; histogram(corrs)
    fprintf('Mean (SD) corr = %3.2f (%3.2f)\n', mean(corrs), std(corrs));
end


function [B,twom] = multicatbipartite_f(A,gamma,omega)
% MULTICATBIPARTITE_F  returns multilayer Barber modularity matrix for unordered undirected bipartite networks, function handle version
%
% Version: 2.2.0
% Date: Thu 11 Jul 2019 12:25:42 CEST
%
% MULTICATBIPARTITE_F [B,twomu] = MULTICATBIPARTITE_F(A,gamma,omega)
%
% Input: A: Cell array of MxN adjacency matrices for each layer of a
%           multilayer undirected bipartite network
%        gamma: resolution parameter
%        omega: interlayer coupling strength
%
% Output: B: function handle where B(i) returns the ith column of the
%            [(M+N)xT]x[(M+N)xT] flattened modularity tensor for the
%            multilayer bipartite network with uniform ordinal coupling (T
%            is the number of layers of the network)
%         twomu: normalisation constant
%
% Usage: [B,twomu]=multicatbipartite_f(A,gamma,omega);
%        [S,Q]=genlouvain(B); % see iterated_genlouvain.m and
%          postprocess_categorical_multilayer.m for how to improve output
%          multilayer partition
%        Q=Q/twom;
%        S=reshape(S,M+N,T);
%
%  [B,twom] = MULTICATBIPARTITE_F(A,GAMMA, OMEGA) with A a cell array of
%   matrices of equal size each representing an undirected bipartite network
%   "layer" computes the multilayer Barber modularity matrix using the quality
%   function described in Mucha et al. 2010, with intralayer resolution
%   parameter GAMMA, and with interlayer coupling OMEGA connecting all-to-all
%   layers. Once the mulilayer modularity matrix is computed,
%   optimization can be performed by the generalized Louvain code GENLOUVAIN
%   or ITERATED_GENLOUVAIN. The  output B can be used with other heuristics,
%   provided the same mapping is used to go from the multilayer tensor to
%   the multilayer flattened matrix. That is, the node-layer tuple (i,s)
%   is mapped to i + (s-1)*(M+N). [Note that we can define a mapping between
%   a multilayer partition S_m stored as an (M+N) by T matrix and the
%   corresponding flattened partition S stored as an MNT by 1 vector. In
%   particular S_m = reshape(S,M+N,T) and S = S_m(:). Note that nodes i=1:M
%   correspond to the first class (i.e. the rows of A) and nodes i=M+1:M+N
%   correspond to the second class (i.e. the columns of A) of the bipartite
%   network.]
%
%   Notes:
%     The matrices in the cell array A are assumed to be of equal size.
%     This assumption is not checked here.
%
%     For smaller systems, it is potentially more efficient (and easier) to
%     directly use the sparse quality/modularity matrix B in MULTICATBIPARTITE.
%
%     This code serves as a template and can be modified for situations
%     with other wrinkles (e.g., different intralayer null models,
%     different numbers of nodes from layer-to-layer, or systems which are
%     both multiplex and longitudinal).  That is, this code is only a
%     starting point; it is by no means exhaustive.
%
%     By using this code, the user implicitly acknowledges that the authors
%     accept no liability associated with that use.  (What are you doing
%     with it anyway that might cause there to be a potential liability?!?)
%
% References:
%       Barber, M. Modularity and community detection in bipartite networks.
%           Phys. Rev. E 76, 066102 (2007).
%
%       Mucha, P. J., Richardson, T., Macon, K., Porter, M. A. & Onnela, J.-P.
%           Community structure in time-dependent, multiscale, and multiplex networks.
%           Science 328, 876-878 (2010).

if nargin<2||isempty(gamma)
    gamma=1;
end

if nargin<3
	omega=1;
end

[m,n]=size(A{1});
N=m+n;
T=length(A);

if length(gamma)==1
    gamma=repmat(gamma,T,1);
end

k=zeros(m,T);
d=zeros(T,n);
mm=zeros(T,1);

twom=0;
for j=1:T
    twom = twom + sum(sum(A{j}));
    k(:,j)=sum(A{j},2);
    d(j,:)=sum(A{j});
    mm(j)=sum(k(:,j));
end

%interslice connections
all2all= N*[(-T+1):-1,1:(T-1)];
C=omega*spdiags(ones(N*T,2*T-2),all2all,N*T,N*T);

%bipartite modularity matrix
    function modi=modf(i)

        s=ceil(i/(N+eps));
        if mm(s)~=0
            ii=i-(s-1)*N;
            if ii<=m
                indx=(m+1:N)+(s-1)*N;
                v=A{s}(ii,:)-gamma(s)*k(ii,s)*d(s,:)/mm(s);

                modi=sparse(indx,1,v,N*T,1,n+2);
            else
                indx=(1:m)+(s-1)*N;
                v=A{s}(:,ii-m)-gamma(s)*k(:,s)*d(s,ii-m)/mm(s);

                modi=sparse(indx,1,v,N*T,1,m+2);
            end

            modi=modi+C(:,i);
        else
            modi=C(:,i);

        end
    end

B=@modf;
twom=2*twom+2*N*(T-1)*T*omega;
end


function [Y,W,SetupStruc] = Process_AuxIVA(s,Transfer,SetupStruc)
K = SetupStruc.AuxIVA.K;
hop = SetupStruc.AuxIVA.hop;
win = hanning(K,'periodic');
win = win/sqrt(sum(win(1:hop:K).^2));
SetupStruc.AuxIVA.win = win;  % Preserve 'win' in 'SetupStruc'
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
N = size(s,2);
for i = 1:N
    X(:,:,i) = fft(enframe(s(:,i),win,hop)');
end
frame_N = size(X,2);
K_m = K/2+1;
Num = size(Transfer,3);
Y = zeros((frame_N-1)*hop+K,Num);
Y_f = zeros(Num,frame_N,K);
%%%%%%%%%%%%%%%%%%%%%%%%%% Obtain processing matrix 'W'
X_sp = zeros(Num,frame_N,K_m);
W_IVA = zeros(Num,Num,K_m);
V_sp = zeros(Num,N,K_m);
theta = 10^-6;
for i = 1:K_m
    X_f = permute(X(i,:,:),[3 2 1]);
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    %%% Initialize W by PCA
    [E,D] = PCA(X_f,1,Num);
    V = sqrt(D)\E';
    V_sp(:,:,i) = V;
    X_sp(:,:,i) = V*X_f;
    %%%%%%%%%%%% Adjust amplitude of 'w'
    W_o = eye(Num);
    y_f = W_o*V*X_f;
%     norm = max(abs(y_f),[],2);
%     if(norm>10)
%         norm = repmat(norm,1,Num);
%         W_o = W_o./norm;
%         y_f = W_o*V*X_f;
%     end
    W_IVA(:,:,i) = W_o;
    Y_f(:,:,i) = y_f;
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%% IVA iterations
max_iteration = 200;
Y_k = zeros(Num,frame_N);
epsi = 1e-6;
pObj = inf;
A = zeros(1001,2)-1; %%%% Show the decrease of non-linear correlation, IVA max iterations 1000
for iteration = 1:max_iteration
    for i = 1:Num
        y_temp = permute(Y_f(i,:,:),[3 2 1]);
        Y_k(i,:) = sqrt(sum(abs(y_temp(1:K_m,:)).^2))+epsi;
    end
    dlw = 0;
    for i = 1:K_m
        W = W_IVA(:,:,i);
        X_f = X_sp(:,:,i);
        dlw = dlw +log(abs(det(W))+epsi);
        for i_n = 1:Num
            G_ = Y_k(i_n,:).^-1;
            G_ = repmat(G_,Num,1);
            Vk = (G_.*X_f)*X_f'/frame_N;
            if rcond(Vk)<theta
                Vk = Vk+eye(Num)*max(eig(Vk))*theta;
            end
            wk = inv(W*Vk);
            wk = wk(:,i_n);
            wk = wk/(sqrt(wk'*Vk*wk)+epsi);
            W(i_n,:) = wk';
        end
        W_IVA(:,:,i) = W;
        Y_f(:,:,i) = W*X_f;
    end
    Obj = (sum(sum(Y_k))/frame_N-2*dlw)/(Num*K_m);
    dObj = pObj-Obj;
    pObj = Obj;
    A(iteration,:) = [Obj,abs(dObj)/abs(Obj)];
    if(abs(dObj)/abs(Obj)<theta)
        break;
    end
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%% Post processing
W = zeros(Num,N,K_m);
Y_f(:,:,1) = zeros(Num,frame_N);
for i = 2:K_m
    W_inv = pinv(W_IVA(:,:,i)*V_sp(:,:,i));
    for ii = 1:Num
        Y_f(ii,:,i) = Y_f(ii,:,i)*W_inv(1,ii);
        W_IVA(ii,:,i) = W_IVA(ii,:,i)*W_inv(1,ii);
    end
    W(:,:,i) = W_IVA(:,:,i)*V_sp(:,:,i);
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    if(i~=K_m)
        Y_f(:,:,K+2-i) = conj(Y_f(:,:,i));
    end
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Recover signals
if(K/hop==2)
    win = ones(K,1);
end
for i = 1:Num
    y_temp = permute(Y_f(i,:,:),[3 2 1]);
    Y(:,i) = overlapadd(real(ifft(y_temp))',win,hop);
end
return;


function [source] = estimate_fwhm2(source, maxdist)

% ESTIMATE_FWHM2(SOURCE, MAXDIST)
%
% This function computes the Gaussian fwhm of the spatial filters, according to
% least-squares Gaussian fit including data points up until MAXDIST from the
% locations of interest.
%
% This function can only deal with scalar filters.

if nargin<2, maxdist = 2.5; end % maxdist should be in units of the pos in source
if isempty(maxdist), maxdist = inf; end

if islogical(source.inside)
  inside = find(source.inside);
else
  inside = source.inside;
end
ninside = numel(inside);

if ~isfield(source.avg, 'filter')
  ft_error('the input should contain spatial filters in');
end

nchan   = size(source.avg.filter{inside(1)},2);
ndir    = size(source.avg.filter{inside(1)},1);
if ndir~=1,
  ft_error('only scalar filters are allowed as input');
end

%get filters and positions
filter = cat(1,source.avg.filter{inside});
pos    = source.pos(inside,:);

%get the filter correlation matrix
Cmat   = filter*filter';
Cmat   = abs(Cmat)./sqrt(diag(Cmat)*diag(Cmat)');

fwhm    = zeros(size(source.pos,1),1);
onesvec = ones(ninside,1);
for k = 1:ninside
 d   = sqrt(sum( (pos-pos(k*onesvec,:)).^2, 2));
 sel = d<=maxdist;
 s   = gaussfit(Cmat(sel,k)',d(sel)');
 fwhm(inside(k)) = s;
end
source.fwhm = fwhm;

% fwhm    = zeros(size(source.pos,1),3,3);
% onesvec = ones(ninside,1);
% for k = 1:ninside
%   dpos = pos-pos(k*onesvec,:);
%   d   = sqrt(sum(dpos.^2, 2));
%   sel = d<=maxdist;
%   [s,dum] = gaussfit3D(dpos(sel,:), Cmat(sel,k));
%   fwhm(inside(k),:,:) = s;
% end
% source.fwhm = fwhm;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [fwhm] = gaussfit(dat, design)

%this function performs a least-squares gaussian fit

%create independent variable
ivar = design.^2;
dat  = log(dat+eps);
beta = dat*ivar'*pinv(ivar*ivar');

sigma = sqrt(-0.5./beta(:,1));
fwhm  = 2.*sqrt(2.*log(2)).*sigma;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [invsigma, R] = gaussfit3D(x, y)

% function for a linear least squares fit of a Gaussian function in 3D
% (with a peak of 1 at (0,0,0)).
%
% use as
%   sigma = gaussfit3D(x, y)
%
% x = Nx3 and y = Nx1, sigma is the covariance describing the gaussian

% create design matrix based in the independent variable x
design = [x(:,1).^2 x(:,2).^2 x(:,3).^2 2.*(x(:,1).*x(:,2)) 2.*(x(:,1).*x(:,3)) 2.*(x(:,2).*x(:,3))];
%design = design-repmat(mean(design,1),[size(design,1) 1]);
design = cat(2, design, ones(size(design,1),1));

% log-transform the dependent variable y
dat = -2.*log(y+eps);

% regression
beta = design\dat;

% residuals
res  = dat - design*beta;
R    = 1-sum(res.^2)./sum(dat.^2);

% create output
invsigma = [beta(1) beta(4) beta(5);beta(4) beta(2) beta(6);beta(5) beta(6) beta(3)];
%sigma = inv(invsigma);

% by construction the exponent to the gaussian looks like this
%
% exp( -0.5.*(-x'*invsigma*x) )
%
% -x'*invsigma*x = [x1 x2 x3]*[a d e [x1
%                              d b f  x2
%                              e f c] x3] =
%
% a*x1^2+b*x2^2+c*x3^2+2d*(x1x2)+2e*(x1x3)+2f*(x2x3)
%
% the variables a through f correspond with the ordered beta weights


function V = est_calcInvCovMatFourier(Rinv,E,foi,fs,M,p, verb)
%
% Obtain the frequency domain transform of the (inverse) covariance matrix
% of an M-variate VAR[p] process.
%
% Let
% This is needed for calculating the renormalized PDC [2]
%
% Inputs:
%
%     Rinv: inverse process covariance matrix obtained from est_calcInvCovMat
%     E:    noise covariance matrix
%     foi:  frequencies of interest (Hz)
%     fs:   sampling rate
%     M:    # chans
%     p:    model order
%     verb: verbosity level. 0 = no output, 1=text.
%
% Outputs:
%
%     V:    Frequency-domain transform of the inverse covariance matrix
%
% References:
%
% [1] Lutkepohl, H. (2007) New Introduction to Time Series Analysis. Springer.
% [2] Schelter et al, (2009). Assessing the strength of directed influences
% among neural signals using renormalized partial directed coherence.
% Journal of Neuroscience Methods. 179:121-130.
%
% See Also: est_calcInvCovMat()
%
% Author: Tim Mullen 2010, SCCN/INC, UCSD
% Email:  tim@sccn.ucsd.edu

% This function is part of the Source Information Flow Toolbox (SIFT)
%
% This program is free software; you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation; either version 3 of the License, or
% (at your option) any later version.
%
% This program is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with this program; if not, write to the Free Software
% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

DEBUG = 0;

%% extract the diagonal elements of H=Rinv
% structure of Hd is (e.g., for p=2):
% [diag(H(1,1)), diag(H(2,1)), diag(H(1,2)), diag(H(2,2))]
% where diag(H(u,v)) is the column vector formed by the diagonal of
% submatrix H(u,v) of H
Hd = zeros(M,p^2);
cnt=1;
for v=1:p
    for u=1:p
        Hd(:,cnt)=diag(Rinv((u-1)*M+1:u*M,(v-1)*M+1:v*M));
        cnt=cnt+1;
    end
end

if DEBUG
    for j=1:N
        try chol(reshape(Hd(j,:),[p p])); catch; fprintf('Hj<0: j=%d - ',j); keyboard; end;
    end
end

%% create u,v index vectors
% us = [1 2 ... p 1 2 ... p ... ]'   (p^2 length)
% vs = [1 1 ... 1 2 2 ... 2 ... ]'   (p^2 length)
us = repmat(1:p,1,p)';
vs = zeros(p^2,1);
for ii=1:p
    vs((ii-1)*p+1:(ii-1)*p+p)=ones(p,1)*ii;
end

%% construct the V matrix for all freqs
fi=0;
COS=zeros(p^2,4);
freqs=(2*pi*foi)/fs;
V = zeros(length(freqs),M,M,2,2); % NOTE: V will end up (M,M,freqs,2,2)
if verb, h=waitbar(0,'calculating V^-1...'); end
for f=freqs
    fi=fi+1;

    %% construct cosine matrix
    COS(:,1) = cos(us*f).*cos(vs*f);
    COS(:,2) = sin(us*f).*cos(vs*f);
    COS(:,3) = cos(us*f).*sin(vs*f);
    COS(:,4) = sin(us*f).*sin(vs*f);

    %% multiply Hd and COS matrices to get matrix where row j is
    % [sum_{u,v=1 : p} Hjj(u,v)COS_11(u,v), ...
    %  sum_{u,v=1 : p} Hjj(u,v)COS_21(u,v), ...
    %  sum_{u,v=1 : p} Hjj(u,v)COS_12(u,v), ...
    %  sum_{u,v=1 : p} Hjj(u,v)COS_22(u,v)]
    %
    % where COSab(u,v) is the a,bth element of the sine-cos transform
    % matrix evaluated at u,v.
    %
    % NOTE: reshaping row j to 2x2 yeilds a matrix proportional to V_ij(f)
    % for some specific i
    Vm = Hd*COS;

    % now we multiply in the variances of the i's (E(i,i)) to generate the
    % full V matrix
    Vm = kron(diag(E)',Vm);   % Vm = kron(diag(p^2*E)',Vm);

    % next, reshape to desired structure (NxNx2x2)
    V(fi,:,:,:,:) = permute(reshape(Vm',2,2,M,M),[3 4 1 2]);

    if verb, waitbar(fi/length(freqs),h); end
end % for freqs

% permute V: (chs,chs,freqs,2,2)
V = permute(V,[2 3 1 4 5]);
if verb, close(h); end


% tutorial4_solutions
%   This script contains the solutions for Tutorial 4, see Tutorial 4 in
%   "RAVEN tutorials.docx" for more details.
%
%   NOTE: Many of these changes are easier to do in the Excel sheet. They
%   are done here in code just to avoid having several model files.

%Import the Excel model
model=importExcelModel('smallYeastBad.xlsx');

%Close all uptake and maximize for production
model=setParam(model,'eq',{'glcIN', 'o2IN'},[0 0]);
model=setParam(model,'obj',{'acOUT' 'biomassOUT' 'co2OUT' 'ethOUT' 'glyOUT'},[1 1 1 1 1]);
sol=solveLP(model);
printFluxes(model,sol.x,true); %Nothing produced, good

%Add some fake reactions
rxns.rxns={'FREE_ATP';'FREE_NADH';'FREE_NADPH'};
rxns.equations={'ATP <=> ADP + phosphate';'NAD(+) <=> NADH';'NADP(+) <=> NADPH'};
model=addRxns(model,rxns,2,'c');
sol=solveLP(model,1);

%Lots of ethanol produced. Also plot the equations to make the error easier
%to find
printFluxes(model,sol.x,false,[],[],'%rxnID (%rxnName):%flux\n\t%eqn\n');

%See that ADH1 should only produce one unit of ethanol. Change the reaction
%equation
model=changeRxns(model,'ADH1','acetaldehyde[c] + NADH[c] => ethanol[c] + NAD(+)[c]',3);
sol=solveLP(model,1);
printFluxes(model,sol.x,true); %Nothing produced, good

%Add excretion of all metabolites
model.b=[model.b inf(numel(model.b),1)];
sol=solveLP(model,1);
printFluxes(model,sol.x,false,10^-5,[],'%rxnID (%rxnName):\n\t%eqn\n\t%flux\n');

%By looking at the reactions which were unbalanced and that were in the
%flux list one can see that FBP should be changed to result in only one unit
%of F6P
model=changeRxns(model,'FBP','beta-D-fructofuranose 1,6-bisphosphate[c] => beta-D-fructofuranose 6-phosphate[c] + phosphate[c]',3);
sol=solveLP(model,1);
printFluxes(model,sol.x,false,10^-5,[],'%rxnID (%rxnName):\n\t%eqn\n\t%flux\n');

%The same thing again and one should change PFK to only give one unit of
%F16P
model=changeRxns(model,'PFK','ATP[c] + beta-D-fructofuranose 6-phosphate[c] => ADP[c] + beta-D-fructofuranose 1,6-bisphosphate[c]',3);
sol=solveLP(model,1);
printFluxes(model,sol.x,false,10^-5,[],'%rxnID (%rxnName):\n\t%eqn\n\t%flux\n'); %Now it works

%Set all uptakes and production to 0
model=setParam(model,'eq',getExchangeRxns(model),0);

%Since it is checked which metabolites could be consumed without
%production, one can no longer have free production of all metabolites
model.b=model.b(:,1);
I=canConsume(model);
disp(model.mets(I)); %These 12 metabolites can be consumed without any production

%Allow all uptake
model.b=[ones(numel(model.b),1)*-1000 model.b];

%Pick CO2 and force uptake of it
model=setParam(model,'eq',{'co2OUT'},-1); %Negative output means input
sol=solveLP(model);
printFluxes(model,sol.x,false,10^-5,[],'%rxnID (%rxnName):\n\t%eqn\n\t%flux\n'); %Now it works

%See that PDC converts pyruvate (3 carbons) to acetaldehyde (2 carbons)
%without any other products. If one googles, one may realize that CO2 is
%missing. This would be simpler to change in the Excel file (or using
%changeRxns), but one can change it here as an exercise. One therefore
%needs to find the index of the reactions and the index of cytosolic CO2 in
%order to change the reaction
Irxn=ismember(model.rxns,'PDC');
Imet=ismember(model.mets,'CO2_c');
model.S(Imet,Irxn)=1; %The coefficient is 1.0

%Display the new equation just to be sure
constructEquations(model,Irxn)

%The solution is now not feasible, meaning that it is no longer possible to
%force uptake of CO2 without any output
sol=solveLP(model);

%***Second part of tutorial
model=importExcelModel('smallYeastBad2.xlsx',true,false,true); %This has to be loaded with the setting to ignore error or it would find the error
[reducedModel, deletedReactions, deletedMetabolites]=simplifyModel(model,false,false,false,true);
disp(deletedReactions);
disp(deletedMetabolites);

%It turned out that G15L_c was spelled G15Lc in one reaction. The best
%solution would be just to change it in the reaction list and remove the
%duplicate metabolite, but one can do it here as an exercise. The indexes
%of the two metabolites are needed.
Igood=ismember(model.mets,'G15L_c');
Ibad=ismember(model.mets,'G15Lc');

%Get all reactions and the coefficients in which the wrong one participates
%move them to be for the right one instead
model.S(Igood,:)=model.S(Igood,:)+model.S(Ibad,:);

%Delete the bad one
model=removeMets(model,'G15Lc');
[reducedModel, deletedReactions, deletedMetabolites]=simplifyModel(model,false,false,false,true);
disp(deletedReactions);
disp(deletedMetabolites);

%The only difference was that there were 20 deleted metabolites instead of
%21. Nothing too spectacular. Check production can tell the user what one
%needs to connect.
[notProducedMets, ~, neededForProductionMat, minToConnect]=checkProduction(model,true,model.comps,false);

%In order to have production of all 54 metabolites one needs to enable
%production of these 12. This small model does not include net synthesis of
%co-factors, so one should concentrate on the other ones. Glycerone
%phosphate allows for connection 18 others, so it seems like a good target.
disp(minToConnect);

%If one googles around a little bit, and knows the metabolism, one would
%find that DHAP (dihydroxyacetone) and GLYP (glycerone phosphate) are
%actually synonymes. Only use DHAP
Igood=ismember(model.mets,'DHAP_c');
Ibad=ismember(model.mets,'GLYP_c');

%Get all reactions and the coefficients in which the wrong one participates
%move them to be for the right one instead
model.S(Igood,:)=model.S(Igood,:)+model.S(Ibad,:);

%Delete the bad one
model=removeMets(model,'GLYP_c');
[reducedModel, deletedReactions, deletedMetabolites]=simplifyModel(model,false,false,false,true);
disp(deletedReactions);
disp(deletedMetabolites);
[notProducedMets, ~, neededForProductionMat, minToConnect]=checkProduction(model,true,model.comps,false);
disp(minToConnect);

%Still quite a lot of gaps and no immediate way to fix it. One could try
%including reactions from a reference network and see if that helps. Use
%the small yeast model from Tutorial 3
refModel=importExcelModel('smallYeast.xlsx');
[newConnected, cannotConnect, addedRxns, newModel]=fillGaps(model,{refModel},false);
disp(addedRxns);
disp(newConnected);

%By including the ALD6 reaction from the reference model it was possible to
%connect 21 reactions
[reducedModel, deletedReactions, deletedMetabolites]=simplifyModel(newModel,false,false,false,true);
disp(deletedMetabolites);
disp(deletedReactions);

%All the model seems to be connected

%All this stuff may be done in a more automated manner as well
model=importExcelModel('smallYeastBad2.xlsx',true,false,true);
gapReport(model,{refModel});


function [alpha, beta, gamma, loglik, xi, gamma2] = fwdback_twoslice(engine, init_state_distrib, transmat, obslik, varargin)
% FWDBACK Compute the posterior probs. in an HMM using the forwards backwards algo.
%
% [alpha, beta, gamma, loglik, xi, gamma2] = fwdback(init_state_distrib, transmat, obslik, ...)
%
% Notation:
% Y(t) = observation, Q(t) = hidden state, M(t) = mixture variable (for MOG outputs)
% A(t) = discrete input (action) (for POMDP models)
%
% INPUT:
% init_state_distrib(i) = Pr(Q(1) = i)
% transmat(i,j) = Pr(Q(t) = j | Q(t-1)=i)
%  or transmat{a}(i,j) = Pr(Q(t) = j | Q(t-1)=i, A(t-1)=a) if there are discrete inputs
% obslik(i,t) = Pr(Y(t)| Q(t)=i)
%   (Compute obslik using eval_pdf_xxx on your data sequence first.)
%
% Optional parameters may be passed as 'param_name', param_value pairs.
% Parameter names are shown below; default values in [] - if none, argument is mandatory.
%
% For HMMs with MOG outputs: if you want to compute gamma2, you must specify
% 'obslik2' - obslik(i,j,t) = Pr(Y(t)| Q(t)=i,M(t)=j)  []
% 'mixmat' - mixmat(i,j) = Pr(M(t) = j | Q(t)=i)  []
%
% For HMMs with discrete inputs:
% 'act' - act(t) = action performed at step t
%
% Optional arguments:
% 'fwd_only' - if 1, only do a forwards pass and set beta=[], gamma2=[]  [0]
% 'scaled' - if 1,  normalize alphas and betas to prevent underflow [1]
% 'maximize' - if 1, use max-product instead of sum-product [0]
%
% OUTPUTS:
% alpha(i,t) = p(Q(t)=i | y(1:t)) (or p(Q(t)=i, y(1:t)) if scaled=0)
% beta(i,t) = p(y(t+1:T) | Q(t)=i)*p(y(t+1:T)|y(1:t)) (or p(y(t+1:T) | Q(t)=i) if scaled=0)
% gamma(i,t) = p(Q(t)=i | y(1:T))
% loglik = log p(y(1:T))
% xi(i,j,t-1)  = p(Q(t-1)=i, Q(t)=j | y(1:T))
% gamma2(j,k,t) = p(Q(t)=j, M(t)=k | y(1:T)) (only for MOG  outputs)
%
% If fwd_only = 1, these become
% alpha(i,t) = p(Q(t)=i | y(1:t))
% beta = []
% gamma(i,t) = p(Q(t)=i | y(1:t))
% xi(i,j,t-1)  = p(Q(t-1)=i, Q(t)=j | y(1:t))
% gamma2 = []
%
% Note: we only compute xi if it is requested as a return argument, since it can be very large.
% Similarly, we only compute gamma2 on request (and if using MOG outputs).
%
% Examples:
%
% [alpha, beta, gamma, loglik] = fwdback(pi, A, multinomial_prob(sequence, B));
%
% [B, B2] = mixgauss_prob(data, mu, Sigma, mixmat);
% [alpha, beta, gamma, loglik, xi, gamma2] = fwdback(pi, A, B, 'obslik2', B2, 'mixmat', mixmat);

if nargout >= 5, compute_xi = 1; else compute_xi = 0; end
if nargout >= 6, compute_gamma2 = 1; else compute_gamma2 = 0; end

[obslik2, mixmat, fwd_only, scaled, act, maximize, compute_xi, compute_gamma2] = process_options(varargin, 'obslik2', [], 'mixmat', [], 'fwd_only', 0, 'scaled', 1, 'act', [], 'maximize', 0, 'compute_xi', compute_xi, 'compute_gamma2', compute_gamma2);

[Q T] = size(obslik);

if isempty(obslik2)
  compute_gamma2 = 0;
end

if isempty(act)
  act = ones(1,T);
  transmat = { transmat } ;
end

scale = ones(1,T);

% scale(t) = Pr(O(t) | O(1:t-1)) = 1/c(t) as defined by Rabiner (1989).
% Hence prod_t scale(t) = Pr(O(1)) Pr(O(2)|O(1)) Pr(O(3) | O(1:2)) = Pr(O(1), ... ,O(T))
% or log P = sum_t log scale(t).
% Rabiner suggests multiplying beta(t) by scale(t), but we can instead
% normalise beta(t) - the constants will cancel when we compute gamma.

loglik = 0;

alpha = zeros(Q,T);
gamma = zeros(Q,T);
if compute_xi
  xi = zeros(Q,Q,T-1);
else
  xi = [];
end

%%%%%%%%% Forwards %%%%%%%%%%

t = 1;
alpha(:,1) = init_state_distrib(:) .* obslik(:,t);
if scaled
  %[alpha(:,t), scale(t)] = normaliseC(alpha(:,t));
  [alpha(:,t), scale(t)] = normalise(alpha(:,t));
end
if scaled, assert(approxeq(sum(alpha(:,t)),1)), end
for t=2:T
  %trans = transmat(:,:,act(t-1))';
  trans = transmat{act(t-1)};
  if maximize
    m = max_mult(trans', alpha(:,t-1));
    %A = repmat(alpha(:,t-1), [1 Q]);
    %m = max(trans .* A, [], 1);
  else
    m = trans' * alpha(:,t-1);
  end
  alpha(:,t) = m(:) .* obslik(:,t);
  if scaled
    %[alpha(:,t), scale(t)] = normaliseC(alpha(:,t));
    [alpha(:,t), scale(t)] = normalise(alpha(:,t));
  end
  if compute_xi & fwd_only  % useful for online EM
    %xi(:,:,t-1) = normaliseC((alpha(:,t-1) * obslik(:,t)') .* trans);
    xi(:,:,t-1) = normalise((alpha(:,t-1) * obslik(:,t)') .* trans);
  end
  if scaled, assert(approxeq(sum(alpha(:,t)),1)), end
end
if scaled
  if any(scale==0)
    loglik = -inf;
  else
    loglik = sum(log(scale));
  end
else
  loglik = log(sum(alpha(:,T)));
end

if fwd_only
  gamma = alpha;
  beta = [];
  gamma2 = [];
  return;
end

%%%%%%%%% Backwards %%%%%%%%%%

beta = zeros(Q,T);
if compute_gamma2
  M = size(mixmat, 2);
  gamma2 = zeros(Q,M,T);
else
  gamma2 = [];
end

beta(:,T) = ones(Q,1);
%gamma(:,T) = normaliseC(alpha(:,T) .* beta(:,T));
gamma(:,T) = normalise(alpha(:,T) .* beta(:,T));
t=T;
if compute_gamma2
  denom = obslik(:,t) + (obslik(:,t)==0); % replace 0s with 1s before dividing
  gamma2(:,:,t) = obslik2(:,:,t) .* mixmat .* repmat(gamma(:,t), [1 M]) ./ repmat(denom, [1 M]);
  %gamma2(:,:,t) = normaliseC(obslik2(:,:,t) .* mixmat .* repmat(gamma(:,t), [1 M])); % wrong!
end
for t=T-1:-1:1
  b = beta(:,t+1) .* obslik(:,t+1);
  %trans = transmat(:,:,act(t));
  trans = transmat{act(t)};
  if maximize
    B = repmat(b(:)', Q, 1);
    beta(:,t) = max(trans .* B, [], 2);
  else
    beta(:,t) = trans * b;
  end
  if scaled
    %beta(:,t) = normaliseC(beta(:,t));
    beta(:,t) = normalise(beta(:,t));
  end
  %gamma(:,t) = normaliseC(alpha(:,t) .* beta(:,t));
  gamma(:,t) = normalise(alpha(:,t) .* beta(:,t));
  if compute_xi
    %xi(:,:,t) = normaliseC((trans .* (alpha(:,t) * b')));
    xi(:,:,t) = normalise((trans .* (alpha(:,t) * b')));
    %xi(:,:,t) = (trans .* (alpha(:,t) * b'));
  end
  if compute_gamma2
    denom = obslik(:,t) + (obslik(:,t)==0); % replace 0s with 1s before dividing
    gamma2(:,:,t) = obslik2(:,:,t) .* mixmat .* repmat(gamma(:,t), [1 M]) ./ repmat(denom, [1 M]);
    %gamma2(:,:,t) = normaliseC(obslik2(:,:,t) .* mixmat .* repmat(gamma(:,t), [1 M]));
  end
end

% We now explain the equation for gamma2
% Let zt=y(1:t-1,t+1:T) be all observations except y(t)
% gamma2(Q,M,t) = P(Qt,Mt|yt,zt) = P(yt|Qt,Mt,zt) P(Qt,Mt|zt) / P(yt|zt)
%                = P(yt|Qt,Mt) P(Mt|Qt) P(Qt|zt) / P(yt|zt)
% Now gamma(Q,t) = P(Qt|yt,zt) = P(yt|Qt) P(Qt|zt) / P(yt|zt)
% hence
% P(Qt,Mt|yt,zt) = P(yt|Qt,Mt) P(Mt|Qt) [P(Qt|yt,zt) P(yt|zt) / P(yt|Qt)] / P(yt|zt)
%                = P(yt|Qt,Mt) P(Mt|Qt) P(Qt|yt,zt) / P(yt|Qt)
%


function [nodes2, edges2] = grMergeNodeClusters(nodes, edges)
%GRMERGENODECLUSTERS Merge cluster of connected nodes in a graph.
%
%   grMergeNodeClusters(nodes, edges)
%   Detects groups of nodes that belongs to the same global node, and
%   replace them by a unique node. Coordinates of reference node is given
%   by the median coordinates of cluster nodes.
%
%   This function is intended to be used as filter after a binary image
%   skeletonization and vectorization.
%
%
%   See also
%   grMergeNodesMedian
%

% ------
% Author: David Legland
% E-mail: david.legland@inrae.fr
% Created: 2003-08-13
% Copyright 2003-2022 INRA - TPV URPOI - BIA IMASTE

%% Initialization

% intialize result
nodes2 = nodes;
edges2 = edges;

% compute degree of each node
degrees = grNodeDegree(1:size(nodes, 1), edges)';

% find index of multiple nodes
indMul = find(degrees > 2);

% indices of edges that link several multiple nodes
indEdges = sum(ismember(edges, indMul), 2) == 2;

% associate a label to each cluster
labels = grLabel(nodes, edges(indEdges, :));
clusterLabels = unique(labels(indMul));

%% Replace each cluster by median point

% iterate on clusters
for i = 1:length(clusterLabels)
    % indices of nodes of the current cluster
    inds = find(labels == clusterLabels(i));

    % coordinates of new reference node
    clusterNodes = nodes(inds, :);
    medianNode = median(clusterNodes, 1);

    % replace coordinates of reference node
    refNode = min(inds);
    nodes2(refNode, :) = medianNode;

    % replace node indices in edge array
    edges2(ismember(edges2, inds)) = refNode;
end

%% Clean up

% keep only relevant nodes
inds = unique(edges2(:));
nodes2 = nodes2(inds, :);

% relabeling of edges
for i = 1:length(inds)
    edges2(edges2 == inds(i)) = i;
end

% remove double edges
edges2 = unique(sort(edges2, 2), 'rows');

% remove 'loops'
edges2(edges2(:,1) == edges2(:,2), :) = [];


function pass = test_mixed_tech( pref )
% This tests the chebfun2 constructor for mixed technology.

if ( nargin < 1 )
    pref = chebfunpref;
end
tol = 1e2 * pref.cheb2Prefs.chebfun2eps;

pass = [];

%% 'trig' in one or both dimensions

% Construct from function handle
u = @(x,y) sin(2*pi*x).*cos(2*pi*y);

f1 = chebfun2(u, 'trigx');
f2 = chebfun2(u, 'periodicy');
f3 = chebfun2(u, 'trig');
f4 = chebfun2(u);

pass(end+1) = ( norm(f1 - f2) < tol );
pass(end+1) = ( norm(f2 - f3) < tol );
pass(end+1) = ( norm(f3 - f4) < tol );

% Construct from data
m = 31;
n = 32;
u = @(x,y) sin(2*pi*x).*cos(2*pi*y);
[xc, yc] = chebpts2(n, m);
[xt, yt] = meshgrid(trigpts(n), trigpts(m));

f1 = chebfun2(u(xt,yc), 'trigx');
f2 = chebfun2(u(xc,yt), 'periodicy');
f3 = chebfun2(u(xt,yt), 'trig');
f4 = chebfun2(u(xc,yc));

pass(end+1) = ( norm(f1 - f2) < tol );
pass(end+1) = ( norm(f2 - f3) < tol );
pass(end+1) = ( norm(f3 - f4) < tol );

%% 'equi' in one or both dimensions
m = 31;
n = 32;
u = @(x,y) sin(x.*y);
[xc, yc] = chebpts2(n, m);
[xe, ye] = meshgrid(linspace(-1,1,n), linspace(-1,1,m));

f1 = chebfun2(u(xe,yc), 'equix');
f2 = chebfun2(u(xc,ye), 'equiy');
f3 = chebfun2(u(xe,ye), 'equi');
f4 = chebfun2(u(xc,yc));

pass(end+1) = ( norm(f1 - f2) < tol );
pass(end+1) = ( norm(f2 - f3) < tol );
pass(end+1) = ( norm(f3 - f4) < tol );

%% 'coeffs' in one or both dimensions
m = 31;
n = 32;
u = @(x,y) sin(x.*y);
[xc, yc] = chebpts2(n, m);

vals_vals     = u(xc,yc);
vals_coeffs   = chebtech2.vals2coeffs( vals_vals );
coeffs_vals   = chebtech2.vals2coeffs( vals_vals.' ).';
coeffs_coeffs = chebtech2.vals2coeffs( vals_coeffs.' ).';

f1 = chebfun2(u);
f2 = chebfun2(coeffs_vals, 'coeffsx');
f3 = chebfun2(vals_coeffs, 'coeffsy');
f4 = chebfun2(coeffs_coeffs, 'coeffs');

pass(end+1) = ( norm(f1 - f2) < tol );
pass(end+1) = ( norm(f2 - f3) < tol );
pass(end+1) = ( norm(f3 - f4) < tol );

%% 'trig' and 'coeffs' in one or both dimensions
m = 31;
n = 32;
u = @(x,y) sin(2*pi*x).*cos(2*pi*y);
[xc, yc] = chebpts2(n, m);
[xt, yt] = meshgrid(trigpts(n), trigpts(m));

trigvals_chebvals     = u(xt,yc);
chebvals_trigvals     = u(xc,yt);
trigvals_trigvals     = u(xt,yt);
trigvals_chebcoeffs   = chebtech2.vals2coeffs( trigvals_chebvals     );
trigcoeffs_trigvals   =  trigtech.vals2coeffs( trigvals_trigvals.'   ).';
trigcoeffs_chebvals   =  trigtech.vals2coeffs( trigvals_chebvals.'   ).';
trigcoeffs_chebcoeffs =  trigtech.vals2coeffs( trigvals_chebcoeffs.' ).';
chebvals_trigcoeffs   =  trigtech.vals2coeffs( chebvals_trigvals     );

f1 = chebfun2(u);
f2 = chebfun2(trigcoeffs_chebvals, 'coeffsx', 'trigx');
f3 = chebfun2(trigvals_chebcoeffs, 'coeffsy', 'trigx');
f4 = chebfun2(trigcoeffs_chebcoeffs, 'coeffs', 'trigx');
f5 = chebfun2(trigcoeffs_trigvals, 'trig', 'coeffsx');
f6 = chebfun2(chebvals_trigcoeffs, 'trigy', 'coeffsy');

pass(end+1) = ( norm(f1 - f2) < tol );
pass(end+1) = ( norm(f2 - f3) < tol );
pass(end+1) = ( norm(f3 - f4) < tol );
pass(end+1) = ( norm(f4 - f5) < tol );
pass(end+1) = ( norm(f5 - f6) < tol );

%% Preferences in one or both dimensions
p = pref;
p.tech = @trigtech;

f = chebfun2(@(x,y) sin(2*pi*x).*y, {p, []});
g = chebfun2(@(x,y) sin(2*pi*x).*y);
pass(end+1) = ( norm(f - g) < tol );

f = chebfun2(@(x,y) sin(2*pi*y).*x, {[], p});
g = chebfun2(@(x,y) sin(2*pi*y).*x);
pass(end+1) = ( norm(f - g) < tol );

f = chebfun2(@(x,y) sin(2*pi*x).*sin(2*pi*y), {p, p});
g = chebfun2(@(x,y) sin(2*pi*x).*sin(2*pi*y));
pass(end+1) = ( norm(f - g) < tol );

%% The last argument takes precedence
f = chebfun2(@(x,y) sin(2*pi*x).*y, 'trig', 'trigx');
g = chebfun2(@(x,y) sin(2*pi*x).*y, 'trigx');
pass(end+1) = ( norm(f - g) < tol );

m = 31;
n = 32;
u = @(x,y) sin(x.*y);
[xx, yy] = meshgrid(linspace(-1,1,n), linspace(-1,1,m));
uu = u(xx,yy);
f = chebfun2(uu, 'equix', 'equi');
g = chebfun2(uu, 'equi');
pass(end+1) = ( norm(f - g) < tol );

prefx = pref;
prefx.tech = @trigtech;
f = chebfun2(@(x,y) x, {prefx, []}, pref);
g = chebfun2(@(x,y) x, pref);
pass(end+1) = ( norm(f - g) < tol );

end


function str=vec2str(vec)
    %       string=vec2str(vector)
    % convert a numeric vector to an equivalent string
    % so that eval(string)=vector                                rcobb 9/95
    %
    if min(size(vec)) > 1,error('error, expecting vector input'),end
    if isempty(vec)
        str = '';
    else
        str=['[',num2str(vec(1))];
        len=length(vec);
        indx = 2;
        while indx <= len
            sindx = indx;
            while vec(sindx-1)+1 == vec(sindx) & sindx <= len-1
                sindx = sindx + 1;
                if sindx == len && vec(sindx-1)+1 == vec(sindx)
                    str=[str,':',num2str(vec(sindx))];
                    str=[str,']'];
                    return
                end
            end
            if sindx == indx
                str=[str,',',num2str(vec(indx))];
                indx =sindx + 1;
            else
                str=[str,':',num2str(vec(sindx-1))];
                indx =sindx;
            end
        end
        str=[str,']'];
    end


function poly = rowToPolygon(row, varargin)
%ROWTOPOLYGON  Create a polygon from a row vector.
%
%   POLY = rowToPolygon(ROW)
%   Convert a 1-by-2*N row vector that concatenates all polygon vertex
%   coordinates into a N-by-2 array of coordinates.
%   Default ordering of coordinates in ROW is:
%   [X1 Y1 X2 Y2 X3 Y3 .... XN YN].
%
%   POLY = rowToPolygon(ROW, METHOD)
%   Specifies the method for concatenating coordinates. METHOS is one of:
%   'interlaced': default method, described above.
%   'packed': the vector ROW has format:
%   [X1 X2 X3 ... XN Y1 Y2 Y3 ... YN].
%
%   POLYS = rowToPolygon(ROWS, ...)
%   When ROWS is a NP-by-NV array containing the vertex coordinates of NP
%   polygons, returns a 1-by-NP cell array containing in each cell the
%   coordinates of the polygon.
%
%
%   Example
%   % Concatenate coordinates of a circle and draw it as a polygon
%     t = linspace (0, 2*pi, 200);
%     row = [cos(t) sin(t)];
%     poly = rowToPolygon(row, 'packed');
%     figure;drawPolygon(poly)
%
%   See also
%   polygons2d, polygonToRow

% ------
% Author: David Legland
% E-mail: david.legland@grignon.inra.fr
% Created: 2010-07-23, using Matlab 7.9.0.529 (R2009b)
% Copyright 2010-2022 INRA - Cepia Software Platform

type = 'interlaced';
if ~isempty(varargin)
    type = varargin{1};
end

% number of polygons
nPolys = size(row, 1);

% polygon vertex number
Np = size(row, 2) / 2;

if strcmp(type, 'interlaced')
    % ordering is [X1 Y1 X2 X2... XN YN]
    if nPolys == 1
        poly = reshape(row, [2 Np])';
    else
        poly = cell(1, nPolys);
        for i = 1:nPolys
            poly{i} = reshape(row(i,:), [2 Np])';
        end
    end

elseif strcmp(type, 'packed')
    % ordering is [X1 X2 X3... XN Y1 Y2 Y3... YN]
    if nPolys == 1
        poly = [row(1:Np)' row(Np+1:end)'];
    else
        poly = cell(1, nPolys);
        for i = 1:nPolys
            poly{i} = [row(i, 1:Np)' row(i, Np+1:end)'];
        end
    end
end


function [ a, u, v, seed ] = r8sm_random ( m, n, seed )

%*****************************************************************************80
%
%% R8SM_RANDOM randomizes a R8SM matrix.
%
%  Discussion:
%
%    The R8SM storage format is used for an M by N Sherman Morrison matrix B,
%    which is defined by an M by N matrix A, an M vector U, and
%    an N vector V, by B = A - U * V'
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    16 February 2005
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer M, N, the number of rows and columns of the matrix.
%
%    Input, integer SEED, a seed for the random number generator.
%
%    Output, real A(M,N), the R8SM matrix.
%
%    Output, real U(M), V(N), the R8SM vectors.
%
%    Output, integer SEED, an updated seed for the random number generator.
%
  for j = 1 : n
    for i = 1 : m
      [ a(i,j), seed ] = r8_uniform_01 ( seed );
    end
  end

  for i = 1 : m
    [ u(i), seed ] = r8_uniform_01 ( seed );
  end

  for j = 1 : n
    [ v(j), seed ] = r8_uniform_01 ( seed );
  end

  return
end


function msm_to_mm_test19 ( )

%*****************************************************************************80
%
%% MSM_TO_MM_TEST19 tests MSM_TO_MM_COORDINATE_REAL_SKEW_SYMMETRIC.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    02 November 2008
%
%  Author:
%
%    John Burkardt
%
  fprintf ( 1, '\n' );
  fprintf ( 1, 'MSM_TO_MM_TEST19\n' );
  fprintf ( 1, '  Convert an MSM to MM coordinate real skew-symmetric format.\n' );

  output_filename = 'msm_to_mm_test19.mm';

  a = r8mat_indicator ( 4, 4 );
  a = a - a';
%
%  Have MSM_TO_MM write the matrix to a file.
%
  msm_to_mm ( output_filename, a, 'coordinate', 'real', 'skew-symmetric' );

  return
end


function design = ula_1d(n, d, name)
%ULA_1D Generates a 1D ULA.
%Syntax:
%   design = ULA_1D(10, wavelength/2);
%   design = ULA_1D(10, 0.5, 'ULA with 10 sensors');
%Inputs:
%   n - Number of elements.
%   d - Inter-element spacing.
%   name - Custom name of the array. Default is 'ULA with n elements'.
%Outputs:
%   design - An array design struct.
if n <= 0
    error('n_sensor must be a positive integer.');
end
if d <= 0 || ~isreal(d)
    error('d must be a positive real number.');
end
if nargin <= 2
    name = sprintf('ULA with %d elements', n);
elseif ~ischar(name)
    error('Name must be a string.');
end
design.element_indices = (0:n-1);
design.element_positions = design.element_indices*d;
design.element_spacing = d;
design.element_count = n;
design.dim = 1;
design.type = 'ula';
design.name = name;
end


function [h, p, ci, stats] = ttest2(D, varname, wh_keep1, wh_keep2, varargin)
% Two sample ttest for two samples of one subject-level variable
%
% :Usage:
% ::
%
%    ttest2(D, varname, wh_keep1, wh_keep2, [optional inputs])
%
% ..
%     Author and copyright information:
%
%     Copyright (C) 2013 Tor Wager
%
%     This program is free software: you can redistribute it and/or modify
%     it under the terms of the GNU General Public License as published by
%     the Free Software Foundation, either version 3 of the License, or
%     (at your option) any later version.
%
%     This program is distributed in the hope that it will be useful,
%     but WITHOUT ANY WARRANTY; without even the implied warranty of
%     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
%     GNU General Public License for more details.
%
%     You should have received a copy of the GNU General Public License
%     along with this program.  If not, see <http://www.gnu.org/licenses/>.
% ..
%
%
% :Inputs:
%
%   **D:**
%        a canlab_dataset object
%
%   **varname:**
%        the name of a valid variable to get from dataset
%
%   **wh_keep1:**
%        subjects forming first sample
%
%   **wh_keep2:**
%        subjects forming second sample
%
% :Optional Inputs:
%
%   **noverbose:**
%         will suppress print out of results and bargraph
%
%   **varargin:**
%         other variables passed directly to MATLAB's ttest2
%
% :Outputs:
%
%   same as MATLAB's ttest2 output
%

if any(wh_keep1 & wh_keep2), warning('YOUR SAMPLES ARE OVERLAPPING!!'); end

verbose=1;
if any(strcmp('noverbose', varargin))
    verbose=0;
    varargin(find(strcmp('noverbose', varargin))) = [];
end

x1 = get_var(D, varname, wh_keep1);
x2 = get_var(D, varname, wh_keep2);

[h, p, ci, stats] = ttest2(x1, x2, varargin{:});

if verbose, ttest2_printout(x1,x2, 1); end

end


function Z = tenfun(fun,varargin)
%TENFUN Apply a function to each element in a tensor.
%
%   TENFUN(F,X,...) applies the function specified by the function
%   handle F to the given arguments.  Either both arguments
%   must be tensors, or one is a tensor and the other is a scalar/MDA.
%
%   Examples
%   Z = tenfun(@(x)(x+1),X) %<-- increase every element by one
%   Z = tenfun(@eq,X,1) %<-- logical comparison of X with scalar
%   Z = tenfun(@plus,X,Y) %<-- adds the two tensors X and Y.
%   Z = tenfun(@max,X,Y,Z) %<-- max over all elements in X,Y,Z
%
%   See also TENSOR.
%
%MATLAB Tensor Toolbox.
%Copyright 2012, Sandia Corporation.

% This is the MATLAB Tensor Toolbox by T. Kolda, B. Bader, and others.
% http://www.sandia.gov/~tgkolda/TensorToolbox.
% Copyright (2012) Sandia Corporation. Under the terms of Contract
% DE-AC04-94AL85000, there is a non-exclusive license for use of this
% work by or on behalf of the U.S. Government. Export of this data may
% require a license from the United States Government.
% The full license terms can be found in the file LICENSE.txt

if nargin < 2
    %  error('TENFUN requires at least two input arguments')
    error('Not enough input arguments.');
end

if ~isa(fun, 'function_handle')
    error('First argument must be a function handle.');
end

%% Case I: NARGIN == 2 (one function and one tensor)
if (nargin == 2) && isa(varargin{1},'tensor')
    Z = varargin{1};
    Z.data(:) = fun(Z.data(:));
    return;
end

%% Determine if function is binary
% Note that we swap the arguments below if 2nd argument is sparse, but need
% to take special measures for those functions that don't commute.
binfuns = {@plus,@minus,@eq,@ge,@gt,@le,@lt,@ne,@and,@or,@xor, ...
           @power,@times,@ldivide,@rdivide};
isbinary = false;
for i = 1 : numel(binfuns)
    if isequal(fun,binfuns{i})
        isbinary = true;
        break;
    end
end

%% Case II: NARGIN == 3 and function is binary
if (nargin == 3) && isbinary

    X = varargin{1};
    Y = varargin{2};

    % Case IIa: X is a scalar/MDA and Y is a tensor
    if isnumeric(X) && isa(Y,'tensor')
        Z = Y;
        Z.data = fun(X,Y.data);
        return;
    end

    % Case IIb: X is a tensor and Y is a scalar/MDA
    if isa(X,'tensor') && isnumeric(Y)
        Z = X;
        Z.data = fun(X.data,Y);
        return;
    end

    % Case IIc: X and Y are both tensors
    if isa(X,'tensor') && isa(Y,'tensor')
        if ~(isequal(size(X),size(Y)))
            error('Tensor size mismatch.')
        end
        data = fun(X.data,Y.data);
        Z = tensor(data,size(X));
        return;
    end

    % Case IId: Either X or Y is a sptensor
    if isa(X,'tensor') && isa(Y,'sptensor')
        if isequal(fun,@lt)
            Z = gt(Y,X);
        elseif isequal(fun,@le)
            Z = ge(Y,X);
        elseif isequal(fun,@gt)
            Z = lt(Y,X);
        elseif isequal(fun,@ge)
            Z = le(Y,X);
        elseif isequal(fun,@minus)
            Z = plus(-Y,X);
        elseif isequal(fun,@power)
            error('Cannot do array power with a sparse and dense tensor');
        elseif isequal(fun,@ldivide)
            error('Cannot do ldivide with a sparse and dense tensor');
        elseif isequal(fun,@rdivide)
            error('Cannot do rdivide with a sparse and dense tensor');
        else
            Z = fun(Y,X);
        end
        return;
    end

    % Case IIe: Either X or Y is not a tensor
    error('For a binary function, arguments must be either two tensors or one tensor and a scalar/MDA.');
end

%% Case III: one function and the rest are same-sized tensors

X = varargin;
n = numel(X);
sz = size(X{1});
m = prod(sz);
Y = zeros(m,n);
for j = 1:n
    Y(:,j) = X{j}.data(:);
end
data = zeros(m,1);
for i = 1:m
    data(i) = fun(Y(i,:));
end
Z = tensor(data,sz);
return;


function [ Qx,qx,theta ] = surface_U_v_direct( W,Hd,Hr,Theta,G,N,K,grt,beta )
    ebs=beta;
    B=Hd*W;
    B=B.';
     %%
    A=zeros(N,1,K,K);
    for i0=1:K
        for k0=1:K
         atp=diag(Hr(k0,:))*G*W(:,i0);
         A(:,:,i0,k0)=atp;
        end
    end
 %%
    theta=diag(Theta');
 %%
    Qx=zeros(N,N);
    for k0=1:K
        for i0=1:K
            Qx=Qx-abs(ebs(k0))^2.*A(:,:,i0,k0)*A(:,:,i0,k0)';
        end
    end
    qx=zeros(N,1);
    for k0=1:K
        tmp=zeros(N,1);
        for i0=1:K
            tmp=tmp+abs(ebs(k0))^2.*(B(i0,k0)')*A(:,:,i0,k0);
        end
        qx=qx+sqrt(grt(k0))*ebs(k0)'*A(:,:,k0,k0)-tmp;
    end
end


%% OPT14_RUN
%
%  Modified:
%
%    08 January 2008
%
   %---------------------------------------------------------------------
   %  Nonlinear Equation Example
   %---------------------------------------------------------------------
   fprintf('---------------------------------------------------------\n')
   fprintf('Running testcase_14:  exact solutions (, )\n')
   fprintf('---------------------------------------------------------\n')
   fname = 'opt14_fgh';
   options = [];
   options.verbose            = 0;
   options.max_iterations     = 180;
   options.max_fevals         = 180;
   options.method             = 'newton';

   fprintf('Newton:\n')
   options.globalization      = 'trust_region';

   x0 = [ -.8; .8; -.8 ];
   x = entrust(fname, x0, options);
   fprintf('Newtons method produced  (%10.7e,%10.7e,%10.7e)\n\n',x(1),x(2),x(3))
   f = opt14_fgh ( x, 'f' );
   fprintf('Value of F(X) = %f\n', f );

   x0 = [ 2; -1; 1 ];
   x = entrust(fname, x0, options);
   fprintf('Newtons method produced  (%10.7e,%10.7e,%10.7e)\n\n',x(1),x(2),x(3))
   f = opt14_fgh ( x, 'f' );
   fprintf('Value of F(X) = %f\n', f );

   %---------------------------------------------------------------------
   %  Test Gauss-Newton strategies.
   %---------------------------------------------------------------------
   fprintf('---------------------------------------------------------\n')
   fprintf('Running testcase_14 as least squares problem: \n')
   fprintf('Exact solution (,)\n')
   fprintf('---------------------------------------------------------\n')
   fname = 'opt14_rj';
   options = [];
   options.verbose            = 0;
   options.method             = 'gauss_newton';
   options.step_tolerance     = 1.e-15;
   options.globalization      = 'none';
   options.gradient_tolerance = 1.e-10;
   options.max_iterations     = 450;

   x0 = [ -.8; .8; -.8 ];
   x = entrust(fname, x0, options);
   fprintf('Gauss-Newton produced  (%10.7e, %10.7e, %10.7e)\n\n',x(1),x(2),x(3))
   [ res, jac ] = opt14_rj ( x, 'f' );
   fprintf('Norm of RES(X) = %f\n', norm ( res ) );

   x0 = [ 2; -1; 1 ];
   x = entrust(fname, x0, options);
   fprintf('Gauss-Newton produced  (%10.7e, %10.7e, %10.7e)\n\n',x(1),x(2),x(3))
   [ res, jac ] = opt14_rj ( x, 'f' );
   fprintf('Norm of RES(X) = %f\n', norm ( res ) );


function [A,Pt] = mci_lds_dfdx (x,u,P,M)
% Jacobian for linear system, dx/dt=Ax, with constrained connectivity
% FORMAT [A,Pt] = mci_lds_dfdx (x,u,P,M)
%
% x     State vector
% u     input
% P     parameters (vectorised)
% M     model structure
%
% A     f=Ax
% Pt    Parameters (transformed from latent pars)
%__________________________________________________________________________
% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging

% Will Penny
% $Id: mci_lds_dfdx.m 6548 2015-09-11 12:39:47Z will $

[Pt,a,b] = mci_lds_lat2par (P,M);
A=diag(a);

Nb=length(b);
for k=1:Nb,
    i=M.Aconn(k,1);
    j=M.Aconn(k,2);
    A(i,j)=b(k);
end


function J=mtfftc(data,tapers,nfft,Fs)
% Multi-taper fourier transform - continuous data
%
% Usage:
% J=mtfftc(data,tapers,nfft,Fs) - all arguments required
% Input:
%       data (in form samples x channels/trials or a single vector)
%       tapers (precalculated tapers from dpss)
%       nfft (length of padded data)
%       Fs   (sampling frequency)
%
% Output:
%       J (fft in form frequency index x taper index x channels/trials)
if nargin < 4; error('Need all input arguments'); end;
data=change_row_to_column(data);
[NC,C]=size(data); % size of data
[NK K]=size(tapers); % size of tapers
if NK~=NC; error('length of tapers is incompatible with length of data'); end;
tapers=tapers(:,:,ones(1,C)); % add channel indices to tapers
data=data(:,:,ones(1,K)); % add taper indices to data
data=permute(data,[1 3 2]); % reshape data to get dimensions to match those of tapers
data_proj=data.*tapers; % product of data with tapers
J=fft(data_proj,nfft)/Fs;   % fft of projected data


function [c, ceq] = ConstraintFCN_models(u,uold,x,N,LBo,UBo,LBdu,UBdu,p,select_model)
%% Constraint function of nonlinear MPC for F8 system
%
% Inputs:
%   u:      optimization variable, from time k to time k+N-1
%   x:      current state at time k
%   Ts:     controller sample time
%   N:      prediction horizon length
%   uold:   latest applied control input
%   LBo:    Lower bound of output x
%   UBo:    Upper bound of output x
%   LBdu:   Lower bound for input difference uk - uk-1
%   UBdu:   Upper bound for input difference uk - uk-1
%   p:      Parameters for model
%   select_model: Selects model future-state prediction
%
% Output:
%   c:      inequality constraints applied across prediction horizon
%   ceq:    equality constraints  (empty)
%

Nvar = length(x);
%% Nonlinear MPC design parameters
% Ensure that all cell populations are positive
zMin = LBo;

%% Integrate system
if strcmp(select_model,'DelayDMDc')
    Uinput = getHankelMatrix_MV([p.udelay(2:end)';u],p.Ndelay)';
    [xk,~] = lsim(p.sys,Uinput,[0:N-1].*p.dt,p.xdelay);
    xk = xk(:,end-Nvar+1:end);
    xk = xk + repmat(p.xmean',[N 1]); xk = xk';
elseif strcmp(select_model,'DMDc')
    [xk,~] = lsim(p.sys,[u',0],[0:N].*p.dt,x-p.xmean);
    xk = xk(2:end,:) + repmat(p.xmean',[N 1]); xk = xk';
elseif strcmp(select_model,'eDMDc')
    Y0 = poolData((x-p.xmean)',Nvar,p.polyorder,p.usesine)';
    [xk,~] = lsim(p.sys,[u' 0],[0:N].*p.dt,Y0(2:end));
    xk = xk(2:end,1:Nvar) + repmat(p.xmean',[N 1]); xk = xk';
elseif strcmp(select_model,'SINDYc')
    Ns = size(x,1);
    xk = zeros(Ns,N+1); xk(:,1) = x;
    for ct=1:N
        % Obtain plant state at next prediction step.
        xk(:,ct+1) = rk4u(@sparseGalerkinControl_Discrete,xk(:,ct),u(ct),p.dt,1,[],p);
    end
    xk = xk(:,2:N+1);
elseif strcmp(select_model,'partialSINDYc')
    Ns = size(x,1);
    xk = zeros(Ns,N+1); xk(:,1) = x;
    for ct=1:N
        % Obtain plant state at next prediction step.
        xk_tmp = rk4u(@sparseGalerkinControl_Discrete,xk(p.SelectVars,ct),u(ct),p.dt,1,[],p);
        xk(p.SelectVars,ct+1) = xk_tmp;
    end
    xk = xk(:,2:N+1);
elseif strcmp(select_model,'NARX')
    Hu = [u',0];
    Hx = zeros(Nvar,length(Hu)); Hx(:,1) = x;
    if p.TRANSFORM_LOG == 1
        Hx = log(Hx);
    end
    [Us,Ui,Si] = preparets(p.net,con2seq(Hu),{},con2seq(Hx));
    xk = p.net(Us,Ui,Si);
    xk = cell2mat(xk);
    if p.TRANSFORM_LOG == 1
        xk = exp(xk);
    end
end

%% Inequality constraints calculation
c = zeros(N,1);
% Apply N population size constraints across prediction horizon, from time
% k+1 to k+N

for ct=1:N
    % -z + zMin < 0 % lower bound
    c(ct) = -xk(1,ct)+zMin;

end

%% No equality constraints
ceq = [];


classdef MaOEADDFC < ALGORITHM
% <multi/many> <real/integer/label/binary/permutation>
% Many-objective evolutionary algorithm based on directional diversity and
% favorable convergence
% K --- 5 --- The number of neighbors for estimating density
% L --- 3 --- The number of candidates for convergence-based selection

%------------------------------- Reference --------------------------------
% J. Cheng, G. G. Yen, and G. Zhang, A many-objective evolutionary
% algorithm with enhanced mating and environmental selections, IEEE
% Transactions on Evolutionary Computation, 2015, 19(4): 592-605.
%------------------------------- 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".
%--------------------------------------------------------------------------

    methods
        function main(Algorithm,Problem)
            %% Parameter setting
            [K,L] = Algorithm.ParameterSet(5,3);

            %% Generate random population
            Population = Problem.Initialization();
            Zmin       = min(Population.objs,[],1);

            %% Optimization
            while Algorithm.NotTerminated(Population)
                MatingPool = MatingSelection(Population.objs,Zmin);
                Offspring  = OperatorGA(Problem,Population(MatingPool));
                Zmin       = min([Zmin;Offspring.objs],[],1);
                Population = EnvironmentalSelection([Population,Offspring],Zmin,Problem.N,K,L);
            end
        end
    end
end


function mypatch=patchtest(some_results, col_heading)

    %{
    some_results is the results from one of the ZmapGridFunctions. It will contain:
    1.  a field called "values" which is a table.  This table will have columns for 'x', 'y' and
        col_heading (where col_heading is the name of one of the columns).
    2.  a field called Grid, which is a ZmapGrid and contains fields X, Y

    %}
    gr_s = some_results.Grid;
    results = some_results.values;

    myX=some_results.values.x;
    myX=reshape(myX,size(some_results.Grid.X));
    hold on;
    % assume: dx is constant for every latitude
    dx_at_lat = min(diff(myX,[],2),[],2);   % Nx1

    % fill all X position nans, because they will cause holes
    for i=1:size(myX,1)
        AnchorIdx = find(~ismissing(myX(i,:)),1,'first');
        AnchorVal = myX(i,AnchorIdx);
        missIdx = find(ismissing(myX(i,:)));
        myX(i,missIdx) = (missIdx - AnchorIdx) .* dx_at_lat(i) + AnchorVal;
    end

    % assume dy is constant everywhere
    dy = mean(min(diff(gr_s.Y)));
    shifted_X = myX - repmat(dx_at_lat ./2, 1, size(myX,2)) ;
    shifted_Y = gr_s.Y-dy./2;

    myresults=results.(col_heading);
    myresults=reshape(myresults,size(shifted_X));

    %% because surfaces and patches are based on the lower-left corner, add col & row.

    % add row to top with same values as existing last (top)row
    myresults(end+1,:)=myresults(end,:);
    shifted_X(end+1,:)=shifted_X(end,:);
    shifted_Y(end+1,:)=shifted_Y(end,:) + dy;

    % add column to end with same values as existing last (right) column
    myresults(:,end+1)=myresults(:,end);
    shifted_X(:,end+1)=shifted_X(:,end) + [dx_at_lat; dx_at_lat(end)];
    shifted_Y(:,end+1)=shifted_Y(:,end);

    pa=surf2patch(shifted_X,shifted_Y,zeros(size(shifted_X)),myresults);

    mypatch=patch(pa);
    mypatch.Faces(end,:)=[]; %this point was made up.
    mypatch.Tag='the_patch';
    mypatch.HitTest='off';
    shading faceted;

    set(gca,'Children',circshift(get(gca,'Children'),-1)); % put this patch at the end
end


%GETWINDOWS Get pixel feature vectors around given pixels in image dataset
%
%		L = GETWINDOWS(A,INDEX,WSIZE,INCLUDE)
%		L = GETWINDOWS(A,[ROW,COL],WSIZE,INCLUDE)
%
% INPUT
%   A       Dataset containing feature images
%   INDEX   Index vector of target pixels in the images (Objects in A)
%   ROW     Column vector of row-coordinates of target pixels
%   COL     Column vector of column-coordinates of target pixels
%   WSIZE   Desired size of rectangular window around target pixels
%   INCLUDE Flag (0/1), indicating whether target pixels should be included
%           (1,default), or not (0) in result.
% OUTPUT
%   L       Index in A of window pixels
%
% DESCRIPTION
% This routine generates all objects in a dataset constructed by image
% features that are in a window of size WSIZE around the target pixels
% given by INDEX, or by [ROW,COL]. If WSIZE is omitted or empty ([],
% default), just the 4 4-conntected neighbors of the target pixels are
% returned. L points to the window pixels, such that A(L,:) is a dataset
% of the corresponding objects.
%
% SEE ALSO (PRTools Guide)
% DATASETS, IM2FEAT

% Copyright: R.P.W. Duin, r.p.w.duin@37steps.com
% Faculty EWI, Delft University of Technology
% P.O. Box 5031, 2600 GA Delft, The Netherlands

function L = getwindows(a,index,wsize,include)

if nargin < 4, includeflag = 1; end
if nargin < 3, wsize = []; end

isdataset(a);
isfeatim(a);

imsize = getobjsize(a);

if size(index,2) == 1
	[row,col] = ind2sub(imsize,index);
elseif size(index,2) == 2
	row = index(:,1);
	col = index(:,2);
end
n = length(row);

imsize = getobjsize(a);

if ~isempty(wsize) % rectangular neighborhoods
	if length(wsize) == 1 % square windows
		row1 = ceil(wsize/2)-1; col1 = row1; % # pixels above / left of target pixel
		row2 = floor(wsize/2);  col2 = row2; % # pixels below / right of target pixel
	elseif length(wsize) == 2 % possibly non-square windows
		row1 = ceil(wsize(1)/2)-1;
		row2 = floor(wsize(1)/2);
		col1 = ceil(wsize(2)/2)-1;
		col2 = floor(wsize(2)/2);
	else
		error('Window size should be 1D or 2D')
	end
	[R,C] = meshgrid([-row1:row2],[-col1:col2]);
	k = length(R(:));
	R = repmat(row(:),1,k)+repmat(R(:)',length(row),1);
	C = repmat(col(:),1,k)+repmat(C(:)',length(col),1);
else
	R = repmat(row(:),1,5)+repmat([0 -1 0 1 0],length(row),1);
	C = repmat(col(:),1,5)+repmat([-1 0 0 0 1],length(col),1);
end
JR = [find(R <= 0); find(R > imsize(1))];
R(JR) = []; C(JR) = [];
JC = [find(C <= 0); find(C > imsize(2))];
R(JC) = []; C(JC) = [];
L = sub2ind(imsize,R,C);
%b(L) = ones(length(L),1);

if include % we are done
	L = unique(L);
else       % remove given objects
	b = zeros(imsize);             % create an image of the right size
	b(L) = ones(length(L),1);      % flag the objects we found.
	Z = sub2ind(imsize,row,col);   % for all original objects,
	b(Z) = zeros(length(Z),1);     % remove flags
	L = find(b > 0);               % and see what is left
end
%b = a(L,:);

L = unique(L);
J = [find(L<1); find(L>prod(imsize))];
L(J) = [];
%b = a(L,:);


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%   Q = INVERSEKINEMATIC_KUKA_KR5_SIXX_R650(robot, T)
%   Solves the inverse kinematic problem for the KUKA KR5_SIXX_R650 robot
%   where:
%   robot stores the robot parameters.
%   T is an homogeneous transform that specifies the position/orientation
%   of the end effector.
%
%   A call to Q=INVERSEKINEMATIC_KUKA_KR5_SIXX_R650 returns 8 possible solutions, thus,
%   Q is a 6x8 matrix where each column stores 6 feasible joint values.
%
%
%   Example code:
%
%   robot=load_robot('kuka', 'KR5_sixx_R650');
%   q = [0 0 0 0 0 0];
%   T = directkinematic(robot, q);
%   %Call the inversekinematic for this robot
%   qinv = inversekinematic(robot, T);
%   check that all of them are feasible solutions!
%   and every Ti equals T
%   for i=1:8,
%        Ti = directkinematic(robot, qinv(:,i))
%   end
%	See also DIRECTKINEMATIC.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

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

%initialize q,
%eight possible solutions are generally feasible
q=zeros(6,8);

% %Evaluate the parameters
% theta = eval(robot.DH.theta);
d = eval(robot.DH.d);
L6=abs(d(6));

%T= [ nx ox ax Px;
%     ny oy ay Py;
%     nz oz az Pz];
Px=T(1,4);
Py=T(2,4);
Pz=T(3,4);

%Compute the position of the wrist, being W the Z component of the end effector's system
W = T(1:3,3);

% Pm: wrist position
Pm = [Px Py Pz]' - L6*W;

%first joint, two possible solutions admited:
% if q(1) is a solution, then q(1) + pi is also a solution
q1=atan2(Pm(2), Pm(1));

%solve for q2
q2_1=solve_for_theta2(robot, [q1 0 0 0 0 0 0], Pm);
%the other possible solution is q1 + pi
q2_2=solve_for_theta2(robot, [q1+pi 0 0 0 0 0 0], Pm);

%solve for q3
q3_1=solve_for_theta3(robot, [q1 0 0 0 0 0 0], Pm);
%solver for q3 for both cases
q3_2=solve_for_theta3(robot, [q1+pi 0 0 0 0 0 0], Pm);

%the next matrix doubles each column. For each two columns, two different
%configurations for theta4, theta5 and theta6 will be computed. These
%configurations are generally referred as wrist up and wrist down solution
q = [q1         q1         q1        q1       q1+pi   q1+pi   q1+pi   q1+pi;
     q2_1(1)    q2_1(1)    q2_1(2)   q2_1(2)  q2_2(1) q2_2(1) q2_2(2) q2_2(2);
     q3_1(1)    q3_1(1)    q3_1(2)   q3_1(2)  q3_2(1) q3_2(1) q3_2(2) q3_2(2);
     0          0          0         0         0      0       0       0;
     0          0          0         0         0      0       0       0;
     0          0          0         0         0      0       0       0];

%leave only the real part of the solutions
q=real(q);

%Note that in this robot, the joint q3 has a non-simmetrical range. In this
%case, the joint ranges from 60 deg to -219 deg, thus, the typical normalizing
%step is avoided in this angle (the next line is commented). When solving
%for the orientation, the solutions are normalized to the [-pi, pi] range
%only for the theta4, theta5 and theta6 joints.

%normalize q to [-pi, pi]
q(1,:) = normalize(q(1,:));
q(2,:) = normalize(q(2,:));

% solve for the last three joints
% for any of the possible combinations (theta1, theta2, theta3)
for i=1:2:size(q,2),
    qtemp = solve_spherical_wrist(robot, q(:,i), T, 1,'geometric'); %wrist up
    qtemp(4:6)=normalize(qtemp(4:6));
    q(:,i)=qtemp;

    qtemp = solve_spherical_wrist(robot, q(:,i), T, -1, 'geometric'); %wrist down
    qtemp(4:6)=normalize(qtemp(4:6));
    q(:,i+1)=qtemp;
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% solve for second joint theta2, two different
% solutions are returned, corresponding
% to elbow up and down solution
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function q2 = solve_for_theta2(robot, q, Pm)

%Evaluate the parameters
theta = eval(robot.DH.theta);
d = eval(robot.DH.d);
a = eval(robot.DH.a);
alpha = eval(robot.DH.alpha);

%See geometry
L2=abs(a(2));
L3=abs(d(4));
A2 = abs(a(3));

%See geometry of the robot
%compute L4
L4 = sqrt(A2^2 + L3^2);

%The inverse kinematic problem can be solved as in the IRB 140 (for example)

%given q1 is known, compute first DH transformation
T01=dh(robot, q, 1);

%Express Pm in the reference system 1, for convenience
p1 = inv(T01)*[Pm; 1];

r = sqrt(p1(1)^2 + p1(2)^2);

beta = atan2(-p1(2), p1(1));
gamma = real(acos((L2^2+r^2-L4^2)/(2*r*L2)));

%return two possible solutions
%elbow up and elbow down
%the order here is important and is coordinated with the function
%solve_for_theta3
q2(1) = pi/2 - beta - gamma; %elbow up
q2(2) = pi/2 - beta + gamma; %elbow down

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% solve for third joint theta3, two different
% solutions are returned, corresponding
% to elbow up and down solution
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function q3 = solve_for_theta3(robot, q, Pm)

%Evaluate the parameters
theta = eval(robot.DH.theta);
d = eval(robot.DH.d);
a = eval(robot.DH.a);
alpha = eval(robot.DH.alpha);

%See geometry
L2=abs(a(2));
L3=abs(d(4));

A2 = abs(a(3));

%See geometry of the robot
%compute L4
L4 = sqrt(A2^2 + L3^2);

%the angle phi is fixed
phi=acos((A2^2+L4^2-L3^2)/(2*A2*L4));

%given q1 is known, compute first DH transformation
T01=dh(robot, q, 1);

%Express Pm in the reference system 1, for convenience
p1 = inv(T01)*[Pm; 1];

r = sqrt(p1(1)^2 + p1(2)^2);

eta = real(acos((L2^2 + L4^2 - r^2)/(2*L2*L4)));

%return two possible solutions
%elbow up and elbow down solutions
%the order here is important
q3(1) = pi - phi - eta;
q3(2) = pi - phi + eta;


function jtrMask3M = jitterMask(mask3M,pctJitter)
% function jtrMask3M = jitterMask(mask3M,pctJitter)
%
% Jitters the input mask3M by translation, rotation and scaling by a factor
% of pctJitter.
%
% Example:
% mask3M = rand(10,10,5)  0.5;
% pctJitter = 5; % percent
% jtrMask3M = jitterMask(mask3M,pctJitter);
%
% APA, 12/2/2018

% global planC
% indexS = planC{end};
%
% structNum = 1;
% scanNum = 1;
% pctJitter = 5;
%
% fullmaks3M = getUniformStr(structNum,planC);
% [rasterSegments, planC, isError] = getRasterSegments(structNum,planC);
% [mask3M, uniqueSlices] = rasterToMask(rasterSegments, scanNum, planC);

jtrMask3M = zeros(size(mask3M),'like',mask3M);
for slc = 1:size(mask3M,3)
    fullSiz = size(mask3M(:,:,slc));
    [iV,jV] = find3d(mask3M(:,:,slc));
    maskM = mask3M(min(iV):max(iV),min(jV):max(jV),slc);
    di = max(iV) - min(iV);
    dj = max(jV) - min(jV);
    iCtr = ceil((max(iV) + min(iV))/2);
    jCtr = ceil((max(jV) + min(jV))/2);
    minRow = -di*pctJitter/100;
    maxRow = di*pctJitter/100;
    minCol = -dj*pctJitter/100;
    maxCol = dj*pctJitter/100;
    numRows = minRow + (maxRow-minRow)*rand(1);
    numCols = minCol + (maxCol-minCol)*rand(1);
    minAng = -pctJitter/100*180;
    maxAng = pctJitter/100*180;
    angl = minAng + (maxAng-minAng)*rand(1);
    scl = (100-pctJitter)/100 + 2*pctJitter/100*rand(1);
    maskM = imtranslate(maskM,[numRows,numCols],'nearest','FillValues',0);
    maskM = imrotate(maskM,angl,'nearest');
    maskM = imresize(maskM, scl, 'nearest');
    newSiz = size(maskM);
    iStart = 1;
    jStart = 1;
    iEnd = 0;
    jEnd = 0;
    iMin = iCtr - ceil(newSiz(1)/2);
    if iMin < 0
        iStart = 1-iMin;
        iMin = 1;
    end
    iMax = iCtr + floor(newSiz(1)/2) - 1;
    if iMax > fullSiz(1)
        iEnd = fullSiz(1) - iMax;
        iMax = fullSiz(1);
    end
    jMin = jCtr - ceil(newSiz(2)/2);
    if jMin < 0
        jStart = 1-jMin;
        jMin = 1;
    end
    jMax = jCtr + floor(newSiz(2)/2) - 1;
    if jMax > fullSiz(2)
        jEnd = fullSiz(2) - jMax;
        jMax = fullSiz(2);
    end
    %mask3M(:,:,slc) = 0;
    jtrMask3M(iMin:iMax,jMin:jMax,slc) = maskM(iStart:end-iEnd,jStart:end-jEnd);
end

% fullmaks3M(:,:,uniqueSlices) = mask3M;
% isUniform = 1;
% strname = [planC{indexS.structures}(structNum).structureName, '_Perturbed'];
% planC = maskToCERRStructure(fullmaks3M, isUniform, scanNum, strname, planC);


function out = putBinA(A,B,r,c)
% Function that places a 2D matrix B into a larger 2D matrix A. The
% upper-left corner of B is placed at coordinates (r,c) in A. If (r,c) are
% not given, then B is put in the upper-left corner of A, a default of
% (1,1). Some error checking is done first to make sure that A and B are 2D
% matrices, A is big enough to contain B, and the given (r,c) will not
% spill B outside of A.

%% Setup variables
[ar ac] = size(A);
[br bc] = size(B);
if nargin < 4
    r = 1; c = 1;
else
    r = round(r); c = round(c);
end

%% Error checking
% check that A and B are 2D matricies
if (numel(size(A))~=2) || (numel(size(B))~=2)
    error('The input matrices must be 2D arrays');
end
% check that A is >= B on both dims
if ar<br || ac<bc
    error('Matrix "A" must be big enough to contain matrix "B"');
end
% check that the (r,c) placement lands B entirely inside of A
if (ar < (r+br-1)) || (ac < (c+bc-1))
    error('Matrix "B" will fall outside matrix "A" with these coordinates');
end

%% Place B in A at (r,c) position
out = A;
rr = r + br - 1;
cc = c + bc - 1;
out(r:rr,c:cc) = B;


classdef nnnonorm < nntest

  properties (TestParameter)
    rows = {2 4}
    cols = {2 5}
    numDims = {4 8}
    batchSize = {1 2 3}
  end

  methods (Test)
    function basic(test, rows, cols, numDims, batchSize)
      H = rows ;
      W = cols ;
      C = numDims ;
      bs = batchSize ;
      x = test.randn(H, W, C, bs) ;
      g = test.randn(1, 1, C, 1) / test.range ;
      b = test.randn(1, 1, C, 1) / test.range ;

      y = vl_nnnonorm(x, g, b) ;
      dzdy = test.randn(size(y)) ;
      [dzdx,dzdg,dzdb] = vl_nnnonorm(x, g, b, dzdy) ;
      test.der(@(x) vl_nnnonorm(x, g, b), x, dzdy, dzdx, test.range * 1e-3) ;
      test.der(@(g) vl_nnnonorm(x, g, b), g, dzdy, dzdg, test.range * 1e-3) ;
      test.der(@(b) vl_nnnonorm(x, g, b), b, dzdy, dzdb, test.range * 1e-3) ;
    end
  end
end