function ft_plot_cloud(pos, val, varargin)

% FT_PLOT_CLOUD visualizes spatially sparse scalar data as spheres or
% spherical clouds of points and optionally 2D slices through those clouds
%
% Use as
%   ft_plot_cloud(pos, val, ...)
% where the first argument are the positions and the second argument are the values
% for each location.
%
% Optional input arguments should come in key-value pairs and can include
%   'cloudtype'          = 'cloud' (default) plots a group of spherically arranged points at each sensor position
%                          'surf' plots a single spherical surface mesh at each sensor position
%   'scalerad'           = scale radius with val, can be 'yes' or 'no' (default = 'yes')
%   'radius'             = scalar, maximum radius of cloud (default = 4 mm)
%   'clim'               = 1x2 vector specifying the min and max for the colorscale
%   'unit'               = string, convert the sensor array to the specified geometrical units (default = [])
%   'mesh'               = string or Nx1 cell-array, triangulated mesh(es), see FT_PREPARE_MESH
%   'slice'              = requires 'mesh' as input (default = 'none')
%                          '2d', plots 2D slices through the cloud with an outline of the mesh
%                          '3d', draws an outline around the mesh at a particular slice
%
% The following inputs apply when 'cloudtype' = 'cloud'
%   'rmin'               = scalar >= 1, minimum radius of cloud if scalerad = 'yes' (default = 1 mm)
%   'colormap'           = colormap for functional data, see COLORMAP
%   'colorgrad'          = 'white' or a scalar (e.g. 1), degree to which the saturatoin of points in cloud changes from its center
%   'ptsize'             = scalar, size of points in cloud (default = 1 mm)
%   'ptdensity'          = scalar, density of points in cloud (default = 20 per mm^3)
%   'ptgradient'         = scalar, degree to which density of points in cloud changes from its center (default = 0.5, i.e. uniform density)
%
% The following inputs apply when 'slice' = '2d' or '3d'
%   'ori'                = 'x', 'y', or 'z', specifies the orthogonal plane which will be plotted (default = 'y')
%   'slicepos'           = 'auto' or Nx1 vector specifying the position of the
%                          slice plane along the orientation axis (default = 'auto': chooses slice(s) with
%                          the most data)
%   'nslices'            = scalar, number of slices to plot if 'slicepos' = 'auto (default = 1)
%   'minspace'           = scalar, minimum spacing between slices if nslices>1
%                          (default = 1)
%   'intersectcolor'     = string, Nx1 cell-array, or Nx3 vector specifying line color (default = 'k')
%   'intersectlinestyle' = string or Nx1 cell-array, line style specification (default = '-')
%   'intersectlinewidth' = scalar or Nx1 vector, line width specification (default = 2)
%
% The following inputs apply when 'cloudtype' = 'surf' and 'slice' = '2d'
%   'ncirc'           = scalar, number of concentric circles to plot for each
%                       cloud slice (default = 15) make this hidden or scale
%   'scalealpha'      = 'yes' or 'no', scale the maximum alpha value of the center circle
%                       with distance from center of cloud
%
% See also FT_ELECTRODEPLACEMENT, FT_PLOT_SENS, FT_PLOT_TOPO, FT_PLOT_TOPO3D

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

% run some checks
if size(pos,2)~=3
  ft_error('positions shoudl be specified as an Nx3 array')
end

if isempty(val)
  val = ones(size(pos,1),1); % vector of ones
end

assert(isrow(val) || iscolumn(val), 'values should be represented as a vector')
val = val(:); % ensure it is a column

unit = ft_getopt(varargin, 'unit');
if isempty(unit)
  % estimate the unit of geometry of the positions, this is used for the other defaults
  unit = ft_estimate_units(norm(range(pos)));
end

% get the generic input arguments
cloudtype          = ft_getopt(varargin, 'cloudtype', 'cloud');
radius             = ft_getopt(varargin, 'radius', 4 * ft_scalingfactor('mm', unit));
rmin               = ft_getopt(varargin, 'rmin',   1 * ft_scalingfactor('mm', unit));
scalerad           = ft_getopt(varargin, 'scalerad', 'yes');
colorgrad          = ft_getopt(varargin, 'colorgrad', 'white');
clim               = ft_getopt(varargin, 'clim');
meshplot           = ft_getopt(varargin, 'mesh');

if ft_platform_supports('parula')
  cmap             = ft_getopt(varargin, 'colormap', 'parula');
else
  cmap             = ft_getopt(varargin, 'colormap', 'jet');
end

% cloud related inputs
ptsize             = ft_getopt(varargin, 'ptsize', 1 * ft_scalingfactor('mm', unit));
ptdensity          = ft_getopt(varargin, 'ptdensityity', 20 / ft_scalingfactor('mm', unit)^3);  % points per unit of volume
ptgradient         = ft_getopt(varargin, 'ptgradient', .5);

% slice related inputs
sli                = ft_getopt(varargin, 'slice', 'none');
ori                = ft_getopt(varargin, 'ori', 'y');
slicepos           = ft_getopt(varargin, 'slicepos', 'auto');
nslices            = ft_getopt(varargin, 'nslices', 1);
minspace           = ft_getopt(varargin, 'minspace', 1);
intersectcolor     = ft_getopt(varargin, 'intersectcolor', {'k'});
intersectlinestyle = ft_getopt(varargin, 'intersectlinestyle', {'-'});
intersectlinewidth = ft_getopt(varargin, 'intersectlinewidth', 2);
ncirc              = ft_getopt(varargin, 'ncirc', 15);
scalealpha         = ft_getopt(varargin, 'scalealpha', 'no');

% mesh related inputs
facecolor          = ft_getopt(varargin, 'facecolor', [0.781 0.762 0.664]);
edgecolor          = ft_getopt(varargin, 'edgecolor', 'none');
facealpha          = ft_getopt(varargin, 'facealpha', 1);
edgealpha          = ft_getopt(varargin, 'edgealpha', 0);
vertexcolor        = ft_getopt(varargin, 'vertexcolor', 'curv');

% give some warnings for possibly inappropriate input
if radius < 1 * ft_scalingfactor('mm', unit)
  ft_warning('radius is smaller than 1 mm');
end
if rmin < 1 * ft_scalingfactor('mm', unit)
  ft_warning('rmin is smaller than 1 mm');
end

if ~isempty(meshplot)
  % mesh should be a cell-array
  if isstruct(meshplot)
    tmp = meshplot;
    meshplot = cell(size(tmp));
    for i=1:numel(tmp)
      meshplot{i} = tmp(i);
    end
  elseif iscell(meshplot)
    % do nothing
  else
    meshplot = {};
  end

  % replace pnt by pos
  for k = 1:numel(meshplot)
    meshplot{k} = fixpos(meshplot{k});
  end

  for k = 1:numel(meshplot)
    if ~isfield(meshplot{k}, 'pos') || ~isfield(meshplot{k}, 'tri')
      % ft_error('the mesh should be a structure with pos and tri');
      meshplot{k}.pos = [];
      meshplot{k}.tri = [];
    end
  end

  % facecolor, edgecolor, and vertexcolor should be cell-array
  if ~iscell(facecolor)
    tmp = facecolor;
    if ischar(tmp)
      facecolor = {tmp};
    elseif ismatrix(tmp) && size(tmp, 2) == 3
      facecolor = cell(size(tmp,1), 1);
      for i=1:size(tmp,1)
        facecolor{i} = tmp(i, 1:3);
      end
    else
      facecolor = {};
    end
  end
  if ~iscell(edgecolor)
    tmp = edgecolor;
    if ischar(tmp)
      edgecolor = {tmp};
    elseif ismatrix(tmp) && size(tmp, 2) == 3
      edgecolor = cell(size(tmp,1), 1);
      for i=1:size(tmp,1)
        edgecolor{i} = tmp(i, 1:3);
      end
    else
      edgecolor = {};
    end
  end
  if ~iscell(vertexcolor)
    tmp = vertexcolor;
    if ischar(tmp)
      vertexcolor = {tmp};
    elseif ismatrix(tmp) && size(tmp, 2) == 3
      vertexcolor = cell(size(tmp,1), 1);
      for i=1:size(tmp,1)
        vertexcolor{i} = tmp(i, 1:3);
      end
    else
      vertexcolor = {};
    end
  end

  % make sure each mesh has plotting options specified
  if numel(meshplot) > 1
    nmesh = numel(meshplot);
    if numel(facecolor) < numel(meshplot)
      for m = numel(facecolor)+1:nmesh
        facecolor{m} = facecolor{1};
      end
    end
    if numel(edgecolor) < numel(meshplot)
      for m = numel(edgecolor)+1:nmesh
        edgecolor{m} = edgecolor{1};
      end
    end
    if numel(facealpha) < numel(meshplot)
      for m = numel(facealpha)+1:nmesh
        facealpha(m) = facealpha(1);
      end
    end
    if numel(edgealpha) < numel(meshplot)
      for m = numel(edgealpha)+1:nmesh
        edgealpha(m) = edgealpha(1);
      end
    end
    if numel(vertexcolor) < numel(meshplot)
      for m = numel(vertexcolor)+1:nmesh
        vertexcolor(m) = vertexcolor(1);
      end
    end
  end
end

if strcmp(sli, '2d') || strcmp(sli, '3d')
  if isempty(meshplot)
    ft_error('plotting a slice requires a mesh as input')
  else
    dointersect = 1;
  end
else
  dointersect = 0;
end

if dointersect % check intersection inputs
  % color and linestyle should be cell-array
  if ~iscell(intersectcolor)
    tmp = intersectcolor;
    if ischar(tmp)
      intersectcolor = {tmp};
    elseif ismatrix(tmp) && size(tmp, 2) == 3
      intersectcolor = cell(size(tmp,1), 1);
      for i=1:size(tmp,1)
        intersectcolor{i} = tmp(i, 1:3);
      end
    else
      intersectcolor = {};
    end
  end
  if ischar(intersectlinestyle)
    intersectlinestyle = {intersectlinestyle};
  elseif iscell(intersectlinestyle)
    % do nothing
  else
    intersectlinestyle = {};
  end

  % make sure each intersection has plotting options specified
  if numel(meshplot) > 1
    nmesh = numel(meshplot);
    if numel(intersectcolor) < numel(meshplot)
      for m = numel(intersectcolor)+1:nmesh
        intersectcolor{m} = intersectcolor{1};
      end
    end
    if numel(intersectlinewidth) < numel(meshplot)
      for m = numel(intersectlinewidth)+1:nmesh
        intersectlinewidth(m) = intersectlinewidth(1);
      end
    end
    if numel(intersectlinestyle) < numel(meshplot)
      for m = numel(intersectlinestyle)+1:nmesh
        intersectlinestyle{m} = intersectlinestyle{1};
      end
    end
  end % end intersection plotting checks
end % end dointersect checks

if dointersect
  % set the orientation of the slice plane
  if strcmp(ori, 'x')
    oriX = 1; oriY = 0; oriZ = 0;
  elseif strcmp(ori, 'y')
    oriX = 0; oriY = 1; oriZ = 0;
  elseif strcmp(ori, 'z')
    oriX = 0; oriY = 0; oriZ = 1;
  else
    ft_error('ori must be "x", "y" or "z"')
  end
end

if isempty(clim)
  clim = [min(val) max(val)]; % use the data
end

% functional data scaling factors
if ischar(cmap)
  if strcmp(cmap, 'default')
    cmapsc = get(0, 'DefaultFigureColormap');
  else
    cmapsc = feval(cmap, 201); % an odd number
  end
else
  cmapsc = cmap;
end

cmid   = size(cmapsc,1)/2;                          % colorbar middle
colscf = 2*( (val-clim(1)) / (clim(2)-clim(1)) )-1; % color between -1 and 1, bottom vs. top colorbar
colscf(colscf>1)=1; colscf(colscf<-1)=-1;           % clip values outside the [-1 1] range

radscf = abs(val-min(abs(val)) * sign(max(val)));   % 'vdown' representation
radscf = 2*( radscf / (clim(2)-clim(1)) );          % radius between 0 and 1, small vs. large pos/neg effect
radscf(radscf>1)=1; radscf(radscf<0)=0;             % clip values outside the [0 1] range

if strcmp(scalerad, 'yes')
  rmax = rmin+(radius-rmin)*radscf; % maximum radius of the clouds
else
  rmax = ones(length(pos), 1)*radius; % each cloud has the same radius
end

if dointersect
  % generate circle points
  angles = linspace(0,2*pi,50);
  x = cos(angles)';
  y = sin(angles)';
  slicedim = zeros(length(angles),1);

  if strcmp(slicepos, 'auto') % search each slice for largest area of data
    % find the potential limits of the interpolation
    intxmax = max(pos(:,1))+radius; intxmin = min(pos(:,1))-radius;
    intymax = max(pos(:,2))+radius; intymin = min(pos(:,2))-radius;
    intzmax = max(pos(:,3))+radius; intzmin = min(pos(:,3))-radius;

    % define potential slices with data
    if oriX; potent_slices = round(intxmin):round(intxmax); end
    if oriY; potent_slices = round(intymin):round(intymax); end
    if oriZ; potent_slices = round(intzmin):round(intzmax); end

    area = NaN(length(pos),length(potent_slices));  % preallocate matrix of electrode interpolation area for each slice
    for s = 1:length(potent_slices)                 % only search slices that could potentially contain data
      distance = NaN(length(pos),1);                % preallocate vector for each electrodes distance from the slice
      for c = 1:length(pos)
        indpos = pos(c, :);
        if oriX; distance(c) = abs(indpos(1)-potent_slices(s)); end
        if oriY; distance(c) = abs(indpos(2)-potent_slices(s)); end
        if oriZ; distance(c) = abs(indpos(3)-potent_slices(s)); end

        if distance(c) < rmax(c) % if there is any data from this electrode in this slice
          % find the circle points for the interpolation of this electrode
          xmax = rmax(c)*x;
          ymax = rmax(c)*y;

          % find the maximum radius of a cross section of the virtual sphere in the given slice
          xmaxdif = abs(xmax-distance(c));
          imindif = find(xmaxdif == min(xmaxdif), 1); % index of x value closest to distance(e)
          rcmax = abs(ymax(imindif));

          area(c, s) = 0.5*pi*rcmax^2;
        else % area must be zero
          area(c, s) = 0;
        end
      end % for each loop
    end % for each slice
    totalarea = sum(area);

    slicepos = zeros(nslices,1);
    for n = 1:nslices
      imaxslice = find(totalarea == max(totalarea), 1);       % index of the slice with the maximum area
      slicepos(n) = potent_slices(imaxslice);                 % position of the yet unlisted slice with the maximum area
      totalarea(imaxslice-minspace:imaxslice+minspace) = 0;   % change the totalarea of the chosen slice and those within minspace to 0 so that it is not chosen again
    end
  end

  % pre-allocate logical array specifying whether intersection with a given mesh (k) actually exists within a given slice (s)
  intersect_exists = zeros(numel(slicepos), numel(meshplot));
