function [ output_data ] = normalize_complex_file( input_filename, varargin )
%NORMALIZE_COMPLEX_FILE Normalize complex SAR data to make it have consistent
%characteristics in frequency domain
%
% Normalized complex data will have
%    1) Centered and deskewed frequency support
%    2) Uniform weighting
%    3) FFT sign of -1
%
% This function only applies these transforms in one dimension since we
% normally only process one dimension at a time, and since in general,
% frequency support deskew can only be applied in one dimension.
%
%    output_data = normalize_complex_file(input_filename, 'PropertyName', PropertyValue, ...)
%
%       Property name     Description
%
%       output_filename   Output data is stored to a complex single-
%                            precision float SIO file.  Default is to not
%                            save to file.
%       block_size        Determines how many lines are processed in memory
%                            at once.  Set to Inf to process whole image at
%                            once.  Default is 500 lines.
%       dim               Dimension over which to perform normalization.
%                            Default = 1 (slow-time).
%       azlimits          Min and max samples in azimuth over which to
%                            compute.  Default = full image.
%       rnglimits         Min and max lines in range over which to compute.
%                            Default = full image.
%       framenumber       Dataset to process if INPUT_FILENAME is a
%                            multi-image file.  Default is 1.
%
% If an output argument is given to the function call, the normalized
% complex data will be returned in an in-memory MATLAB array to
% OUTPUT_DATA. WARNING: Make sure your computer has enough memory to hold
% resulting array, if you use this option.
%
% Author: Wade Schwartzkopf, NGA/R
%
% //////////////////////////////////////////
% /// CLASSIFICATION: UNCLASSIFIED       ///
% //////////////////////////////////////////

%% Parse input parameters
p = inputParser; % Extract parameter-value pairs
p.KeepUnmatched=true;
p.addParamValue('output_filename','');
p.addParamValue('block_size',500);
p.addParamValue('dim',1);
p.addParamValue('azlimits',[]);
p.addParamValue('rnglimits',[]);
p.addParamValue('framenumber',1);
p.FunctionName = 'normalize_complex_file';
p.parse(varargin{:});

save_to_sio=~any(strcmp(p.UsingDefaults,'output_filename'))||~isempty(p.Results.output_filename);
save_to_variable=(nargout>0);
if ~save_to_sio&&~save_to_variable % Nothing to do
    warning('NORMALIZE_COMPLEX_FILE:NO_OUTPUT','Neither output filename nor output variable was specified.  No ouput will be calculated.');
    return;
end

%% Open data file
reader_obj=open_reader(input_filename);
if iscell(reader_obj) % If file contains more than one image
    for i=1:length(reader_obj)
        if i~=p.Results.framenumber
            reader_obj{i}.close();
        end
    end
    reader_obj=reader_obj{p.Results.framenumber};
end
meta=reader_obj.get_meta();
% Calculate AOI
if any(strcmp(p.UsingDefaults,'azlimits'))
    azlimits=[1 meta.ImageData.NumCols];
else
    azlimits=p.Results.azlimits;
    if strcmp(azlimits,'full')
        azlimits=[1 meta.ImageData.NumCols];
    end
end
if any(strcmp(p.UsingDefaults,'rnglimits'))
    rnglimits=[1 meta.ImageData.NumRows];
else
    rnglimits=p.Results.rnglimits;
    if strcmp(rnglimits,'full')
        rnglimits=[1 meta.ImageData.NumRows];
    end
end

%% Calculate input parameters for normalize_complex_mem
if p.Results.dim==1
    sicd_grid_struct = meta.Grid.Col;
else
    sicd_grid_struct = meta.Grid.Row;
end
% Vectors describing range and azimuth distances from SCP (in meters) for rows and columns
[ DeltaKCOAPoly, az_coords_m, rg_coords_m ] = deskewparams( meta, p.Results.dim );
coords_m_args = {az_coords_m, rg_coords_m};
coords_ind = {azlimits(1):azlimits(end), rnglimits(1):rnglimits(end)};
other_dim = 3-p.Results.dim;
coords_m = coords_m_args{other_dim};
coords_m_args{p.Results.dim}=coords_m_args{p.Results.dim}(coords_ind{p.Results.dim});
% Zeropad
if isfield(sicd_grid_struct,'ImpRespBW')&&isfield(sicd_grid_struct,'SS')
    zeropad = max(1,1/(sicd_grid_struct.ImpRespBW*sicd_grid_struct.SS));
else
    zeropad = 1;
end
% Weighting function
try
    weight_fun = sicdweight2fun(sicd_grid_struct);
catch % Unable to determine weighting function from metadata.
    % Estimate weighting from complex data instead
    weight_fun = estimate_weighting_file(reader_obj, p.Results.dim);
end
% FFT sign
if isfield(sicd_grid_struct,'Sgn')
    fft_sign = sicd_grid_struct.Sgn;
else
    fft_sign = -1;
end

%% Setup loop for processing through image in blocks of azimuth lines
if save_to_variable
    output_data = zeros(diff(azlimits)+1,diff(rnglimits)+1);
    output_data_args = {1:size(output_data,1),1:size(output_data,2)};
end
if save_to_sio
    writer_object=open_sio_writer(p.Results.output_filename);
    if p.Results.dim==1
        writer_object.writeline=writer_object.write_row;
    else
        writer_object.writeline=writer_object.write_column;
    end
end
if p.Results.dim==1
    linelimits = rnglimits;
    read_lines_fun=@(lims) reader_obj.read_chip(azlimits,lims);
else
    linelimits = azlimits;
    read_lines_fun=@(lims) reader_obj.read_chip(lims,rnglimits);
end
wb_hand=waitbar(0,'Normalizing data...');
first_line_in_block = linelimits(1);
while(first_line_in_block<linelimits(2))
    line_indices=first_line_in_block:...
        min(linelimits(2), first_line_in_block + p.Results.block_size - 1);
    coords_m_args{other_dim} = coords_m(line_indices);
    normalized_block = normalize_complex_mem(...
        single(read_lines_fun(line_indices([1 end]))), DeltaKCOAPoly, ...
        coords_m_args{:}, weight_fun, zeropad, fft_sign, p.Results.dim);
    if save_to_variable
        output_data_args{other_dim} = line_indices - linelimits(1) + 1;
        output_data(output_data_args{:}) = normalized_block;
    end
    if save_to_sio
        writer_object.writeline(normalized_block);
    end
    first_line_in_block = line_indices(end) + 1;
    waitbar(double(first_line_in_block-linelimits(1))/double(diff(linelimits)+1),wb_hand);
end

%% Cleanup
close(wb_hand);
reader_obj.close();
if save_to_sio, writer_object.close(); end;

end

% //////////////////////////////////////////
% /// CLASSIFICATION: UNCLASSIFIED       ///
% //////////////////////////////////////////