end

% draw figure
if strcmp(sli, '2d')
  % pre-allocate interpolation limits of each slice to facilitate inding overall limits of all slices after plotting
  xsmax = NaN(numel(slicepos),1); xsmin = NaN(numel(slicepos),1);
  ysmax = NaN(numel(slicepos),1); ysmin = NaN(numel(slicepos),1);
  zsmax = NaN(numel(slicepos),1); zsmin = NaN(numel(slicepos),1);

  for s = 1:numel(slicepos) % slice loop
    subplot(numel(slicepos),1,s); hold on;

    % pre-allocate interpolation limits of each cloud to facilitate finding slice limits after plotting
    xcmax = NaN(length(pos(:,1)),1); xcmin = NaN(length(pos(:,1)),1);
    ycmax = NaN(length(pos(:,1)),1); ycmin = NaN(length(pos(:,1)),1);
    zcmax = NaN(length(pos(:,1)),1); zcmin = NaN(length(pos(:,1)),1);

    for c = 1:length(pos(:,1)) % cloud loop
      indpos = pos(c, :);
      % calculate distance from slice
      if oriX; distance = abs(indpos(1)-slicepos(s)); end
      if oriY; distance = abs(indpos(2)-slicepos(s)); end
      if oriZ; distance = abs(indpos(3)-slicepos(s)); end

      if distance < rmax(c)
        switch (cloudtype)
          case 'surf'
            xmax = rmax(c)*x;
            ymax = rmax(c)*y;

            if strcmp(scalealpha, 'yes')
              maxalpha = (rmax(c)-distance)/rmax(c);
            else
              maxalpha = 1;
            end

            % find the maximum radius of a cross section of the virtual sphere in the given slice
            xmaxdif = abs(xmax-distance);
            imindif = find(xmaxdif == min(xmaxdif), 1); % index of x value closest to distance(e)
            rcmax = abs(ymax(imindif));

            % determine points along outermost circle
            xe = rcmax*x;
            ye = rcmax*y;

            % jitter values of points in the slice plane so no surfaces overlap
            slicedime = slicedim+(0.01*rand*ones(length(x), 1));

            % plot concentric circles
            for n = 0:ncirc-1 % circle loop
              xo = xe*((ncirc-n)/ncirc);    % outer x points
              yo = ye*((ncirc-n)/ncirc);    % outer z points
              xi = xe*((ncirc-1-n)/ncirc);  % inner x points
              yi = ye*((ncirc-1-n)/ncirc);  % inner z points
              if n == ncirc-1 % for the last concentric circle
                if oriX; hs = fill3(slicepos(s)+slicedime, indpos(2)+xo, indpos(3)+yo, val(c)); end
                if oriY; hs = fill3(indpos(1)+xo, slicepos(s)+slicedime, indpos(3)+yo, val(c)); end
                if oriZ; hs = fill3(indpos(1)+xo, indpos(2)+yo, slicepos(s)+slicedime, val(c)); end
              else
                if oriX; hs = fill3([slicepos(s)+slicedime; slicepos(s)+slicedim], [indpos(2)+xo; indpos(2)+xi], [indpos(3)+yo; indpos(3)+yi], val(c)); end
                if oriY; hs = fill3([indpos(1)+xo; indpos(1)+xi], [slicepos(s)+slicedime; slicepos(s)+slicedim], [indpos(3)+yo; indpos(3)+yi], val(c)); end
                if oriZ; hs = fill3([indpos(1)+xo; indpos(1)+xi], [indpos(2)+yo; indpos(2)+yi], [slicepos(s)+slicedime; slicepos(s)+slicedim], val(c)); end
              end
              set(hs, 'EdgeColor', 'none', 'FaceAlpha', maxalpha*n/ncirc)
            end % end circle loop

            % find the limits of the plotted surfaces for this electrode
            if oriX
              xcmax(c) = max(slicedime+slicepos(s)); xcmin(c) = min(slicedime+slicepos(s));
              ycmax(c) = max(xe+indpos(2)); ycmin(c) = min(xe+indpos(2));
              zcmax(c) = max(ye+indpos(3)); zcmin(c) = min(ye+indpos(3));
            elseif oriY
              xcmax(c) = max(xe+indpos(1)); xcmin(c) = min(xe+indpos(1));
              ycmax(c) = max(slicedime+slicepos(s)); ycmin(c) = min(slicedime+slicepos(s));
              zcmax(c) = max(ye+indpos(3)); zcmin(c) = min(ye+indpos(3));
            elseif oriZ
              xcmax(c) = max(xe+indpos(1)); xcmin(c) = min(xe+indpos(1));
              ycmax(c) = max(ye+indpos(2)); ycmin(c) = min(ye+indpos(2));
              zcmax(c) = max(slicedime+slicepos(s)); zcmin(c) = min(slicedime+indpos(s));
            end

          case 'cloud'
            rng(0, 'twister');                          % random number generator
            npoints = round(ptdensity*pi*rmax(c)^2);       % number of points based on area of cloud cross section
            azimuth = 2*pi*rand(npoints,1);             % azimuthal angle for each point
            radii = rmax(c)*(rand(npoints,1).^ptgradient);  % radius value for each point
            radii = sort(radii);                        % sort radii in ascending order so they are plotted from inside out
            % convert to Carthesian; note that second input controls third output
            if oriX; [y,z,x] = sph2cart(azimuth, zeros(npoints,1)+0.01*rand(npoints,1), radii); end
            if oriY; [x,z,y] = sph2cart(azimuth, zeros(npoints,1)+0.01*rand(npoints,1), radii); end
            if oriZ; [x,y,z] = sph2cart(azimuth, zeros(npoints,1)+0.01*rand(npoints,1), radii); end

            % color axis with radius scaling
            if strcmp(colorgrad, 'white') % color runs up to white
              fcolidx = ceil(cmid) + sign(colscf(c))*floor(abs(colscf(c)*cmid));
              if fcolidx == 0; fcolidx = 1; end
              fcol = cmapsc(fcolidx,:); % color [Nx3]
              ptcol = [linspace(fcol(1), 1, npoints)' linspace(fcol(2), 1, npoints)' linspace(fcol(3), 1, npoints)'];
            elseif isscalar(colorgrad) % color runs down towards colorbar middle
              rnorm = radii/rmax(c); % normalized radius
              if radscf(c)>=.5 % extreme values
                ptcol = val(c) - (flip(1-rnorm).^inv(colorgrad))*val(c); % scaled fun [Nx1]
              elseif radscf(c)<.5 % values closest to zero
                ptcol = val(c) + (flip(1-rnorm).^inv(colorgrad))*abs(val(c)); % scaled fun [Nx1]
              end
            else
              ft_error('color gradient should be either ''white'' or a scalar determining the fall-off')
            end

            % draw the points
            if oriX; scatter3(x+slicepos(s), y+indpos(2), z+indpos(3), ptsize, ptcol, '.'); end
            if oriY; scatter3(x+indpos(1), y+slicepos(s), z+indpos(3), ptsize, ptcol, '.'); end
            if oriZ; scatter3(x+indpos(1), y+indpos(2), z+slicepos(s), ptsize, ptcol, '.'); end

            % find the limits of the plotted points for this electrode
            if oriX
              xcmax(c) = max(x+slicepos(s)); xcmin(c) = min(x+slicepos(s));
              ycmax(c) = max(y+indpos(2)); ycmin(c) = min(y+indpos(2));
              zcmax(c) = max(z+indpos(3)); zcmin(c) = min(z+indpos(3));
            elseif oriY
              xcmax(c) = max(x+indpos(1)); xcmin(c) = min(x+indpos(1));
              ycmax(c) = max(y+slicepos(s)); ycmin(c) = min(y+slicepos(s));
              zcmax(c) = max(z+indpos(3)); zcmin(c) = min(z+indpos(3));
            elseif oriZ
              xcmax(c) = max(x+indpos(1)); xcmin(c) = min(x+indpos(1));
              ycmax(c) = max(y+indpos(2)); ycmin(c) = min(y+indpos(2));
              zcmax(c) = max(z+slicepos(s)); zcmin(c) = min(z+slicepos(s));
            end

          otherwise
            ft_error('unsupported cloudtype "%s"', cloudtype);

        end % switch cloudtype
      end % if distance < rmax(c)
    end % for each position

    if dointersect
      if oriX; ori = [1 0 0]; loc = [slicepos(s) 0 0]; end
      if oriY; ori = [0 1 0]; loc = [0 slicepos(s) 0]; end
      if oriZ; ori = [0 0 1]; loc = [0 0 slicepos(s)]; end

      % normalise the orientation vector to one
      ori = ori./sqrt(sum(ori.^2));

      % shift the location to be along the orientation vector
      loc = ori*dot(loc,ori);

      % determine three points on the plane
      inplane = eye(3) - (eye(3) * ori') * ori;
      v1 = loc + inplane(1,:);
      v2 = loc + inplane(2,:);
      v3 = loc + inplane(3,:);

      for k = 1:numel(meshplot)

        % only plot if the mesh actually intersects the plane
        xmmax = max(meshplot{k}.pos(:,1)); xmmin = min(meshplot{k}.pos(:,1));
        ymmax = max(meshplot{k}.pos(:,2)); ymmin = min(meshplot{k}.pos(:,2));
        zmmax = max(meshplot{k}.pos(:,3)); zmmin = min(meshplot{k}.pos(:,3));

        if oriX
          if slicepos(s) < xmmax && slicepos(s) > xmmin
            intersect_exists(s,k) = 1;
          else
            intersect_exists(s,k) = 0;
          end
        elseif oriY
          if slicepos(s) < ymmax && slicepos(s) > ymmin
            intersect_exists(s,k) = 1;
          else
            intersect_exists(s,k) = 0;
          end
        elseif oriZ
          if slicepos(s) < zmmax && slicepos(s) > zmmin
            intersect_exists(s,k) = 1;
          else
            intersect_exists(s,k) = 0;
          end
        end

        if intersect_exists(s,k)
          [xmesh, ymesh, zmesh] = intersect_plane(meshplot{k}.pos, meshplot{k}.tri, v1, v2, v3);

          % draw each individual line segment of the intersection
          if ~isempty(xmesh)
            p = patch(xmesh', ymesh', zmesh', nan(1, size(xmesh,1)));
            if ~isempty(intersectcolor),     set(p, 'EdgeColor', intersectcolor{k}); end
            if ~isempty(intersectlinewidth), set(p, 'LineWidth', intersectlinewidth(k)); end
            if ~isempty(intersectlinestyle), set(p, 'LineStyle', intersectlinestyle{k}); end
          end

          % find the limits of the lines and add them to the limits of the
          % interpolation to facilitate finding the limits of the slice
          xcmax(end+1) = max(xmesh(:)); xcmin(end+1) = min(xmesh(:));
          ycmax(end+1) = max(ymesh(:)); ycmin(end+1) = min(ymesh(:));
          zcmax(end+1) = max(zmesh(:)); zcmin(end+1) = min(zmesh(:));
        end
      end % for each mesh
    end % if dointersect

    % find limits of this particular slice
    xsmax(s) = max(xcmax); xsmin(s) = min(xcmin);
    ysmax(s) = max(ycmax); ysmin(s) = min(ycmin);
    zsmax(s) = max(zcmax); zsmin(s) = min(zcmin);

    % color settings
    ft_colormap(cmap);
    if ~isempty(clim) && clim(2)>clim(1)
      caxis(gca, clim);
    end

    % axis and view settings
    set(gca, 'DataAspectRatio', [1 1 1])
    if oriX
      view([90 0]);
    elseif oriY
      view([180 0]);
    elseif oriZ
      view([90 90]);
    end

    % add title to differentiate slices
    if oriX; title(['slicepos = [' num2str(slicepos(s)) ' 0 0]']); end
    if oriY; title(['slicepos = [0 ' num2str(slicepos(s)) ' 0]']); end
    if oriZ; title(['slicepos = [0 0 ' num2str(slicepos(s)) ']']); end
  end

  % set matching limits in the non-slice dimensions for each slice
  for s = 1:numel(slicepos) % slice loop
    subplot(numel(slicepos),1,s);
    if oriX
      xlim([xsmin(s)-2 xsmax(s)+2]);
      ylim([min(ysmin)-2 max(ysmax)+2]);
      zlim([min(zsmin)-2 max(zsmax)+2]);
    elseif oriY
      xlim([min(xsmin)-2 max(xsmax)+2]);
      ylim([ysmin(s)-2 ysmax(s)+2]);
      zlim([min(zsmin)-2 max(zsmax)+2]);
    elseif oriZ
      xlim([min(xsmin)-2 max(xsmax)+2]);
      ylim([min(ysmin)-2 max(ysmax)+2]);
      zlim([zsmin(s)-2 zsmax(s)+2]);
    end
  end

else % plot 3d cloud
  hold on;
  for n = 1:size(pos,1) % cloud loop
    switch (cloudtype)
      case 'cloud'
        % point cloud with radius scaling
        rng(0, 'twister'); % random number generator
        npoints   = round(ptdensity*(4/3)*pi*rmax(n)^3);      % number of points based on cloud volume
        elevation = asin(2*rand(npoints,1)-1);                % elevation angle for each point
        azimuth   = 2*pi*rand(npoints,1);                     % azimuth angle for each point
        radii     = rmax(n)*(rand(npoints,1).^ptgradient);    % radius value for each point
        radii     = sort(radii);                              % sort radii in ascending order so they are plotted from inside out
        [x,y,z]   = sph2cart(azimuth, elevation, radii);      % convert to Carthesian

        % color axis with radius scaling
        if strcmp(colorgrad, 'white')                   % color runs up to white
          indx  = ceil(cmid) + sign(colscf(n))*floor(abs(colscf(n)*cmid));
          indx  = max(min(indx,size(cmapsc,1)),1);      % index should fall within the colormap
          fcol  = cmapsc(indx,:);                       % color [Nx3]
          ptcol = [linspace(fcol(1), 1, npoints)' linspace(fcol(2), 1, npoints)' linspace(fcol(3), 1, npoints)'];
        elseif isscalar(colorgrad)                      % color runs down towards colorbar middle
          rnorm = radii/rmax(n);                        % normalized radius
          if radscf(n)>=.5                              % extreme values
            ptcol = val(n) - (flip(1-rnorm).^inv(colorgrad))*val(n); % scaled fun [Nx1]
          elseif radscf(n)<.5                           % values closest to zero
            ptcol = val(n) + (flip(1-rnorm).^inv(colorgrad))*abs(val(n)); % scaled fun [Nx1]
          end
        else
          ft_error('color gradient should be either ''white'' or a scalar determining color falloff')
        end

        % draw the points
        scatter3(x+pos(n,1), y+pos(n,2), z+pos(n,3), ptsize, ptcol, '.');

      case 'surf'
        indx  = ceil(cmid) + sign(colscf(n))*floor(abs(colscf(n)*cmid));
        indx  = max(min(indx,size(cmapsc,1)),1);  % index should fall within the colormap
        fcol  = cmapsc(indx,:);                   % color [Nx3]
        [xsp, ysp, zsp] = sphere(100);
        hs = surf(rmax(n)*xsp+pos(n,1), rmax(n)*ysp+pos(n,2), rmax(n)*zsp+pos(n,3));
        set(hs, 'EdgeColor', 'none', 'FaceColor', fcol, 'FaceAlpha', 1);

      otherwise
        ft_error('unsupported cloudtype "%s"', cloudtype);

    end % switch cloudtype
  end % end cloud loop

  if ~isempty(meshplot)
    for k = 1:numel(meshplot) % mesh loop
      ft_plot_mesh(meshplot{k}, 'facecolor', facecolor{k}, 'EdgeColor', edgecolor{k}, ...
        'facealpha', facealpha(k), 'edgealpha', edgealpha(k), 'vertexcolor', vertexcolor{k});
      material dull
    end % end mesh loop

    if dointersect % plot the outlines on the mesh
      for s = 1:numel(slicepos) % slice loop
        if oriX; ori = [1 0 0]; loc = [slicepos(s) 0 0]; end
        if oriY; ori = [0 1 0]; loc = [0 slicepos(s) 0]; end
        if oriZ; ori = [0 0 1]; loc = [0 0 slicepos(s)]; end

        % normalise the orientation vector to one
        ori = ori./sqrt(sum(ori.^2));

        % shift the location to be along the orientation vector
        loc = ori*dot(loc,ori);

        % determine three points on the plane
        inplane = eye(3) - (eye(3) * ori') * ori;
        v1 = loc + inplane(1,:);
        v2 = loc + inplane(2,:);
        v3 = loc + inplane(3,:);

        for k = 1:numel(meshplot)

          % only plot if the mesh actually intersects the plane
          xmmax = max(meshplot{k}.pos(:,1)); xmmin = min(meshplot{k}.pos(:,1));
          ymmax = max(meshplot{k}.pos(:,2)); ymmin = min(meshplot{k}.pos(:,2));
          zmmax = max(meshplot{k}.pos(:,3)); zmmin = min(meshplot{k}.pos(:,3));

          if oriX
            if slicepos(s) < xmmax && slicepos(s) > xmmin
              intersect_exists(s,k) = 1;
            else
              intersect_exists(s,k) = 0;
            end
          elseif oriY
            if slicepos(s) < ymmax && slicepos(s) > ymmin
              intersect_exists(s,k) = 1;
            else
              intersect_exists(s,k) = 0;
            end
          elseif oriZ
            if slicepos(s) < zmmax && slicepos(s) > zmmin
              intersect_exists(s,k) = 1;
            else
              intersect_exists(s,k) = 0;
            end
          end

          if intersect_exists(s,k)
            [xmesh, ymesh, zmesh] = intersect_plane(meshplot{k}.pos, meshplot{k}.tri, v1, v2, v3);

            % draw each individual line segment of the intersection
            if ~isempty(xmesh)
              p = patch(xmesh', ymesh', zmesh', nan(1, size(xmesh,1)));
              if ~isempty(intersectcolor),     set(p, 'EdgeColor', intersectcolor{k}); end
              if ~isempty(intersectlinewidth), set(p, 'LineWidth', intersectlinewidth(k)); end
              if ~isempty(intersectlinestyle), set(p, 'LineStyle', intersectlinestyle{k}); end
            end
          end
        end % for each mesh
      end % for each slice
    end % if dointersect
  end % if plotting mesh

  % axis settings
  axis off
  axis vis3d
  axis equal

  % color settings
  ft_colormap(cmap);
  if ~isempty(clim) && clim(2)>clim(1)
    caxis(gca, clim);
  end
end


function fusion = qfuser_backoff(w,scores,scrQ,ndx,backoff_scores)
% A wrapper around qfuser_linear that substitutes backoff scores
% for any trials that are missing scores from the quality fuser.
% The backoff scores will typically be the scores from the
% non-quality fusion.
% Inputs:
%   w: The trained quality fusion weights.
%   scores: The system scores.
%   scrQ: The quality measures.
%   ndx: A Key or Ndx object indicating trials.
%   backoff_scores: A vector of scores to use if missing from
%     quality fusion output.
% Outputs:
%   fusion: A vector of scores; one for each trial.  Scores will be
%     from quality fusion unless they are missing in which case
%     they will be copied over from backoff_scores.

assert(isa(scrQ,'Quality'))
assert(isa(ndx,'Ndx')||isa(ndx,'Key'))
assert(isvector(backoff_scores))

if isa(ndx,'Ndx')
    trials = ndx.trialmask;
else
    trials = ndx.tar | ndx.non;
end

[m,n] = size(scores);
assert(n==sum(trials(:)));

% Get fused scores from quality fuser.
fusion = qfuser_linear(w,scores,scrQ,ndx);

% Check for missing scores.
assert(all(size(trials)==size(scrQ.scoremask)));
missing = trials & ~scrQ.scoremask;
missing = missing(trials(:));
nmiss = sum(missing(:));

% Use backoff scores for missing scores.
if nmiss > 0
    log_warning('Quality missing for %i of %i trials, substituting backoff scores.\n',nmiss,n);
    fusion(missing) = backoff_scores(missing);
end

end


function module = noise_module_fixed(tau)

module.initialize = @initialize;
module.update = @update;
module.get_struct = @get_struct;

  function [Tau] = initialize(M,N)
  Tau = bsxfun(@times, tau, ones(M,N));
  end

  function S = get_struct()
  S.module = 'noise_module_fixed';
  S.tau = tau;
  end

  function [Tau, LogTau, KL_Tau] = update(iter, E2, Obs)

  I = ones(size(Obs));
  Tau = bsxfun(@times, tau, I);
  LogTau = bsxfun(@times, log(tau), I);
  KL_Tau = 0;

  end

end


function rez = final_clustering(rez)

wPCA  = rez.wPCA;
wroll = [];
tlag = [-2, -1, 1, 2];
for j = 1:length(tlag)
    wroll(:,:,j) = circshift(wPCA, tlag(j), 1)' * wPCA;
end

%% split templates into batches
rmin = 0.75;
nlow = 100;
n0 = 0;

Nchan = rez.ops.Nchan;
Nk = size(iC,2);
yunq = unique(rez.yc);

ktid = int32(st3(:,2));

uweigh = abs(rez.U(:,:,1));
uweigh = uweigh ./ sum(uweigh,1);
ycup = sum(uweigh .* rez.yc, 1);

Nfilt =  size(rez.W,2);
dWU = gpuArray.zeros(ops.nt0, ops.Nchan, Nfilt, 'double');
for j = 1:Nfilt
    dWU(:,:,j) = rez.mu(j) * squeeze(rez.W(:, j, :)) * squeeze(rez.U(:, j, :))';
end

ops = rez.ops;
NchanNear   = min(ops.Nchan, 16);

[iC, mask, C2C] = getClosestChannels(rez, ops.sigmaMask, NchanNear);

[~, iW] = max(abs(dWU(ops.nt0min, :, :)), [], 2);
iW = int32(squeeze(iW));

iC = gather(iC(:,iW));
%%
ss = double(st3(:,1)) / ops.fs;

ycenter = 25:40:3025;

Wpca = zeros(6, Nchan, 1000, 'single');
nst = numel(ktid);
hid = zeros(nst,1 , 'int32');
amp = zeros(1000,1 , 'single');

% ycup = rez.yc;

tic
for j = 1:numel(ycenter)
    fprintf('GROUP %d/%d \n', j, numel(ycenter))
    y0 = ycenter(j);

    xchan = abs(ycup - y0) < 20;
    itemp = find(xchan);

    if isempty(itemp)
        continue;
    end
    tin = ismember(ktid, itemp);
    pid = ktid(tin);
    data = tF(tin, :, :);

    ich = unique(iC(:, itemp));
    ch_min = ich(1)-1;
    ch_max = ich(end);

    nsp = size(data,1);
    dd = zeros(nsp, 6, ch_max-ch_min, 'single');
    for k = 1:length(itemp)
        ix = pid==itemp(k);
        dd(ix, :, iC(:,itemp(k))-ch_min) = data(ix,:,:);
    end

    [kid, aj] = run_pursuit(dd, nlow, rmin, n0, wroll, ss(tin));

    nmax = max(kid);
    for t = 1:nmax
        Wpca(:, ch_min+1:ch_max, t + n0) = gather(sq(mean(dd(kid==t,:,:),1)));
    end

    hid(tin) = gather(kid + n0);
    amp(n0+[1:nmax]) = aj;
    n0 = n0 + nmax;
end
Wpca = Wpca(:,:,1:n0);
amp = amp(1:n0);
toc
%%
clust_good = check_clusters(hid, ss);
sum(clust_good)

rez.W = zeros(61,0, 3, 'single');
rez.U = zeros(ops.Nchan,0,3, 'single');
rez.mu = zeros(1,0, 'single');
for  t = 1:n0
    dWU = wPCA * gpuArray(Wpca(:,:,t));
    [w,s,u] = svdecon(dWU);
    wsign = -sign(w(21,1));
    rez.W(:,t,:) = wsign * w(:,1:3);
    rez.U(:,t,:) = wsign * u(:,1:3) * s(1:3,1:3);
    rez.mu(t) = sum(sum(rez.U(:,t,:).^2))^.5;
    rez.U(:,t,:) = rez.U(:,t,:) / rez.mu(t);
end

%%
rez1 = rez;
rez1.st3 = st3;
rez1.st3(:,1) = rez1.st3(:,1); % +  ops.ntbuff;
rez1.st3(:,2) = hid;
rez1.st3(:,3) = amps;
rez1.simScore = (U(:,:) * U(:,:)') .* (W(:,:) * W(:,:)')/6;

amps = sq(sum(sum(tF.^2,2),3)).^.5;

rez1.cProj = [];
rez1.iNeigh = [];
rez1.cProjPC = [];
rez1.iNeighPC = [];
% rez1.st3(:,1) = rez1.st3(:,1); % - 30000 * 2000;

rez1.U = permute(Wpca, [2,3,1]);

rez1.mu = sum(sum(rez1.U.^2, 1),3).^.5;
rez1.U = rez1.U ./ rez1.mu;
rez1.mu = rez1.mu(:);

rez1.W = reshape(rez.wPCA, [61, 1, 6]);
rez1.W = repmat(rez1.W, [1, n0, 1]);
rez1.est_contam_rate = ones(n0,1);

U = permute(rez1.U, [2,1,3]);
W = permute(rez1.W, [2,1,3]);
%%

rez1 = find_merges(rez1, wroll, 1);

%%
hid = int32(rez1.st3(:,2));
clust_good = check_clusters(hid, ss);
sum(clust_good)
rez1.good = clust_good;

%%
rezToPhy2(rez1, rootZ);

%%
tmp_chan = iC(1,:);
y0 = 700;
xchan = abs(rez.yc - y0) < 20;
itemp = find(xchan(tmp_chan));

tin = ismember(ktid, itemp);
pid = ktid(tin);
data = rez.cProjPC(tin, :, :);

ich = unique(iC(:, itemp));
ch_min = ich(1)-1;
ch_max = ich(end);

nsp = size(data,1);
dd = zeros(nsp, 6, ch_max-ch_min, 'single');
for k = 1:length(itemp)
    ix = pid==itemp(k);
    dd(ix, :, iC(:,itemp(k))-ch_min) = data(ix,:,:);
end

size(dd)
%%
tic
kid = run_pursuit(dd, nlow, rmin, 0);
toc
%%
subplot(2,1,1)
[~, isort] = sort(pid);
imagesc(dd(isort(1:10:end), :)', [-10, 10])
colormap(redblue)
subplot(2,1,2)
[~, isort] = sort(kid);
imagesc(dd(isort(1:10:end), :)', [-10, 10])

hold on
ksort = sort(kid);
nc = max(kid);
plot(size(dd,2) * ksort(1:10:end) / nc, 'k', 'linewidth', 3)


function obj = viewMovieFiltering(obj)
	% Allows testing of spatial filtering.
	% Biafra Ahanonu
	% branched from controllerAnalysis: 2014.08.01 [16:09:16]
	% inputs
		%
	% outputs
		%

	% changelog
		% 2021.08.10 [09:57:36] - Updated to handle CIAtah v4.0 switch to all functions inside ciapkg package.
	% TODO
		%

	import ciapkg.api.* % import CIAtah functions in ciapkg package API.

	[fileIdxArray idNumIdxArray nFilesToAnalyze nFiles] = obj.getAnalysisSubsetsToAnalyze();

	movieSettings = inputdlg({...
			'start:end frames (leave blank for all)',...
			'FFT filter type (bandpass, lowpass, highpass)',...
			'FFT filter mask type (gaussian or binary)',...
			'movie regular expression',...
			'HDF5 dataset name',...
			'Show division and dfof of movie'...
		},...
		'Settings for movie spatial filtering check',[1 100],...
		{...
			'1:25',...
			'bandpass',...
			'gaussian',...
			'concat',...
			'/1',...
			'1'...
		}...
	);
	frameList = str2num(movieSettings{1});
	% options.normalizeFreqLow = str2num(movieSettings{1});
	% options.normalizeFreqHigh = str2num(movieSettings{1});
	options.normalizeBandpassType = movieSettings{2};
	options.normalizeBandpassMask = movieSettings{3};
	fileFilterRegexp = movieSettings{4};
	inputDatasetName = movieSettings{5};
	testDuplicateDfof = str2num(movieSettings{6});

	% loop over all directories and copy over a specified chunk of the movie file
	for thisFileNumIdx = 1:nFilesToAnalyze
			thisFileNum = fileIdxArray(thisFileNumIdx);
			obj.fileNum = thisFileNum;
			display(repmat('=',1,21))
			display([num2str(thisFileNumIdx) '/' num2str(nFilesToAnalyze) ' (' num2str(thisFileNum) '/' num2str(nFiles) '): ' obj.fileIDNameArray{obj.fileNum} 10 obj.inputFolders{obj.fileNum}]);
			movieList = getFileList(obj.inputFolders{obj.fileNum}, fileFilterRegexp);
			movieList = movieList{1};

			% get list of frames and correct for user input
			if isempty(frameList)
				frameListTmp = frameList;
			else
				movieDims = loadMovieList(movieList,'convertToDouble',0,'frameList',[],'inputDatasetName',inputDatasetName,'getMovieDims',1);
				nMovieFrames = sum(movieDims.z);
				display(['movie frames: ' num2str(nMovieFrames)]);
				if nMovieFrames<nanmax(frameList)
					frameListTmp = frameList;
					frameListTmp(frameListTmp>nMovieFrames) = [];
				else
					frameListTmp = frameList;
				end
			end

			% get movie, normalize, and display
			[primaryMovie] = loadMovieList(movieList,'convertToDouble',0,'frameList',frameListTmp(:),'inputDatasetName',inputDatasetName);
			primaryMovie = single(primaryMovie);

			stopTesting = 1;
			while ~isempty(stopTesting)
				[inputMovieNormalized] = normalizeMovie(primaryMovie,'normalizationType','matlabFFT_test','bandpassType',options.normalizeBandpassType,'bandpassMask',options.normalizeBandpassMask,'testDuplicateDfof',testDuplicateDfof);

				playMovie(inputMovieNormalized,'colormapColor','gray');

				stopTesting = strmatch(questdlg('Repeat?', ...
					'Run filtering for this movie again?', ...
					'yes','no','yes'),'yes');
			end
	end

end


function outp = newcnstr( prob, x, y, op )
persistent map_eq map_le map_ge map_ne

%
% Check problem
%

if ~isa( prob, 'cvxprob' ),
    error( 'A cvx problem must be created first.' );
end
global cvx___
p = index( prob );

%
% Check arguments
%

cx = isnumeric( x ) | isa( x, 'cvx' );
cy = isnumeric( y ) | isa( y, 'cvx' );
if ~cx,
    x = cvx_collapse( x, false, true );
    cx = isnumeric( x ) | isa( x, 'cvx' );
end
if ~cy,
    y = cvx_collapse( y, false, true );
    cy = isnumeric( y ) | isa( y, 'cvx' );
end
sx = size( x );
sy = size( y );
if ~cx || ~cy,
    if cx || cy || op(1) ~= '=',
        error( 'Invalid CVX constraint: {%s} %s {%s}', class( x ), op, class( y ) );
    elseif ~isequal( sx, sy ),
        error( 'The left- and right-hand sides have incompatible sizes.' );
    else
        nx = prod( sx );
        for k = 1 : nx,
            newcnstr( prob, x{k}, y{k}, op );
        end
        if nargout,
            outp = cvxcnst( prob );
        end
        return
    end
end
xs = all( sx == 1 );
ys = all( sy == 1 );
if ~xs && ~ys && ~isequal( sx, sy ),
    error( 'Matrix dimensions must agree.' );
end

%
% Check readlevel
%

tx = cvx_readlevel( x );
ty = cvx_readlevel( y );
tx = any( tx( : ) > p );
ty = any( ty( : ) > p );
if tx || ty,
    error( 'Constraints may not involve internal, read-only variables.' );
end

%
% Check for a dual reference
%

if isa( x, 'cvx' ),
    dx = getdual( x );
else
    dx = '';
end
if isa( y, 'cvx' ),
    dy = getdual( y );
else
    dy = '';
end
if isempty( dx ),
    dx = dy;
elseif ~isempty( dy ),
    error( [ 'Two dual variables found: "', dx, '","', dy, '"' ] );
end
if ~isempty( dx ),
    duals = cvx___.problems( p ).duals;
    try
        dual = builtin( 'subsref', duals, dx );
    catch
        nm = cvx_subs2str( dx );
        error( [ 'Dual variable "', nm(2:end), '" has not been declared.' ] );
    end
    if ~isempty( dual ),
        nm = cvx_subs2str( dx );
        error( [ 'Dual variable "', nm(2:end), '" already in use.' ] );
    end
end

%
% Handle the SDP case
%

sdp_mode = false;
mx = sx( 1 ) > 1 & sx( 2 ) > 1;
my = sy( 1 ) > 1 & sy( 2 ) > 1;
if op(1) ~= '=' && cvx___.problems( p ).sdp && ( mx || my ),

    if sx( 1 ) ~= sx( 2 ) || sy( 1 ) ~= sy( 2 ),
        error( 'SDP constraint must be square.' );
    elseif xs && cvx_isnonzero( x ),
        error( 'SDP constraint {scalar} %s {matrix} valid only if the scalar is zero.', op );
    elseif ys && cvx_isnonzero( y ),
        error( 'SDP constraint {matrix} %s {scalar} valid only if the scalar is zero.', op );
    elseif ~cvx_isaffine( x ) || ~cvx_isaffine( y ),
        error( 'Both sides of an SDP constraint must be affine.' );
    elseif op( 1 ) == '>',
        x = minus( x, y );
        y = semidefinite( size( x ), ~isreal( x ) );
        sz = size( x );
    else
        y = minus( y, x );
        x = semidefinite( size( y ), ~isreal( y ) );
        sz = size( y );
    end
    op = '==';
    sdp_mode = true;

else

    if isempty( map_ge ),
        temp    = cvx_remap( 'constant' );
        temp    = ~ ( temp' * temp );

        map_ne  = cvx_remap;

        map_eq2 = cvx_remap( 'log-affine' );
        map_eq2 = ( map_eq2' * map_eq2 ) & temp;
        map_eq3 = cvx_remap( 'affine' );
        map_eq3 = map_eq3' * map_eq3;
        map_eq  = 2 * map_eq2 + map_eq3;

        % Trivially feasible constraints
        map_ge1 = ( cvx_remap( 'log-concave' )' * cvx_remap( 'nonpositive' ) ) & temp;
        % Trivially infeasible constraints
        map_ge2 = ( cvx_remap( 'nonpositive' )' * cvx_remap( 'log-convex'  ) ) & temp;
        % Full geometric constraints
        map_ge3  = ( cvx_remap( 'log-concave' )' * cvx_remap( 'log-convex'  ) ) & temp;
        % Linear constraints
        map_ge4 = ( cvx_remap( 'concave' )' * cvx_remap( 'convex' ) ) & ~map_ge3;
        map_ge = map_ge4 + 2 * map_ge3 + 3 * map_ge2 + 4 * map_ge1;

        map_le = map_ge';
    end
    switch op(1),
        case '<',
            remap = map_le;
        case '>',
            remap = map_ge;
        case '~',
            remap = map_ne;
        otherwise,
            remap = map_eq;
    end
    vx = cvx_classify( x );
    vy = cvx_classify( y );
    vm = vx + size( remap, 1 ) * ( vy - 1 );
    vr = remap( vm );
    tt = vr( : ) == 0;
    if any( tt ),
        [ vu, vi ] = unique( vm );
        vi = vi( remap( vu ) == 0 );
        strs = {};
        xt = x; yt = y;
        for k = 1 : length( vi ),
            if any( sx ~= 1 ), xt = x(vi(k)); end
            if any( sy ~= 1 ), yt = y(vi(k)); end
            strs{k+1} = sprintf( '\n   Invalid constraint: {%s} %s {%s}', cvx_class( xt, false, true ), op, cvx_class( yt, false, true ) );
        end
        error( [ sprintf( 'Disciplined convex programming error:' ), strs{:} ] );
    end
    tt = vr( : ) == 2;
    if any( tt ),
        if all( tt ),
            x = log( x );
            y = log( y );
        else
            if isscalar( x ), x = x * ones(size(y)); end
            if isscalar( y ), y = y * ones(size(x)); end
            x( tt ) = log( x( tt ) );
            y( tt ) = log( y( tt ) );
        end
    end
    tt = vr( : ) == 3;
    if any( tt ),
        x( tt ) = 0;
        y( tt ) = 1 - 2 * ( op(1) == '<' );
    end
    tt = vr( : ) == 4;
    if any( tt ),
        x( tt ) = 0;
        y( tt ) = 1 - 2 * ( op(1) == '>' );
    end
end

%
% Eliminate lexical redundancies
%

if op(1) == '<',
    z = minus( y, x );
    op(1) = '>';
else
    z = minus( x, y );
end
if op( 1 ) == '=' || sdp_mode,
    cmode = 'full';
else
    cmode = 'magnitude';
end
[ zR, zL, zS ] = bcompress( z, cmode );
if sdp_mode,
    if isreal( zR ),
        nnq = 0.5 * sz( 1 ) * ( sz( 1 ) + 1 );
    else
        nnq = sz( 1 ) * sz( 1 );
    end
    if size( zR, 1 ) > nnq * prod( sz( 3 : end ) ),
        warning( 'CVX:UnsymmetricLMI', [ ...
            'This linear matrix inequality appears to be unsymmetric. This is\n', ...
            'very likely an error that will produce unexpected results. Please check\n', ...
            'the LMI; and, if necessary, re-enter the model.' ], 1 );
    end
end

%
% Add the (in)equalities
%

touch( prob, zL, op(1) == '=' );
mO = length( cvx___.equalities );
mN = length( zL );
cvx___.equalities = vertcat( cvx___.equalities, zL );
cvx___.needslack( end + 1 : end + mN, : ) = op( 1 ) ~= '=';

%
% Create the dual
%

if ~isempty( dx ),
    zI = cvx_invert_structure( zR )';
    zI = sparse( mO + 1 : mO + mN, 1 : mN, 1 ) * zI;
    zI = cvx( zS, zI );
    duals = cvx___.problems( p ).duals;
    duals = builtin( 'subsasgn', duals, dx, zI );
    cvx___.problems( p ).duals = duals;
end

%
% Create the output object
%

if nargout,
    outp = cvxcnst( prob );
end

% Copyright 2010 Michael C. Grant and Stephen P. Boyd.
% See the file COPYING.txt for full copyright information.
% The command 'cvx_where' will show where this file is located.


function test_tutorial_coherence

% MEM 5gb
% WALLTIME 00:20:00
% DEPENDENCY ft_freqanalysis ft_connectivityanalysis ft_multiplotER ft_singleplotER ft_topoplotER ft_sourceanalysis ft_sourceinterpolate ft_prepare_sourcemodel headsurface

addpath(dccnpath('/home/common/matlab/fieldtrip/data/ftp/tutorial/coherence')); % this contains the trial function

% find the interesting epochs of data
cfg = [];
cfg.trialfun                  = 'trialfun_left';
cfg.dataset                   = dccnpath('/home/common/matlab/fieldtrip/data/ftp/test/ctf/SubjectCMC.ds');
cfg = ft_definetrial(cfg);

% detect EOG artifacts in the MEG data
cfg.continuous                = 'yes';
cfg.artfctdef.eog.padding     = 0;
cfg.artfctdef.eog.bpfilter    = 'no';
cfg.artfctdef.eog.detrend     = 'yes';
cfg.artfctdef.eog.hilbert     = 'no';
cfg.artfctdef.eog.rectify     = 'yes';
cfg.artfctdef.eog.cutoff      = 2.5;
cfg.artfctdef.eog.interactive = 'no';
cfg = ft_artifact_eog(cfg);

% detect jump artifacts in the MEG data
cfg.artfctdef.jump.interactive = 'no';
cfg.padding                    = 5;
cfg = ft_artifact_jump(cfg);

% detect muscle artifacts in the MEG data
cfg.artfctdef.muscle.cutoff   = 8;
cfg.artfctdef.muscle.interactive = 'no';
cfg = ft_artifact_muscle(cfg);

% reject the epochs that contain artifacts
cfg.artfctdef.reject          = 'complete';
cfg = ft_rejectartifact(cfg);

% preprocess the MEG data
cfg.demean                    = 'yes';
cfg.dftfilter                 = 'yes';
cfg.channel                   = {'MEG'};
cfg.continuous                = 'yes';
meg = ft_preprocessing(cfg);

% preprocess the EMG data
cfg              = [];
cfg.dataset      = meg.cfg.dataset;
cfg.trl          = meg.cfg.trl;
cfg.continuous   = 'yes';
cfg.demean       = 'yes';
cfg.dftfilter    = 'yes';
cfg.channel      = {'EMGlft' 'EMGrgt'};
cfg.hpfilter     = 'yes';
cfg.hpfreq       = 10;
cfg.rectify      = 'yes';
emg = ft_preprocessing(cfg);

% concatenate the two data-structures into one structure
data = ft_appenddata([], meg, emg);

% visualisation
figure
subplot(2,1,1);
plot(data.time{1},data.trial{1}(77,:));
axis tight;
legend(data.label(77));

subplot(2,1,2);
plot(data.time{1},data.trial{1}(152:153,:));
axis tight;
legend(data.label(152:153));

% spectral analysis: fourier
cfg            = [];
cfg.output     = 'fourier';
cfg.method     = 'mtmfft';
cfg.foilim     = [5 100];
cfg.tapsmofrq  = 5;
cfg.keeptrials = 'yes';
cfg.channel    = {'MEG' 'EMGlft' 'EMGrgt'};
freqfourier    = ft_freqanalysis(cfg, data);

% spectral analysis: powandcsd
cfg            = [];
cfg.output     = 'powandcsd';
cfg.method     = 'mtmfft';
cfg.foilim     = [5 100];
cfg.tapsmofrq  = 5;
cfg.keeptrials = 'yes';
cfg.channel    = {'MEG' 'EMGlft' 'EMGrgt'};
cfg.channelcmb = {'MEG' 'EMGlft'; 'MEG' 'EMGrgt'};
freq           = ft_freqanalysis(cfg, data);

% compute coherence
cfg            = [];
cfg.method     = 'coh';
cfg.channelcmb = {'MEG' 'EMG'};
fd             = ft_connectivityanalysis(cfg, freq);
fdfourier      = ft_connectivityanalysis(cfg, freqfourier);

% visualisation
cfg                  = [];
cfg.parameter        = 'cohspctrm';
cfg.xlim             = [5 80];
cfg.refchannel       = 'EMGlft';
cfg.layout           = 'CTF151.lay';
cfg.showlabels       = 'yes';
figure; ft_multiplotER(cfg, fd)

cfg.channel = 'MRC21';
figure; ft_singleplotER(cfg, fd);

cfg                  = [];
cfg.parameter        = 'cohspctrm';
cfg.xlim             = [15 20];
cfg.zlim             = [0 0.1];
cfg.refchannel       = 'EMGlft';
cfg.layout           = 'CTF151.lay';
figure; ft_topoplotER(cfg, fd)

% 2 Hz smoothing
cfg            = [];
cfg.output     = 'powandcsd';
cfg.method     = 'mtmfft';
cfg.foilim     = [5 100];
cfg.tapsmofrq  = 2;
cfg.keeptrials = 'yes';
cfg.channel    = {'MEG' 'EMGlft'};
cfg.channelcmb = {'MEG' 'EMGlft'};
freq2          = ft_freqanalysis(cfg,data);

cfg            = [];
cfg.method     = 'coh';
cfg.channelcmb = {'MEG' 'EMG'};
fd2            = ft_connectivityanalysis(cfg,freq2);

cfg               = [];
cfg.parameter     = 'cohspctrm';
cfg.refchannel    = 'EMGlft';
cfg.xlim          = [5 80];
cfg.channel       = 'MRC21';
figure; ft_singleplotER(cfg, fd, fd2);

% 10 Hz smoothing
cfg            = [];
cfg.output     = 'powandcsd';
cfg.method     = 'mtmfft';
cfg.foilim     = [5 100];
cfg.keeptrials = 'yes';
cfg.channel    = {'MEG' 'EMGlft'};
cfg.channelcmb = {'MEG' 'EMGlft'};
cfg.tapsmofrq = 10;
freq10        = ft_freqanalysis(cfg,data);

cfg            = [];
cfg.method     = 'coh';
cfg.channelcmb = {'MEG' 'EMG'};
fd10          = ft_connectivityanalysis(cfg,freq10);

cfg               = [];
cfg.parameter     = 'cohspctrm';
cfg.xlim          = [5 80];
cfg.ylim          = [0 0.2];
cfg.refchannel    = 'EMGlft';
cfg.channel       = 'MRC21';
figure;ft_singleplotER(cfg, fd, fd2, fd10);

% 50 trials
cfg            = [];
cfg.output     = 'powandcsd';
cfg.method     = 'mtmfft';
cfg.foilim     = [5 100];
cfg.tapsmofrq  = 5;
cfg.keeptrials = 'yes';
cfg.channel    = {'MEG' 'EMGlft'};
cfg.channelcmb = {'MEG' 'EMGlft'};
cfg.trials     = 1:50;
freq50         = ft_freqanalysis(cfg,data);

cfg            = [];
cfg.method     = 'coh';
cfg.channelcmb = {'MEG' 'EMG'};
fd50           = ft_connectivityanalysis(cfg,freq50);

cfg                  = [];
cfg.parameter        = 'cohspctrm';
cfg.xlim             = [5 100];
cfg.ylim             = [0 0.2];
cfg.refchannel       = 'EMGlft';
cfg.channel          = 'MRC21';
figure; ft_singleplotER(cfg, fd, fd50);

% source reconstruction
cfg            = [];
cfg.method     = 'mtmfft';
cfg.output     = 'powandcsd';
cfg.foilim     = [18 18];
cfg.tapsmofrq  = 5;
cfg.keeptrials = 'yes';
cfg.channelcmb = {'MEG' 'MEG';'MEG' 'EMGlft'};
freq           = ft_freqanalysis(cfg, data);

cfg           = [];
cfg.method    = 'dics';
cfg.refchan   = 'EMGlft';
cfg.frequency = 18;
cfg.headmodel = dccnpath('/home/common/matlab/fieldtrip/data/ftp/test/ctf/SubjectCMC.hdm');
cfg.inwardshift = 1;
cfg.sourcemodel.resolution = 1;
cfg.sourcemodel.unit       = 'cm';
source        = ft_sourceanalysis(cfg, freq);

mri = ft_read_mri(dccnpath('/home/common/matlab/fieldtrip/data/ftp/test/ctf/SubjectCMC.mri'));
mri = ft_volumereslice([], mri);

cfg            = [];
cfg.parameter  = 'coh';
cfg.downsample = 2;
interp         = ft_sourceinterpolate(cfg, source, mri);

cfg              = [];
cfg.method       = 'ortho';
%cfg.interactive  = 'yes';
cfg.funparameter = 'coh';
figure; ft_sourceplot(cfg, interp);

%--------------------------------
% subfunction
function trl = trialfun_left(cfg)

% read in the triggers and create a trial-matrix
% consisting of 1-second data segments, in which
% left ECR-muscle is active.

event = ft_read_event(cfg.dataset);
trig  = [event(find(strcmp('backpanel trigger', {event.type}))).value];
indx  = [event(find(strcmp('backpanel trigger', {event.type}))).sample];

%left-condition
sel = [find(trig==1028):find(trig==1029)];

trig = trig(sel);
indx = indx(sel);

trl = [];
for j = 1:length(trig)-1
  trg1 = trig(j);
  trg2 = trig(j+1);
  if trg1<=100 && trg2==2080
    trlok      = [[indx(j)+1:1200:indx(j+1)-1200]' [indx(j)+1200:1200:indx(j+1)]'];
    trlok(:,3) = [0:-1200:-1200*(size(trlok,1)-1)]';
    trl        = [trl; trlok];
  end
end


function organizeFusedTextures_STS(pathWORK,nPatient,MRIinv_cell,MRIweight_mat,R_mat,scale_cell,algo_cell,Ng_mat)
% -------------------------------------------------------------------------
% function organizeFusedTextures(pathWORK,nPatient,MRIinv_cell,MRIweight_mat,R_mat,scale_cell,algo_cell,Ng_mat)
% -------------------------------------------------------------------------
% DESCRIPTION:
% This function organizes FUSED texture features computed for all
% patients, for all different combinations of the following texture
% extraction parameters:
% - MRI Inv.: Inversion of MRI intensities in the PET/MRI fusion process.
% - MRI weight: Weight given to MRI wavelet sub-bands in the PET/MRI fusion
%               process.
% - WPBF ratio 'R': Ratio specifying the ratio of weight given to band-pass
%                   coefficients to the weigth given to the rest of
%                   coefficients (HHH and LLL) in the wavelet band-pass
%                   filtering process.
% - Scale: Resolution at which 'volume' is isotropically resampled.
% - Quant. Algo.: Quantization algorithm used on 'volume'.
% - Ng: Number of gray-levels on the quantization process.
%
% Different extraction parameters are passed as arrays or cells in the
% function in order to read all possible combinations. This function is
% used for FUSED scans specifically. See Ref. [1] and 'prepareVolume.m'
% for more details.
%
% Textures are organized in three cells: 'textures_PET_T1' and
% 'textures_PET_T2FS', where each cell entry organizes the data for all
% patients for a specific extraction parameter combination. T2-weighted
% fat-saturated and STIR scans are combined in the same texture category
% and simply refered to as 'T2FS' scans (T2-weighted fat-suppression scans).
%
% -- > For example, size(textures_PET_T1) is equal to:
% [numel(MRIinv_cell),numel(MRIweight_mat),numel(R_mat),numel(scale_cell),numel(algo_cell),numel(Ng_mat)]
%
% In addition, the Spearman's rank correlation between each texture
% features and lung metastases is computed and saved in the cells for each
% texture feature of each entry. Results are saved in the STS WORKSPACE
% with the names given above.
% -------------------------------------------------------------------------
% REFERENCE:
% [1] Vallieres, M. et al. (2015). A radiomics model from joint FDG-PET and
%     MRI texture features for the prediction of lung metastases in soft-tissue
%     sarcomas of the extremities. Physics in Medicine and Biology, 60(14),
%     5471-5496. doi:10.1088/0031-9155/60/14/5471
% -------------------------------------------------------------------------
% INPUTS:
% - pathWORK: Full path to the STS WORKSPACE directory.
% - nPatient: Number of patients to read.
% - MRIinv_cell: Cell vector specifying the different 'MRI Inv.' values to test.
% - MRIweight_mat: Array vector specifying the different 'MRI weight' values to test.
% - R_mat: Array vector specifying the different 'R' ratio values to test.
% - scale_cell: Cell vector specifying the different 'Scale' values to test.
% - algo_cell: Cell vector specifying the different 'Quant. Algo.' values to test.
% - Ng_mat: Array vector specifying the different 'Ng' values to test.
% -------------------------------------------------------------------------
% EXAMPLE:
% MRIinv_cell = {'NoInv','Inv'};
% MRIweight_mat = [1/4,1/3,1/2,2/3,3/4];
% R_mat = [1/2,2/3,1,3/2,2];
% scale_cell = {'pixelW',1,2,3,4,5};
% algo_cell = {'Equal','Lloyd'};
% Ng_mat = [8,16,32,64];
% -------------------------------------------------------------------------
% AUTHOR(S): Martin Vallieres <mart.vallieres@gmail.com>
% -------------------------------------------------------------------------
% HISTORY:
% - Creation: May 2015
%--------------------------------------------------------------------------
% STATEMENT:
% This file is part of <https://github.com/mvallieres/radiomics/>,
% a package providing MATLAB programming tools for radiomics analysis.
% --> Copyright (C) 2015  Martin Vallieres
%
%    This package 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 package 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 package.  If not, see <http://www.gnu.org/licenses/>.
% -------------------------------------------------------------------------

startpath = pwd;
cd(pathWORK), load('outcome')

% INITIALIZATION
scans = {'PET_T1','PET_T2FS'};
nScans = numel(scans);
suffix = ['_TEXT'];

fprintf('ORGANIZING TEXTURES FROM FUSED SCANS ... ')
for scan = 1:numel(scans)
    cd([pathWORK,'/TEXTURES'])
    temp = load('Patient1_PET_T1_TEXT'); temp = struct2cell(temp); temp = temp{1}; % In order to get the necessary 'nameType' and 'nameFeature' fields
    nameType = fieldnames(temp.Experiment1); nType = numel(nameType); % All texture types are the same
    text = cell(numel(MRIinv_cell),numel(MRIweight_mat),numel(R_mat),numel(scale_cell),numel(algo_cell),numel(Ng_mat)); % Initialization of master cell
    tempText = cell(1,nPatient); % Cell used to load patient textures only once
    for p = 1:nPatient
        load(['Patient',num2str(p),'_',scans{scan},suffix]) % Variable 'textures' is now in MATLAB workspace
        tempText{p} = textures;
    end
    experiment = 0;
    for i = 1:numel(MRIinv_cell)
        for w = 1:numel(MRIweight_mat)
            for r = 1:numel(R_mat)
                for s = 1:numel(scale_cell)
                    for a = 1:numel(algo_cell)
                        for n = 1:numel(Ng_mat)
                            text{i,w,r,s,a,n} = struct;
                            experiment = experiment + 1;
                            strExperiment = ['Experiment',num2str(experiment)];
                            for t = 1:nType-1 % Last field is 'parameters'
                                nameFeature = fieldnames(temp.(strExperiment).(nameType{t})); nFeature = numel(nameFeature);
                                for f = 1:nFeature
                                    data = zeros(nPatient,1);
                                    for p = 1:nPatient
                                        data(p,1) = tempText{p}.(strExperiment).(nameType{t}).(nameFeature{f});
                                    end
                                    text{i,w,r,s,a,n}.(nameType{t}).(nameFeature{f}).Data = data;
                                    text{i,w,r,s,a,n}.(nameType{t}).(nameFeature{f}).Spearman = corr(data,outcome,'type','Spearman');
                                end
                            end
                        end
                    end
                end
            end
        end
    end
    cd(pathWORK), save(['textures_',scans{scan}],'text')
end
fprintf('DONE\n')

cd(startpath)
end


% Copyright (C) 2017-2018 Titus Cieslewski, RPG, University of Zurich,
%   Switzerland
%   You can contact the author at <titus at ifi dot uzh dot ch>
% Copyright (C) 2017-2018 Siddharth Choudhary, College of Computing,
%   Georgia Institute of Technology, Atlanta, GA, USA
% Copyright (C) 2017-2018 Davide Scaramuzza, RPG, University of Zurich,
%   Switzerland
%
% This file is part of dslam_open.
%
% dslam_open 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.
%
% dslam_open 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 dslam_open. If not, see <http://www.gnu.org/licenses/>.

function T = fixT(T)
assert(all(size(T) == [4 4]));
T(1:3, 1:3) = fixR(T(1:3, 1:3));
assert(all(T(4, :) == [0 0 0 1]));
end


function [ state ] = movePTPLineEefRelBase( t , Pos, relVel)
%% This function is used for moving the endeffector on a line, for the KUKA iiwa 7 R 800.

%% Syntax:
% [ state ] = movePTPLineEefRelBase( t , Pos, vel)

%% About:
% This function is used to perform linear relative movetion of the end-effector on a line,
% this performs only a linear motion, the orientation of end effector will not be changed.
% For more information refer to the example file (kuka0_move_lin_relative.m).

%% Arreguments:
% t: is the TCP/IP connection
% Pos: is the relative displacement, described in base frame, it is 1x3
% cell array, (unit millimeters)
% vel: is a double, represents the linear velocity of the motion (unit
% mm/sec).

% Copy right, Mohammad SAFEEA, 9th of May 2017

    theCommand=['jRelVel_',num2str(relVel),'_']; % set over ride.
    fprintf(t, theCommand);
    message=fgets(t);
    % The new position of end-effector, described in robot
    newPos={0,0,0,0,0,0};

    newPos{1}=Pos{1};
    newPos{2}=Pos{2};
    newPos{3}=Pos{3};

    sendEEfPositions( t ,newPos); % send destination joint positions.

    theCommand='doPTPinCSRelBase';
    fprintf(t, theCommand); % start the point to point motion.
    message=fgets(t);

    readingFlag=false;

    message='';
    state=false;
    while readingFlag==false
        message=fgets(t);
        state=checkAcknowledgment(message);
        if state
            readingFlag=true;
        end
    end

end


%{
 * Copyright (C) 2020-2030, The Regents of The University of Michigan.
 * All rights reserved.
 * This software was developed in the Biped Lab (https://www.biped.solutions/)
 * under the direction of Jessy Grizzle, grizzle@umich.edu. This software may
 * be available under alternative licensing terms; contact the address above.
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * The views and conclusions contained in the software and documentation are those
 * of the authors and should not be interpreted as representing official policies,
 * either expressed or implied, of the Regents of The University of Michigan.
 *
 * AUTHOR: Bruce JK Huang (bjhuang[at]umich.edu)
 * WEBSITE: https://www.brucerobot.com/
%}

function rand = getNoise(app, mu, noise, num_points)
    rand = zeros(1, num_points);
    for i = 1:num_points
        rand(i) = normrnd(mu, noise);
    end
end


function vis_combined_scoremap(expidx, img_idx, ends)

p = exp_params(expidx);
load(p.testGT)

im_fn = annolist(img_idx).image.name;
[~,im_name,~] = fileparts(im_fn);
im = imread(im_fn);

scmap_name = fullfile(p.unary_scoremap_dir, [im_name '.mat']);
load(scmap_name, 'scoremaps');

colors = [1 0 1; 1 1 0; 0 1 1; 1 0 0; 0 1 0; 0 0 1; 1 1 1; 1 1 0; 1 0 1; 0 1 1; 1 0 0; 0 1 0; 0 0 1; 1 1 1];

pw_map_img = im;
un_map_img = im;

for end_ = ends

    if true
        scmap = [];

        for start = 1:14
            if start ~= end_
                current = visualise_pairwise_probabilities(expidx, img_idx, 1, start, end_, false);
                if isempty(current)
                    continue;
                end
                if isempty(scmap)
                    scmap = current;
                else
                    scmap = scmap .* current;
                end
            end
        end
        %save('~/pw_scmap.mat', 'scmap');
    else
        %load('~/pw_scmap.mat', 'scmap');
    end

    %figure(1);
    %imagesc(scmap);
    %colorbar;

    scmap_rescaled = scmap/max(scmap(:));
    pw_map_img = vis_scoremap_on_img(pw_map_img, scmap_rescaled, colors(end_,:));

    unary_map = scoremaps(:, :, end_);
    un_map_img = vis_scoremap_on_img(un_map_img, unary_map, colors(end_,:));
end

out_dir = '/tmp/';

figure(1);
fname = fullfile(out_dir, [num2str(img_idx) '_cidxs_all.png']);
showsaveimg(pw_map_img, fname);

figure(2);
fname = fullfile(out_dir, [num2str(img_idx) '_unary.png']);
showsaveimg(un_map_img, fname);

end

function showsaveimg(im, fname)
imshow(im);
axis off;
set(gca, 'LooseInset', get(gca, 'TightInset'));
print(gcf,'-dpng', fname);
end

function final_img = vis_scoremap_on_img(im, scoremap, color1)
scoremap_img = imresize(scoremap, [size(im, 1), size(im, 2)], 'bicubic');

c = reshape(color1, 1, 1, 3);
color_rep = repmat(c, size(im, 1), size(im, 2), 1);
color_rep = color_rep*255;

scoremap_img = repmat(scoremap_img, 1, 1, 3);
%figure(4);
%imagesc(pw_prob_img);

final_img = (scoremap_img) .* double(color_rep) + (1-scoremap_img) .* double(im);
final_img = uint8(final_img);

end


function convertNetwork(obj)
% convertNetwork(obj)
%
% Converts a network from test to train, inserting lossed, dropout and
% adopting the classification layer.
%
% Copyright by Holger Caesar, 2015

fprintf('Converting test network to train network (dropout, loss, etc.)...\n');

% Add dropout layers after relu6 and relu7
fprintf('Adding dropout to network...\n');
dropout6Layer = dagnn.DropOut();
dropout7Layer = dagnn.DropOut();
insertLayer(obj.net, 'relu6', 'fc7', 'dropout6', dropout6Layer);
insertLayer(obj.net, 'relu7', 'fc8', 'dropout7', dropout7Layer);

% Rename variable prob to x%d+ style to make insertLayer work
% (Required for beta18 or later matconvnet default networks)
predVarIdx = obj.net.getVarIndex('prediction');
if ~isnan(predVarIdx)
    freeVarName = getFreeVariable(obj.net);
    obj.net.renameVar('prediction', freeVarName);
end

% Replace softmax with correct loss for training (default: softmax)
switch obj.nnOpts.lossFnObjective
    case 'softmaxlog'
        softmaxlossBlock = dagnn.LossWeighted('loss', 'softmaxlog');
        replaceLayer(obj.net, 'prob', 'softmaxloss', softmaxlossBlock, 'label');
        objectiveName = obj.net.layers(obj.net.getLayerIndex('softmaxloss')).outputs;
        obj.net.renameVar(objectiveName, 'objective');
    case 'hinge'
        hingeLossBlock = dagnn.Loss('loss', 'hinge');
        replaceLayer(obj.net, 'prob', 'hingeloss', hingeLossBlock, 'label');
        objectiveName = obj.net.layers(obj.net.getLayerIndex('hingeloss')).outputs;
        obj.net.renameVar(objectiveName, 'objective');
    otherwise
        error('Unknown loss specified');
end

% Adapt number of classes in softmaxloss layer from 1000 to numClasses
fc8Idx = obj.net.getLayerIndex('fc8');
obj.net.layers(fc8Idx).block.size(4) = obj.imdb.numClasses;
newParams = obj.net.layers(fc8Idx).block.initParams();
obj.net.params(obj.net.layers(fc8Idx).paramIndexes(1)).value = newParams{1} / std(newParams{1}(:)) * 0.01; % Girshick initialization
obj.net.params(obj.net.layers(fc8Idx).paramIndexes(2)).value = newParams{2}';

% Rename input
if ~isnan(obj.net.getVarIndex('x0'))
    % Input variable x0 is renamed to input
    obj.net.renameVar('x0', 'input');
else
    % Input variable already has the correct name
    assert(~isnan(obj.net.getVarIndex('input')));
end

% Modify for Fast Rcnn (ROI pooling, bbox regression etc.)
if obj.nnOpts.fastRcnn
    obj.convertNetworkToFastRcnn();
end


function [VW, sptHist] = LMdenseVisualWords(D, HOMEIMAGES, VWparam)
%
% Compute dense visual words and spatial pyramid histogram.
%
% The SIFT grid + visual words will be defined by the parameters VWparam:
%   VWparam.imagesize = 256; % normalized image size (images will be scaled so that the maximal axis has this dimension before computing the sift features)
%   VWparam.grid_spacing = 1; % distance between grid centers
%   VWparam.patch_size = 16; % size of patch from which to compute SIFT descriptor (it has to be a factor of 4)
%   VWparam.NumVisualWords = 200; % number of visual words
%   VWparam.Mw = 2; % number of spatial scales for spatial pyramid histogram
%
% Run demoVisualWords.m to see an example of how it works.
%
% The SIFT descriptor at each location has 128 dimensions and is vector quantized.
%
% This function can be called as:
%
% [VW, sptHist] = LMdenseVisualWords(img, HOMEIMAGES, param);
% [VW, sptHist] = LMdenseVisualWords(D(n), HOMEIMAGES, param);
% [VW, sptHist] = LMdenseVisualWords(filename, HOMEIMAGES, param);
%
%
% Antonio Torralba, 2008

if ~isfield(VWparam, 'descriptor')
    VWparam.descriptor = 'sift';
end

if isfield(VWparam, 'imageSize')
    VWparam.imagesize = VWparam.imageSize;
end

if isfield(VWparam, 'storewords')
    storewords = VWparam.storewords;
else
    storewords = 1;
end

if isfield(VWparam, 'imagesize')
    if length(VWparam.imagesize)==1
        VWparam.imagesize = [1 1]*VWparam.imagesize;
    end
end

if nargin==4
    precomputed = 1;
    % get list of folders and create non-existing ones
    %listoffolders = {D(:).annotation.folder};
else
    precomputed = 0;
    HOMESIFT = '';
end

if nargin<3
    % Default parameters
    VWparam.grid_spacing = 1; % distance between grid centers
    VWparam.patch_size = 16; % size of patch from which to compute SIFT descriptor (it has to be a factor of 4)
end

switch VWparam.descriptor
    case 'sift'
        Nfeatures = 128;
        w = VWparam.patch_size-2; % boundary for SIFT
    case 'hog'
        Nfeatures = 124;
        w = floor(VWparam.patch_size/2*2.5)*2; % boundary for HOG
end

if isstruct(D)
    % [gist, param] = LMdenseVisualWords(D, HOMEIMAGES, param);
    Nscenes = length(D);
    typeD = 1;
end
if iscell(D)
    % [gist, param] = LMdenseVisualWords(filename, HOMEIMAGES, param);
    Nscenes = length(D);
    typeD = 2;
end
if isnumeric(D)
    % [gist, param] = LMdenseVisualWords(img, HOMEIMAGES, param);
    Nscenes = size(D,4);
    typeD = 3;
end

if Nscenes >1
    fig = figure;
end

% Loop: Compute visual words for all scenes
if storewords==1
    if isfield(VWparam, 'imagesize')
        n = VWparam.imagesize(1)-w;
        m = VWparam.imagesize(2)-w;
    else
        % read one image to check size. This will only work if all the images
        % have the same size
        switch typeD
            case 1
                img = LMimread(D, 1, HOMEIMAGES);
            case 2
                img = imread(fullfile(HOMEIMAGES, D{1}));
            case 3
                img = D(:,:,:,1);
        end
        n = size(img,1)-w;
        m = size(img,2)-w;
    end

    if VWparam.NumVisualWords<256
        VW = zeros([n m Nscenes], 'uint8');
    else
        VW = zeros([n m Nscenes], 'uint16');
    end
else
    VW= [];
end

sptHist = zeros([VWparam.NumVisualWords*((4^VWparam.Mw-1)/3) Nscenes], 'single');
for n = 1:Nscenes
    g = [];
    todo = 1;
    % otherwise compute gist
    if todo==1
        if Nscenes>1
            disp([n Nscenes])
        end

        % load image
        try
            switch typeD
                case 1
                    img = LMimread(D, n, HOMEIMAGES);
                case 2
                    img = imread(fullfile(HOMEIMAGES, D{n}));
                case 3
                    img = D(:,:,:,n);
            end
        catch
            disp(D(n).annotation.folder)
            disp(D(n).annotation.filename)
            rethrow(lasterror)
        end

        % Reshape image to standard format
        if isfield(VWparam, 'imagesize')
            img = imresizecrop(img, VWparam.imagesize, 'bilinear');
        end

        %M = max(size(img,1), size(img,2));
        %if M~=VWparam.imagesize
        %    img = imresize(img, VWparam.imagesize/M, 'bilinear');
        %end

        % get  descriptors
        switch VWparam.descriptor
            case 'sift'
                v = LMdenseSift(img, HOMEIMAGES, VWparam);
            case 'hog'
                v = dense_hog2x2(img, VWparam);

        end

        %sift = single(LMdenseSift(img, HOMEIMAGES, VWparam));
        [nrows ncols nf] = size(v);
        v = reshape(v, [size(v,1)*size(v,2) Nfeatures]);

        % vector quantization
        [fitd, w] = min(distMat(single(VWparam.visualwordcenters), v));
        w = reshape(w, [nrows ncols]);

        if storewords==1
            if VWparam.NumVisualWords<256
                VW(:,:,n) = uint8(w);
            else
                VW(:,:,n) = uint16(w);
            end
        end

        % Compute spatial histogram
        sptHist(:,n) = single(spatialHistogram(w, VWparam.Mw, VWparam.NumVisualWords));

        if Nscenes >1
            figure(fig);
            subplot(121)
            imshow(uint8(img))
            subplot(122)
            imagesc(w)
            axis('equal')
            axis('off')
        end
    end

    drawnow
end

function D=distMat(P1, P2)
%
% Euclidian distances between vectors

if nargin == 2
    X1=repmat(single(sum(P1.^2,2)),[1 size(P2,1)]);
    X2=repmat(single(sum(P2.^2,2)),[1 size(P1,1)]);
    R=P1*P2';
    D=X1+X2'-2*R;
else
    % each vector is one column
    X1=repmat(sum(P1.^2,1),[size(P1,2) 1]);
    R=P1'*P1;
    D=X1+X1'-2*R;
    D = sqrt(D);
end

function h = spatialHistogram(W, Mw, Nwords)
% Mw = number of spatial windows for computing histograms
coef = 1./[2^(Mw-1) 2.^(Mw-(1:(Mw-1)))];

h = [];
for M = 1:Mw
    lx = round(linspace(1, size(W,2)-1, 2^(M-1)+1));
    ly = round(linspace(1, size(W,1)-1, 2^(M-1)+1));
    for x = 1:2^(M-1)
        for y = 1:2^(M-1)
            ww = W(ly(y)+1:ly(y+1), lx(x)+1:lx(x+1));
            hh = hist(ww(:), 1:Nwords);
            h = [h coef(M)*hh];
        end
    end
end

% store words
h = h /sum(h);


function doTheCalcThingSLI(obj)
		%everything for the calculation itself goes in here
		import mapseis.projector.*;
		import mapseis.declustering.*;

		%get parameter into variables
		ShortCat = getShortCatalog(obj.Datastore,obj.SendedEvents);
		Sli_Type=obj.CalcParameter.Sli_Type;
		MainMag=obj.CalcParameter.MainMag;
		BackTime=obj.CalcParameter.BackTime;
		ForeTime=obj.CalcParameter.ForeTime;
		MinRad=obj.CalcParameter.MinRad;
		MagRadScale=obj.CalcParameter.MagRadScale;
		MagRadConst=obj.CalcParameter.MagRadConst;

		if Sli_Type==1
			%run declustering with magnitude
			disp('please wait....');
			tic;
			[clusterID,EventType,AlgoInfo] = clusterSLIDERmag(ShortCat,MainMag,BackTime,ForeTime,MinRad,MagRadScale,MagRadConst);
			obj.CalcTime=toc;
			disp('finished!');

		elseif Sli_Type==2
			%run declustering with time
			disp('please wait....');
			tic;
			[clusterID,EventType,AlgoInfo] = clusterSLIDERtime(ShortCat,MainMag,BackTime,ForeTime,MinRad,MagRadScale,MagRadConst);
			obj.CalcTime=toc;
			disp('finished!');

		end

		%store result
		obj.CalcRes.clusterID=clusterID;
		obj.CalcRes.EventType=EventType;
		obj.CalcRes.AlgoInfo=AlgoInfo;

		%write to datastore
		obj.Datastore.setDeclusterData(EventType,clusterID,obj.SendedEvents,[]);
		obj.Datastore.DeclusterMetaData=AlgoInfo;

		%correct for old datastore version, it makes sense to do this here, because it is needed only by the declustering
		NumberedUserData=getNumberedFields(obj.Datastore);
		setNumberedFields(obj.Datastore,union(NumberedUserData,{'Month','Day','Hour','Minute','Second','MilliSecond','DecYear'}));

end


function y = cat( dim, varargin )

%Disciplined convex/geometric programming information for CAT:
%   CAT imposes no convexity restrictions on its arguments.

if ~isnumeric( dim ) || any( size( dim ) ~= 1 ) || dim <= 0 || dim ~= floor( dim ),
    error( 'First argument must be a dimension.' );
end

%
% Quick exit
%

if nargin == 2,
    y = varargin{1};
    return
end

%
% Determine the final size and check consistency
%

sz    = [];
nz    = 1;
nzer  = 0;
nargs = 0;
isr   = true;
for k = 1 : nargin - 1,
    x = varargin{k};
    sx = size( x );
    if any( sx ),
        x = cvx( x );
        bx = x.basis_;
        nz = max( nz, size( bx, 1 ) );
        nzer = nzer + nnz( bx );
        sx( end + 1 : dim ) = 1;
        if isempty( sz ),
            sz = sx;
        elseif length( sx ) ~= length( sz ) || nnz( sx - sz ) > 1,
            error( 'All dimensions but the one being concatenated (%d) must be equal.', dim );
        else
            sz( dim ) = sz( dim ) + sx( dim );
        end
        if ~isreal( bx ),
            isr = false;
        end
        if all( sx ),
            nargs = nargs + 1;
            varargin{nargs} = x;
        end
    end
end

%
% Simple cases
%

if nargs == 0,

    if isempty( sz ),
        sz( dim ) = nargin;
    end
    y = cvx( sz, [] );
    return

elseif nargs == 1,

    y = varargin{1};
    return

end

%
% Harder cases
%

msiz = sz( dim );
lsiz = prod( sz( 1 : dim - 1 ) );
rsiz = prod( sz( dim + 1 : end ) );
psz  = lsiz * msiz * rsiz;
issp = cvx_use_sparse( [ nz, psz ], nzer, isr );
for k = 1 : nargs,
    x = varargin{k}.basis_;
    if issp ~= issparse(x),
        if issp,
            x = sparse(x);
        else
            x = full(x);
        end
    end
    x( end + 1 : nz, end ) = 0;
    if rsiz > 1,
        x = reshape( x, numel(x) / rsiz, rsiz );
    end
    varargin{k} = x;
end
if rsiz > 1,
    yb = builtin( 'cat', 1, varargin{1:nargs} );
else
    yb = builtin( 'cat', 2, varargin{1:nargs} );
end
yb = reshape( yb, nz, psz );

%
% Create object
%

y = cvx( sz, yb );

% Copyright 2010 Michael C. Grant and Stephen P. Boyd.
% See the file COPYING.txt for full copyright information.
% The command 'cvx_where' will show where this file is located.


classdef TestBlendLinear
    %TestBlendLinear

    methods (Static)
        function test_1
            src1 = imread(fullfile(mexopencv.root(),'test','left01.jpg'));
            src2 = imread(fullfile(mexopencv.root(),'test','right01.jpg'));
            assert(isequal(size(src1), size(src2)));

            [rows,cols,~] = size(src1);
            weights1 = 0.5*ones(rows, cols);
            weights2 = 0.5*ones(rows, cols);
            dst = cv.blendLinear(src1, src2, weights1, weights2);
            validateattributes(dst, {class(src1)}, {'size',size(src1)});
        end

        function test_error_argnum
            try
                cv.blendLinear();
                throw('UnitTest:Fail');
            catch e
                assert(strcmp(e.identifier,'mexopencv:error'));
            end
        end
    end

end


clear; close all; clc;

confmat = magic(3); % sample data
% plotting
plotConfMat(confmat, {'Dog', 'Cat', 'Horse'});


function showposlat(model, start, pos, fp_count, overlap)

% AUTORIGHTS
% -------------------------------------------------------
% Copyright (C) 2009-2012 Ross Girshick
%
% This file is part of the voc-releaseX code
% (http://people.cs.uchicago.edu/~rbg/latent/)
% and is available under the terms of an MIT-like license
% provided in COPYING. Please retain this notice and
% COPYING if you use this file (or a portion of it) in
% your project.
% -------------------------------------------------------

conf = voc_config();

% get training data
if nargin < 3
  pos = pascal_data(model.class, model.year);
end

numpos = length(pos);
model.interval = conf.training.interval_fg;
pixels = model.minsize * model.sbin / 2;
minsize = prod(pixels);
if nargin < 2
  start = 1;
end

if nargin < 5
  overlap = 0.7;
end
show_slabs = true;

% compute latent filter locations and record target bounding boxes
k = 0;
for i = start:numpos
  fprintf('%s %s: show pos lat: %d/%d\n', procid(), model.class, i, numpos);
  if max(pos(i).sizes) < minsize
    fprintf('  all too small\n');
    continue;
  end

  % do image level operations
  im = color(imreadx(pos(i)));
  [im, boxes] = croppos(im, pos(i).boxes);
  [pyra, model_dp] = gdetect_pos_prepare(im, model, boxes, overlap);
  %fprintf('%s\n', pos(i).im);

  % process each box in the image
  for b = 1:size(boxes,1)
    fg_ibox = b;
    bg_iboxes = 1:size(boxes,1);
    bg_iboxes(b) = [];

    fg_box = boxes(b,:);
    bg_boxes = boxes;
    bg_boxes(b,:) = [];

    if show_slabs
      boxesc = [fg_box zeros(1,model.numfilters*4-4+2) 2];
    else
      boxesc = [fg_box 2];
    end
    if ~isempty(bg_boxes)
      if show_slabs
        boxesc = cat(1, boxesc, padarray(bg_boxes, [0 model.numfilters*4-4+2+1], 3, 'post'));
      else
        boxesc = cat(1, boxesc, padarray(bg_boxes, [0 1], 3, 'post'));
      end
    end

    % skip small examples
    if pos(i).sizes(b) < minsize
      fprintf('  %d: too small\n', b);
      continue;
    end
    [det, bs, trees] = gdetect_pos(pyra, model_dp, 1+fp_count, fg_ibox, ...
                                  overlap, bg_iboxes, 0.5);
    if ~isempty(det)
      o = boxoverlap(clipboxes(im, det), fg_box);
      fprintf('  %d: comp %d  score %.4f  overlap=%0.4f\n', ...
              b, bs(1,end-1), bs(1,end), o(1));
      if size(det,1) > 1
        %losses = lossfunc(o);
        losses = [];
        for ii = 1:length(trees)
          losses = [losses; trees{ii}(13,1)];
        end
        for j = 2:length(o)
          score = det(j,end);
          fprintf('  > %d: score=%0.4f (%0.4f + %0.4f)  comp %d  overlap=%0.4f\n', ...
                  j, score, score-losses(j), losses(j), det(j,end-1), o(j));
        end
        if show_slabs
          boxesc = cat(1, boxesc, padarray(bs(2:end,:), [0 1], 1, 'post'));
        else
          boxesc = cat(1, boxesc, padarray(det(2:end,1:4), [0 1], 1, 'post'));
        end
      end
      if show_slabs
        boxesc = cat(1, boxesc, padarray(bs(1,:), [0 1], 0, 'post'));
        boxesc = cat(1, boxesc, padarray(det(1,1:4), [0 model.numfilters*4-4+2+1], 4, 'post'));
      else
        boxesc = cat(1, boxesc, padarray(det(1,1:4), [0 1], 0, 'post'));
      end
      %showboxes(im, bs(1,:));
      tree = trees{1};
    else
      fprintf('  %d: no overlap\n', b);
      tree = [];
    end

    subplot(1,2,1);
    showboxesc(im, boxesc);
    title('green = fg box;  blue = bg boxes;  red = loss adjusted boxes;  cyan = label completed detection');
    subplot(1,2,2);
    if ~isempty(tree)
      vis_derived_filter(model, tree);
    end

    pause;
  end
end


function targets=FSEOF(model,biomassRxn,targetRxn,iterations,coefficient,outputFile)
% FSEOF: implements the algorithm of Flux Scanning based on Enforced Objective Flux.
%
%   model           a model structure
%   biomassRxn      string with reaction ID of the biomass formation or
%                   growth reaction
%   targetRxn       string with reaction ID of target reaction
%   iterations      double indicating number of iterations (opt, default 10)
%   coefficient     double indicating ratio of optimal target reaction
%                   flux, must be less than 1 (opt, default 0.9)
%   outputFile      string with output filename (opt, default prints to
%                   command window)
%
%   targets         structure with target identifying information for each reaction
%       logical     logical array indicating FSEOF identified target reaction
%       slope       double array with FSEOF calculated slopes for each reaction

%OUTPUTS
%   This function writes an tab-delimited file or prints to command window. If an output
%   has been specified (targets), it will also generate a structure indicating for
%   each reaction whether it is identified by FSEOF as a target and the slope of the
%   reaction when switching from biomass formation to product formation.
%
%   Usage: targets=FSEOF(model,biomassRxn,targetRxn,iterations,coefficient,outputFile)

biomassRxn=char(biomassRxn);
targetRxn=char(targetRxn);

if nargin<4
    iterations=10;
    coefficient=0.9;
end

if nargin <5
    coefficient=0.9;
end

if nargin == 6
    output=1;
else
    output=0;
end

%Find out the maximum theoretical yield of target reaction
model=setParam(model,'obj',targetRxn,1);
sol=solveLP(model,1);
targetMax=abs(sol.f*coefficient);   % 90 percent of the theoretical yield

model=setParam(model,'obj',biomassRxn,1);

fseof.results=zeros(length(model.rxns),iterations);
fseof.target=zeros(length(model.rxns),1);
rxnDirection=zeros(length(model.rxns),1);

%Enforce objective flux iteratively
for i=1:iterations
    n=i*targetMax/iterations;
    model=setParam(model,'lb',targetRxn,n);

    sol=solveLP(model,1);

    fseof.results(:,i)=sol.x;

    %Loop through all fluxes and identify the ones that increased upon the
    %enforced objective flux
    for j=1:length(fseof.results)
        if fseof.results(j,1) > 0   %Check the positive fluxes

            if i == 1   %The initial round
                rxnDirection(j,1)=1;
                fseof.target(j,1)=1;
            else

                if (fseof.results(j,i) > fseof.results(j,i-1)) & fseof.target(j,1)
                    fseof.target(j,1)=1;
                else
                    fseof.target(j,1)=0;
                end
            end

        elseif fseof.results(j,1) < 0 %Check the negative fluxes

            if i == 1   %The initial round
                rxnDirection(j,1)=-1;
                fseof.target(j,1)=1;
            else
                if (fseof.results(j,i) < fseof.results(j,i-1)) & fseof.target(j,1)
                    fseof.target(j,1)=1;
                else
                    fseof.target(j,1)=0;
                end
            end

        end

    end
end

%Generating output
formatSpec='%s\t%s\t%s\t%s\t%s\t%s\t%s\n';
if output == 1    %Output to a file
    outputFile=char(outputFile);
    fid=fopen(outputFile,'w');
    fprintf(fid,formatSpec,'Slope','rowID','Enzyme ID','Enzyme Name','Subsystems','Direction','Gr Rule');
else              %Output to screen
    fprintf(formatSpec,'Slope','rowID','Enzyme ID','Enzyme Name','Subsystems','Direction','Gr Rule');
end

for num=1:length(fseof.target)
    if fseof.target(num,1) == 1
        A0=num2str(abs(fseof.results(num,iterations)-fseof.results(num,1))/abs(targetMax-targetMax/iterations)); %Slope calculation
        A1=num2str(num);                                  %row ID
        A2=char(model.rxns(num));                         %enzyme ID
        A3=char(model.rxnNames(num));                     %enzyme Name
        if isfield(model,'subSystems') && ~isempty(model.subSystems{num});
            A4=char(strjoin(model.subSystems{num,1},';'));                   %Subsystems
        else
            A4='';
        end
        A5=num2str(model.rev(num)*rxnDirection(num,1));   %reaction Dirction
        A6=char(model.grRules(num));                      %Gr Rule
        if output == 1    %Output to a file
            fprintf(fid,formatSpec,A0,A1,A2,A3,A4,A5,A6);
        else              %Output screen
            fprintf(formatSpec,A0,A1,A2,A3,A4,A5,A6);
        end
    end
end

if output == 1    %Output to a file
    fclose(fid);
end

if nargout == 1
    targets.logical=logical(fseof.target);
    targets.slope=abs(fseof.results(:,iterations)-fseof.results(:,1))/abs(targetMax-targetMax/iterations); %Slope calculation
end
end


function [val] = optGeneFitnessTilt(rxn_vector_matrix, model, targetRxn, rxnListInput, isGeneList)
% The fitness function
%
% USAGE:
%
%    [val] = optGeneFitnessTilt(rxn_vector_matrix, model, targetRxn, rxnListInput, isGeneList)
%
% INPUTS:
%    rxn_vector_matrix:    reactions vectors in a matrix
%    model:                model structure
%    targetRxn:            target reactions
%    rxnListInput:         list of reactions
%    isGeneList:           bolean checking if it is a gene list
%
% OUTPUT:
%    val:                  fitness value

global MaxKnockOuts
global CBT_LP_SOLVER
%size(rxn_vector_matrix)

%rxnGeneMat is a required field for this function, so if it does not exist,
%build it.
if isGeneList && ~isfield(model,'rxnGeneMat')
    model = buildRxnGeneMat(model);
end

popsize = size(rxn_vector_matrix,1);
val = zeros(1,popsize);

for i = 1:popsize
    rxn_vector = rxn_vector_matrix(i,:);
    rxnList = rxnListInput(logical(rxn_vector));

    %see if we've done this before
    val_temp = memoize(rxn_vector);
    if ~ isempty(val_temp)
        val(i) = val_temp;
        continue;
    end

    % check to see if mutations is above the max number allowed
    nummutations = sum(rxn_vector);
    if nummutations > MaxKnockOuts
        continue;
    end

    % generate knockout.
    if isGeneList
        modelKO = deleteModelGenes(model, rxnList);
    else % is reaction list
        [isValidRxn,removeInd] = ismember(rxnList,model.rxns);
        removeInd = removeInd(isValidRxn);
        modelKO = model;
        modelKO.ub(removeInd) = 0;
        modelKO.lb(removeInd) = 0;
    end
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    % augment BOF (tilt)
    [modelKO] = augmentBOF(modelKO, targetRxn, .001);

    % find growthrate
    if exist('LPBasis', 'var')
        modelKO.LPBasis = LPBasis;
    end

    if ~isempty(CBT_LP_SOLVER) && strcmp(CBT_LP_SOLVER,'cplex')
        [solKO, LPOUT] = solveCobraLPCPLEX(modelKO, 0,1);
        LPBasis = LPOUT.LPBasis;
        growthrate = solKO.obj;
        [~,tar_loc] = ismember(targetRxn,modelKO.rxns);
        minProdAtSetGR = solKO.full(tar_loc);
    else
        solKO = optimizeCbModel(modelKO);
        if isempty(solKO.x)
            continue;
        else
            growthrate = solKO.f;
            [~,tar_loc] = ismember(targetRxn,modelKO.rxns);
            minProdAtSetGR = solKO.x(tar_loc);
        end
    end

    % check to ensure that GR is above a certain value
    if growthrate < .10
        continue;
    end

    % %    display('second optimization');
    %     % find the lowesest possible production rate (a hopefully high number)
    %     % at the max growth rate minus some set factor gamma (a growth rate slightly
    %     % smaller than the max). A positive value will eliminate solutions where the
    %     % production envelope has a vertical line at the max GR, a "non-unique"
    %     % solution. Set value to zero if "non-unique" solutions are not an issue.
    %     gamma = 0.01; % proportional to Grwoth Rate (hr-1), a value around 0.5 max.
    %
    %     %find indicies of important vectors
    %     indBOF = find(modelKO.c);
    %     indTar = findRxnIDs(modelKO, targetRxn);
    %     % generate a model with a fixed max KO growth rate
    %     modelKOsetGR = modelKO;
    %     modelKOsetGR.lb(indBOF) = growthrate - gamma; % this growth rate is required as lb.
    %     modelKOsetGR.c = zeros(size(modelKO.c));
    %     modelKOsetGR.c(indTar) = -1; % minimize for this variable b/c we want to look at the very minimum production.
    %
    %     % find the minimum production rate for the targeted reaction.
    %
    % %     solKOsetGR = optimizeCbModel(modelKOsetGR);
    % %     minProdAtSetGR1 = -solKOsetGR.f;  % This should be a negative value b/c of the minimization setup, so -1 is necessary.
    %
    %     if exist('LPBasis2', 'var')
    %         modelKOsetGR.LPBasis = LPBasis2;
    %     end
    %
    %     [solKOsetGR, LPOUT2] = solveCobraLPCPLEX(modelKOsetGR, 0,1);
    %     LPBasis2 = LPOUT2.LPBasis;
    %     minProdAtSetGR = -solKOsetGR.obj;

    % objective function for optGene algorithm = val (needs to be a negative value, since it is
    % a minimization)
    val(i) = -minProdAtSetGR;
    % penalty for a greater number of mutations

    %val(i) = -minProdAtSetGR * (.98^nummutations);

    % select best substrate-specific productivity
    % val(i) = -minProdAtSetGR * (.98^nummutations) * growthrate;

    % check to prevent very small values from being considerered improvments
    if val(i) > -1e-3
        val(i) = 0;
    end

    memoize(rxn_vector, val(i));
end

return;

%% Memoize
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%% MEMOIZE %%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% internal function to speed things up.

function [value] =  memoize(gene_vector, value)
global HTABLE
hashkey = num2str(gene_vector);
hashkey = strrep(hashkey,' ',''); % cut out white space from string (more space efficient).

if nargin == 1
    value = HTABLE.get(hashkey);
    return;
else
    if HTABLE.size() > 50000
        HTABLE = java.util.Hashtable;  %reset the hashtable if more than 50,000 entries.
    end
    HTABLE.put(hashkey, value);
    value = [];
    return;
end


function [StopFlag, Status] = StopCritBDCA(nfxk, Niter, Nmap, T, MaxNumIter, MaxNumMapEval, TimeLimit, epsilon, Stopping_Crit)
% Function checking that one of the stopping criteria
% holds to terminate LLM and GLM. It perepare the status determining why
% the algorithm is stopped.
%
% USAGE:
%
%    [StopFlag, Status] = StopCritBDCA(nfxk, Niter, Nmap, T, MaxNumIter, MaxNumMapEval, TimeLimit, epsilon, Stopping_Crit)
%
% INPUTS:
%    nhxk:             the norm 2 of `h(xk)`
%    Niter:            the number of iterations
%    Nmap:             the number of mapping calls
%    T:                the running time
%    MaxNumIter:       maximum number of iterations
%    MaxNumMapEval:    maximum number of function evaluations
%    TimeLimit:        maximum running time
%    epsilon:          accuracy parameter
%    Stopping_Crit:    stopping criterion:
%
%                        1. stop if :math:`||nfxk|| \leq \epsilon`
%                        2. stop if `MaxNumIter` is reached
%                        3. stop if `MaxNumMapEval` is reached
%                        4. stop if `TimeLimit` is reached
%                        5. stop if (default) :math:`||hxk|| \leq \epsilon` or `MaxNumIter` is reached
%
% OUTPUTS:
%    StopFlag:         1: if one of the stopping criteria holds, 0: if none of the stopping criteria holds
%    Status:           the reason of the scheme termination

switch Stopping_Crit

  case 1
    if nhxk <= epsilon
      StopFlag = 1;
      Status   = 'A solution of nonlinear system is found.';
    else
      StopFlag = 0;
      Status   = [];
    end

  case 2
    if Niter >= MaxNumIter
      StopFlag = 1;
      Status   = 'Maximum number of iterations is reached.';
    else
      StopFlag = 0;
      Status   = [];
    end

  case 3
    if Nmap >= MaxNumMapEval
      StopFlag = 1;
      Status   = 'Maximum number of mapping evaluations is reached.';
    else
      StopFlag = 0;
      Status   = [];
    end

  case 4
    if T >= TimeLimit
      StopFlag = 1;
      Status   = 'Time limit is reached.';
    else
      StopFlag = 0;
      Status   = [];
    end

  case 5
    if (nfxk <= epsilon || Niter >= MaxNumIter)
      StopFlag = 1;
      if Niter < MaxNumIter
          Status = 'a solution is found';
      else
          Status = 'Maximum number of iterations is reached.';
      end
    else
      StopFlag = 0;
      Status   = [];
    end

end

end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%% End of StopCritBDCA.m %%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


clear all;
clc;

addpath = './util';

load('300W_test_indoor.mat');

title = '300W-Test-Indoor';

num = 4;

colors = lines(num);

x_limit = 0.08;

linewidth = 3;

fontsize = 12;

% Render curves
plot_ced(x, y, methods, strrep(title,'_',' '), ...
         x_limit, colors, linewidth, fontsize);

ax = gca;
outerpos = ax.OuterPosition;
ti = ax.TightInset;
left = outerpos(1) + ti(1);
bottom = outerpos(2) + ti(2);
ax_width = outerpos(3) - ti(1) - ti(3);
ax_height = outerpos(4) - ti(2) - ti(4);
ax.Position = [left bottom ax_width ax_height];

fig = gcf;
fig.PaperPositionMode = 'auto';
fig_pos = fig.PaperPosition;
fig.PaperSize = [fig_pos(3) fig_pos(4)]* 1.05;

print(fig,'-dpdf',[title '.pdf']);


classdef PostProcessDataBaseCreator < handle

    properties (Access = private)
        mesh
        outFileName
    end

    methods (Access = public)

        function obj = PostProcessDataBaseCreator(dI)
            obj.init(dI);
        end

        function data = create(obj)
            d.outFileName = obj.outFileName;
            d.coordinates = obj.mesh.coord;
            d.connectivities = obj.mesh.connec;
            d.nnode = size(obj.mesh.connec,2);
            d.npnod = size(obj.mesh.coord,1);
            d.gtype = obj.mesh.type;
            d.nelem = size(obj.mesh.connec,1);
            d.etype = obj.computeGiDElementType(d.gtype);
            data = d;
        end

    end

    methods (Access = private)

        function init(obj,cParams)
            if isfield(cParams,'outName')
                cParams.outFileName = cParams.outName;
            end
            obj.mesh        = cParams.mesh;
            obj.outFileName = cParams.outFileName;
        end

    end

    methods (Access = private, Static)

        function et = computeGiDElementType(gt)
            switch  gt
                case 'TRIANGLE'
                    et = 'Triangle';
                case 'QUAD'
                    et = 'Quadrilateral';
                case 'TETRAHEDRA'
                    et = 'Tetrahedra';
                case 'HEXAHEDRA'
                    et = 'Hexahedra';
            end
        end

    end

end