function varargout = colorspace(Conversion,varargin)
%COLORSPACE  Transform a color image between color representations.
%   B = COLORSPACE(S,A) transforms the color representation of image A
%   where S is a string specifying the conversion.  The input array A
%   should be a real full double array of size Mx3 or MxNx3.  The output B
%   is the same size as A.
%
%   S tells the source and destination color spaces, S = 'dest<-src', or
%   alternatively, S = 'src->dest'.  Supported color spaces are
%
%     'RGB'              sRGB IEC 61966-2-1
%     'YCbCr'            Luma + Chroma ("digitized" version of Y'PbPr)
%     'JPEG-YCbCr'       Luma + Chroma space used in JFIF JPEG
%     'YDbDr'            SECAM Y'DbDr Luma + Chroma
%     'YPbPr'            Luma (ITU-R BT.601) + Chroma
%     'YUV'              NTSC PAL Y'UV Luma + Chroma
%     'YIQ'              NTSC Y'IQ Luma + Chroma
%     'HSV' or 'HSB'     Hue Saturation Value/Brightness
%     'HSL' or 'HLS'     Hue Saturation Luminance
%     'HSI'              Hue Saturation Intensity
%     'XYZ'              CIE 1931 XYZ
%     'Lab'              CIE 1976 L*a*b* (CIELAB)
%     'Luv'              CIE L*u*v* (CIELUV)
%     'LCH'              CIE L*C*H* (CIELCH)
%     'CAT02 LMS'        CIE CAT02 LMS
%
%  All conversions assume 2 degree observer and D65 illuminant.
%
%  Color space names are case insensitive and spaces are ignored.  When
%  sRGB is the source or destination, it can be omitted. For example
%  'yuv<-' is short for 'yuv<-rgb'.
%
%  For sRGB, the values should be scaled between 0 and 1.  Beware that
%  transformations generally do not constrain colors to be "in gamut."
%  Particularly, transforming from another space to sRGB may obtain
%  R'G'B' values outside of the [0,1] range.  So the result should be
%  clamped to [0,1] before displaying:
%     image(min(max(B,0),1));  % Clamp B to [0,1] and display
%
%  sRGB (Red Green Blue) is the (ITU-R BT.709 gamma-corrected) standard
%  red-green-blue representation of colors used in digital imaging.  The
%  components should be scaled between 0 and 1.  The space can be
%  visualized geometrically as a cube.
%
%  Y'PbPr, Y'CbCr, Y'DbDr, Y'UV, and Y'IQ are related to sRGB by linear
%  transformations.  These spaces separate a color into a grayscale
%  luminance component Y and two chroma components.  The valid ranges of
%  the components depends on the space.
%
%  HSV (Hue Saturation Value) is related to sRGB by
%     H = hexagonal hue angle   (0 <= H < 360),
%     S = C/V                   (0 <= S <= 1),
%     V = max(R',G',B')         (0 <= V <= 1),
%  where C = max(R',G',B') - min(R',G',B').  The hue angle H is computed on
%  a hexagon.  The space is geometrically a hexagonal cone.
%
%  HSL (Hue Saturation Lightness) is related to sRGB by
%     H = hexagonal hue angle                (0 <= H < 360),
%     S = C/(1 - |2L-1|)                     (0 <= S <= 1),
%     L = (max(R',G',B') + min(R',G',B'))/2  (0 <= L <= 1),
%  where H and C are the same as in HSV.  Geometrically, the space is a
%  double hexagonal cone.
%
%  HSI (Hue Saturation Intensity) is related to sRGB by
%     H = polar hue angle        (0 <= H < 360),
%     S = 1 - min(R',G',B')/I    (0 <= S <= 1),
%     I = (R'+G'+B')/3           (0 <= I <= 1).
%  Unlike HSV and HSL, the hue angle H is computed on a circle rather than
%  a hexagon.
%
%  CIE XYZ is related to sRGB by inverse gamma correction followed by a
%  linear transform.  Other CIE color spaces are defined relative to XYZ.
%
%  CIE L*a*b*, L*u*v*, and L*C*H* are nonlinear functions of XYZ.  The L*
%  component is designed to match closely with human perception of
%  lightness.  The other two components describe the chroma.
%
%  CIE CAT02 LMS is the linear transformation of XYZ using the MCAT02
%  chromatic adaptation matrix.  The space is designed to model the
%  response of the three types of cones in the human eye, where L, M, S,
%  correspond respectively to red ("long"), green ("medium"), and blue
%  ("short").
%
% Author::
% Pascal Getreuer 2005-2010

%%% Input parsing %%%
if nargin < 2, error('Not enough input arguments.'); end
[SrcSpace,DestSpace] = parse(Conversion);

if nargin == 2
   Image = varargin{1};
elseif nargin >= 3
   Image = cat(3,varargin{:});
else
   error('Invalid number of input arguments.');
end

FlipDims = (size(Image,3) == 1);

if FlipDims, Image = permute(Image,[1,3,2]); end
if ~isa(Image,'double'), Image = double(Image)/255; end
if size(Image,3) ~= 3, error('Invalid input size.'); end

SrcT = gettransform(SrcSpace);
DestT = gettransform(DestSpace);

if ~ischar(SrcT) && ~ischar(DestT)
   % Both source and destination transforms are affine, so they
   % can be composed into one affine operation
   T = [DestT(:,1:3)*SrcT(:,1:3),DestT(:,1:3)*SrcT(:,4)+DestT(:,4)];
   Temp = zeros(size(Image));
   Temp(:,:,1) = T(1)*Image(:,:,1) + T(4)*Image(:,:,2) + T(7)*Image(:,:,3) + T(10);
   Temp(:,:,2) = T(2)*Image(:,:,1) + T(5)*Image(:,:,2) + T(8)*Image(:,:,3) + T(11);
   Temp(:,:,3) = T(3)*Image(:,:,1) + T(6)*Image(:,:,2) + T(9)*Image(:,:,3) + T(12);
   Image = Temp;
elseif ~ischar(DestT)
   Image = rgb(Image,SrcSpace);
   Temp = zeros(size(Image));
   Temp(:,:,1) = DestT(1)*Image(:,:,1) + DestT(4)*Image(:,:,2) + DestT(7)*Image(:,:,3) + DestT(10);
   Temp(:,:,2) = DestT(2)*Image(:,:,1) + DestT(5)*Image(:,:,2) + DestT(8)*Image(:,:,3) + DestT(11);
   Temp(:,:,3) = DestT(3)*Image(:,:,1) + DestT(6)*Image(:,:,2) + DestT(9)*Image(:,:,3) + DestT(12);
   Image = Temp;
else
   Image = feval(DestT,Image,SrcSpace);
end

%%% Output format %%%
if nargout > 1
   varargout = {Image(:,:,1),Image(:,:,2),Image(:,:,3)};
else
   if FlipDims, Image = permute(Image,[1,3,2]); end
   varargout = {Image};
end

return;

function [SrcSpace,DestSpace] = parse(Str)
% Parse conversion argument

if ischar(Str)
   Str = lower(strrep(strrep(Str,'-',''),'=',''));
   k = find(Str == '>');

   if length(k) == 1         % Interpret the form 'src->dest'
      SrcSpace = Str(1:k-1);
      DestSpace = Str(k+1:end);
   else
      k = find(Str == '<');

      if length(k) == 1      % Interpret the form 'dest<-src'
         DestSpace = Str(1:k-1);
         SrcSpace = Str(k+1:end);
      else
         error(['Invalid conversion, ''',Str,'''.']);
      end
   end

   SrcSpace = alias(SrcSpace);
   DestSpace = alias(DestSpace);
else
   SrcSpace = 1;             % No source pre-transform
   DestSpace = Conversion;
   if any(size(Conversion) ~= 3), error('Transformation matrix must be 3x3.'); end
end
return;

function Space = alias(Space)
Space = strrep(strrep(Space,'cie',''),' ','');

if isempty(Space)
   Space = 'rgb';
end

switch Space
case {'ycbcr','ycc'}
   Space = 'ycbcr';
case {'hsv','hsb'}
   Space = 'hsv';
case {'hsl','hsi','hls'}
   Space = 'hsl';
case {'rgb','yuv','yiq','ydbdr','ycbcr','jpegycbcr','xyz','lab','luv','lch'}
   return;
end
return;

function T = gettransform(Space)
% Get a colorspace transform: either a matrix describing an affine transform,
% or a string referring to a conversion subroutine
switch Space
case 'ypbpr'
   T = [0.299,0.587,0.114,0;-0.1687367,-0.331264,0.5,0;0.5,-0.418688,-0.081312,0];
case 'yuv'
   % sRGB to NTSC/PAL YUV
   % Wikipedia: http://en.wikipedia.org/wiki/YUV
   T = [0.299,0.587,0.114,0;-0.147,-0.289,0.436,0;0.615,-0.515,-0.100,0];
case 'ydbdr'
   % sRGB to SECAM YDbDr
   % Wikipedia: http://en.wikipedia.org/wiki/YDbDr
   T = [0.299,0.587,0.114,0;-0.450,-0.883,1.333,0;-1.333,1.116,0.217,0];
case 'yiq'
   % sRGB in [0,1] to NTSC YIQ in [0,1];[-0.595716,0.595716];[-0.522591,0.522591];
   % Wikipedia: http://en.wikipedia.org/wiki/YIQ
   T = [0.299,0.587,0.114,0;0.595716,-0.274453,-0.321263,0;0.211456,-0.522591,0.311135,0];
case 'ycbcr'
   % sRGB (range [0,1]) to ITU-R BRT.601 (CCIR 601) Y'CbCr
   % Wikipedia: http://en.wikipedia.org/wiki/YCbCr
   % Poynton, Equation 3, scaling of R'G'B to Y'PbPr conversion
   T = [65.481,128.553,24.966,16;-37.797,-74.203,112.0,128;112.0,-93.786,-18.214,128];
case 'jpegycbcr'
   % Wikipedia: http://en.wikipedia.org/wiki/YCbCr
   T = [0.299,0.587,0.114,0;-0.168736,-0.331264,0.5,0.5;0.5,-0.418688,-0.081312,0.5]*255;
case {'rgb','xyz','hsv','hsl','lab','luv','lch','cat02lms'}
   T = Space;
otherwise
   error(['Unknown color space, ''',Space,'''.']);
end
return;

function Image = rgb(Image,SrcSpace)
% Convert to sRGB from 'SrcSpace'
switch SrcSpace
case 'rgb'
   return;
case 'hsv'
   % Convert HSV to sRGB
   Image = huetorgb((1 - Image(:,:,2)).*Image(:,:,3),Image(:,:,3),Image(:,:,1));
case 'hsl'
   % Convert HSL to sRGB
   L = Image(:,:,3);
   Delta = Image(:,:,2).*min(L,1-L);
   Image = huetorgb(L-Delta,L+Delta,Image(:,:,1));
case {'xyz','lab','luv','lch','cat02lms'}
   % Convert to CIE XYZ
   Image = xyz(Image,SrcSpace);
   % Convert XYZ to RGB
   T = [3.2406, -1.5372, -0.4986; -0.9689, 1.8758, 0.0415; 0.0557, -0.2040, 1.057];
   R = T(1)*Image(:,:,1) + T(4)*Image(:,:,2) + T(7)*Image(:,:,3);  % R
   G = T(2)*Image(:,:,1) + T(5)*Image(:,:,2) + T(8)*Image(:,:,3);  % G
   B = T(3)*Image(:,:,1) + T(6)*Image(:,:,2) + T(9)*Image(:,:,3);  % B
   % Desaturate and rescale to constrain resulting RGB values to [0,1]
   AddWhite = -min(min(min(R,G),B),0);
   R = R + AddWhite;
   G = G + AddWhite;
   B = B + AddWhite;
   % Apply gamma correction to convert linear RGB to sRGB
   Image(:,:,1) = gammacorrection(R);  % R'
   Image(:,:,2) = gammacorrection(G);  % G'
   Image(:,:,3) = gammacorrection(B);  % B'
otherwise  % Conversion is through an affine transform
   T = gettransform(SrcSpace);
   temp = inv(T(:,1:3));
   T = [temp,-temp*T(:,4)];
   R = T(1)*Image(:,:,1) + T(4)*Image(:,:,2) + T(7)*Image(:,:,3) + T(10);
   G = T(2)*Image(:,:,1) + T(5)*Image(:,:,2) + T(8)*Image(:,:,3) + T(11);
   B = T(3)*Image(:,:,1) + T(6)*Image(:,:,2) + T(9)*Image(:,:,3) + T(12);
   Image(:,:,1) = R;
   Image(:,:,2) = G;
   Image(:,:,3) = B;
end

% Clip to [0,1]
Image = min(max(Image,0),1);
return;

function Image = xyz(Image,SrcSpace)
% Convert to CIE XYZ from 'SrcSpace'
WhitePoint = [0.950456,1,1.088754];

switch SrcSpace
case 'xyz'
   return;
case 'luv'
   % Convert CIE L*uv to XYZ
   WhitePointU = (4*WhitePoint(1))./(WhitePoint(1) + 15*WhitePoint(2) + 3*WhitePoint(3));
   WhitePointV = (9*WhitePoint(2))./(WhitePoint(1) + 15*WhitePoint(2) + 3*WhitePoint(3));
   L = Image(:,:,1);
   Y = (L + 16)/116;
   Y = invf(Y)*WhitePoint(2);
   U = Image(:,:,2)./(13*L + 1e-6*(L==0)) + WhitePointU;
   V = Image(:,:,3)./(13*L + 1e-6*(L==0)) + WhitePointV;
   Image(:,:,1) = -(9*Y.*U)./((U-4).*V - U.*V);                  % X
   Image(:,:,2) = Y;                                             % Y
   Image(:,:,3) = (9*Y - (15*V.*Y) - (V.*Image(:,:,1)))./(3*V);  % Z
case {'lab','lch'}
   Image = lab(Image,SrcSpace);
   % Convert CIE L*ab to XYZ
   fY = (Image(:,:,1) + 16)/116;
   fX = fY + Image(:,:,2)/500;
   fZ = fY - Image(:,:,3)/200;
   Image(:,:,1) = WhitePoint(1)*invf(fX);  % X
   Image(:,:,2) = WhitePoint(2)*invf(fY);  % Y
   Image(:,:,3) = WhitePoint(3)*invf(fZ);  % Z
case 'cat02lms'
    % Convert CAT02 LMS to XYZ
   T = inv([0.7328, 0.4296, -0.1624;-0.7036, 1.6975, 0.0061; 0.0030, 0.0136, 0.9834]);
   L = Image(:,:,1);
   M = Image(:,:,2);
   S = Image(:,:,3);
   Image(:,:,1) = T(1)*L + T(4)*M + T(7)*S;  % X
   Image(:,:,2) = T(2)*L + T(5)*M + T(8)*S;  % Y
   Image(:,:,3) = T(3)*L + T(6)*M + T(9)*S;  % Z
otherwise   % Convert from some gamma-corrected space
   % Convert to sRGB
   Image = rgb(Image,SrcSpace);
   % Undo gamma correction
   R = invgammacorrection(Image(:,:,1));
   G = invgammacorrection(Image(:,:,2));
   B = invgammacorrection(Image(:,:,3));
   % Convert RGB to XYZ
   T = inv([3.2406, -1.5372, -0.4986; -0.9689, 1.8758, 0.0415; 0.0557, -0.2040, 1.057]);
   Image(:,:,1) = T(1)*R + T(4)*G + T(7)*B;  % X
   Image(:,:,2) = T(2)*R + T(5)*G + T(8)*B;  % Y
   Image(:,:,3) = T(3)*R + T(6)*G + T(9)*B;  % Z
end
return;

function Image = hsv(Image,SrcSpace)
% Convert to HSV
Image = rgb(Image,SrcSpace);
V = max(Image,[],3);
S = (V - min(Image,[],3))./(V + (V == 0));
Image(:,:,1) = rgbtohue(Image);
Image(:,:,2) = S;
Image(:,:,3) = V;
return;

function Image = hsl(Image,SrcSpace)
% Convert to HSL
switch SrcSpace
case 'hsv'
   % Convert HSV to HSL
   MaxVal = Image(:,:,3);
   MinVal = (1 - Image(:,:,2)).*MaxVal;
   L = 0.5*(MaxVal + MinVal);
   temp = min(L,1-L);
   Image(:,:,2) = 0.5*(MaxVal - MinVal)./(temp + (temp == 0));
   Image(:,:,3) = L;
otherwise
   Image = rgb(Image,SrcSpace);  % Convert to sRGB
   % Convert sRGB to HSL
   MinVal = min(Image,[],3);
   MaxVal = max(Image,[],3);
   L = 0.5*(MaxVal + MinVal);
   temp = min(L,1-L);
   S = 0.5*(MaxVal - MinVal)./(temp + (temp == 0));
   Image(:,:,1) = rgbtohue(Image);
   Image(:,:,2) = S;
   Image(:,:,3) = L;
end
return;

function Image = lab(Image,SrcSpace)
% Convert to CIE L*a*b* (CIELAB)
WhitePoint = [0.950456,1,1.088754];

switch SrcSpace
case 'lab'
   return;
case 'lch'
   % Convert CIE L*CH to CIE L*ab
   C = Image(:,:,2);
   Image(:,:,2) = cos(Image(:,:,3)*pi/180).*C;  % a*
   Image(:,:,3) = sin(Image(:,:,3)*pi/180).*C;  % b*
otherwise
   Image = xyz(Image,SrcSpace);  % Convert to XYZ
   % Convert XYZ to CIE L*a*b*
   X = Image(:,:,1)/WhitePoint(1);
   Y = Image(:,:,2)/WhitePoint(2);
   Z = Image(:,:,3)/WhitePoint(3);
   fX = f(X);
   fY = f(Y);
   fZ = f(Z);
   Image(:,:,1) = 116*fY - 16;    % L*
   Image(:,:,2) = 500*(fX - fY);  % a*
   Image(:,:,3) = 200*(fY - fZ);  % b*
end
return;

function Image = luv(Image,SrcSpace)
% Convert to CIE L*u*v* (CIELUV)
WhitePoint = [0.950456,1,1.088754];
WhitePointU = (4*WhitePoint(1))./(WhitePoint(1) + 15*WhitePoint(2) + 3*WhitePoint(3));
WhitePointV = (9*WhitePoint(2))./(WhitePoint(1) + 15*WhitePoint(2) + 3*WhitePoint(3));

Image = xyz(Image,SrcSpace); % Convert to XYZ
Denom = Image(:,:,1) + 15*Image(:,:,2) + 3*Image(:,:,3);
U = (4*Image(:,:,1))./(Denom + (Denom == 0));
V = (9*Image(:,:,2))./(Denom + (Denom == 0));
Y = Image(:,:,2)/WhitePoint(2);
L = 116*f(Y) - 16;
Image(:,:,1) = L;                        % L*
Image(:,:,2) = 13*L.*(U - WhitePointU);  % u*
Image(:,:,3) = 13*L.*(V - WhitePointV);  % v*
return;

function Image = lch(Image,SrcSpace)
% Convert to CIE L*ch
Image = lab(Image,SrcSpace);  % Convert to CIE L*ab
H = atan2(Image(:,:,3),Image(:,:,2));
H = H*180/pi + 360*(H < 0);
Image(:,:,2) = sqrt(Image(:,:,2).^2 + Image(:,:,3).^2);  % C
Image(:,:,3) = H;                                        % H
return;

function Image = cat02lms(Image,SrcSpace)
% Convert to CAT02 LMS
Image = xyz(Image,SrcSpace);
T = [0.7328, 0.4296, -0.1624;-0.7036, 1.6975, 0.0061; 0.0030, 0.0136, 0.9834];
X = Image(:,:,1);
Y = Image(:,:,2);
Z = Image(:,:,3);
Image(:,:,1) = T(1)*X + T(4)*Y + T(7)*Z;  % L
Image(:,:,2) = T(2)*X + T(5)*Y + T(8)*Z;  % M
Image(:,:,3) = T(3)*X + T(6)*Y + T(9)*Z;  % S
return;

function Image = huetorgb(m0,m2,H)
% Convert HSV or HSL hue to RGB
N = size(H);
H = min(max(H(:),0),360)/60;
m0 = m0(:);
m2 = m2(:);
F = H - round(H/2)*2;
M = [m0, m0 + (m2-m0).*abs(F), m2];
Num = length(m0);
j = [2 1 0;1 2 0;0 2 1;0 1 2;1 0 2;2 0 1;2 1 0]*Num;
k = floor(H) + 1;
Image = reshape([M(j(k,1)+(1:Num).'),M(j(k,2)+(1:Num).'),M(j(k,3)+(1:Num).')],[N,3]);
return;

function H = rgbtohue(Image)
% Convert RGB to HSV or HSL hue
[M,i] = sort(Image,3);
i = i(:,:,3);
Delta = M(:,:,3) - M(:,:,1);
Delta = Delta + (Delta == 0);
R = Image(:,:,1);
G = Image(:,:,2);
B = Image(:,:,3);
H = zeros(size(R));
k = (i == 1);
H(k) = (G(k) - B(k))./Delta(k);
k = (i == 2);
H(k) = 2 + (B(k) - R(k))./Delta(k);
k = (i == 3);
H(k) = 4 + (R(k) - G(k))./Delta(k);
H = 60*H + 360*(H < 0);
H(Delta == 0) = nan;
return;

function Rp = gammacorrection(R)
Rp = zeros(size(R));
i = (R <= 0.0031306684425005883);
Rp(i) = 12.92*R(i);
Rp(~i) = real(1.055*R(~i).^0.416666666666666667 - 0.055);
return;

function R = invgammacorrection(Rp)
R = zeros(size(Rp));
i = (Rp <= 0.0404482362771076);
R(i) = Rp(i)/12.92;
R(~i) = real(((Rp(~i) + 0.055)/1.055).^2.4);
return;

function fY = f(Y)
fY = real(Y.^(1/3));
i = (Y < 0.008856);
fY(i) = Y(i)*(841/108) + (4/29);
return;

function Y = invf(fY)
Y = fY.^3;
i = (Y < 0.008856);
Y(i) = (fY(i) - 4/29)*(108/841);
return;


function acc = st_to_cc_values ( nst, ist, jst, ast, ncc, n, icc, ccc )

%*****************************************************************************80
%
%% ST_TO_CC_VALUES creates CC values from ST data.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    14 July 2014
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer NST, the number of ST elements.
%
%    Input, integer IST(NST), JST(NST), the ST rows and columns.
%
%    Input, real AST(NST), the ST values.
%
%    Input, integer NCC, the number of CC elements.
%
%    Input, integer N, the number of columns.
%
%    Input, integer ICC(NCC), the CC rows.
%
%    Input, integer CCC(N+1), the CC compressed columns.
%
%    Output, real ACC(NCC), the CC values.
%
  acc = zeros ( ncc, 1 );

  for kst = 1 : nst

    i = ist(kst);
    j = jst(kst);

    clo = ccc(j);
    chi = ccc(j+1);

    fail = 1;

    for kcc = clo : chi - 1
      if ( icc(kcc) == i )
        acc(kcc) = acc(kcc) + ast(kst);
        fail = 0;
        break
      end
    end

    if ( fail )
      fprintf ( 1, '\n' );
      fprintf ( 1, 'ST_TO_CC_VALUES - Fatal error!\n' );
      fprintf ( 1, '  ST entry cannot be located in CC array.\n' );
      fprintf ( 1, '  ST index KST    = %d\n', kst );
      fprintf ( 1, '  ST row IST(KST) = %d\n', ist(kst) );
      fprintf ( 1, '  ST col JST(KST) = %d\n', jst(kst) );
      fprintf ( 1, '  ST val AST(KST) = %g\n', ast(kst) );
      error ( 'ST_TO_CC_VALUES - Fatal error!' );
    end

  end

  return
end


%%*******************************************************************
%% HSDNTdirfun: compute (dX,dZ), given dy, for the NT direction.
%%
%% SDPT3: version 3.1
%% Copyright (c) 1997 by
%% K.C. Toh, M.J. Todd, R.H. Tutuncu
%% Last Modified: 16 Sep 2004
%%*******************************************************************

    function [par,dX,dy,dZ] = HSDNTdirfun(blk,At,par,Rd,EinvRc,xx);

    global solve_ok

    dX = cell(size(blk,1),1); dZ = cell(size(blk,1),1); dy = [];
    if (any(isnan(xx)) | any(isinf(xx)))
       solve_ok = 0;
       fprintf('\n  HSDNTdirfun: solution contains NaN or inf.');
       return;
    end
%%
    m = par.m;
    dy2 = xx(1:m+2);
%%
    for p=1:size(blk,1)
       pblk = blk(p,:);
       if strcmp(pblk{1},'l')
          dZ(p) = ops(Rd(p),'-',Atyfun(pblk,At(p,:),[],[],dy2));
          tmp   = par.dd{p}.*dZ{p};
          dX{p} = EinvRc{p} - tmp;
       elseif strcmp(pblk{1},'q')
          dZ(p) = ops(Rd(p),'-',Atyfun(pblk,At(p,:),[],[],dy2));
          tmp = par.dd{p}.*dZ{p} + qops(pblk,qops(pblk,dZ{p},par.ee{p},1),par.ee{p},3);
          dX{p} = EinvRc{p} - tmp;
       elseif strcmp(pblk{1},'s')
          dZ(p) = ops(Rd(p),'-',Atyfun(pblk,At(p,:),par.permA(p,:),par.isspAy(p),dy2));
          tmp   = Prod3(pblk,par.W{p},dZ{p},par.W{p},1);
          dX{p} = EinvRc{p}-tmp;
       end
    end
    dy  = dy2(1:m);
    par.dtau = dy2(m+1);
    par.dtheta = dy2(m+2);
    par.dkap = (par.mu./par.tau - par.kap) - par.kap*(par.dtau/par.tau);
%%*******************************************************************


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Spherical Harmonic Modeling and Analysis Toolkit (SPHARM-MAT) is a 3D
% shape modeling and analysis toolkit.
% It is a software package developed at Shenlab in Center for Neuroimaging,
% Indiana University (SpharmMat@gmail.com, http://www.iupui.edu/~shenlab/)
% It is available to the scientific community as copyright freeware
% under the terms of the GNU General Public Licence.
%
% Copyright 2009, 2010, ShenLab, Center for Neuroimaging, Indiana University
%
% This file is part of SPHARM-MAT.
%
% SPHARM-MAT 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.
%
% SPHARM-MAT 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 SPHARM-MAT. If not, see <http://www.gnu.org/licenses/>.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

function SpharmMatUtilDisplayObjs(confs, objs, cpath)

numSbj = size(objs,2);

tplt = [];
if exist(confs.Template,'file')
    if strcmp(confs.Template(end-3:end),'.mat')==1
        tplt = load(confs.Template);
        if ~isfield(tplt,'fvec')
            disp('Template file does not contain fvec');
            tplt = [];
        end
    else
        disp('Template should be a .mat file');
    end
end

for i = 1:numSbj
    file = objs{i};
    [pa,na,ex]=fileparts(file);
    nastr = strrep(na,'_','-');

    clear adc; clear cdata; rmsd = [];

    suffix = '';

    load(file);
    if exist('bim','var')
        roi = bim;
        DIM = size(roi);

        % Make binary image
        ind = find(roi>0); roi(ind) = 1;
        ind = find(roi<1); roi(ind) = 0;

        % Generate voxel surface for the binary image
        [vertices, faces] =  gen_surf_data(roi,origin,vxsize);
    end

    h = figure('Name', ['Object: ' na],'NumberTitle', 'off');
    cameratoolbar(h, 'Show');

    % calculate SPHARM reconstruction
    if exist('fvec','var')
        % get regular mesh in the parameter space: sph_verts, faces
        switch strtrim(char(confs.Mesh))
            % to-do-item: add an option for reconstruction using original mesh
            case 'quad32'
                load(fullfile(cpath,'ParamMeshes/R32_quad.mat'));
            case 'quad64'
                load(fullfile(cpath,'ParamMeshes/R64_quad.mat'));
            case 'quad128'
                load(fullfile(cpath,'ParamMeshes/R128_quad.mat'));
            case 'quad256'
                load(fullfile(cpath,'ParamMeshes/R256_quad.mat'));
            case 'quad512'
                load(fullfile(cpath,'ParamMeshes/R512_quad.mat'));
            case 'icosa1'
                load(fullfile(cpath,'ParamMeshes/L1_icosa.mat'));
            case 'icosa2'
                load(fullfile(cpath,'ParamMeshes/L2_icosa.mat'));
            case 'icosa3'
                load(fullfile(cpath,'ParamMeshes/L3_icosa.mat'));
            case 'icosa4'
                load(fullfile(cpath,'ParamMeshes/L4_icosa.mat'));
            case 'icosa5'
                load(fullfile(cpath,'ParamMeshes/L5_icosa.mat'));
            case 'icosa6'
                load(fullfile(cpath,'ParamMeshes/L6_icosa.mat'));
        end

        % calculate SPHARM reconstruction using the regular mesh: vertices
        if ~strcmp(deblank(char(confs.Mesh)), 'orig')
            % get the user-specified degree
            if isempty(confs.Degree)
                degree = Inf;
            else
                degree = confs.Degree;
            end
            degree = min(degree,sqrt(size(fvec,1))-1);
            suffix = sprintf('%s_d%d',strtrim(char(confs.Mesh)),degree);
            % do the calculation
            Z = calculate_SPHARM_basis(sph_verts, degree);
            lb = 1;
            ub = (degree+1)^2;
            vertices = real(Z(:,lb:ub)*fvec(lb:ub,:));
            % calculate RMSD if there is a template
            if ~isempty(tplt)
                tfvec = tplt.fvec;
                tcnt = size(tfvec,1);
                if tcnt>=ub
                    tfvec = tfvec(1:ub,:);
                else
                    tfvec = zeros(1:tcnt,3);
                    tfvec(1:tcnt,:) = tplt.fvec;
                end
                rmsd = SPHARM_rmsd(fvec,tfvec);
            end
        end
    end

    % option for display in object space, parameter space, or both
    switch strtrim(char(confs.Space))
        case 'object'
            if ~exist('vertices','var') | ~exist('faces','var')
                disp(sprintf('Cannot display %s in the object space',nastr));
                break;
            end
            subp = 0;
            titleStr = sprintf('Object: %s',nastr);
        case 'param'
            if ~exist('sph_verts','var') | ~exist('faces','var')
                disp(sprintf('Cannot display %s in the parameter space',nastr));
                break;
            end
            subp = 1;
            titleStr = sprintf('Parameterization: %s',nastr);
        case 'both'
            if ~exist('faces','var') | (~exist('vertices','var') & ~exist('sph_verts','var'))
                disp(sprintf('Cannot display %s in object and parameter spaces',nastr));
                break;
            end
            subp = 2;
            titleStrLeft = sprintf('%s',nastr);
            titleStrRight = sprintf('Parameterization');
    end

    % option to visualize parameterization in color
    % ADC is shown together with this option
    over1 = 0;
    switch strtrim(char(confs.Overlay))
        case 'none'
            overl = 0;
        case 'adc_paramap'
            if exist('vertices','var') & exist('sph_verts','var') & exist('faces','var')
                overl = 1;
                % calculate the color coding
                [THETA,PHI,R] = cart2sph(sph_verts(:,1),sph_verts(:,2),sph_verts(:,3));
                ix = find(THETA<0); THETA(ix) = THETA(ix)+pi*2;
                cdata = THETA.*sign(PHI);
                % calculate the area distortion cost: adc
                asr = calc_asr(vertices, sph_verts, faces); adc = asr.prmstc;
            else
                disp(sprintf('Color coded parameterization cannot be calculated for %s due to missing variables',nastr));
            end
    end

    % option for solid shading, mesh view, or both
    switch strtrim(char(confs.Shade))
        case 'solid'
            if overl == 1
                if subp == 0
                    patch_color(vertices, faces, cdata); title(titleStr);
                elseif subp == 1
                    patch_color(sph_verts, faces, cdata); title(titleStr);
                elseif subp == 2
                    subplot(1,2,1);
                    patch_color(vertices, faces, cdata); title(titleStrLeft);
                    subplot(1,2,2);
                    patch_color(sph_verts, faces, cdata); title(sprintf('%s: ADC=%f',titleStrRight,adc));
                end
            else
                if subp == 0
                    patch_light(vertices, faces); title(titleStr);
                elseif subp == 1
                    patch_light(sph_verts, faces); title(titleStr);
                elseif subp == 2
                    subplot(1,2,1);
                    patch_light(vertices, faces); title(titleStrLeft);
                    subplot(1,2,2);
                    patch_light(sph_verts, faces); title(titleStrRight);
                end
            end
        case 'mesh'
            if overl == 1
                disp('Color coded parameterization is not shown in mesh view');
                if subp == 0
                    patch_mesh(vertices, faces); title(titleStr);
                elseif subp == 1
                    patch_mesh(sph_verts, faces); title(titleStr);
                elseif subp == 2
                    subplot(1,2,1);
                    patch_mesh(vertices, faces); title(titleStrLeft);
                    subplot(1,2,2);
                    patch_mesh(sph_verts, faces); title(sprintf('%s: ADC=%f',titleStrRight,adc));
                end
            else
                if subp == 0
                    patch_mesh(vertices, faces); title(titleStr);
                elseif subp == 1
                    patch_mesh(sph_verts, faces); title(titleStr);
                elseif subp == 2
                    subplot(1,2,1);
                    patch_mesh(vertices, faces); title(titleStrLeft);
                    subplot(1,2,2);
                    patch_mesh(sph_verts, faces); title(titleStrRight);
                end
            end
        case 'both'
            if overl == 1
                if subp == 0
                    patch_colormesh(vertices, faces, cdata); title(titleStr);
                elseif subp == 1
                    patch_colormesh(sph_verts, faces, cdata); title(titleStr);
                elseif subp == 2
                    subplot(1,2,1);
                    patch_colormesh(vertices, faces, cdata); title(titleStrLeft);
                    subplot(1,2,2);
                    patch_colormesh(sph_verts, faces, cdata); title(sprintf('%s: ADC=%f',titleStrRight,adc));
                end
            else
                if subp == 0
                    patch_lightmesh(vertices, faces); title(titleStr);
                elseif subp == 1
                    patch_lightmesh(sph_verts, faces); title(titleStr);
                elseif subp == 2
                    subplot(1,2,1);
                    patch_lightmesh(vertices, faces); title(titleStrLeft);
                    subplot(1,2,2);
                    patch_lightmesh(sph_verts, faces); title(titleStrRight);
                end
            end
    end

    if ~isempty(suffix)
        suffix = sprintf('%s_%s',confs.Space(1:3),suffix);
    else
        suffix = confs.Space(1:3);
    end
    if ~isempty(rmsd)
        zstr = sprintf('%s, rmsd=%0.3f',suffix,rmsd);
        zlabel(strrep(zstr,'_','-'));
    else
        zlabel(strrep(suffix,'_','-'));
    end

    % display and/or export
    switch strtrim(char(confs.Export))
        case 'screen'
            % display the figure
        case 'png'
            % save as png
            pa_png = fullfile(pa,'PNG');
            if ~exist(pa_png,'dir')
                mkdir(pa_png);
            end
            saveas(h,fullfile(pa_png,[na '_' suffix]),'png'); close(h);
        case 'both'
            % do both
            pa_png = fullfile(pa,'PNG');
            if ~exist(pa_png,'dir')
                mkdir(pa_png);
            end
            saveas(h,fullfile(pa_png,[na '_' suffix]),'png');
    end

end

return;


function[varargout]=mat2col(varargin)
%MAT2COL  Compress NAN-padded matrix data into long columns.
%
%   C=MAT2COL(M), where the columns of matrix M are data vectors of
%   nonuniform length with NANs filling out the gaps in each column,
%   appends together all the columns into a vector C in which isolated
%   NANs mark the transitions between columns.
%
%   [C1,C2,...,CN]=MAT2COL(M1,M2,...,MN) also works for multiple input
%   matrices of the same size.  In this case the locations of NANs in
%   M1 are used as the key for appending all the MN into columns.
%
%   MAT2COL, COL2MAT, and COLBREAKS together form a system for moving
%   data with segments of nonuniform length rapidly back and forth
%   between a column format and a padded-matrix format.
%
%   MAT2COL(M1,M2,...,MN); with no output arguments overwrites the
%   original input variables.
%
%   See also COL2MAT, COLBREAKS, COL2CELL, CELL2COL.
%   _________________________________________________________________
%   This is part of JLAB --- type 'help jlab' for more information
%   (C) 2000--2016 J.M. Lilly --- type 'help jlab_license' for details

%no loops!

if strcmpi(varargin{1},'--t')
    return
end

if isempty(varargin{1})
   for i=1:nargout
       varargout{i}=[];
   end
   return
end

i=0;
while i<nargin
	i=i+1;
	mat=varargin{i};

	if i==1
		if ~all(isnan(mat(end,:)))
			mat(size(mat,1)+1,:)=nan*ones(size(mat(1,:)));
		end
		mat=mat(:);

		%new matrix should include all data points
		bool=false(size(mat));
		index=find(~isnan(mat));
		bool(index)=ones(size(index));
		index2=find(diff([bool;0])==-1)+1;
		if ~isempty(index2)
			bool(index2)=ones(size(index2));
		end
		index=find(bool);
	end
	mat=mat(index);
	col=mat;
	varargout{i}=col;
end

if nargout>nargin
        varargout{end+1}=index;
	%eval(['c' int2str(nargout) '=index;']);
end

if nargout==0
   %for i=1:nargin
   %   eval(['varargout{',int2str(i),'}=c',int2str(i),';'])
   %end
  eval(to_overwrite(nargin));
end


function rs = medianfilt(s, len)

%tstoolbox/@signal/medianfilt
%   Syntax:
%     * rs = medianfilt(s, len)
%
%   Moving median filter of width len samples for a scalar time series
%   (len should be odd).
%
% Copyright 1997-2001 DPI Goettingen, License http://www.physik3.gwdg.de/tstool/gpl.txt

narginchk(2,2);

c = medianfilt(s.core, len);
rs = signal(c, s);				% special constructor calling syntax for working routines

a = getaxis(s,1);
a = setfirst(a, ((len+1)/2) * delta(a));

rs = setaxis(rs, 1, a);
rs = addhistory(rs,  ['Moving average with window of ' num2str(len) ' samples'] );
rs = addcommandlines(rs, 's = medianfilt(s', len);


function [ DCM ] = tapas_dcm_euler_make_indices(DCM)
% [ DCM ] = tapas_dcm_euler_make_indices(DCM)
%
% Get the indices for the Euler integration
%
%   Input:
%   	DCM         - model structure
%
%   Output:
%       DCM         - model structure with indices
%

% ----------------------------------------------------------------------
%
% Authors: Stefan Fraessle (stefanf@biomed.ee.ethz.ch), Ekaterina I. Lomakina
%
% Copyright (C) 2016-2018 Translational Neuromodeling Unit
%                         Institute for Biomedical Engineering
%                         University of Zurich & ETH Zurich
%
% This file is part of the TAPAS rDCM Toolbox, which is released under the
% terms of the GNU General Public License (GPL), version 3.0 or later. You
% can redistribute and/or modify the code under the terms of the GPL. For
% further see COPYING or <http://www.gnu.org/licenses/>.
%
% Please note that this toolbox is in an early stage of development. Changes
% are likely to occur in future releases.
%
% ----------------------------------------------------------------------

% length of input
L = size(DCM.U.u,1);

% number of regions
nr = size(DCM.a, 1);

% check whether there is a delay specified
if ~isfield('DCM','delay')
    DCM.delay = ones(nr,1);
end

% create index array
Indices = repmat(1 : L, 1, 1);

% get the indices that coincide wiht the data timepoints
idx = zeros(1, L);
idx(DCM.delay:floor(DCM.Y.dt/DCM.U.dt):end) = 1;

% asign those timings
Indices = Indices(idx>0);
DCM.M.idx = Indices;

end


function fun = sym2fun(sobj,svar,var,skipSubs)
% SYM2FUN  Convert Symbolic Expression into Matlab Function Handle

if(nargin < 3 || isempty(var)), var = 'x'; end
if(nargin < 4 || isempty(skipSubs)), skipSubs = false; end

%Build cell array to store indexed vars
ivar = convIndex(svar);

%Subs out individual symbolic variables into our indexed list
if(~skipSubs)
    wstate = warning('off','symbolic:sym:sym:DeprecateExpressions');
    eq = subs(sobj,svar,ivar);
    warning(wstate);
else
    eq = sobj;
end

%Build a MATLAB function
str = char(eq);
%Remove 'matrix' if is found
ind = strfind(str,'matrix');
if(ind)
    str = str(ind+7:end-1);
end
%Ensure we have right size
[r,c] = size(eq);
if(c==1 && r > 1)
    str = regexprep(str,',',';'); %force column
elseif(c>1 && r>1)
    str = regexprep(str,'],','];'); %keep matrix
end
%Build function handle
fun = str2func(['@(' var ') ' str]);

%Convert index from m1 to m(1)
function [ivar,var] = convIndex(svar)
ivar = cell(size(svar));
%Find indexing number
for i = 1:length(svar)
    str = char(svar(i));
    for j = 1:length(str)
        if(~isletter(str(j)))
            break;
        end
    end
    if(i == 1) %assume first variable contains decision variable
        var = str(1:j-1);
    end
%   ivar{i} = sprintf('%s(%s)',str(1:j-1),str(j:end)); %this uses user index...
    ivar{i} = sprintf('%s(%d)',str(1:j-1),i); %this uses index based on array
end


function [ y, x ] = cvx_check_dimlist( x, emptyok )

% CVX_CHECK_DIMLIST Verifies the input is a valid dimension list.

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

if nargin < 2 || emptyok,
    xmin = 0;
else
    xmin = 1;
end
if isa( x, 'cell' ),
    nel = numel( x );
    xnew = zeros( 1, nel );
    y = false;
    for k = 1 : nel,
        if ~isnumeric( x{k} ) || length( x{k} ) ~= 1, return; end
        xnew( k ) = x{k};
    end
    x = xnew;
end
if isnumeric( x ) && ndims( x ) <= 2 && ~all( size( x ) > 1 ) && isreal( x ) && ~any( isnan( x ) | isinf( x ) ) && all( x >= xmin ) && all( floor( x ) == x ),
    y = true;
else
    y = false;
end
if y && nargout > 1,
    x = [ x( : )', 1, 1 ];
    x = x( 1 : max( [ 2, find( x > 1 ) ] ) );
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 [iV,jV,kV]=find3d(mask3M)
%"find3d"
%   This is the 3D equivalent of the builtin command, find.
%   It returns the i,j,k indices of non-zero entries.
%
%JOD, 16 Nov 98
%JOD, 26 Feb 03, bugfix.
%JRA, 26 Feb 04, new algorithm, also implements the new fastind2sub.
%
%Usage:
%   [iV,jV,kV]=find3d(mask3M);
%
% Copyright 2010, Joseph O. Deasy, on behalf of the CERR development team.
%
% This file is part of The Computational Environment for Radiotherapy Research (CERR).
%
% CERR development has been led by:  Aditya Apte, Divya Khullar, James Alaly, and Joseph O. Deasy.
%
% CERR has been financially supported by the US National Institutes of Health under multiple grants.
%
% CERR is distributed under the terms of the Lesser GNU Public License.
%
%     This version of CERR is free software: you can redistribute it and/or modify
%     it under the terms of the GNU General Public License as published by
%     the Free Software Foundation, either version 3 of the License, or
%     (at your option) any later version.
%
% CERR is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
% without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
% See the GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with CERR.  If not, see <http://www.gnu.org/licenses/>.

indV = find(mask3M(:));
[iV,jV,kV] = fastind2sub(size(mask3M), indV);
iV = iV';
jV = jV';
kV = kV';


function y = stdignorenan(x,varargin)

y = std(x(~isnan(x)),varargin{:});


function plotData(X, y)
%PLOTDATA Plots the data points X and y into a new figure
%   PLOTDATA(x,y) plots the data points with + for the positive examples
%   and o for the negative examples. X is assumed to be a Mx2 matrix.
%
% Note: This was slightly modified such that it expects y = 1 or y = 0

% Find Indices of Positive and Negative Examples
pos = find(y == 1); neg = find(y == 0);

% Plot Examples
plot(X(pos, 1), X(pos, 2), 'k+','LineWidth', 1, 'MarkerSize', 7)
hold on;
plot(X(neg, 1), X(neg, 2), 'ko', 'MarkerFaceColor', 'y', 'MarkerSize', 7)
hold off;

end


function [U,data,SS,R] = arap(V,F,b,bc,varargin)
  % ARAP Solve for the as-rigid-as-possible deformation according to various
  % manifestations including:
  %   (1) "As-rigid-as-possible Surface Modeling" by [Sorkine and Alexa 2007]
  %   (2) "A local-global approach to mesh parameterization" by [Liu et al.
  %     2010] or "A simple geometric model for elastic deformation" by [Chao et
  %     al.  2010]
  %   (3) Adapted version of "As-rigid-as-possible Surface Modeling" by
  %     [Sorkine and Alexa 2007] presented in section 4.2 of or "A simple
  %     geometric model for elastic deformation" by [Chao et al.  2010]
  %
  % U = arap(V,F,b,bc) given a rest mesh (V,F) and list of constraint vertex
  % indices (b) and their new postions (bc) solve for pose mesh positions (U),
  % using default choice for 'Energy'
  %
  % U = arap(V,F,b,bc,'ParameterName','ParameterValue',...)
  %
  % Inputs:
  %   V  #V by dim list of rest domain positions
  %   F  #F by {3|4} list of {triangle|tetrahedra} indices into V
  %   b  #b list of indices of constraint (boundary) vertices
  %   bc  #b by dim list of constraint positions for b
  %   Optional:
  %     'Energy'
  %       followed by a string specifying which arap energy definition to use.
  %       One of the following:
  %         'spokes'  "As-rigid-as-possible Surface Modeling" by [Sorkine and
  %           Alexa 2007], rotations defined at vertices affecting incident
  %           edges
  %         'elements'  "A local-global approach to mesh parameterization" by
  %           [Liu et al.  2010] or "A simple geometric model for elastic
  %           deformation" by [Chao et al.  2010], rotations defined at
  %           elements (triangles or tets)
  %         'spokes-and-rims'  Adapted version of "As-rigid-as-possible Surface
  %           Modeling" by [Sorkine and Alexa 2007] presented in section 4.2 of
  %           or "A simple geometric model for elastic deformation" by [Chao et
  %           al.  2010], rotations defined at vertices affecting incident
  %           edges and opposite edges
  %     'V0' #V by dim list of initial guess positions
  %       dim by dim by #C list of linear transformations initial guesses,
  %       optional (default is to use identity transformations)
  %     'Data' see output
  %     'Groups'
  %       followed by #V list of group indices (1 to k) for each vertex, such
  %       that vertex i is assigned to group G(i)
  %     'Tol'
  %       stopping critera parameter. If variables (linear transformation matrix
  %       entries) change by less than 'tol' the optimization terminates,
  %       default is 0.75 (weak tolerance)
  %     'MaxIter'
  %       max number of local-global iterations, default is 100
  %     'Dynamic'
  %        #V by dim list of external forces
  %     'TimeStep'
  %        scalar time step value
  %     'Vm1'
  %        #V by dim positions at time t-1
  %     'Tikhonov' followed by constant Tikhonov regularization parameter
  %       alpha:
  %       http://en.wikipedia.org/wiki/Tikhonov_regularization#Relation_to_probabilistic_formulation
  %     'Youngs'  followed by young's modulus (only for dynamics)
  %     'Flat' followed by whether to add the constraint that Z=0 {false}
  %     'RemoveRigid' followed by whether to add a constraint that places an
  %       arbitrary point at the origin and another along the x-axis {false}
  %     'Aeq'/'Beq'  followed by #Aeq by dim*#V linear equality constraints
  %        matrix and righthand sides respectively {[]}/{[]}
  % Outputs:
  %   U  #V by dim list of new positions
  %   data  struct of reusable data
  %     .CSM dim*n by dim*n sparse matrix containing special laplacians along the
  %       diagonal so that when multiplied by repmat(U,dim,1) gives covariance
  %       matrix elements, can be used to speed up next time this function is
  %       called, see function definitions
  %
  % Known issues: 'Flat',true + 'Energy','elements' should only need a 2D
  % rotation fit, but this does 3D to stay general (e.g. if one were to use
  % 'spokes' then the edge-set cannot be pre-mapped to a common plane)
  %
  % See also: takeo_arap
  %

  % parse input
  G = [];

  % number of vertices
  n = size(V,1);
  assert(isempty(b) || max(b) <= n,'b should be in bounds [1,size(V,1)]');
  assert(isempty(b) || min(b) >= 1,'b should be in bounds [1,size(V,1)]');

  indices = 1:n;
  max_iterations = 100;
  tol = 0.001;
  interior = indices(~ismember(indices,b));
  U = [];
  Vm1 = [];
  youngs = 1.2e9;
  % default is Sorkine and Alexa style local rigidity energy
  energy = [];
  % default is no external forces
  fext = [];
  % defaults is unit time step
  h = 1;
  % Tikhonov regularization alpha
  alpha_tik = 0;
  % flatten/parameterization
  flat = false;
  % remove rigid transformation invariance
  remove_rigid = false;
  G = [];
  debug = false;
  data = [];
  Aeq = [];
  Beq = [];
  ref_data = [];
  weight = [];

  % Map of parameter names to variable names
  params_to_variables = containers.Map( ...
    {'Energy','V0','Data','Tikhonov','Groups','Tol', ...
     'MaxIter','Dynamic','TimeStep','Vm1','RemoveRigid','Debug', ...
     'Aeq','Beq','Flat','Ref','Weight','Youngs'}, ...
    {'energy','U','data','alpha_tik','G','tol','max_iterations','fext', ...
    'h','Vm1','remove_rigid','debug','Aeq','Beq','flat','ref_data','weight','youngs'});
  v = 1;
  while v <= numel(varargin)
    param_name = varargin{v};
    if isKey(params_to_variables,param_name)
      assert(v+1<=numel(varargin));
      v = v+1;
      % Trick: use feval on anonymous function to use assignin to this workspace
      feval(@()assignin('caller',params_to_variables(param_name),varargin{v}));
    else
      error('Unsupported parameter: %s',varargin{v});
    end
    v=v+1;
  end

  if isempty(energy)
    switch size(F,2)
    case 4
      energy = 'elements';
    case 3
      energy = 'spokes-and-rims';
    end
  end
  if isempty(fext)
    dynamic = false;
    fext = zeros(size(V));
  else
    dynamic = true;
  end

  if isempty(data)
    if strcmp(energy,'elements') && numel(G) ~= size(F,1) && numel(G) == n
      % groups are defined per vertex, convert to per face using mode
      data.G = mode(G(F),2);
    else
      data.G = G;
    end
  end

  if isempty(data.G)
    k = n;
  else
    k = max(data.G);
  end

  ref_map = [];
  if isempty(ref_data)
    if flat
      [ref_V,ref_F,ref_map] = plane_project(V,F);
      assert(strcmp(energy,'elements'),'flat only makes sense with elements');
    else
      ref_V = V;
      ref_F = F;
    end
  else
    ref_V = ref_data{1};
    ref_F = ref_data{2};
    ref_map = ref_data{3};
  end
  dim = size(ref_V,2);

  if isempty(bc)
    bc = sparse(0,dim);
  end

  assert(dim == size(bc,2));
  assert(size(Aeq,1) == size(Beq,1));
  assert((size(Aeq,2) == 0) || (size(Aeq,2) == dim*size(V,1)));

  if isempty(U)
    if(dim == 2)
      U = laplacian_mesh_editing(V,F,b,bc);
    else
      U = V;
    end
    U = U(:,1:dim);
  end
  assert(n == size(U,1));
  assert(dim == size(U,2));

  if dynamic
    V0 = U(:,1:dim);
    if isempty(Vm1)
      Vm1 = V0;
    end
    %% This parameter seem good for a 0.1m tall hotdog with h=0.0333
    % Larger --> more rigid
    dyn_alpha = h^2*youngs;
    % Smaller --> more damped
    mom = 1e10;
    M = massmatrix(ref_V,ref_F);
    if ~isempty(ref_map)
      M = ref_map*M*ref_map';
    end
    DQ = 0.5*mom*M;
    Dl =     mom*M*(-2*V0 + Vm1) - h^2*fext;
  else
    dyn_alpha = 1;
    DQ = sparse(size(V,1),size(V,1));
    Dl = sparse(size(V,1),dim);
  end

  if ~isfield(data,'L') || isempty(data.L)
    if isempty(weight)
      data.L = cotmatrix(ref_V,ref_F);
    else
      wA = doublearea(ref_V,ref_F);
      wG = grad(ref_V,ref_F);
      data.L = -0.5*wG'*( repmat(wA.*weight,dim,1) .* wG);
    end

    if ~isempty(ref_map)
      data.L = ref_map*data.L*ref_map';
    end
  end

  rr.b = cell(dim,1);
  rr.bc = cell(dim,1);
  if ~isfield(data,'rr')
    data.rr = [];
    if ~isfield(data.rr,'preF')
      data.rr.preF = cell(dim,1);
    end
  end
  if ~isfield(data,'preF')
    data.preF = [];
  end
  if remove_rigid
    if ~isempty(b)
      warning('RemoveRigid`s constraints are not typically wanted if |b|>0');
    end
    % the only danger is picking two points which end up mapped very close to
    % each other
    [~,f] = farthest_points(V,dim);
    for c = 1:dim
      rr.b{c} = f([1:dim-(c-1)])';
      rr.bc{c} = zeros(numel(rr.b{c}),1);
    end
    % Immediately remove rigid transformation from initial guess
    U = bsxfun(@minus,U,U(f(1),:));
    % We know that f(1) is at origin
    switch dim
    case 3
      % rotate about y-axis so that f(2) has (x=0)
      theta = atan2(U(f(2),1),U(f(2),3));
      R = axisangle2matrix([0 1 0],theta);
      U = U*R;
      % rotate about x-axis so that f(2) has (y=0)
      theta = atan2(U(f(2),3),U(f(2),2))+pi/2;
      R = axisangle2matrix([1 0 0],theta);
      U = U*R;
      % rotate about z-axis so that f(3) has (x=0)
      theta = atan2(U(f(3),2),U(f(3),1))+pi/2;
      R = axisangle2matrix([0 0 1],theta);
      U = U*R;
    case 2
      % rotate so that f(2) is on y-axis (x=0)
      theta = atan2(U(f(2),2),U(f(2),1))+pi/2;
      R = [cos(theta) -sin(theta);sin(theta) cos(theta)];
      U = U*R;
    otherwise
      error('Unsupported dimension');
    end
  end

  all = [interior(:);b(:)]';

  % cholesky factorization
  %cholL = chol(-L(interior,interior),'lower');
  % Why lu and not cholesky?
  %[luL,luU,luP,luQ,luR] = lu(-L(interior,interior));

  %R = repmat(eye(dim,dim),[1 1 n]);

  if ~isfield(data,'ae') || isempty(data.ae)
    data.ae = avgedge(V,F);
  end

  % build covariance scatter matrix used to build covariance matrices we'll
  % later fit rotations to
  if ~isfield(data,'CSM') || isempty(data.CSM)
    %assert(size(ref_V,2) == dim);
    data.CSM = ...
      covariance_scatter_matrix(ref_V,ref_F,'Energy',energy);
    if ~isempty(ref_map)
      data.CSM = data.CSM * repdiag(ref_map',dim);
    end

    % if there are groups then condense scatter matrix to only build
    % covariance matrices for each group
    if ~isempty(G)
      G_sum = group_sum_matrix(data.G,k);
      %CSM = [G_sum sparse(k,n); sparse(k,n) G_sum] * CSM;
      data.CSM = repdiag(G_sum,dim) * data.CSM;
    end
  end

  % precompute rhs premultiplier
  if ~isfield(data,'K') || isempty(data.K)
    [~,data.K] = arap_rhs(ref_V,ref_F,[],'Energy',energy,'Weight',weight);
    if ~isempty(ref_map)
      data.K = repdiag(ref_map,dim) * data.K;
    end
  end

  % initialize rotations with identies (not necessary)
  R = repmat(eye(dim,dim),[1 1 size(data.CSM,1)/dim]);

  iteration = 1;
  U_prev = V;
  data.energy = inf;
  while true

    if iteration > max_iterations
      if debug
        fprintf('arap: Iter (%d) > max_iterations (%d)\n',iteration,max_iterations);
      end
      break;
    end

    if iteration > 1
      change = max(abs(U(:)-U_prev(:)));
      if debug
        fprintf('arap: iter: %d, change: %g, energy: %g\n', ...
          iteration,change/data.ae,data.energy);
      end
      if change/data.ae <tol
        if debug
          fprintf('arap: change (%g) < tol*ae (%g * %g)\n',change,tol,data.ae);
        end
        break;
      end
    end

    U_prev = U;

    % energy after last global step
    U(b,:) = bc;
    %E = arap_energy(V,F,U,R);
    %[1 E]

    % compute covariance matrix elements
    S = zeros(size(data.CSM,1),dim);
    S(:,1:dim) = data.CSM*repmat(U,dim,1);
    % dim by dim by n list of covariance matrices
    SS = permute(reshape(S,[size(data.CSM,1)/dim dim dim]),[2 3 1]);
    % fit rotations to each deformed vertex
    R = fit_rotations(SS,'SinglePrecision',false);

    % energy after last local step
    U(b,:) = bc;
    %E = arap_energy(V,F,U,R);
    %[2 E]

    % This is still the SLOW way of building the right hand side, the entire
    % step of building the right hand side should collapse into a
    % #handles*dim*dim+1 by #groups matrix times the rotations at for group

    % distribute group rotations to vertices in each group
    if ~isempty(data.G)
      R = R(:,:,data.G);
    end

    U(b,:) = bc;

    %B = arap_rhs(V,F,R);
    Rcol = reshape(permute(R,[3 1 2]),size(data.K,2),1);
    Bcol = data.K * Rcol;
    B = reshape(Bcol,[size(Bcol,1)/dim dim]);

    zQ = -0.5*dyn_alpha*data.L+DQ+alpha_tik*speye(size(data.L));
    zL = -dyn_alpha*B+Dl;
    % RemoveRigid requires to solve each indepently
    if remove_rigid
      assert(isempty(Aeq),'Linear equality constraints not supported')
      for c = 1:dim
        eff_b = [b rr.b{c}];
        eff_bc = [bc(:,c);rr.bc{c}];
        [U(:,c),data.rr.preF{c}] = min_quad_with_fixed( ...
          zQ, ...
          zL(:,c),eff_b,eff_bc,[],[],data.rr.preF{c});
      end
    else
      if isempty(Aeq)
        cellfun(@(v) assignin('base',v,evalin('caller',v)),{'zQ','zL'});
        [U,data.preF] = min_quad_with_fixed( ...
          zQ, ...
          zL,b,bc,Aeq,Beq,data.preF);
      else
        assert(dim == size(bc,2));
        assert(dim == size(B,2));
        % repeat diagonal to solve for all coordinates together
        mQ = repdiag(zQ,dim);
        mL = reshape(zL,[],1);
        mb = reshape(bsxfun(@plus,b(:),size(V,1)*(0:dim-1)),[],1)';
        mbc = bc(:);
        [U,data.preF] = min_quad_with_fixed(mQ,mL,mb,mbc,Aeq,Beq,data.preF);
        U = reshape(U,[],dim);
      end
    end
    energy_prev = data.energy;
    data.energy = trace(U'*(zQ)*U+U'*(zL))+trace(V'*(0.5*dyn_alpha*data.L*V));
    if data.energy > energy_prev
      if debug
        fprintf('arap: energy (%g) increasing (over %g) (iter %d)\n', ...
          data.energy,energy_prev,iteration);
      end
      break;
    end

    %U(interior,:) = -L(interior,interior) \ (B(interior,:) + L(interior,b)*bc);
    %U(interior,:) = -L(interior,all)*L(all,interior) \ (L(interior,all)*B(all,:) + L(interior,all)*L(all,b)*bc);
    %U(interior,:) = luQ*(luU\(luL\(luP*(luR\(B(interior,:)+L(interior,b)*bc)))));
    %U(interior,:)=cholL\((B(interior,:)+L(interior,b)*bc)'/cholL)';
    iteration = iteration + 1;
  end
  U = U(:,1:dim);
end


function opt=fmin_fvalfind_ppform(pp,x,y)

yf=ppval(pp,x);
opt=abs(yf-y)^2;

end

%%
% _*GIBBON footer text*_
%
% License: <https://github.com/gibbonCode/GIBBON/blob/master/LICENSE>
%
% GIBBON: The Geometry and Image-based Bioengineering add-On. A toolbox for
% image segmentation, image-based modeling, meshing, and finite element
% analysis.
%
% Copyright (C) 2006-2022 Kevin Mattheus Moerman and the GIBBON contributors
%
% This program is free software: you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% (at your option) any later version.
%
% This program is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with this program.  If not, see <http://www.gnu.org/licenses/>.


%%*******************************************************************
%%  Read in a problem in SDPA sparse format.
%%
%%  [blk,At,C,b] = read_sdpa(fname)
%%
%%  Input: fname = name of the file containing SDP data in
%%                 SDPA foramt.
%%  Important: the data is assumed to contain only
%%             semidefinite and linear blocks.
%%*****************************************************************
%% SDPT3: version 4.0
%% Copyright (c) 1997 by
%% Kim-Chuan Toh, Michael J. Todd, Reha H. Tutuncu
%% Last Modified: 16 Sep 2004
%%*****************************************************************

   function [blk,At,C,b] = read_sdpa(fname,deblksize)

   if (nargin<2); deblksize = 100; end
%%
%%  Open the file for input
%%
   compressed = 0;
   if exist(fname)
      fid = fopen(fname,'r');
   elseif exist([fname,'.Z']);
      compressed = 1;
      unix(['uncompress ',fname,'.Z']);
      fid = fopen(fname,'r');
   elseif exist([fname,'.gz']);
      compressed = 2;
      unix(['gunzip ',fname,'.gz']);
      fid = fopen(fname,'r');
   else
      fprintf('*** Problem not found, please specify the correct path or problem name. \n');
      blk = []; At = []; C = []; b = [];
      return;
   end
%%
%%  Clean up special characters and comments from the file
%%
   [datavec,count] = fscanf(fid,'%c');
   linefeeds = findstr(datavec,char(10));
   comment_chars = '*"=';
   cumidx = [];
   for i=1:length(comment_chars)
      idx = findstr(datavec,comment_chars(i));
      cumidx = [cumidx,idx];
   end
   for j=length(cumidx):-1:1
      if (cumidx(j)==1) | (strcmp(datavec(cumidx(j)-1),char(10)))
         datavec(cumidx(j):linefeeds(min(find(cumidx(j)<linefeeds))))='';
      else
         datavec(cumidx(j):linefeeds(min(find(cumidx(j)<linefeeds)))-1)='';
      end
   end
   special_chars = ',{}()';
   cumidx=[];
   for i=1:length(special_chars)
      idx = findstr(datavec,special_chars(i));
      cumidx = [cumidx,idx];
   end
   datavec(cumidx) = blanks(length(cumidx));
   clear linefeeds;
%%
%% Close the file
%%
   fclose('all');
   if compressed==1; unix(['compress ',fname]); end;
   if compressed==2; unix(['gzip ',fname]); end;
%%
%%  Next, read in basic problem size parameters.
%%
   datavec = sscanf(datavec,'%f');
   if size(datavec,1) < size(datavec,2); datavec = datavec'; end;
   m = datavec(1);
   numblk  = datavec(2);
   blksize = datavec(2+[1:numblk]);
   if size(blksize,1) > size(blksize,2); blksize = blksize'; end
%%
%% Get input  b.
%%
   idxstrt = 2+numblk;
   b = datavec(idxstrt+[1:m]);
   idxstrt = idxstrt+m;
   b = -b;
%%
%% Construct blk
%%
   spblkidxtmp = find( (blksize>1) & (blksize < deblksize) );
   spblkidxtmp = sort(spblkidxtmp);
   deblkidx = find( (blksize<=1) | (blksize >= deblksize) );
   denumblk = length(deblkidx);
   linblkidx = zeros(1,denumblk);
   for p = 1:denumblk
      n = blksize(deblkidx(p));
      if (n > 1);
         blk{p,1} = 's'; blk{p,2} = n;
         n2 = n*(n+1)/2;
         At{p,1} = sparse(n2,m);
         C{p,1} = sparse(n,n);
      else
         linblkidx(p) = p;
         blk{p,1} = 'l'; blk{p,2} = abs(n);
         At{p,1} = sparse(abs(n),m);
         C{p,1} = sparse(abs(n),1);
      end
   end
   if ~isempty(spblkidxtmp)
      maxnumblk = 200;
      spnumblk = ceil(length(spblkidxtmp)/maxnumblk);
      for q = 1:spnumblk
         if (q < spnumblk)
            spblkidxall{q} = spblkidxtmp([(q-1)*maxnumblk+1: q*maxnumblk]);
         else
            spblkidxall{q} = spblkidxtmp([(q-1)*maxnumblk+1: length(spblkidxtmp)]);
         end
         tmp = blksize(spblkidxall{q});
         blk{denumblk+q,1} = 's';
         blk{denumblk+q,2} = tmp;
         n2 = sum(tmp.*(tmp+1))/2;
         At{denumblk+q,1} = sparse(n2,m);
         C{denumblk+q,1} = sparse(sum(tmp),sum(tmp));
      end
   else
      spnumblk = 0;
   end
   linblkidx(denumblk+[1:spnumblk]) = zeros(1,spnumblk);
%%
%% Construct single blocks of A,C
%%
   len = length(datavec);
   Y = reshape(datavec(idxstrt+1:len),5,(len-idxstrt)/5)';
   clear datavec;
   Y = sortrows(Y,[1 2]);
   matidx = [0; find(diff(Y(:,1)) ~= 0); size(Y,1)];
%%
   for k = 1:length(matidx)-1
      idx = [matidx(k)+1 : matidx(k+1)];
      Ytmp  = Y(idx,1:5);
      matno = Ytmp(1,1);
      Ytmp2 = Ytmp(:,2);
      for p = 1:denumblk
         n  = blksize(deblkidx(p));
         idx = find(Ytmp2 == deblkidx(p));
         ii = Ytmp(idx,3); jj = Ytmp(idx,4); vv =Ytmp(idx,5);
         len = length(idx);
         if (n > 1)
            idxtmp = find(ii > jj);
            if ~isempty(idxtmp);
               tmp = jj(idxtmp);
               jj(idxtmp) = ii(idxtmp); ii(idxtmp) = tmp;
            end
            tmp = -sparse(ii,jj,vv,n,n);
            tmp = tmp + triu(tmp,1)';
         else
            tmp = -sparse(ii,ones(len,1),vv,abs(n),1);
         end
         if (matno == 0)
            C{p,1} = tmp;
         else
            if (n > 1)
               At{p,1}(:,matno) = svec(blk(p,:),tmp,1);
            else
               At{p,1}(:,matno) = tmp;
            end
         end
      end
   end
%%
%% Construct big sparse block of A,C
%%
if (spnumblk > 0)
   Y1 = Y(:,1);
   diffY1 = find(diff([-1; Y1; inf]));
   for kk = 1:length(diffY1)-1
      idx = [diffY1(kk) : diffY1(kk+1)-1];
      matno = Y1(diffY1(kk));
      Ytmp  = Y(idx,1:5);
      Ytmp2 = Ytmp(:,2);
      maxYtmp2  = Ytmp2(length(Ytmp2));
      minYtmp2  = Ytmp2(1);
      diffYtmp2 = Ytmp2(find(diff([-1; Ytmp2])));
      for q = 1:spnumblk
         spblkidx    = spblkidxall{q};
         maxspblkidx = spblkidx(length(spblkidx));
         minspblkidx = spblkidx(1);
         count = 0;
         if (minYtmp2 <= maxspblkidx) & (maxYtmp2 >= minspblkidx)
            tmpblksize = blksize(spblkidx);
            n = sum(tmpblksize);
            cumspblksize = [0 cumsum(tmpblksize)];
            n2 = sum(tmpblksize.*(tmpblksize+1))/2;
            idx = zeros(n2,1); offset = zeros(n2,1);
            for t = [1:length(diffYtmp2)]
  	       p = find(spblkidx == diffYtmp2(t));
               if ~isempty(p)
	          idxtmp = find(Ytmp2 == spblkidx(p));
                  len = length(idxtmp);
     	          idx(count+[1:len]) = idxtmp;
                  offset(count+[1:len]) = cumspblksize(p);
                  count = count + len;
               end
            end
            idx = idx(1:count); offset = offset(1:count);
            ii = Ytmp(idx,3)+offset; jj = Ytmp(idx,4)+offset; vv = Ytmp(idx,5);
            idxtmp = find(ii > jj);
            if ~isempty(idxtmp);
               tmp = jj(idxtmp);
               jj(idxtmp) = ii(idxtmp); ii(idxtmp) = tmp;
            end
	    idxeq = find(ii==jj);
            tmp = spconvert([ii jj -vv; jj ii -vv; n n 0]) ...
                  + spconvert([ii(idxeq) jj(idxeq) vv(idxeq); n n 0]);
            if (matno == 0)
               C{denumblk+q,1} = tmp;
            else
               At{denumblk+q,1}(:,matno) = svec(blk(denumblk+q,:),tmp,1);
            end
         end
      end
   end
end
%%
%% put all linear blocks together as a single linear block
%%
   idx = find(linblkidx);
   if (length(idx) > 1)
      sdpidx = find(linblkidx==0);
      blktmp = 0; Atmp = []; Ctmp = [];
      for k = 1:length(idx)
          tmp = linblkidx(idx(k));
          blktmp = blktmp+blk{tmp,2};
          Atmp = [Atmp; At{tmp}];
          Ctmp = [Ctmp; C{tmp}];
      end
      At = At(sdpidx); C = C(sdpidx); blk = blk(sdpidx,:);
      len = length(sdpidx);
      blk(2:len+1,:) = blk;
      blk{1,1} = 'l'; blk{1,2} = blktmp;
      At(2:len+1,1) = At; C(2:len+1,1) = C;
      At{1,1} = Atmp; C{1,1} = Ctmp;
   end
%%******************************************************************


function [F,V,faceBoundaryMarker]=quadBox(boxDim,boxEl)

%%
F=[]; V=[]; faceBoundaryMarker=[];

[Xtb,Ytb]=meshgrid(0:boxEl(1),0:boxEl(2));

[Ylr,Zlr]=meshgrid(0:boxEl(2),0:boxEl(3));
[f,v]=surf2patch(0*ones(size(Ylr)),Ylr,Zlr); %Left
f=fliplr(f);
F=[F;f+size(V,1)]; V=[V;v;]; faceBoundaryMarker=[faceBoundaryMarker; 1*ones(size(f,1),1)];

[f,v]=surf2patch(boxEl(1)*ones(size(Ylr)),Ylr,Zlr); %Right
F=[F;f+size(V,1)]; V=[V;v;]; faceBoundaryMarker=[faceBoundaryMarker; 2*ones(size(f,1),1)];

[Yfb,Zfb]=meshgrid(0:boxEl(1),0:boxEl(3));
[f,v]=surf2patch(Yfb,0*ones(size(Yfb)),Zfb); %Front
F=[F;f+size(V,1)]; V=[V;v;]; faceBoundaryMarker=[faceBoundaryMarker; 3*ones(size(f,1),1)];

[f,v]=surf2patch(Yfb,boxEl(2)*ones(size(Yfb)),Zfb); %Back
f=fliplr(f);
F=[F;f+size(V,1)]; V=[V;v;]; faceBoundaryMarker=[faceBoundaryMarker; 4*ones(size(f,1),1)];

[f,v]=surf2patch(Xtb,Ytb,0*ones(size(Xtb))); %Bottom
f=fliplr(f);
F=[F;f+size(V,1)]; V=[V;v;]; faceBoundaryMarker=[faceBoundaryMarker; 5*ones(size(f,1),1)];

[f,v]=surf2patch(Xtb,Ytb,boxEl(3)*ones(size(Xtb))); %Top
F=[F;f+size(V,1)]; V=[V;v;]; faceBoundaryMarker=[faceBoundaryMarker; 6*ones(size(f,1),1)];

%%
% Remove double nodes by merging
[F,V]=mergeVertices(F,V);

%%
% Scale coordinates
maxV=max(V,[],1);
V=V./maxV(ones(size(V,1),1),:);
V=V.*boxDim(ones(size(V,1),1),:);
V=V-boxDim(ones(size(V,1),1),:)/2;

%%
% _*GIBBON footer text*_
%
% License: <https://github.com/gibbonCode/GIBBON/blob/master/LICENSE>
%
% GIBBON: The Geometry and Image-based Bioengineering add-On. A toolbox for
% image segmentation, image-based modeling, meshing, and finite element
% analysis.
%
% Copyright (C) 2006-2022 Kevin Mattheus Moerman and the GIBBON contributors
%
% This program is free software: you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% (at your option) any later version.
%
% This program is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with this program.  If not, see <http://www.gnu.org/licenses/>.


function Mh = hmxSingle(Mh)
%+========================================================================+
%|                                                                        |
%|         OPENHMX - LIBRARY FOR H-MATRIX COMPRESSION AND ALGEBRA         |
%|           openHmx is part of the GYPSILAB toolbox for Matlab           |
%|                                                                        |
%| COPYRIGHT : Matthieu Aussal (c) 2017-2018.                             |
%| PROPERTY  : Centre de Mathematiques Appliquees, Ecole polytechnique,   |
%| route de Saclay, 91128 Palaiseau, France. All rights reserved.         |
%| LICENCE   : This program is free software, distributed in the hope that|
%| it will be useful, but WITHOUT ANY WARRANTY. Natively, you can use,    |
%| redistribute and/or modify it under the terms of the GNU General Public|
%| License, as published by the Free Software Foundation (version 3 or    |
%| later,  http://www.gnu.org/licenses). For private use, dual licencing  |
%| is available, please contact us to activate a "pay for remove" option. |
%| CONTACT   : matthieu.aussal@polytechnique.edu                          |
%| WEBSITE   : www.cmap.polytechnique.fr/~aussal/gypsilab                 |
%|                                                                        |
%| Please acknowledge the gypsilab toolbox in programs or publications in |
%| which you use it.                                                      |
%|________________________________________________________________________|
%|   '&`   |                                                              |
%|    #    |   FILE       : hmxSingle.m                                   |
%|    #    |   VERSION    : 0.40                                          |
%|   _#_   |   AUTHOR(S)  : Matthieu Aussal                               |
%|  ( # )  |   CREATION   : 14.03.2017                                    |
%|  / 0 \  |   LAST MODIF : 14.03.2018                                    |
%| ( === ) |   SYNOPSIS   : Convert H-Matrix to single precision          |
%|  `---'  |                                                              |
%+========================================================================+

% Position
Mh.pos = {single(Mh.pos{1}),single(Mh.pos{2})};

%%% H-Matrix (recursion)
if (Mh.typ == 0)
    for i = 1:4
        Mh.chd{i} = hmxSingle(Mh.chd{i});
        Mh.row{i} = single(Mh.row{i});
        Mh.col{i} = single(Mh.col{i});
    end
    Mh = hmxFusion(Mh);

%%% Compressed leaf
elseif (Mh.typ == 1)
    Mh.dat{1} = single(Mh.dat{1});
    Mh.dat{2} = single(Mh.dat{2});

%%% Full leaf
elseif (Mh.typ == 2)
    Mh.dat = single(Mh.dat);

%%% Unknown type
else
    error('hmxSingle.m : unavailable case')
end
end


function [ output_args ] = plotRoom2( obblist, labellist, scheme )

% scheme == 0: show objects with different color
% scheme == 1: show areas with different color
linewidth = 2;

corlist = {[1,0,0,0.3],[0,1,0,0.3],[0,1,1,0.3],[0,0,1,0.3],[1,0,1,0.3]};

floor_ind = strmatch('floor',labellist,'exact');
ceiling_ind = strmatch('ceiling',labellist,'exact');
wall_ind = strmatch('wall',labellist,'exact');
com_ind = union(floor_ind,ceiling_ind);
com_ind = union(com_ind,wall_ind);
% idx = 1:length(p_data.labellist);
% idx = setdiff(idx,com_ind);
% labellist = labellist;
% obblist = obblist;

front = [0,0,1];
up = [0,1,0];
axes = [1,0,0];

for i = 1:length(labellist)
    p = obblist(:,i);

    center = p(1:3);
    dir_1 = p(4:6);
    dir_2 = p(7:9);
    lengths = p(10:12);

    dir_1 = dir_1/norm(dir_1);
    dir_2 = dir_2/norm(dir_2);
    dir_3 = cross(dir_1,dir_2);
    dir_3 = dir_3/norm(dir_3);
%     dir = lengths(1)*dir_1+lengths(2)*dir_2+lengths(3)*dir_3;

    d1 = 0.5*lengths(1)*dir_1;
    d2 = 0.5*lengths(2)*dir_2;
    d3 = 0.5*lengths(3)*dir_3;
%     d1 = d1*front(:);
%     d2 = d2*up(:);
%     d3 = d3*axes(:);
    cornerpoints(1,:) = center-d1-d2-d3;
    cornerpoints(2,:) = center+d1-d2-d3;
    cornerpoints(3,:) = center-d1-d2+d3;
    cornerpoints(4,:) = center+d1-d2+d3;
    %cornerpoints(:,1) = 1 - cornerpoints(:,1);

    idx = find(com_ind==i);
    if(length(idx)==0)
        if(scheme==1&&isfield(p_data,'area_id'))
            aid = p_data.area_id(i);
            if(aid==0)
                cor = [0,0,0,0.3];
            else
                cor = corlist{mod(aid-1,length(corlist))+1};
            end
        else
            cor = corlist{mod(i-1,length(corlist))+1};
        end
        plot([cornerpoints(1,1),cornerpoints(2,1)],[cornerpoints(1,3),cornerpoints(2,3)],'Color',cor(1:3),'linewidth',linewidth);
        hold on
        plot([cornerpoints(2,1),cornerpoints(4,1)],[cornerpoints(2,3),cornerpoints(4,3)],'Color',cor(1:3),'linewidth',linewidth);
        plot([cornerpoints(4,1),cornerpoints(3,1)],[cornerpoints(4,3),cornerpoints(3,3)],'Color',cor(1:3),'linewidth',linewidth);
        plot([cornerpoints(3,1),cornerpoints(1,1)],[cornerpoints(3,3),cornerpoints(1,3)],'Color',cor(1:3),'linewidth',linewidth);
        id = strfind(labellist{i},'_');
        label = labellist{i};
        if(length(id)>0)
            label(id) = ' ';
        end
        min_x = min(cornerpoints(:,1));
        max_z = max(cornerpoints(:,3));
        t = text(min_x+0.01,max_z-0.02,[label,num2str(i)]);
        t.BackgroundColor = cor;
        t.FontSize = 8;
    else
        plot([cornerpoints(1,1),cornerpoints(2,1)],[cornerpoints(1,3),cornerpoints(2,3)],'k','linewidth',linewidth);
        hold on
        plot([cornerpoints(2,1),cornerpoints(4,1)],[cornerpoints(2,3),cornerpoints(4,3)],'k','linewidth',linewidth);
        plot([cornerpoints(4,1),cornerpoints(3,1)],[cornerpoints(4,3),cornerpoints(3,3)],'k','linewidth',linewidth);
        plot([cornerpoints(3,1),cornerpoints(1,1)],[cornerpoints(3,3),cornerpoints(1,3)],'k','linewidth',linewidth);
    end
end

axis equal

end


function [V,F] = poly2mesh(filename,minimum_angle,maximum_area)
  % POLY2MESH Triangulate interoir of polygon read from .poly  file using
  % Triangle
  %
  %
  % [V,F] = poly2mesh(filename,minimum_angle,maximum_area)
  %
  % Inputs:
  %  filename  path to .poly file
  %  minimum_angle  minimum angle parameter for Triangle
  %  maximum_area  maximum area parameter for Triangle
  %
  % Outputs:
  %   V  #vertices by 2, list of vertex positions
  %   F  #faces by 3, list of face indices
  %
  % Copyright 2011, Alec Jacobson (jacobson@inf.ethz.ch)
  %
  % See also: png2objandtga, png2poly, writePOLY, poly2VEH, triangle
  %
  warning('THIS FILE IS DEPRECATED. CALL TRIANGLE.M DIRECTLY INSTEAD');
  basename = regexprep(filename,'\.poly$','');

  minimum_angle_args = '';
  if(exist('minimum_angle'))
    minimum_angle_args = ['q' num2str(minimum_angle)];
  end
  maximum_area_args = '';
  if(exist('maximum_area'))
    maximum_area_args = ['a' num2str(maximum_area)];
  end

  % use triangle to triangulate interior
  command_line_args = ['-p' minimum_angle_args maximum_area_args];
  [V,F] = execute_triangle(command_line_args,basename);
end


function z = ldivide( x, y )

%Disciplined convex programming information for LDIVIDE:
%   For DCP purposes, the LDIVIDE division operator X.\Y is equivalent to
%   (1./X).*Y. The left-hand term must be constant and non-zero; and if the
%   right-hand term is nonlinear, t constant must also be real.
%
%Disciplined geometric programming information for LDIVIDE:
%   Terms in a left divide must have opposite log-curvature, so the
%   following products are permitted:
%      {log-convex} .\ {log-concave}  {log-concave} .\ {log-convex}
%      {log-affine} .\ {log-affine}
%   Note that log-affine expressions are both log-convex and log-concave.
%
%For vectors, matrices, and arrays, these rules are verified indepdently
%for each element.

z = times( x, y, '.\' );

% 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 [values, components, full_data_objects, l2norms] = extract_gray_white_csf(obj,varargin)
% Extracts mean values (values) and top 5 component scores (components)
% from each of gray, white, and CSF masks.
%
% - Images must be in standard MNI space for this to apply.
% - Uses canonical masks in CANlab tools:'gray_matter_mask.img' 'canonical_white_matter.img' 'canonical_ventricles.img'
%
% This currently uses the images
%       'gray_matter_mask.img' 'canonical_white_matter.img' 'canonical_ventricles.img'
%       These images are based on the SPM8 a priori tissue probability
%       maps, but they have been cleaned up and made symmetrical and/or eroded
%       so that the white and CSF compartments are unlikely to contain very
%       much gray matter.  The gray compartment is currently more
%       inclusive. The potential value of this is that signal in the CSF/white
%       compartments may be removed from images prior to/during analysis
%
% :Usage:
% ::
%
%     [values, components] = extract_gray_white_csf(obj,options)
%
% :Inputs:
%
%   **obj:**
%        an image_vector (e.g., fmri_data) object
%
% :Options:
%
%   **'eval':**
%       A function handle to use for computing summary statistics of each
%       tissue class. Must accept exactly 1 argument and handle 'nan'
%       gracefully. e.g. '@(x1)(nanvar(x1))'
%
% :Outputs:
%
%   **values:**
%        mean gray matter, white, CSF. If 'eval' option is passed,
%        specified function will be used in place of mean.
%
%   **components:**
%        first 5 components from each tissue class, observation x 5
%
%   **full_data_objects:**
%        Masked data objects for {gray white CSF}
%
%   **l2norms:**
%        Length-adjusted L2 norms (divided by sqrt(nvox))
%
%   **masks:**
%        optional input for using different masks; it should provide gray,
%        white and ventricle images in order.
% ..
%    Tor Wager, July 21, 2015
% ..

% Programmers' notes:
% Jan 2017:  Issue with vector lengths if obj has removed images, fixed (Tor)
%
% Feb 2017: change to mask. Gray matter is now sparse, old gray_matter_mask.img thresholded at .5.
%
% Sept 2017: added option for custom function evaluation in place of mean.

fxn = @(x1)(nanmean(x1,1));
masks = {'gray_matter_mask_sparse.img' 'canonical_white_matter.img' 'canonical_ventricles.img'};

for i = 1:length(varargin)
    if ischar(varargin{i})
        switch varargin{i}
            case 'eval'
                fxn = varargin{i+1};
            case 'masks'
                masks = varargin{i+1};
        end
    end
end

numcomps = 5;

obj = remove_empty(obj);  % return only non-empty values
nimgs = size(obj.dat, 2); % - sum(obj.removed_images);

values = NaN * zeros(nimgs, length(masks));
components = cell(1, length(masks));
full_data_objects = [];

for i = 1:length(masks)

    maskname = which(masks{i});

    if isempty(maskname)
       fprintf('Image %s cannot be found on path.\n', masks{i});
       error('Exiting');
    else
        fprintf('Extracting from %s.\n', masks{i});
    end

    masked_obj = apply_mask(obj, maskname);

    % get all values, if requested
    if nargout > 2

        full_data_objects{i} = remove_empty(masked_obj);

    end

    % get means
    masked_obj.dat(masked_obj.dat == 0) = NaN;

    myvalues = fxn(masked_obj.dat);
    myvalues = myvalues(:);

    % may need to insert omitted - no, return in reduced space
%     if length(masked_obj.removed_images) > 1
%         myvalues = naninsert(masked_obj.removed_images, myvalues);
%     end

    values(:, i) = myvalues;

    % get components
    if nargout > 1

        % NaNs will mess this up - remove voxel-wise
        [wasnan, dataforpca] = nanremove(masked_obj.dat);
        if any(wasnan), fprintf('Removing %3.0f voxels with one or more NaNs\n', sum(wasnan)); end

        [~, components{i}] = pca(dataforpca', 'Economy', true, 'NumComponents', numcomps);

        % may need to insert omitted
%         if length(masked_obj.removed_images) > 1
%             components{i} = naninsert(masked_obj.removed_images, components{i});
%         end

    end

    if nargout > 3

        l2norms(:, i) = getnorms(masked_obj);

    end

end % end masks

end % function

function n = getnorms(masked_obj)

% Vector L2 norm / sqrt(length) of vector

% divide by this value to normalize image

normfun = @(x) sum(x .^ 2) .^ .5;

%masked_obj = remove_empty(masked_obj);

x = masked_obj.dat;

%nv = size(x, 1);

for i = 1:size(x, 2)

    % remove nans, 0s
    xx = x(:, i);
    xx(xx == 0) = [];
    xx(isnan(xx)) = [];

    % divide by sqrt(length) so number of elements will not change scaling

    n(i) = normfun(xx) ./ sqrt(length(xx));

end

end % function


function [c,ceq] = nl_terminalconstraints(t, x, mpcModel , varargin)

    c(1) = x(2)-mpcModel.battery.range(2);
    c(2) = mpcModel.battery.range(1)-x(2);

    ceq = [];
end


function[nodes, categories, heights] = parentsToTrees(parents)
% [nodes, categories, heights] = parentsToTrees(parents)
%
% Converts an [s x 2] dimensional cell of strings into a tree where the
% first column indicates nodes and the second column indicates their parent
% nodes.
%
% The output can be plotted using Matlab's treeplot function:
% treeplot(nodes');
% [xs, ys] = treelayout(nodes);
%
% Copyright by Holger Caesar, 2017

% Extract categories and make sure they are unique
categories = parents(:, 1);
assert(numel(categories) == numel(unique(categories)));

% Create pointers to parent nodes
categoryCount = size(categories, 1);
nodes = nan(categoryCount, 1);
heights = nan(categoryCount, 1);
nodes(1) = 0;
heights(1) = 0;
for i = 2 : categoryCount
    childNode = find(strcmp(parents(:, 1), categories{i}));
    if isempty(childNode)
        error('Error: No parent node found for %s!', categories{i});
    end
    parentNode = find(strcmp(categories, parents(childNode, 2)));
    assert(numel(parentNode) == 1, 'Error: Node %s has %d parent labels! Should be 1.', parents{childNode, 2}, numel(parentNode));
    nodes(i) = parentNode;
    heights(i) = heights(parentNode) + 1;
end


function [max,maxrest] = getMaxInARow(stimList,varargin)
	if nargin>1,dojitter = varargin{1},else dojitter = 0;,end

	max = 1; counter = 1;maxrest = 1;counterrest = 1;
        if dojitter, restnum = max(stimList);,else restnum = Inf;,end
	for i = 2:size(stimList,1)
		if stimList(i,1) == stimList(i-1,1)& ~stimList(i,1) == 0,
			counter = counter + 1;
			if counter > max, max = counter;,end
		else counter = 1;
                end

                if dojitter
                     if stimList(i,1) == restnum & stimList(i-1,1) == restnum
                        counterrest = counterrest+1;
                        if counterrest > maxrest,maxrest = counterrest;,end
                     else counterrest = 1;
		     end
                end

	end

return


function [modelPruned] = extractConditionSpecificModel(model,threshold)
% The function prunes a subnetwork based on a user-defined threshold. The subnetwork does not contain blocked reactions. Please note that Recon has blocked reactions which will always be removed. Thus, not all reactions are removed as a consequence of the data integration.
% Depends on fastFVA, fluxVariability analysis
%
% USAGE:
%
%    [modelPruned] = extractConditionSpecificModel(model, threshold)
%
% INPUTS:
%    model:           Global metabolic model (Recon) - constrained
%    threshold:        Fluxes below the threshold will be considered zero and respective reactions as blocked, e.g., 10e-6.
%
% OUTPUTS:
%    modelPruned:     submodel without blocked reactions
%
% .. Author: - Maike K. Aurich 13/02/15

[minFlux,maxFlux] = fluxVariability(model,0);
%[minFluxMODEL,maxFluxMODEL] = fastFVA(model,0);

Flux = [minFlux maxFlux];

for i = 1 : length(Flux);
    x = length (find (abs(Flux(i,:))<=threshold))==2;
    Blockedrxns(i,1) = x;
end

Blocked = model.rxns(Blockedrxns);

modelPruned = removeRxns(model,Blocked(:,1));

end


function DrawTextureHead(vertex, tri, color, keyPoints)
n = size(vertex, 2);

% draw
trisurf(tri', vertex(1, :), vertex(2, :), vertex(3, :), 1 : n, 'edgecolor', 'none');

if nargin == 4
    hold on
    plot3(keyPoints(1, :), keyPoints(2, :), keyPoints(3, :), 'g.', 'MarkerSize', 20)
end

% color
colormap(color');

shading interp
axis equal
%axis([1 250 1 250])
axis vis3d
title('fitted head');
xlabel('x');
ylabel('y');
zlabel('z');
view([0,90]);


%DAGNN.UNIFORMSCALINGGRIDGENERATOR  Generate an iso-tropic scaling + translation
%   grid for bilinear resampling.
%   This layer maps 1 x 1 x 3 x N transformation parameters corresponding to
%   the scale and translation y and x respectively, to 2 x Ho x Wo x N
%   sampling grids compatible with dagnn.BlilinearSampler.

% (c) 2016 Ankush Gupta
classdef UniformScalingGridGenerator < dagnn.Layer

 properties
     Ho = 0;
     Wo = 0;
 end

  properties (Transient)
    % the grid --> this is cached
    % has the size: [2 x HoWo]
    xxyy ;
  end

  methods

    function outputs = forward(obj, inputs, ~)
      % input is a 1x1x3xN TENSOR corresponding to:
      % [  s 0 ty ]
      % [  0 s tx ]
      %
      % OUTPUT is a 2xHoxWoxN grid

      % reshape the tfm params into matrices:
      T = inputs{1};
      % check shape:
      sz_T = size(T);
      assert(all(sz_T(1:3) == [1 1 3]), 'transforms have incorrect shape');
      nbatch = size(T,4);
      S = reshape(T(1,1,1,:), 1,1,nbatch); % x,y scaling
      t = reshape(T(1,1,2:3,:), 2,1,nbatch); % translation
      % generate the grid coordinates:
      % use gpu?:
      useGPU = isa(T, 'gpuArray');
      if isempty(obj.xxyy)
        obj.initGrid(useGPU);
      end
      % transform the grid:
      g = bsxfun(@times, obj.xxyy, S); % scale
      g = bsxfun(@plus, g, t); % translate
      g = reshape(g, 2,obj.Ho,obj.Wo,nbatch);
      outputs = {g};
    end

    function [derInputs, derParams] = backward(obj, inputs, ~, derOutputs)
      %fprintf('restricted-affineGridGenerator backward\n');
      dY = derOutputs{1};
      useGPU = isa(dY, 'gpuArray');
      nbatch = size(dY,4);

      % create the gradient buffer:
      dA = zeros([1,1,3,nbatch], 'single');
      if useGPU, dA = gpuArray(dA); end

      dY  = reshape(dY, 2,obj.Ho*obj.Wo,nbatch);
      % gradient wrt the linear part:
      dA(1,1,1,:) = reshape(obj.xxyy,1,[]) * reshape(dY, [],nbatch);
      % gradient wrt translation (or bias):
      dA(1,1,2:3,:) = reshape(sum(dY,2),1,1,2,[]);

      derInputs = {dA};
      derParams = {};
    end

    function outputSizes = getOutputSizes(obj, inputSizes)
      nBatch = inputSizes{1}(4);
      outputSizes = {[2, obj.Ho, obj.Wo, nBatch]};
    end

    function obj = UniformScalingGridGenerator(varargin)
      obj.load(varargin);
      % get the output sizes:
      obj.Ho = obj.Ho;
      obj.Wo = obj.Wo;
      obj.xxyy = [];
    end

    function obj = reset(obj)
      reset@dagnn.Layer(obj) ;
      obj.xxyy = [] ;
    end

    function initGrid(obj, useGPU)
      % initialize the grid:
      % this is a constant
      xi = linspace(-1, 1, obj.Ho);
      yi = linspace(-1, 1, obj.Wo);
      [yy,xx] = meshgrid(yi,xi);
      xxyy = [xx(:), yy(:)]' ; % 2xM
      if useGPU
        xxyy = gpuArray(xxyy);
      end
      obj.xxyy = xxyy ;
    end

  end
end


classdef ShapeCircle < ShapeGeneral
    %ShapeCircle represents a circular geographical selection of events
    %
    % see also ShapeGeneral, ShapePolygon

    properties (SetObservable = true, AbortSet=true)
        Radius (1,1) double = 5 % active radius in units defined by the RefEllipsoid
    end

    methods
        function obj=ShapeCircle(varargin)
            % SHAPECIRCLE create a circular shape
            %
            % ShapeCircle() :
            % ShapeCircle('dlg') create via a dialg box
            %
            % CIRCLE: select using circle with a defined radius. define with 2 clicks or mouseover and press "R"

            % UNASSIGNED: clear shape

            obj@ShapeGeneral();
            if ~isempty(varargin)
               dosomething
            end

            report_this_filefun();

            %axes(findobj(gcf,'Tag','mainmap_ax')); % should be the map, with lon/lat
            obj.Type='circle';
            try
                ra=ShapeGeneral.ShapeStash.Radius;
            catch
                ra=obj.Radius;
            end
            obj.AllowVertexEditing = false;
            addlistener(obj, 'Radius', 'PostSet', @obj.notifyShapeChange);
            if numel(varargin)==0
                do_nothing;
            elseif strcmpi(varargin{1},'dlg')
                stashedshape = ShapeGeneral.ShapeStash;
                sdlg.prompt = ['Choose Radius [',obj.RefEllipsoid.LengthUnit,']:'];
                sdlg.value = ra;
                sdlg(2).prompt = 'Center X :'; sdlg(2).value=stashedshape.X0;
                sdlg(3).prompt = 'Center Y :'; sdlg(3).value=stashedshape.Y0;
                [~,cancelled,obj.Radius,obj.Points(1),obj.Points(2)]=smart_inputdlg('Define Circle',sdlg);
                if cancelled
                    beep
                    disp('Circle creation cancelled by user')
                    return
                end
            else
                oo=ShapeCircle.selectUsingMouse(gca, obj.RefEllipsoid);
                if ~isempty(oo)
                    obj=oo;
                else
                    return
                end
            end
        end

        function val=Outline(obj, col)
            if iscartesian(obj.RefEllipsoid)
                pts = exp(1i*pi*linspace(0,2*pi,3600)') .* obj.Radius;
                x = real(pts)+ obj.X0;
                y = imag(pts) + obj.Y0;
                val = [x,y];
            else
                [lat,lon]=reckon(obj.Y0,obj.X0,obj.Radius,(0:.1:360)',obj.RefEllipsoid);
                val=[lon, lat];
            end
            if exist('col','var')
                val=val(:,col);
            end
        end

        function moveTo(obj, x, y)
            if isnan(obj.Points)
                obj.Points=[0 0];
            end
            moveTo@ShapeGeneral(obj,x,y)
        end

        function s=toStruct(obj)
            s=toStruct@ShapeGeneral(obj);
            s.RadiusKm = obj.Radius;
        end

        function s = toStr(obj)
            cardinalDirs='SNWE';
            isN=obj.Y0>=0; NS=cardinalDirs(isN+1);

            isE=obj.X0>=0; EW=cardinalDirs(isE+3);
            s = sprintf('Circle with R:%s %s, centered at ( %s %s, %s %s)',...
                num2str(obj.Radius),...
                obj.RefEllipsoid.LengthUnit,...
                num2str(abs(obj.Y0)), NS,...
                num2str(abs(obj.X0)), EW);
        end

        function summary(obj)
            helpdlg(obj.toStr,'Circle');
        end

        function add_shape_specific_context(obj,c)
            uimenu(c,'label','Choose Radius','MenuSelectedFcn',@chooseRadius)
            uimenu(c,'label','Snap To N Events','MenuSelectedFcn',@snapToEvents)

            function snapToEvents(~,~)
                ZG=ZmapGlobal.Data;
                nc=inputdlg('Number of events to enclose','Edit Circle',1,{num2str(ZG.ni)});
                nc=round(str2double(nc{1}));
                if ~isempty(nc) && ~isnan(nc)
                    ZG.ni=nc;
                    zmw=get(ancestor(c,'figure'),'UserData');
                    if isa(zmw,'ZmapWindow')
                        ca=zmw.rawcatalog;
                    else
                        ca=ZG.primeCatalog;
                    end

                    [~,obj.Radius]=ca.selectClosestEvents(obj.Y0, obj.X0, [],nc,'DistanceOnly');
                    obj.Radius=obj.Radius;%+0.005;
                end
            end

            function chooseRadius(~,~)
                radiusInputText = ['Choose Radius [',obj.RefEllipsoid.LengthUnit,']'];
                nc=inputdlg(radiusInputText,'Edit Circle',1,{num2str(obj.Radius)});
                nc=str2double(nc{1});
                if ~isempty(nc) && ~isnan(nc)
                    obj.Radius=nc;
                end

            end

        end

        function [mask]=isinterior(obj,otherX, otherY, include_boundary)
            % isinterior true if value is within this circle's radius of center. Radius inclusive.
            %
            % overridden because using polygon approximation is too inaccurate for circles
            %
            % [mask]=obj.isinterior(otherX, otherY)

            if ~exist('include_boundary','var')
                include_boundary = true;
            end
            if isempty(obj.Points)||isnan(obj.Points(1))
                mask = ones(size(otherX));
            else
                otherX(ismissing(otherY))= missing;
                otherY(ismissing(otherX))= missing;
                % return a vector of size otherX that is true where item is inside polygon

                if iscartesian(obj.RefEllipsoid)
                    dists = sqrt((otherY-obj.Y0).^2 + (otherX-obj.X0).^2);
                else
                    dists = distance(obj.Y0, obj.X0, otherY, otherX, obj.RefEllipsoid);
                end

                if ~include_boundary
                    mask = dists < obj.Radius;
                else
                    mask = dists <= obj.Radius;
                end
            end
        end

        function finishedMoving(obj, movedObject, deltas)
            centerX = mean(bounds2(movedObject.XData));
            centerY = mean(bounds2(movedObject.YData));

            obj.Radius=obj.Radius.* abs(deltas(3)); % NO NEGATIVE RADII
            obj.Points=[centerX,centerY];
        end

        function save(obj, filelocation, delimiter)
            persistent savepath
            if ~exist('filelocation','var') || isempty(filelocation)
                if isempty(savepath)
                    savepath = ZmapGlobal.Data.Directories.data;
                end
                [filename,pathname,filteridx]=uiputfile(...
                    {'*.mat','MAT-files (*.mat)';...
                    '*.csv;*.txt;*.dat','ASCII files (*.csv, *.txt, *.dat)'},...
                    'Save Circle',...
                    fullfile(savepath,'zmap_shape.mat'));
                if filteridx==0
                    msg.dbdisp('user cancelled shape save');
                    return
                end
                filelocation=fullfile(pathname,filename);
            end
            [savepath,~,ext] = fileparts(filelocation);
            if ext==".mat"
                zmap_shape = obj; %#ok<NASGU>
                save(filelocation,'zmap_shape');
            else
                if ~exist('delimiter','var'), delimiter = ',';end
                radiusName = ['Radius[',shortenLengthUnit(obj.RefEllipsoid.LengthUnit),']'];
                tb=table(obj.X0, obj.Y0,obj.Radius,'VariableNames',{'Latitude','Longitude',radiusName});
                writetable(tb,filelocation,'Delimiter',delimiter);
            end

        end
    end

    methods(Static)

        function obj=selectUsingMouse(ax, ref_ellipsoid)

            [ss,ok] = selectSegmentUsingMouse(ax,'r', @circ_update);
            delete(findobj(gca,'Tag','tmp_circle_outline'));
            if ~ok
                obj=[];
                return
            end
            obj=ShapeCircle();
            obj.Points=ss.xy1;
            obj.Radius=ss.dist;

            function circ_update(stxy, ~, d)
                h=findobj(gca,'Tag','tmp_circle_outline');
                if isempty(h)
                    h=line(nan,nan,'Color','r','DisplayName','Rough Outline','LineWidth',2,'Tag','tmp_circle_outline');
                end
                if iscartesian(ref_ellipsoid)
                    pts = exp(1i*pi*linspace(0,2*pi,120)') .* d;
                    h.XData = real(pts)+ stxy(1);
                    h.YData = imag(pts) + stxy(2);
                else
                    [lat,lon]=reckon(stxy(2),stxy(1),d,(0:3:360)',ref_ellipsoid);
                    h.XData=lon;
                    h.YData=lat;
                end
            end
        end
    end

end


% Read data from files generated by HList
% Author: Xiao Xiong
% Date Created: 25 Jan 2005
% Date Lasted modefied: 25 Jan 2005

function x = readHList(file_name, vector_size);

    % read in the log Mel vectors first
    FILE = fopen( file_name );
    j=0;
    fsearch(':', FILE);     % skip the first ':'
    while fsearch(':', FILE) == 0   % read N_spectral number immediately after ':'
        j = j+1;
        temp = textscan(FILE, '%n', vector_size);
        x(:,j) = temp{1};        % store the vecors as column vector of logmel
    end
    num_of_vector_in_current_file = j;