function xyf_display ( prefix )

%*****************************************************************************80
%
%% XYF_DISPLAY displays points and faces in 2D.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    08 January 2009
%
%  Author:
%
%    John Burkardt
%
%  Usage:
%
%    xyf_display ( 'prefix' )
%
%    where
%
%    'prefix.xy' contains the point coordinates
%    'prefix.xyf' contains lists of point indices forming faces.
%
%  Parameters:
%
%    Input, string PREFIX, the prefix of the two input files.
%
  fprintf ( 1, '\n' );
  timestamp ( );

  fprintf ( 1, '\n' );
  fprintf ( 1, 'XYF_DISPLAY\n' );
  fprintf ( 1, '  MATLAB version\n' );
  fprintf ( 1, '\n' );
  fprintf ( 1, '  Read an XY file containing coordinates of points in 2D,\n' );
  fprintf ( 1, '  and an XYF file listing point indices forming faces.\n' );
  fprintf ( 1, '  Display the points and faces in a MATLAB graphics window.\n' );
%
%  First argument is the file prefix.
%
  if ( nargin < 1 )
    fprintf ( 1, '\n' );
    fprintf ( 1, 'XYF_DISPLAY:\n' );
    prefix = input ( '  Enter the common prefix of the XY and XYF files (in ''quotes''!).\n' );
  end
%
%  Read the XY data.
%
  xy_filename = strcat ( prefix, '.xy' );

  point_num = xy_header_read ( xy_filename );

  xy_header_print ( point_num );

  xy = xy_data_read ( xy_filename, point_num );
%
%  Read the XYF data.
%
  xyf_filename = strcat ( prefix, '.xyf' );

  [ face_num, face_data_num ] = xyf_header_read ( xyf_filename );

  xyf_header_print ( point_num, face_num, face_data_num );

  [ face_pointer, face_data ] = xyf_data_read ( xyf_filename, ...
    face_num, face_data_num );

  if ( 0 )

    xyf_data_print ( point_num, face_num, face_data_num, face_pointer, ...
      face_data );

  end
%
%  If necessary, move from 0-based indexing.
%
  if ( min ( face_data(1:face_data_num) ) == 0 )
    fprintf ( 1, '\n' );
    fprintf ( 1, '  Moving FACE_DATA from 0 base to 1 base.\n' );
    face_data(1:face_data_num) = face_data(1:face_data_num) + 1;
  end
%
%  Get the scale.
%
  xy_min(1) = min ( xy(1,:) );
  xy_max(1) = max ( xy(1,:) );

  xy_min(2) = min ( xy(2,:) );
  xy_max(2) = max ( xy(2,:) );

  xy_range(1:2) = xy_max(1:2) - xy_min(1:2);

  margin = 0.025 * max ( xy_range(1), xy_range(2) );

  x_min = xy_min(1) - margin;
  x_max = xy_max(1) + margin;
  y_min = xy_min(2) - margin;
  y_max = xy_max(2) + margin;
%
%  Draw the picture.
%
  face_color = 'g';

  for face = 1 : face_num
    index = face_pointer(face) : face_pointer(face+1) - 1;
    p = face_data(index);
    patch ( xy(1,p), xy(2,p), face_color );
  end

  line_color = 'r';

  for face = 1 : face_num
    index = [face_pointer(face) : face_pointer(face+1) - 1, face_pointer(face)];
    p = face_data(index);
    line ( 'XData', xy(1,p), 'YData', xy(2,p), 'Color', line_color );
  end
%
%  May I take a moment hear to complain that MATLAB's graphic capabilities
%  are awkward and poorly documented?  You can draw a line, if you work hard
%  at it.  But can you draw a point?  Nope!
%
  hold on

  point_size = 40;
  point_color = 'b';
  scatter ( xy(1,:), xy(2,:), point_size, point_color, 'filled' );

  xlabel ( '--X axis--' )
  ylabel ( '--Y axis--' )
%
%  The TITLE function will interpret underscores in the title.
%  We need to unescape such escape sequences!
%
  title_string = s_escape_tex ( prefix );
  title ( title_string )

  axis ( [ x_min, x_max, y_min, y_max ] );
  axis equal

  hold off
%
%  Terminate.
%
  fprintf ( 1, '\n' );
  fprintf ( 1, 'XYF_DISPLAY\n' );
  fprintf ( 1, '  Normal end of execution.\n' );

  fprintf ( 1, '\n' );
  timestamp ( );

  return
end
function s2 = s_escape_tex ( s1 )

%*****************************************************************************80
%
%% S_ESCAPE_TEX de-escapes TeX escape sequences.
%
%  Discussion:
%
%    In particular, every occurrence of the characters '\', '_',
%    '^', '{' and '}' will be replaced by '\\', '\_', '\^',
%    '\{' and '\}'.  A TeX interpreter, on seeing these character
%    strings, is then likely to return the original characters.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    19 January 2007
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, string S1, the string to be de-escaped.
%
%    Output, string S2, a copy of the string, modified to avoid TeX escapes.
%
  s1_length = length ( s1 );

  s1_pos = 0;
  s2_pos = 0;
  s2 = [];

  while ( s1_pos < s1_length )

    s1_pos = s1_pos + 1;

    if ( s1(s1_pos) == '\' || ...
         s1(s1_pos) == '_' || ...
         s1(s1_pos) == '^' || ...
         s1(s1_pos) == '{' || ...
         s1(s1_pos) == '}'  )
      s2_pos = s2_pos + 1;
      s2 = strcat ( s2, '\' );
    end

    s2_pos = s2_pos + 1;
    s2 = strcat ( s2, s1(s1_pos) );

  end

  return
end
function len = s_len_trim ( s )

%*****************************************************************************80
%
%% S_LEN_TRIM returns the length of a character string to the last nonblank.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    14 June 2003
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, string S, the string to be measured.
%
%    Output, integer LEN, the length of the string up to the last nonblank.
%
  len = length ( s );

  while ( 0 < len )
    if ( s(len) ~= ' ' )
      return
    end
    len = len - 1;
  end

  return
end
function word_num = s_word_count ( s )

%*****************************************************************************80
%
%% S_WORD_COUNT counts the number of "words" in a string.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    30 January 2006
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, string S, the string to be examined.
%
%    Output, integer WORD_NUM, the number of "words" in the string.
%    Words are presumed to be separated by one or more blanks.
%
  FALSE = 0;
  TRUE = 1;

  word_num = 0;
  s_length = length ( s );

  if ( s_length <= 0 )
    return;
  end

  blank = TRUE;

  for i = 1 : s_length

    if ( s(i) == ' ' )
      blank = TRUE;
    elseif ( blank == TRUE )
      word_num = word_num + 1;
      blank = FALSE;
    end

  end

  return
end
function timestamp ( )

%*****************************************************************************80
%
%% TIMESTAMP prints the current YMDHMS date as a timestamp.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    14 February 2003
%
%  Author:
%
%    John Burkardt
%
  t = now;
  c = datevec ( t );
  s = datestr ( c, 0 );
  fprintf ( 1, '%s\n', s );

  return
end
function xy = xy_data_read ( input_filename, point_num )

%*****************************************************************************80
%
%% XY_DATA_READ reads data from an XY file.
%
%  Discussion:
%
%    The number of points in the file can be determined by calling
%    XY_HEADER_READ first.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    06 February 2010
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, string INPUT_FILENAME, the name of the input file.
%
%    Input, integer POINT_NUM, the number of points.
%
%    Output, real XY(2,POINT_NUM), the point coordinates.
%
  input_unit = fopen ( input_filename, 'r' );

  if ( input_unit < 0 )
    fprintf ( 1, '\n' );
    fprintf ( 1, 'XY_DATA_READ - Error!\n' );
    fprintf ( 1, '  Could not open the input file "%s".\n', input_filename );
    error ( 'XY_DATA_READ - Error!' );
  end

  i = 0;
  xy = zeros ( 2, point_num );
%
%  Use FGETL, not FGETS, so we don't pick up the NEWLINE character.
%
  while ( i < point_num )

    text = fgetl ( input_unit );

    if ( text(1) == '#' )
      continue;
    end

    if ( s_len_trim ( text ) == 0 )
      continue;
    end

    temp = sscanf ( text, '%f %f' );

    i = i + 1;
    xy(1,i) = temp(1);
    xy(2,i) = temp(2);

  end
%
%  Close the file.
%
  fclose ( input_unit );

  return
end
function xy_header_print ( point_num )

%*****************************************************************************80
%
%% XY_HEADER_PRINT prints the header of an XY file.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    04 January 2009
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer POINT_NUM, the number of points.
%
  fprintf ( 1, '\n');
  fprintf ( 1, '  Number of points = %d\n', point_num );

  return
end
function point_num = xy_header_read ( input_filename )

%*****************************************************************************80
%
%% XY_HEADER_READ determines the number of pairs of data in an XY file.
%
%  Discussion:
%
%    This routine assumes that the file contains exactly three kinds of
%    records:
%
%    COMMENTS which begin with a '#' character in column 1;
%    BLANKS which contain nothing but 'whitespace';
%    XY coordinates, which each contain one pair of real values.
%
%    The routine ignores comments and blank lines and returns
%    the number of lines containing XY coordinates.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    06 February 2010
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, string INPUT_FILENAME, the name of the input file.
%
%    Output, integer POINT_NUM, the number of points in the file.
%
  point_num = 0;
%
%  Open the file.
%
  input_unit = fopen ( input_filename, 'r' );

  if ( input_unit < 0 )
    fprintf ( 1, '\n' );
    fprintf ( 1, 'XY_HEADER_READ - Error!\n' );
    fprintf ( 1, '  Could not open the input file "%s".\n', input_filename );
    error ( 'XY_HEADER_READ - Error!' );
  end
%
%  Read every line in the file.
%
%  Use FGETL, not FGETS, so we don't pick up the NEWLINE character.
%
  while ( 1 )

    text = fgetl ( input_unit );

    if ( text == -1 )
      break;
    end

    if ( text(1) == '#' )
      continue;
    end

    if ( s_len_trim ( text ) == 0 )
      continue;
    end

    sscanf ( text, '%f %f' );

    point_num = point_num + 1;

  end
%
%  Close the file.
%
  fclose ( input_unit );

  return
end
function [ face_pointer, face_data ] = xyf_data_read ( input_filename, ...
  face_num, face_data_num )

%*****************************************************************************80
%
%% XYF_DATA_READ reads the data in an XYF file.
%
%  Discussion:
%
%    This routine assumes that the file contains exactly three kinds of
%    records:
%
%    COMMENTS which begin with a '#' character in column 1;
%    BLANKS which contain nothing but 'whitespace';
%    FACE ITEMS, which are indices of points on a face, or -1 to terminate a face.
%
%    The routine ignores comments and blanks and returns
%    the number of face items.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    06 February 2010
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, string INPUT_FILENAME, the name of the input file.
%
%    Input, integer FACE_NUM, the number of faces.
%
%    Input, integer FACE_DATA_NUM, the number of face items.
%
%    Output, integer FACE_POINTER(FACE_NUM+1), pointers to the
%    first face item for each face.
%
%    Output, integer FACE_DATA(FACE_DATA_NUM), the face items.
%
  input_unit = fopen ( input_filename, 'r' );

  if ( input_unit < 0 )
    fprintf ( 1, '\n' );
    fprintf ( 1, 'XYF_DATA_READ - Error!\n' );
    fprintf ( 1, '  Could not open the input file "%s".\n', input_filename );
    error ( 'XYF_DATA_READ - Error!' );
  end

  num = 0;
  face_pointer = zeros ( face_num + 1, 1 );
  face_data = zeros ( face_data_num, 1 );
  face_pointer(1) = 1;
%
%  Use FGETL, not FGETS, so we don't pick up the NEWLINE character.
%
  while ( num < face_num )

    text = fgetl ( input_unit );

    if ( text(1) == '#' || s_len_trim ( text ) == 0 )
      continue
    end

    num = num + 1;

    n = s_word_count ( text );
    face_pointer(num+1) = face_pointer(num) + n;

    ilo = face_pointer(num);
    ihi = face_pointer(num+1) - 1;

    [ temp, count ] = sscanf ( text, '%d' );

    face_data(ilo:ihi) = temp(1:count);

  end
%
%  Close the file.
%
  fclose ( input_unit );

  return
end
function xyf_header_print ( point_num, face_num, face_data_num )

%*****************************************************************************80
%
%% XYF_HEADER_PRINT prints the header of an XYF file.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    04 January 2009
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer POINT_NUM, the number of points.
%
%    Input, integer FACE_NUM, the number of faces.
%
%    Input, integer FACE_DATA_NUM, the number of face items.
%
  fprintf ( 1, '\n');
  fprintf ( 1, '  Number of points =     %d\n', point_num );
  fprintf ( 1, '  Number of faces =      %d\n', face_num );
  fprintf ( 1, '  Number of face items = %d\n', face_data_num );

  return
end
function [ face_num, face_data_num ] = xyf_header_read ( input_filename )

%*****************************************************************************80
%
%% XYF_HEADER_READ determines the number of face items in an XYF file.
%
%  Discussion:
%
%    This routine assumes that the file contains exactly three kinds of
%    records:
%
%    COMMENTS which begin with a '#' character in column 1;
%    BLANKS which contain nothing but 'whitespace';
%    FACE ITEMS, which are indices of points on a face, or -1 to terminate a face.
%
%    The routine ignores comments and blanks and returns
%    the number of face items.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    06 February 2010
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, string INPUT_FILENAME, the name of the input file.
%
%    Output, integer FACE_NUM, the number of faces.
%
%    Output, integer FACE_DATA_NUM, the number of face items.
%
  face_data_num = 0;
  face_num = 0;
%
%  Open the file.
%
  input_unit = fopen ( input_filename, 'r' );

  if ( input_unit < 0 )
    fprintf ( 1, '\n' );
    fprintf ( 1, 'XYF_HEADER_READ - Error!\n' );
    fprintf ( 1, '  Could not open the input file "%s".\n', input_filename );
    error ( 'XYF_HEADER_READ - Error!' );
  end
%
%  Use FGETL, not FGETS, so we don't pick up the NEWLINE character.
%
  while ( 1 )

    text = fgetl ( input_unit );

    if ( text == -1 )
      break;
    end

    if ( text(1) == '#' || s_len_trim ( text ) == 0 )
      continue
    end

    n = s_word_count ( text );

    face_data_num = face_data_num + n;

    face_num = face_num + 1;

  end
%
%  Close the file.
%
  fclose ( input_unit );

  return
end


function x = p14_start ( i, x )

%*****************************************************************************80
%
%% P14_START returns a starting point for problem 14.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    09 May 2011
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer I, the index of the starting point.
%
%    Output, real X, the starting point.
%
  if ( i == 1 )
    x = 3.0;
  elseif ( i == 2 )
    x = - 0.5;
  elseif ( i == 3 )
    x = 0.0;
  elseif ( i == 4 )
    x = 2.12742;
  else
    fprintf ( 1, '\n' );
    fprintf ( 1, 'P14_START - Fatal error!\n' );
    fprintf ( 1, '  Illegal starting point index = %d\n', i );
    error ( 'P14_START - Fatal error!' );
  end

  return
end


function x = p21_start ( n )

%*****************************************************************************80
%
%% P21_START returns a starting point for optimization for problem 21.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    20 March 2000
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer N, the number of variables X.
%
%    Output, real X(N), a starting point for the optimization.
%
  x = ones ( n, 1 );

  return
end


function ssmult_install (dotests)
%SSMULT_INSTALL compiles, installs, and tests ssmult.
% Note that the "lcc" compiler provided with MATLAB for Windows can generate
% slow code; use another compiler if possible.  Your current directory must be
% SSMULT for ssmult_install to work properly.  If you use Linux/Unix/Mac, I
% recommend that you use COPTIMFLAGS='-O3 -DNDEBUG' in your mexopts.sh file.
%
% Example:
%   ssmult_install          % compile and install
%   ssmult_install (0)      % just compile and install, do not test
%
% See also ssmult, ssmultsym, sstest, sstest2, mtimes.

% Copyright 2009, Timothy A. Davis, University of Florida

fprintf ('Compiling SSMULT:\n') ;

%-------------------------------------------------------------------------------
% compile ssmult and add it to the path
%-------------------------------------------------------------------------------

d = '' ;
if (~isempty (strfind (computer, '64')))
    % 64-bit MATLAB
    d = ' -largeArrayDims -DIS64' ;
end

v = getversion ;
if (v < 6.5)
    % mxIsDouble is false for a double sparse matrix in MATLAB 6.1 or earlier
    d = [d ' -DMATLAB_6p1_OR_EARLIER'] ;
end

cmd = sprintf ('mex -O%s ssmult.c ssmult_mex.c ssmult_saxpy.c ssmult_dot.c ssmult_transpose.c', d) ;
disp (cmd) ;
eval (cmd) ;

cmd = sprintf ('mex -O%s ssmultsym.c', d) ;
disp (cmd) ;
eval (cmd) ;

cmd = sprintf ('mex -O%s sptranspose.c ssmult_transpose.c', d) ;
disp (cmd) ;
eval (cmd) ;

addpath (pwd) ;
fprintf ('\nssmult has been compiled, and the following directory has been\n') ;
fprintf ('added to your MATLAB path.  Use pathtool to add it permanently:\n') ;
fprintf ('\n%s\n\n', pwd) ;
fprintf ('If you cannot save your path with pathtool, add the following\n') ;
fprintf ('to your MATLAB startup.m file (type "doc startup" for help):\n') ;
fprintf ('\naddpath (''%s'') ;\n\n', pwd) ;

%-------------------------------------------------------------------------------
% test ssmult and ssmultsym
%-------------------------------------------------------------------------------

if (nargin < 1)
    dotests = 1 ;
end
if (~dotests)
    return
end

fprintf ('Please wait while your new ssmult function is tested ...\n') ;

fprintf ('\nTesting large sparse column vectors (1e7-by-1)\n') ;
x = sprandn (1e7,1,1e-4) ;
y = sprandn (1e7,1,1e-4) ;
x (1) = pi ;
y (1) = exp (1) ;
tic ; a = x'*y ; t1 = toc ;
tic ; b = ssmult (x, y, 1) ; t2 = toc ;
fprintf ('s=x''*y in MATLAB: %8.3f seconds\n', t1) ;
fprintf ('s=ssmult(x,y,1):  %8.3f seconds; error %g\n', t2, abs (full(a-b))) ;
fprintf ('SSMULT speedup: %8.3g\n\n', t1/t2) ;

load west0479
A = west0479 ;
B = sprand (A) ;
C = A*B ;
D = ssmult (A,B) ;
err = norm (C-D,1) / norm (C,1) ;
fprintf ('west0479 error: %g\n', err) ;

fprintf ('\ntesting large matrices (may fail if you are low on memory):\n')
rand ('state', 0) ;

n = 10000 ;
A = sprand (n, n, 0.01) ;
B = sprand (n, n, 0.001) ;
test_large (A,B) ;

msg = { 'real', 'complex' } ;

% all of these calls to ssmult should fail:
fprintf ('\ntesting error handling (the errors below are expected):\n') ;
A = { 3, 'gunk', sparse(1), sparse(1), sparse(rand(3,2)) } ;
B = { 4,   0   , 5,         msg,       sparse(rand(3,4)) } ;
for k = 1:length(A)
    try
        % the following statement is supposed to fail
        C = ssmult (A {k}, B {k}) ;                                         %#ok
        error ('test failed\n') ;
    catch
        disp (lasterr) ;
    end
end
fprintf ('error handling tests: ok.\n') ;

% err should be zero:
rand ('state', 0)
for Acomplex = 0:1
    for Bcomplex = 0:1
        err = 0 ;
        fprintf ('\ntesting C = A*B where A is %s, B is %s\n', ...
            msg {Acomplex+1}, msg {Bcomplex+1}) ;
        for m = [ 0:30 100 ]
            fprintf ('.') ;
            for n = [ 0:30 100 ]
                for k = [ 0:30 100 ]
                    A = sprand (m,k,0.1) ;
                    if (Acomplex)
                        A = A + 1i*sprand (A) ;
                    end
                    B = sprand (k,n,0.1) ;
                    if (Bcomplex)
                        B = B + 1i*sprand (B) ;
                    end
                    C = A*B ;
                    D = ssmult (A,B) ;
                    s = ssmultsym (A,B) ;
                    err = max (err, norm (C-D,1)) ;
                    err = max (err, nnz (C-D)) ;
                    err = max (err, isreal (D) ~= (norm (imag (D), 1) == 0)) ;
                    err = max (err, s.nz > nnz (C)) ;
                    [i j x] = find (D) ;                                    %#ok
                    if (~isempty (x))
                        err = max (err, any (x == 0)) ;
                    end
                end
            end
        end
        fprintf (' maximum error: %g\n', err) ;
    end
end

sstest ;
fprintf ('\nSSMULT tests complete.\n') ;

%-------------------------------------------------------------------------------
function [v,pc] = getversion
% determine the MATLAB version, and return it as a double.
% only the primary and secondary version numbers are kept.
% MATLAB 7.0.4 becomes 7.0, version 6.5.2 becomes 6.5, etc.
v = version ;
t = find (v == '.') ;
if (length (t) > 1)
    v = v (1:(t(2)-1)) ;
end
v = str2double (v) ;
try
    % ispc does not appear in MATLAB 5.3
    pc = ispc ;
catch
    % if ispc fails, assume we are on a Windows PC if it's not unix
    pc = ~isunix ;
end

%-------------------------------------------------------------------------------
function test_large (A,B)
% test large matrices
n = size (A,1) ;
fprintf ('dimension %d   nnz(A): %d   nnz(B): %d\n', n, nnz (A), nnz (B)) ;
c = ssmultsym (A,B) ;
fprintf ('nnz(C): %d   flops: %g   memory: %g MB\n', ...
    c.nz, c.flops, c.memory/2^20) ;
try
    % warmup for accurate timings
    C = A*B ;                                                               %#ok
    D = ssmult (A,B) ;                                                      %#ok
    tic ;
    C = A*B ;
    t1 = toc ;
    tic ;
    D = ssmult (A,B) ;
    t2 = toc ;
    tic ;
    t3 = toc ;
    fprintf ('MATLAB time:          %g\n', t1) ;
    err = norm (C-D,1) ;
    fprintf ('SSMULT time:          %g err: %g\n', t2, err) ;
catch
    disp (lasterr)
    fprintf ('tests with large random matrices failed ...\n') ;
end
clear C D


%CLASSSIZES Get sizes of classes in a dataset
%
%   [N,LABLIST] = CLASSSIZES(A)
%
% INPUT
%   A        Dataset
%
% OUTPUT
%   N        Vector of the class cardinalities
%   LABLIST  Label list
%
% DESCRIPTION
% Returns class cardinalities of the dataset A. The order of the classes
% is identical to the list of class labels returned in LABLIST.


function neqn = p20_neqn ( )

%*****************************************************************************80
%
%% P20_NEQN returns the number of equations for problem P20.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    17 February 2013
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Output, integer NEQN, the number of equations.
%
  neqn = 4;

  return
end


function n = p29_n ( )

%*****************************************************************************80
%
%% P29_N returns the number of variables for problem 29.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    08 January 2001
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Output, integer N.  If N is positive, N represents the
%    only legal value for N for this problem.  If N is
%    negative, the absolute value of N is the least legal
%    value of N, but other values are allowable.
%
  n = 2;

  return
end


function calpak_test48 ( )

%*****************************************************************************80
%
%% CALPAK_TEST48 tests S_TO_YMD_COMMON.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    07 April 2013
%
%  Author:
%
%    John Burkardt
%
  n_test = 5;

  fprintf ( 1, '\n' );
  fprintf ( 1, 'CALPAK_TEST48\n' );
  fprintf ( 1, '  S_TO_YMD_COMMON converts a string to a YMD date.\n' );
  fprintf ( 1, '\n' );

  for i_test = 1 : n_test

    if ( i_test == 1 )
      s = '1999-10-31';
      p = 'yyyy-mm-dd';
    elseif ( i_test == 2 )
      s = '01/04/2004';
      p = 'dd/mm/yyyy';
    elseif ( i_test == 3 )
      s = '8/8/88';
      p = 'd/m/yy';
    elseif ( i_test == 4 )
      s = '4 7';
      p = 'd m';
    elseif ( i_test == 5 )
      s = 'On day 1 of month 3 of year 1945';
      p = 'xx xxx d xx xxxxx m xx xxxx yyyy';
    end

    [ y, m, d ] = s_to_ymd_common ( s, p );

    fprintf ( 1, '\n' );
    fprintf ( 1, '  %s\n', s );
    fprintf ( 1, '  %s\n', p );
    fprintf ( 1, '\n' );
    fprintf ( 1, '  %4d  %2d  %2d\n', y, m, d );

  end

  return
end


function removed = removeIndicesInDimenion(input,dimension,indices)
% Remove the indices in a specified field in the given dimension
% USAGE:
%    removed = removeIndicesInDimenion(input, dimension, indices)
%
% INPUTS:
%
%    input:              The input matrix or array
%    dimension:          The dimension from which to remove the indices
%    indices:            The indices to remove
%
% OUTPUT:
%
%    removed:          The array/matrix with the given indices removed.
%
% NOTE:
%     Based on https://stackoverflow.com/questions/22537326/on-shape-agnostic-slicing-of-ndarrays
%
% .. Authors:
%                   - Thomas Pfau Sept 2017, adapted to merge all fields.

inputDimensions = ndims(input);
S.subs = repmat({':'},1,inputDimensions);
S.subs{dimension} = indices;
S.type = '()';
removed = subsref(input,S);


function [ y, next ] = p19_equil ( neqn, next )

%*****************************************************************************80
%
%% P19_EQUIL returns equilibrium solutions of problem p19.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    19 February 2013
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer NEQN, the number of equations.
%
%    Input, integer NEXT, the index of the previous
%    equilibrium, which should be 0 on first call.
%
%    Output, real Y(NEQN), the "next" equilibrium solution, if any.
%
%    Output, integer NEXT, the index of the current equilibrium,
%    or 0 if there are no more.
%)
  if ( next == 0 )
    next = 1;
    y(1:neqn,1) = 0.0;
  else
    next = 0;
    y = [];
  end

  return
end


function tests = test_isPointInEllipsoid
% Test suite for the file isPointInEllipsoid.
%
%   Test suite for the file isPointInEllipsoid
%
%   Example
%   test_isPointInEllipsoid
%
%   See also
%     isPointInEllipsoid

% ------
% Author: David Legland
% e-mail: david.legland@inrae.fr
% Created: 2020-11-20,    using Matlab 9.8.0.1323502 (R2020a)
% Copyright 2020 INRAE - BIA-BIBS.

tests = functiontests(localfunctions);

function test_Inside(testCase) %#ok<*DEFNU>
% Test call of function without argument.

elli = [10 20 30   50 30 10   5 10 0];
p = [20 30 35];

b = isPointInEllipsoid(p, elli);

assertTrue(testCase, b);

function test_Outside(testCase) %#ok<*DEFNU>
% Test call of function without argument.

elli = [10 20 30   50 30 10   5 10 0];
p = [-20 10 25];

b = isPointInEllipsoid(p, elli);

assertFalse(testCase, b);


function boundary_segment_num = p05_boundary_segment_num ( )

%*****************************************************************************80
%
%% P05_BOUNDARY_SEGMENT_NUM counts the boundary segments in problem 05.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    12 April 2009
%
%  Author:
%
%    John Burkardt
%
%  Reference:
%
%    Per-Olof Persson and Gilbert Strang,
%    A Simple Mesh Generator in MATLAB,
%    SIAM Review,
%    Volume 46, Number 2, June 2004, pages 329-345.
%
%  Parameters:
%
%    Output, integer BOUNDARY_SEGMENT_NUM, the number of boundary segments.
%
  boundary_segment_num = 1;

  return
end


function scale = p40_scale ( neqn )

%*****************************************************************************80
%
%% P40_SCALE returns scale factors for problem p40.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    18 February 2013
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer NEQN, the number of equations.
%
%    Output, real SCALE(NEQN), the scaling factors.
%
  scale(1:neqn,1) = 1.0;

  return
end


classdef Triplets < handle
    %TRIPLET Summary of this class goes here
    %   Detailed explanation goes here

    properties (Access = public)
    nmax,m,n,nnz;
    nzval,ri,ci;
    end

    methods (Access = public)
        function obj=Triplets()
            obj.nmax = 0;
            obj.m = 0;
            obj.n = 0;
            obj.nnz = 0;
            obj.ri = 0;
            obj.ci = 0;
        end
        function clear(obj)
            obj.nmax = 0;
            obj.m = 0;
            obj.n = 0;
            obj.nnz = 0;
            obj.ri = 0;
            obj.ci = 0;
        end
        function reserve(obj,rows,cols,num)
           clear(obj);
           obj.m = rows;
           obj.n = cols;
           obj.nmax = num;
           obj.nzval = zeros(num);
           obj.ri = zeros(num);
           obj.ci = zeros(num);
        end

        function add_row(obj,r,c,val)
           obj.ri(obj.nnz+1) = r;
           obj.ci(obj.nnz+1) = c;
           obj.nzval(obj.nnz+1) = val;
           obj.nnz = obj.nnz + 1;
           obj.nnz

        end
    end
end


function [ y, next ] = p12_equil ( neqn, next )

%*****************************************************************************80
%
%% P12_EQUIL returns equilibrium solutions of problem p12.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    19 February 2013
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer NEQN, the number of equations.
%
%    Input, integer NEXT, the index of the previous
%    equilibrium, which should be 0 on first call.
%
%    Output, real Y(NEQN), the "next" equilibrium solution, if any.
%
%    Output, integer NEXT, the index of the current equilibrium,
%    or 0 if there are no more.
%
  if ( next == 0 )
    next = 1;
    y(1:neqn,1) = 0.0;
  else
    next = 0;
    y = [];
  end

  return
end


function scale = p30_scale ( neqn )

%*****************************************************************************80
%
%% P30_SCALE returns scale factors for problem p30.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    18 February 2013
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer NEQN, the number of equations.
%
%    Output, real SCALE(NEQN), the scaling factors.
%
  scale(1:neqn,1) = 1.0;

  return
end


function OUT = Num2NaN(DATA)
% =======================================================================
% Substitute 123456789 values with NaN
% =======================================================================
% OUT = Num2NaN(DATA)
% -----------------------------------------------------------------------
% INPUT
%	- DATA: a (m x n) matrix with some 123456789
% -----------------------------------------------------------------------
% OUTPUT
%	- OUT: a (m x n) matrix with NaN in place of 123456789
% -----------------------------------------------------------------------
% EXAMPLE
%   x = [123456789 2; 123456789 4; 5 6; 7 8; 9 10];
%   OUT = Num2NaN(x)
% =======================================================================
% VAR Toolbox 3.0
% Ambrogio Cesa-Bianchi
% ambrogiocesabianchi@gmail.com
% March 2012. Updated November 2020
% -----------------------------------------------------------------------

OUT=DATA;
for i=1:size(DATA,1)
    for j=1:size(DATA,2)
        if DATA(i,j) == 123456789
            OUT(i,j) = NaN;
        end
    end
end


function r8vec_print_part ( n, a, max_print, title )

%*****************************************************************************80
%
%% R8VEC_PRINT_PART prints "part" of an R8VEC.
%
%  Discussion:
%
%    The user specifies MAX_PRINT, the maximum number of lines to print.
%
%    If N, the size of the vector, is no more than MAX_PRINT, then
%    the entire vector is printed, one entry per line.
%
%    Otherwise, if possible, the first MAX_PRINT-2 entries are printed,
%    followed by a line of periods suggesting an omission,
%    and the last entry.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    25 September 2013
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer N, the number of entries of the vector.
%
%    Input, real A(N), the vector to be printed.
%
%    Input, integer MAX_PRINT, the maximum number of lines
%    to print.
%
%    Input, string TITLE, a title.
%
  if ( max_print <= 0 )
    return
  end

  if ( n <= 0 )
    return
  end

  fprintf ( 1, '\n' );
  fprintf ( 1, '%s\n', title );
  fprintf ( 1, '\n' );

  if ( n <= max_print )

    for i = 1 : n
      fprintf ( 1, '  %4d  %14g\n', i, a(i) );
    end

  elseif ( 3 <= max_print )

    for i = 1 : max_print-2
      fprintf ( 1, '  %4d  %14g\n', i, a(i) );
    end
    fprintf ( 1, '  ......  ..............\n' );
    i = n;
    fprintf ( 1, '  %4d  %14g\n', i, a(i) );

  else

    for i = 1 : max_print - 1
      fprintf ( 1, '  %4d  %14g\n', i, a(i) );
    end
    i = max_print;
    fprintf ( 1, '  %4d  %14g  ...more entries...\n', i, a(i) );

  end

  return
end


function nvar = p19_nvar ( option )

%*****************************************************************************80
%
%% P19_NVAR sets the number of variables for problem 19.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    03 September 2008
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer OPTION, the option index.
%
%    Output, integer NVAR, the number of variables.
%
  nvar = 13;

  return
end


function scale = p13_scale ( neqn )

%*****************************************************************************80
%
%% P13_SCALE returns scale factors for problem p13.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    18 February 2013
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer NEQN, the number of equations.
%
%    Output, real SCALE(NEQN), the scaling factors.
%
  scale(1:neqn,1) = 1.0;

  return
end


function [ t_start, y_start ] = p11_start ( neqn )

%*****************************************************************************80
%
%% P11_START returns the starting point for problem p11.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    23 February 2013
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer NEQN, the number of equations.
%
%    Output, real T_START, Y_START(NEQN,1), the initial data.
%
  y_start = zeros ( neqn, 1 );

  t_start = 0.0;
  y_start(1) = 1.0;
  y_start(2:10) = 0.0;

  return
end


% ==============================================================================
% This code is part of the Matlab-based toolbox
% FAIR - Flexible Algorithms for Image Registration.
% For details see
% - https://github.com/C4IR and
% - http://www.siam.org/books/fa06/
%==============================================================================
%
% Tutorial for FAIR: shows how to visulaize 2D data with FAIR.
%
% - load data             (here 'US.jpg')
% - setup viewer          (viewImage2D)
% - view  data
%==============================================================================

clear, close all, help(mfilename);
echo on

% load data, get size and specify physical domain
dataT = double(imread('US.jpg'));
m     = size(dataT);
omega = [0,m(1),0,m(2)];

% setup image viewer
viewImage('reset','viewImage','viewImage2D','colormap','gray(256)','axis','off');

% view data
viewImage(dataT,omega,m,'title','my first image');

echo off
%==============================================================================


function ndx = sub2indFast(siz, varargin)
% ndx = sub2indFast(siz, varargin)
%
% Like sub2ind but without time-intensive checks.
%
% Copyright by Mathworks
% Modified by Holger Caesar, 2014

assert(all(numel(siz) == numel(varargin)));

%Compute linear indices
k = [1, cumprod(siz(1:end-1))];
ndx = 1;
numOfIndInput = nargin-1;
for i = 1:numOfIndInput
    v = varargin{i};
    ndx = ndx + (double(v)-1) * k(i);
end


function niederreiter2_test03 ( )

%*****************************************************************************80
%
%% NIEDERREITER2_TEST03 tests EXOR.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    27 February 2014
%
%  Author:
%
%    John Burkardt
%
  fprintf ( 1, '\n' );
  fprintf ( 1, 'NIEDERREITER2_TEST03\n' );
  fprintf ( 1, '  EXOR returns the exclusive OR of two integers.\n' );

  fprintf ( 1, '\n' );
  fprintf ( 1, '     I     J     EXOR(I,J)\n' );
  fprintf ( 1, '\n' );

  seed = get_seed ( );

  for test = 1 : 10

    [ i, seed ] = i4_uniform ( 0, 100, seed );
    [ j, seed ] = i4_uniform ( 0, 100, seed );
    k = exor ( i, j );

    fprintf ( 1, '%6d %6d %6d\n', i, j, k );

  end

  return
end


%kmexchg 'Exchange Rows or Columns of a Matrix'
% This MatLab function was automatically generated by a converter (KhorosToMatLab) from the Khoros mexchg.pane file
%
% Parameters:
% InputFile: i 'Input Matrix', required: 'Input matrix'
% OutputFile: o 'Output Matrix', required: 'Output matrix'
% Integer: r1 'Row #1 ', default: 1: 'row #1'
% Integer: r2 'Row #2 ', default: 1: 'row #2'
% Integer: c1 'Column #1 ', default: 1: 'Column #1'
% Integer: c2 'Column #2 ', default: 1: 'Column #2'
%
% Example: o = kmexchg(i, {'i','';'o','';'r1',1;'r2',1;'c1',1;'c2',1})
%
% Khoros helpfile follows below:
%
%  PROGRAM
% mexchg - Exchange Rows or Columns of a Matrix
%
%  DESCRIPTION
% .I mexchg
% is used to interchage a pair of columns and/or a pair of rows of a matrix.
% If only a row exchange is desired, set the column indices equal to each other.
% Likewise, if only a column exhange is desired, set the row indices equal to each
% other.
%
%
%
%  EXAMPLES
%
%  "SEE ALSO"
%
%  RESTRICTIONS
%
%  REFERENCES
%
%  COPYRIGHT
% Copyright (C) 1993 - 1997, Khoral Research, Inc. ("KRI")  All rights reserved.
%

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

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


function wedge_integrals_test ( )

%*****************************************************************************80
%
%% WEDGE_INTEGRALS_TEST tests the WEDGE_INTEGRALS library.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    20 August 2014
%
%  Author:
%
%    John Burkardt
%
  timestamp ( );
  fprintf ( 1, '\n' );
  fprintf ( 1, 'WEDGE_INTEGRALS_TEST\n' );
  fprintf ( 1, '  MATLAB version\n' );
  fprintf ( 1, '  Test the WEDGE_INTEGRALS library.\n' );

  wedge_integrals_test01 ( );
%
%  Terminate.
%
  fprintf ( 1, '\n' );
  fprintf ( 1, 'WEDGE_INTEGRALS_TEST\n' );
  fprintf ( 1, '  Normal end of execution.\n' );
  fprintf ( 1, '\n' );
  timestamp ( );

  return
end


function [ t_start, y_start ] = p28_start ( neqn )

%*****************************************************************************80
%
%% P28_START returns the starting point for problem p28.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    23 February 2013
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer NEQN, the number of equations.
%
%    Output, real T_START, Y_START(NEQN,1), the initial data.
%
  y_start = zeros ( neqn, 1 );

  t_start = 0.0;
  y_start = [ 0.0; 0.0 ];

  return
end


function [ y, m, d, h, n ] = minute_borrow_common ( y, m, d, h, n )

%*****************************************************************************80
%
%% MINUTE_BORROW_COMMON "borrows" an hour of minutes in a Common date.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    14 July 2012
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input/output, integer Y, M, D, H, N, the year,
%    month, day, hour and minute representing a date.  On input, N
%    might be negative.
%    On output, H should have decreased by one, and N gone up by 60.
%
  while ( n < 0 )

    n = n + 60;
    h = h - 1;

    [ y, m, d, h ] = hour_borrow_common ( y, m, d, h );

  end

  return
end


function TS_Compute(doParallel,ts_id_range,op_id_range,computeWhat,customFile,howVocal)
% TS_Compute    Computes missing elements of TS_DataMat
%
%---EXAMPLE USAGE:
% TS_Compute;
%
%---INPUTS:
% doParallel: if true, attempts to use the Parallel Computing Toolbox to run
%               computations in parallel over multiple cores.
% ts_id_range: a custom range of time series IDs to compute (default: [] -- compute all)
% op_id_range: a custom range of operation IDs to compute (default: [] -- compute all)
% computeWhat: whether to compute just missing values ('missing', default), or
% 				ALSO retry results that previously threw an error ('error'), or
% 				ALSO retry any result that previously did not return a good value ('bad')
% customFile: reads in and writes to a custom output file
% howVocal:   {'fast','minimal','full'}: how much output to show about the calculation of operations.
%
%---OUTPUTS:
% Writes output to customFile (HCTSA.mat by default)

% ------------------------------------------------------------------------------
% Copyright (C) 2020, Ben D. Fulcher <ben.d.fulcher@gmail.com>,
% <http://www.benfulcher.com>
%
% If you use this code for your research, please cite the following two papers:
%
% (1) B.D. Fulcher and N.S. Jones, "hctsa: A Computational Framework for Automated
% Time-Series Phenotyping Using Massive Feature Extraction, Cell Systems 5: 527 (2017).
% DOI: 10.1016/j.cels.2017.10.001
%
% (2) B.D. Fulcher, M.A. Little, N.S. Jones, "Highly comparative time-series
% analysis: the empirical structure of time series and their methods",
% J. Roy. Soc. Interface 10(83) 20130048 (2013).
% DOI: 10.1098/rsif.2013.0048
%
% This work is licensed under the Creative Commons
% Attribution-NonCommercial-ShareAlike 4.0 International License. To view a copy of
% this license, visit http://creativecommons.org/licenses/by-nc-sa/4.0/ or send
% a letter to Creative Commons, 444 Castro Street, Suite 900, Mountain View,
% California, 94041, USA.
% ------------------------------------------------------------------------------

% --------------------------------------------------------------------------
%% Check inputs and set defaults
% --------------------------------------------------------------------------

% Use Matlab's Parallel Computing toolbox?
if nargin < 1
	doParallel = false;
end

% Custom range of ts_ids to compute
if nargin < 2
    ts_id_range = []; % compute all ts_ids in the file by default
end

% Custom range of op_ids to compute
if nargin < 3
    op_id_range = []; % compute all op_ids in the file by default
end

if nargin < 4 || isempty(computeWhat)
	computeWhat = 'missing';
end
if ~ismember(computeWhat,{'missing','error','bad'})
	error('Unknown setting ''%s''',computeWhat);
end

% Custom HCTSA.mat file:
if nargin < 5 || isempty(customFile)
    customFile = 'raw';
end

% Be vocal?
if nargin < 6
    howVocal = 'full'; % Write back full information on all calculations to screen
    % prints every piece of code evaluated (nice for error checking)
end

% --------------------------------------------------------------------------
%% Load information from local files
% --------------------------------------------------------------------------
[TS_DataMat,TimeSeries,Operations,customFile] = TS_LoadData(customFile);
fileVarsStruct = whos('-file',customFile);
fileVars = {fileVarsStruct.name};
if ~all(ismember({'TimeSeries','Operations','MasterOperations','TS_DataMat'},fileVars))
	error('\nCannot compute on %s: there are missing variables',customFile);
end
MasterOperations = TS_GetFromData(customFile,'MasterOperations');
if ismember('TS_CalcTime',fileVars)
	TS_CalcTime = TS_GetFromData(customFile,'TS_CalcTime');
end
if ismember('TS_Quality',fileVars)
	TS_Quality = TS_GetFromData(customFile,'TS_Quality');
end

%-------------------------------------------------------------------------------
% Tell the user about settings
%-------------------------------------------------------------------------------
if doParallel
    fprintf(1,['Computation will be performed across multiple cores' ...
            ' using Matlab''s Parallel Computing Toolbox.\n']);
else % use single-threaded for loops
    fprintf(1,'Computations will be performed serially without parallelization.\n');
end

% ------------------------------------------------------------------------------
% Get indices if computing a subset
% ------------------------------------------------------------------------------
allIDs = TimeSeries.ID;
if isempty(ts_id_range)
    ts_id_range = allIDs;
    tsIndex = 1:height(TimeSeries);
else
    ts_id_range = intersect(ts_id_range,allIDs);
    tsIndex = find(ismember(allIDs,ts_id_range));
end
allIDs = Operations.ID;
if isempty(op_id_range)
    op_id_range = allIDs;
    opCompute = ones(1,height(Operations));
else
    op_id_range = intersect(op_id_range,allIDs);
    opCompute = ismember(allIDs,op_id_range)';
end

% Definitions
numTimeSeries = length(ts_id_range); % Number of time series
numOps = length(op_id_range); % Number of operations

% Check that some computable range exists
if numTimeSeries==0 || numOps==0
    fprintf(1,'%u time series and %u operations match the ids provided. Exiting.\n',...
						numTimeSeries,numOps);
    return
end

if (nargin < 6) && (numOps < 30)
    howVocal = 'fast';
end

%-------------------------------------------------------------------------------
fprintf(1,'[%s]: Extracting %u features from each of %u time series.\n',...
                    datestr(now),numOps,numTimeSeries);

% The times vector stores the time taken for each time series to have its
% operations calculated (for determining time remaining)
times = zeros(numTimeSeries,1);

% Initialize TS_CalcTime and TS_Quality if they don't yet exist
if ~exist('TS_CalcTime','var')
    TS_CalcTime = zeros(size(TS_DataMat));
end
if ~exist('TS_Quality','var')
    TS_Quality = zeros(size(TS_DataMat));
end

% --------------------------------------------------------------------------
%% Computation
% --------------------------------------------------------------------------
if strcmp(howVocal,'fast')
    % Just show a progress bar over time series (not across features for a given time series)
    % makes sense for, say, catch22, where computations are super fast
    BF_ProgressBar('new')
end

numCalc_all = zeros(numTimeSeries,1);

for i = 1:numTimeSeries

    tsInd = tsIndex(i);
	bigTimer = tic;

    % ----
    % Which operations need calculating for this time series?:
    % ----
	qualityLabels = TS_Quality(tsInd,:); % The calculation states of any existing results for the current time series, a line of TS_Quality
	   	  % NaN indicates a value never before calculated, 1 indicates fatal error before (try again now)

	% Determine which operations are awaiting calculation for this time series:
	switch computeWhat
	case 'missing'
		% try to compute missing values (i.e, never previously computed for this time series)
	    toCalc = (opCompute & isnan(qualityLabels));
	case 'error'
		% compute missing or previously threw an error
		toCalc = (opCompute & (isnan(qualityLabels) | qualityLabels == 1));
	case 'bad'
		% compute missing, or anything that wasn't previously a good value
		toCalc = (opCompute & (isnan(qualityLabels) | qualityLabels > 0)); % Operations awaiting calculation
	end
    numCalc = sum(toCalc); % Number of operations to evaluate
	numCalc_all(i) = numCalc; % keep a record of how many were calculated at each iteration

    % -----
    % Check that all operations have a master ID attached:
    % -----
    if sum(toCalc) < numCalc
        % Error in the database structure; some operations are missing MasterID assignment
        error('?? Database structure error: some operations have not been assigned a valid master operation');
    end

    switch howVocal
    case 'minimal'
        fprintf(1,'- - - - - - - Time series %u / %u: %s - - - - -\n',i,numTimeSeries,TimeSeries.Name{tsInd});
    case 'full'
        fprintf(1,'\n\n=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n');
        fprintf(1,'; ; ; : : : : ; ; ;    %s     ; ; ; : : : ; ; ;\n',datestr(now));
        fprintf(1,'- - - - - - - - - - - Time series %u / %u - - - - - - - - - - -\n',i,numTimeSeries);
    end

    if numCalc > 0 % some to calculate
		try
	        [featureVector,calcTimes,calcQuality] = TS_CalculateFeatureVector(TimeSeries(tsInd,:),...
								doParallel,Operations(toCalc,:),MasterOperations,true,howVocal);
		catch ME
			% Skip to the next time series; the entries for this time series in TS_DataMat etc. will remain NaNs
			warning('Calculation for time series %u / %u failed:\n%s',i,numTimeSeries,ME.message)
			continue
		end

        % ------------------------------------------------------------------------------
		%% Store the calculated information back to local matrices
        % ------------------------------------------------------------------------------
        TS_DataMat(tsInd,toCalc) = featureVector; % store outputs in TS_DataMat
		TS_CalcTime(tsInd,toCalc) = calcTimes; % store calculation times in TS_CalcTime
		TS_Quality(tsInd,toCalc) = calcQuality; % store quality labels in TS_Quality
		% NB: the calculation time assigned for individual operations is the total calculation
		% time taken to evaluate the master code.
    else
    	fprintf(1,'Nothing calculated! All %u operations already complete!!\n',numOps);
	end

    % The time taken to calculate (or not, if numCalc = 0) all operations for this time series:
    times(i) = toc(bigTimer);

    % Update progress to the user
    switch howVocal
    case 'fast'
        BF_ProgressBar(i/numTimeSeries)
    case 'minimal'

    case 'full'
        if i < numTimeSeries
            fprintf(1,'- - - - - - - -  %u time series remaining - - - - - - - -\n',numTimeSeries-i);
            fprintf(1,'- - - - - - - -  %s remaining - - - - - - - - -\n', ...
                                                BF_TheTime(((numTimeSeries-i)*mean(times(1:i))),1));
        else % The final time series
            fprintf(1,'- - - - - - - - - - All %u time series calculated! - - - - - - - - - -\n', ...
                                                        numTimeSeries);
        end
        fprintf(1,'********************************************************************\n');
    end

end

% --------------------------------------------------------------------------
% --------------------------------------------------------------------------
%% Finished calculating!!
% --------------------------------------------------------------------------
% --------------------------------------------------------------------------
if strcmp(howVocal,'fast')
    BF_ProgressBar('close')
    fprintf(1,'Calculations complete in a total of %s.\n',BF_TheTime(sum(times),1));
else
    fprintf(1,'!! !! !! !! !! !! Calculation completed !! !! !! !! !!\n');
    fprintf(1,'[%s]: Calculations complete in a total of %s.\n',...
                        datestr(now),BF_TheTime(sum(times),1));
end

% Save back to local files (if results were computed):
if any(numCalc_all > 0)
	fprintf(1,'Saving all results to %s...',customFile);
	saveTimer = tic;
	save(customFile,'TS_DataMat','TS_CalcTime','TS_Quality','-append')
	fprintf(1,' Saved in %s.\n',BF_TheTime(toc(saveTimer)));
end

end


% [INPUT]
% data = A float t-by-n matrix containing the time series to be converted into rolling windows.
% bw = An integer [2,Inf) representing the dimension of each rolling window.
% truncate = A boolean that indicates whether to exclude all the rolling windows with a dimension less than the bandwidth (optional, default=false).
%
% [OUTPUT]
% windows = A column cell array of bw-by-n float matrices representing the rolling windows.
%
% [NOTES]
% If the number of observations is less than or equal to the specified bandwidth, a single rolling window containing all the observations is returned.

function windows = extract_rolling_windows(varargin)

    persistent ip;

    if (isempty(ip))
        ip = inputParser();
        ip.addRequired('data',@(x)validateattributes(x,{'double'},{'2d' 'nonempty'}));
        ip.addRequired('bw',@(x)validateattributes(x,{'double'},{'real' 'finite' 'integer' '>=' 2 'scalar'}));
        ip.addOptional('truncate',false,@(x)validateattributes(x,{'logical'},{'scalar'}));
    end

    ip.parse(varargin{:});

    ipr = ip.Results;
    data = ipr.data;
    bw = ipr.bw;
    truncate = ipr.truncate;

    nargoutchk(1,1);

    windows = extract_rolling_windows_internal(data,bw,truncate);

end

function windows = extract_rolling_windows_internal(data,bw,truncate)

    t = size(data,1);

    if (bw >= t)
        windows = cell(1,1);
        windows{1} = data;
        return;
    end

    limit = t - bw + 1;

    if (truncate)
        windows = cell(limit,1);

        for i = 1:limit
            windows{i} = data(i:bw+i-1,:);
        end
    else
        windows = cell(t,1);

        k = max(round(nthroot(bw,1.81),0),5);

        for i = 1:(bw - 1)
            windows{i} = data(1:max(i,k),:);
        end

        for i = 1:limit
            windows{i+bw-1} = data(i:bw+i-1,:);
        end
    end

end


function neqn = p19_neqn ( )

%*****************************************************************************80
%
%% P19_NEQN returns the number of equations for problem P19.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    17 February 2013
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Output, integer NEQN, the number of equations.
%
  neqn = 4;

  return
end


% Convert seconds to milliseconds
function ms = sec2ms(sec)
    ms = sec * 1000;
end


function [ t_stop, y_stop ] = p18_stop ( neqn )

%*****************************************************************************80
%
%% P18_STOP returns the stopping point for problem p18.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    23 February 2013
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer NEQN, the number of equations.
%
%    Output, real T_STOP, Y_STOP(NEQN), the final data.
%
  y_stop = zeros ( neqn, 1 );

  t_stop = 20.0;
  y_stop = [ ...
   -5.780432953035361e-01; ...
    8.633840009194193e-01; ...
   -9.595083730380727e-01; ...
   -6.504915126712089e-02 ];

  return
end


function fixed_num = p10_fixed_num ( )

%*****************************************************************************80
%
%% P10_FIXED_NUM returns the number of fixed points in problem 10.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    12 April 2009
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Output, integer FIXED_NUM, the number of fixed points.
%
  fixed_num = 4;

  return
end


function fixed = p06_fixed_points ( m, fixed_num )

%*****************************************************************************80
%
%% P06_FIXED_POINTS returns the fixed points in problem 06.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    12 April 2009
%
%  Author:
%
%    John Burkardt
%
%  Reference:
%
%    Per-Olof Persson and Gilbert Strang,
%    A Simple Mesh Generator in MATLAB,
%    SIAM Review,
%    Volume 46, Number 2, June 2004, pages 329-345.
%
%  Parameters:
%
%    Input, integer M, the spatial dimension.
%
%    Input, integer FIXED_NUM, the number of fixed points.
%
%    Output, real FIXED(M,FIXED_NUM), the fixed points.
%
  fixed = [];

  return
end


%% <../index.html MATLAB xUnit Test Framework>: How to Put Multiple Test Cases in One M-file
% The Quick Start example showed how you can write a simple M-file
% to be a single test case.  This example shows you how to put multiple
% test cases in one M-file.
%
% Name your M-file beginning or ending with "test", like
% "testMyFunc".  Start by putting the following two lines at the
% beginning of the file.  It's important that the output variable
% name on line 1 be |test_suite|.
%
%    function test_suite = testMyFunc
%    initTestSuite;
%
% Next, add subfunctions to the file.  Each subfunction beginning
% or ending with "test" becomes an individual test case.
%
% The directory example_subfunction_tests contains a test M-file
% containing subfunction test cases for the |fliplr| function.

cd example_subfunction_tests

type testFliplr

%%
% As usual, run the test cases using |runtests|:

runtests

%%
% <../index.html Back to MATLAB xUnit Test Framework>

%%
% Copyright 2008-2010 The MathWorks, Inc.


function days = month_length_eg_civil ( y, m )

%*****************************************************************************80
%
%% MONTH_LENGTH_EG_CIVIL returns the number of days in an Egyptian Civil month.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    19 July 2012
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer Y, the year in which the month occurred.
%
%    Input, integer M, the number of the month.
%
%    Output, integer DAYS, the number of days
%    in the month.
%
  if ( m < 1 )
    days = 0;
  elseif ( m <= 12 )
    days = 30;
  elseif ( m == 13 )
    days = 5;
  else
    days = 0;
  end

  return
end


function r83row_print_part ( n, a, max_print, title )

%*****************************************************************************80
%
%% R83ROW_PRINT_PART prints "part" of an R83ROW.
%
%  Discussion:
%
%    An R83ROW is a (3,N) array of R8's.
%
%    The user specifies MAX_PRINT, the maximum number of lines to print.
%
%    If N, the size of the vector, is no more than MAX_PRINT, then
%    the entire vector is printed, one entry per line.
%
%    Otherwise, if possible, the first MAX_PRINT-2 entries are printed,
%    followed by a line of periods suggesting an omission,
%    and the last entry.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    25 September 2013
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer N, the number of entries of the vector.
%
%    Input, real A(3,N), the vector to be printed.
%
%    Input, integer MAX_PRINT, the maximum number of lines
%    to print.
%
%    Input, string TITLE, a title.
%
  if ( max_print <= 0 )
    return
  end

  if ( n <= 0 )
    return
  end

  fprintf ( 1, '\n' );
  fprintf ( 1, '%s\n', title );
  fprintf ( 1, '\n' );

  if ( n <= max_print )

    for i = 1 : n
      fprintf ( 1, '  %4d  %14g  %14g  %14g\n', i, a(1:3,i) );
    end

  elseif ( 3 <= max_print )

    for i = 1 : max_print-2
      fprintf ( 1, '  %4d  %14g  %14g  %14g\n', i, a(1:3,i) );
    end
    fprintf ( 1, '  ....  ..............  ..............  ..............\n' );
    i = n;
    fprintf ( 1, '  %4d  %14g  %14g  %14g\n', i, a(1:3,i) );

  else

    for i = 1 : max_print - 1
      fprintf ( 1, '  %4d  %14g  %14g  %14g\n', i, a(1:3,i) );
    end
    i = max_print;
    fprintf ( 1, '  %4d  %14g  %14g  %14g  ...more entries...\n', i, a(1:3,i) );

  end

  return
end


function f = p01_f ( n, x )

%*****************************************************************************80
%
%% P01_F returns the integrand for problem 1.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    14 January 2012
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer N, the number of variables.
%
%    Input, real X(N), the arguments.
%
%    Output, real F(N), the values of the integrand.
%
  f = 1.1 * ones ( n, 1 );

  return
end


%
% data = read_matrix(FID,M,N)
%
% This function read a two dimensional matrix from the file specified. The
% dimension of the matrix need to be specified. The one dimensional vector
% can be accepted as a row matrix or column matrix.
%
% Author: Xiao Xiong
% Created: 6 Feb, 2007
% Last modified: 6 Feb, 2007

function data = read_matrix(FID,M,N)
% M and N are the number of rows and columns respectively
data = zeros(M,N);
for i=1:M
    line = fgetl(FID);
    temp = textscan(line, '%n', N);
    data(i,:) = temp{1};
end


function scale = p23_scale ( neqn )

%*****************************************************************************80
%
%% P23_SCALE returns scale factors for problem p23.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    18 February 2013
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer NEQN, the number of equations.
%
%    Output, real SCALE(NEQN), the scaling factors.
%
  scale(1:neqn,1) = [ 1.0; 1.0 ];

  return
end


function dim_num = p06_dim_num ( )

%*****************************************************************************80
%
%% P06_DIM_NUM returns the spatial dimension for problem p06.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    01 August 2011
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Output, integer DIM_NUM, the spatial dimension of the
%    dependent variables.
%
  dim_num = 2;

  return
end


function submitWeb(partId)
%SUBMITWEB Generates a base64 encoded string for web-based submissions
%   SUBMITWEB() will generate a base64 encoded string so that you can submit your
%   solutions via a web form

  fprintf('==\n== [ml-class] Submitting Solutions | Programming Exercise %s\n==\n', ...
          homework_id());
  if ~exist('partId', 'var') || isempty(partId)
    partId = promptPart();
  end

  % Check valid partId
  partNames = validParts();
  if ~isValidPartId(partId)
    fprintf('!! Invalid homework part selected.\n');
    fprintf('!! Expected an integer from 1 to %d.\n', numel(partNames));
    fprintf('!! Submission Cancelled\n');
    return
  end

  [login] = loginPrompt();
  if isempty(login)
    fprintf('!! Submission Cancelled\n');
    return
  end

  [result] = submitSolution(login, partId, output(partId), ...
                            source(partId));
  result = base64encode(result);

  fprintf('\nSave as submission file [submit_ex%s_part%d.txt]: ', ...
          homework_id(), partId);
  saveAsFile = input('', 's');
  if (isempty(saveAsFile))
    saveAsFile = sprintf('submit_ex%s_part%d.txt', homework_id(), partId);
  end

  fid = fopen(saveAsFile, 'w');
  if (fid)
    fwrite(fid, result);
    fclose(fid);
    fprintf('\nSaved your solutions to %s.\n\n', saveAsFile);
    fprintf(['You can now submit your solutions through the web \n' ...
             'form in the programming exercises. Select the corresponding \n' ...
             'programming exercise to access the form.\n']);

  else
    fprintf('Unable to save to %s\n\n', saveAsFile);
    fprintf(['You can create a submission file by saving the \n' ...
             'following text in a file: (press enter to continue)\n\n']);
    pause;
    fprintf(result);
  end

end

% ================== CONFIGURABLES FOR EACH HOMEWORK ==================

function id = homework_id()
  id = '2';
end

function [partNames] = validParts()
  partNames = { 'Sigmoid Function ', ...
                'Logistic Regression Cost', ...
                'Logistic Regression Gradient', ...
                'Predict', ...
                'Regularized Logistic Regression Cost' ...
                'Regularized Logistic Regression Gradient' ...
                };
end

function srcs = sources()
  % Separated by part
  srcs = { { 'sigmoid.m' }, ...
           { 'costFunction.m' }, ...
           { 'costFunction.m' }, ...
           { 'predict.m' }, ...
           { 'costFunctionReg.m' }, ...
           { 'costFunctionReg.m' } };
end

function out = output(partId)
  % Random Test Cases
  X = [ones(20,1) (exp(1) * sin(1:1:20))' (exp(0.5) * cos(1:1:20))'];
  y = sin(X(:,1) + X(:,2)) > 0;
  if partId == 1
    out = sprintf('%0.5f ', sigmoid(X));
  elseif partId == 2
    out = sprintf('%0.5f ', costFunction([0.25 0.5 -0.5]', X, y));
  elseif partId == 3
    [cost, grad] = costFunction([0.25 0.5 -0.5]', X, y);
    out = sprintf('%0.5f ', grad);
  elseif partId == 4
    out = sprintf('%0.5f ', predict([0.25 0.5 -0.5]', X));
  elseif partId == 5
    out = sprintf('%0.5f ', costFunctionReg([0.25 0.5 -0.5]', X, y, 0.1));
  elseif partId == 6
    [cost, grad] = costFunctionReg([0.25 0.5 -0.5]', X, y, 0.1);
    out = sprintf('%0.5f ', grad);
  end
end

% ========================= SUBMIT HELPERS =========================

function src = source(partId)
  src = '';
  src_files = sources();
  if partId <= numel(src_files)
      flist = src_files{partId};
      for i = 1:numel(flist)
          fid = fopen(flist{i});
          while ~feof(fid)
            line = fgets(fid);
            src = [src line];
          end
          fclose(fid);
          src = [src '||||||||'];
      end
  end
end

function ret = isValidPartId(partId)
  partNames = validParts();
  ret = (~isempty(partId)) && (partId >= 1) && (partId <= numel(partNames));
end

function partId = promptPart()
  fprintf('== Select which part(s) to submit:\n', ...
          homework_id());
  partNames = validParts();
  srcFiles = sources();
  for i = 1:numel(partNames)
    fprintf('==   %d) %s [', i, partNames{i});
    fprintf(' %s ', srcFiles{i}{:});
    fprintf(']\n');
  end
  fprintf('\nEnter your choice [1-%d]: ', ...
          numel(partNames));
  selPart = input('', 's');
  partId = str2num(selPart);
  if ~isValidPartId(partId)
    partId = -1;
  end
end

function [result, str] = submitSolution(email, part, output, source)

  result = ['a:5:{' ...
            p_s('homework') p_s64(homework_id()) ...
            p_s('part') p_s64(part) ...
            p_s('email') p_s64(email) ...
            p_s('output') p_s64(output) ...
            p_s('source') p_s64(source) ...
            '}'];

end

function s = p_s(str)
   s = ['s:' num2str(numel(str)) ':"' str '";'];
end

function s = p_s64(str)
   str = base64encode(str, '');
   s = ['s:' num2str(numel(str)) ':"' str '";'];
end

% =========================== LOGIN HELPERS ===========================

function [login] = loginPrompt()
  % Prompt for password
  [login] = basicPrompt();
end

function [login] = basicPrompt()
  login = input('Login (Email address): ', 's');
end

% =========================== Base64 Encoder ============================
% Thanks to Peter John Acklam
%

function y = base64encode(x, eol)
%BASE64ENCODE Perform base64 encoding on a string.
%
%   BASE64ENCODE(STR, EOL) encode the given string STR.  EOL is the line ending
%   sequence to use; it is optional and defaults to '\n' (ASCII decimal 10).
%   The returned encoded string is broken into lines of no more than 76
%   characters each, and each line will end with EOL unless it is empty.  Let
%   EOL be empty if you do not want the encoded string broken into lines.
%
%   STR and EOL don't have to be strings (i.e., char arrays).  The only
%   requirement is that they are vectors containing values in the range 0-255.
%
%   This function may be used to encode strings into the Base64 encoding
%   specified in RFC 2045 - MIME (Multipurpose Internet Mail Extensions).  The
%   Base64 encoding is designed to represent arbitrary sequences of octets in a
%   form that need not be humanly readable.  A 65-character subset
%   ([A-Za-z0-9+/=]) of US-ASCII is used, enabling 6 bits to be represented per
%   printable character.
%
%   Examples
%   --------
%
%   If you want to encode a large file, you should encode it in chunks that are
%   a multiple of 57 bytes.  This ensures that the base64 lines line up and
%   that you do not end up with padding in the middle.  57 bytes of data fills
%   one complete base64 line (76 == 57*4/3):
%
%   If ifid and ofid are two file identifiers opened for reading and writing,
%   respectively, then you can base64 encode the data with
%
%      while ~feof(ifid)
%         fwrite(ofid, base64encode(fread(ifid, 60*57)));
%      end
%
%   or, if you have enough memory,
%
%      fwrite(ofid, base64encode(fread(ifid)));
%
%   See also BASE64DECODE.

%   Author:      Peter John Acklam
%   Time-stamp:  2004-02-03 21:36:56 +0100
%   E-mail:      pjacklam@online.no
%   URL:         http://home.online.no/~pjacklam

   if isnumeric(x)
      x = num2str(x);
   end

   % make sure we have the EOL value
   if nargin < 2
      eol = sprintf('\n');
   else
      if sum(size(eol) > 1) > 1
         error('EOL must be a vector.');
      end
      if any(eol(:) > 255)
         error('EOL can not contain values larger than 255.');
      end
   end

   if sum(size(x) > 1) > 1
      error('STR must be a vector.');
   end

   x   = uint8(x);
   eol = uint8(eol);

   ndbytes = length(x);                 % number of decoded bytes
   nchunks = ceil(ndbytes / 3);         % number of chunks/groups
   nebytes = 4 * nchunks;               % number of encoded bytes

   % add padding if necessary, to make the length of x a multiple of 3
   if rem(ndbytes, 3)
      x(end+1 : 3*nchunks) = 0;
   end

   x = reshape(x, [3, nchunks]);        % reshape the data
   y = repmat(uint8(0), 4, nchunks);    % for the encoded data

   %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   % Split up every 3 bytes into 4 pieces
   %
   %    aaaaaabb bbbbcccc ccdddddd
   %
   % to form
   %
   %    00aaaaaa 00bbbbbb 00cccccc 00dddddd
   %
   y(1,:) = bitshift(x(1,:), -2);                  % 6 highest bits of x(1,:)

   y(2,:) = bitshift(bitand(x(1,:), 3), 4);        % 2 lowest bits of x(1,:)
   y(2,:) = bitor(y(2,:), bitshift(x(2,:), -4));   % 4 highest bits of x(2,:)

   y(3,:) = bitshift(bitand(x(2,:), 15), 2);       % 4 lowest bits of x(2,:)
   y(3,:) = bitor(y(3,:), bitshift(x(3,:), -6));   % 2 highest bits of x(3,:)

   y(4,:) = bitand(x(3,:), 63);                    % 6 lowest bits of x(3,:)

   %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   % Now perform the following mapping
   %
   %   0  - 25  ->  A-Z
   %   26 - 51  ->  a-z
   %   52 - 61  ->  0-9
   %   62       ->  +
   %   63       ->  /
   %
   % We could use a mapping vector like
   %
   %   ['A':'Z', 'a':'z', '0':'9', '+/']
   %
   % but that would require an index vector of class double.
   %
   z = repmat(uint8(0), size(y));
   i =           y <= 25;  z(i) = 'A'      + double(y(i));
   i = 26 <= y & y <= 51;  z(i) = 'a' - 26 + double(y(i));
   i = 52 <= y & y <= 61;  z(i) = '0' - 52 + double(y(i));
   i =           y == 62;  z(i) = '+';
   i =           y == 63;  z(i) = '/';
   y = z;

   %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   % Add padding if necessary.
   %
   npbytes = 3 * nchunks - ndbytes;     % number of padding bytes
   if npbytes
      y(end-npbytes+1 : end) = '=';     % '=' is used for padding
   end

   if isempty(eol)

      % reshape to a row vector
      y = reshape(y, [1, nebytes]);

   else

      nlines = ceil(nebytes / 76);      % number of lines
      neolbytes = length(eol);          % number of bytes in eol string

      % pad data so it becomes a multiple of 76 elements
      y = [y(:) ; zeros(76 * nlines - numel(y), 1)];
      y(nebytes + 1 : 76 * nlines) = 0;
      y = reshape(y, 76, nlines);

      % insert eol strings
      eol = eol(:);
      y(end + 1 : end + neolbytes, :) = eol(:, ones(1, nlines));

      % remove padding, but keep the last eol string
      m = nebytes + neolbytes * (nlines - 1);
      n = (76+neolbytes)*nlines - neolbytes;
      y(m+1 : n) = '';

      % extract and reshape to row vector
      y = reshape(y, 1, m+neolbytes);

   end

   % output is a character array
   y = char(y);

end


function unicycle_print ( n, u, title )

%*****************************************************************************80
%
%% UNICYCLE_PRINT prints a unicycle given in sequence form.
%
%  Example:
%
%    Input:
%
%      U = 7 1 4 5 2 3 6
%
%    Printed output:
%
%      7 1 4 5 2 3 6
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    15 June 2012
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer N, the order of the unicycle.
%
%    Input, integer U(N), the unicycle sequence vector.
%
%    Input, string TITLE, a title.
%
  inc = 20;

  if ( 0 < s_len_trim ( title ) )
    fprintf ( 1, '\n' );
    fprintf ( 1, '%s\n', title );
    fprintf ( 1, '\n' );
  end

  for ilo = 1 : inc : n
    ihi = min ( n, ilo + inc - 1 );
    for i = ilo : ihi
      fprintf ( 1, '  %4d', u(i) );
    end
    fprintf ( 1, '\n' );
  end

  return
end


function scale = p22_scale ( neqn )

%*****************************************************************************80
%
%% P22_SCALE returns scale factors for problem p22.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    18 February 2013
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer NEQN, the number of equations.
%
%    Output, real SCALE(NEQN), the scaling factors.
%
  scale(1:neqn,1) = [ 1.0; 1.0 ];

  return
end


function saveTightFigure(h,outfilename)
% SAVETIGHTFIGURE(OUTFILENAME) Saves the current figure without the white
%   space/margin around it to the file OUTFILENAME. Output file type is
%   determined by the extension of OUTFILENAME. All formats that are
%   supported by MATLAB's "saveas" are supported.
%
%   SAVETIGHTFIGURE(H, OUTFILENAME) Saves the figure with handle H.
%
% E Akbas (c) Aug 2010
% * Updated to handle subplots and multiple axes. March 2014.
%

if nargin==1
    hfig = gcf;
    outfilename = h;
else
    hfig = h;
end

%% find all the axes in the figure
hax = findall(hfig, 'type', 'axes');

%% compute the tighest box that includes all axes
tighest_box = [Inf Inf -Inf -Inf]; % left bottom right top
for i=1:length(hax)
    set(hax(i), 'units', 'centimeters');

    p = get(hax(i), 'position');
    ti = get(hax(i), 'tightinset');

    % get position as left, bottom, right, top
    p = [p(1) p(2) p(1)+p(3) p(2)+p(4)] + ti.*[-1 -1 1 1];

    tighest_box(1) = min(tighest_box(1), p(1));
    tighest_box(2) = min(tighest_box(2), p(2));
    tighest_box(3) = max(tighest_box(3), p(3));
    tighest_box(4) = max(tighest_box(4), p(4));
end

%% move all axes to left-bottom
for i=1:length(hax)
    if strcmp(get(hax(i),'tag'),'legend')
        continue
    end
    p = get(hax(i), 'position');
    set(hax(i), 'position', [p(1)-tighest_box(1) p(2)-tighest_box(2) p(3) p(4)]);
end

%% resize figure to fit tightly
set(hfig, 'units', 'centimeters');
p = get(hfig, 'position');

width = tighest_box(3)-tighest_box(1);
height =  tighest_box(4)-tighest_box(2);
set(hfig, 'position', [p(1) p(2) width height]);

%% set papersize
set(hfig,'PaperUnits','centimeters');
set(hfig,'PaperSize', [width height]);
set(hfig,'PaperPositionMode', 'manual');
set(hfig,'PaperPosition',[0 0 width height]);

%% save
saveas(hfig,outfilename);


function scale = p16_scale ( neqn )

%*****************************************************************************80
%
%% P16_SCALE returns scale factors for problem p16.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    18 February 2013
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer NEQN, the number of equations.
%
%    Output, real SCALE(NEQN), the scaling factors.
%
  scale(1:neqn,1) = 1.0;

  return
end


function scale = p14_scale ( neqn )

%*****************************************************************************80
%
%% P14_SCALE returns scale factors for problem p14.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    18 February 2013
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer NEQN, the number of equations.
%
%    Output, real SCALE(NEQN), the scaling factors.
%
  scale(1:neqn,1) = 1.0;

  return
end


% THIS SOFTWARE AND ANY ACCOMPANYING DOCUMENTATION IS RELEASED "AS IS."  THE U.S. GOVERNMENT MAKES NO WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, CONCERNING THIS SOFTWARE AND ANY ACCOMPANYING DOCUMENTATION, INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  IN NO EVENT WILL THE U.S. GOVERNMENT BE LIABLE FOR ANY DAMAGES, INCLUDING ANY LOST PROFITS, LOST SAVINGS OR OTHER INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE, OR INABILITY TO USE, THIS SOFTWARE OR ANY ACCOMPANYING DOCUMENTATION, EVEN IF INFORMED IN ADVANCE OF THE POSSIBILITY OF SUCH DAMAGES.
%
% file: binopdf_01ok.m
% The regular binopdf returns NaN whenever x=0 and p=0 or whenever
%   x=n and p=1.  The correct value for binopdf to return in both cases is 1.0.
% This corrects that bug.
% This also avoids calling binopdf when it would give an inappropriate warning
%   about taking the log of zero.
% This could more easily be fixed within binopdf.m, but that for MathWorks
%
% 022400 tdr created
% 033100 tdr added check for binopdf returning NaN or Inf, which it may for n > 1000, e.g., x=500, n=1030, p=0.1

function y = binopdf_01ok(x,n,p)

if nargin < 3,
    error('Requires three input arguments (x,n,p)');
end

[errorcode x n_bar p] = distchck(3,x,n,p);

if errorcode > 0
    error('Requires non-scalar arguments to match in size.');
end

y = zeros(size(x));

k = find(~((p == 0) | (p == 1)));
if any(k),
   y(k) = binopdf(x(k),n,p(k));
   if ~isfinite(y),
      error('NaN or Inf value returned from binopdf.m')
   end;
end

k = find((x ~= 0 & p == 0) | (x ~= n & p == 1));
if any(k),
   y(k) = 0;
end

k = find((x == 0 & p == 0) | (x == n & p == 1));
if any(k),
   y(k) = 1;
end


function n = p01_n ( )

%*****************************************************************************80
%
%% P01_N returns the number of variables for problem 1.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    16 October 2011
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Output, integer N.  If N is positive, N represents the
%    only legal value for N for this problem.  If N is
%    negative, the absolute value of N is the least legal
%    value of N, but other values are allowable.
%
  n = 3;

  return
end


function scale = p02_scale ( neqn )

%*****************************************************************************80
%
%% P02_SCALE returns scale factors for problem p02.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    18 February 2013
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer NEQN, the number of equations.
%
%    Output, real SCALE(NEQN), the scaling factors.
%
  scale(1:neqn,1) = 1.0;

  return
end


function p07_story ( )

%*****************************************************************************80
%
%% P07_STORY prints the "story" for problem p07.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    20 July 2012
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    None
%
  fprintf ( 1, '\n' );
  fprintf ( 1, '  This data is a simple symmetric set of 4 points,\n' );
  fprintf ( 1, '  for which it is interesting to develop the Shepard\n' );
  fprintf ( 1, '  interpolants for varying values of the exponent p.\n' );

  return
end


function value = year_length_months_coptic ( y )

%*****************************************************************************80
%
%% YEAR_LENGTH_MONTHS_COPTIC returns the number of months in a Coptic year.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    23 September 2012
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer Y, the year to be checked.
%
%    Output, integer VALUE, the number of months in the year.
%
  value = 12;

  return
end


function [x_min,x_max,y_min,y_max] = handle_cross_boundary(x_min,x_max,y_min,y_max,img_size)
    row = img_size(1);
    col = img_size(2);
    if x_min < 1
        x_min = 1;
    end
    if y_min < 1
        y_min = 1;
    end
    if x_max > row
        x_max = row;
    end
    if y_max > col
        y_max = col;
    end
end


function root_num = p05_root_num ( )

%*****************************************************************************80
%
%% P05_ROOT_NUM returns the number of known roots for problem 5.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    06 May 2011
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Output, integer ROOT_NUM, the number of known roots.
%
  root_num = 3;

  return
end


function p = SquareSubplot(n,i)

%SquareSubplot - Layout subplots in a square arrangement.
%
%
%  USAGE
%
%    p = SquareSubplot(n,i)
%
%    n              total number of subplots
%    i              current subplot

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

if nargin < 2,
  error('Incorrect number of parameters (type ''help SquareSubplot'' for details).');
end

M = round(sqrt(n));
N = ceil(n/M);
p = subplot(M,N,i);


function result = c_strcmp(str1,str2)
%C_STRCMP Compare strings like C function "strcmp"
% Function C_STRCMP compares two strings, and returns
% a -1 if str1 < str2, a 0 if str1 == str2, and a
% +1 if str1 > str2.

% Define variables:
%   diff      -- Logical array of string differences
%   msg       -- Error message
%   result    -- Result of function
%   str1      -- First string to compare
%   str2      -- Second string to compare
%   strings   -- Padded array of strings
%
%  Record of revisions:
%      Date       Programmer          Description of change
%      ====       ==========          =====================
%    02/25/07    S. J. Chapman        Original code

% Check for a legal number of input arguments.
msg = nargchk(2,2,nargin);
error(msg);

% Check to see if the arguments are strings
if ~(isstr(str1) & isstr(str2))
   error('Both str1 and str2 must both be strings!')
else

   % Pad strings
   strings = strvcat(str1,str2);

   % Compare strings
   diff = strings(1,:) ~= strings(2,:);
   if sum(diff) == 0

      % Strings match, so return a zero!
      result = 0;
   else
      % Find first difference between strings
      ival = find(diff);
      if strings(1,ival(1)) > strings(2,ival(1))
         result = 1;
      else
         result = -1;
      end
   end
end


function [XXs,YYs] = Delequalsamples(XXs,YYs)

%------------------------------- Copyright --------------------------------
% Copyright (c) 2023 BIMK Group. You are free to use the PlatEMO for
% research purposes. All publications which use this platform or any code
% in the platform should acknowledge the use of "PlatEMO" and reference "Ye
% Tian, Ran Cheng, Xingyi Zhang, and Yaochu Jin, PlatEMO: A MATLAB platform
% for evolutionary multi-objective optimization [educational forum], IEEE
% Computational Intelligence Magazine, 2017, 12(4): 73-87".
%--------------------------------------------------------------------------

    zerosindex        = YYs == 0;
    XXs(zerosindex,:) = [];
    YYs(zerosindex)   = [];
end


function [ y, next ] = p03_equil ( neqn, next )

%*****************************************************************************80
%
%% P03_EQUIL returns equilibrium solutions of problem p03.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    19 February 2013
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer NEQN, the number of equations.
%
%    Input, integer NEXT, the index of the previous
%    equilibrium, which should be 0 on first call.
%
%    Output, real Y(NEQN), the "next" equilibrium solution, if any.
%
%    Output, integer NEXT, the index of the current equilibrium,
%    or 0 if there are no more.
%
  if ( next == 0 )
    next = 1;
    y(1:neqn,1) = 0.0;
  else
    next = 0;
    y = [];
  end

  return
end


function seed_rand()
% Initialize random number generator to a fixed seed
% This ensures that results are reproducible

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

% Try to make different versions of matlab happy
try
  rng(3, 'twister')
catch
  rand('twister',3);
end


function [ t_stop, y_stop ] = p06_stop ( neqn )

%*****************************************************************************80
%
%% P06_STOP returns the stopping point for problem p06.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    23 February 2013
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer NEQN, the number of equations.
%
%    Output, real T_STOP, Y_STOP(NEQN), the final data.
%
  y_stop = zeros ( neqn, 1 );

  t_stop = 20.0;
  y_stop = [ ...
    0.6761876008576667; ...
    0.1860816099640036 ];

  return
end


function obj_normal_vector_print ( normal_num, normal_vector )

%*****************************************************************************80
%
%% OBJ_NORMAL_VECTOR_PRINT prints the normal vectors.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    26 September 2008
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer NORMAL_NUM, the number of normal vectors.
%
%    Input, real NORMAL_VECTOR(3,NORMAL_NUM), the normal vectors.
%
  fprintf ( 1, '\n' );
  fprintf ( 1, '  Normal Vectors:\n' );
  fprintf ( 1, '\n' );

  for normal = 1 : normal_num

    fprintf ( 1, '  %8d  %14f  %14f  %14f\n', ...
      normal,  normal_vector(1:3,normal) );

  end

  return
end


function patch2STL(varargin)

% ------------------------------------------------------------------------
% function patch2STL(fileName,V,F,N,solidName)
%
% This function generates an STL file using the vertices (V) and triangular
% faces (F) provided
%
%
% Kevin Mattheus Moerman
% kevinmoerman@hotmail.com
% 2014/04/08
%
% CHANGE LOG:
% 09/12/2009
% * Created
% 2014/04/08
% * Changed %f to %6.6e
% * Added solidName input option
% * Created varargin based input parsing
% ------------------------------------------------------------------------

%% Parse input

%Check if at least 3 inputs and not more than 6 are provided
if nargin<3 || nargin>6
    error('Wrong number of input arguments');
end

%Get first 3 manditory inputs
fileName=varargin{1};
V=varargin{2};
F=varargin{3};

% Check if faces input is consistent with triangles
if ~size(F,2)==3
    error('Patch data does not represent triangles. Convert to triangles first.');
end

%Check N and solidName entries
switch nargin
    case 3 %N, solidName and writeOpt not provided
        N=[];
        solidName='path2STL';
        writeOpt='w';
    case 4 %solidName and writeOpt not provided
        N=varargin{4};
        solidName='path2STL';
        writeOpt='w';
    case 5
        N=varargin{4};
        solidName=varargin{5};
        writeOpt='w';
    case 6
        N=varargin{4};
        solidName=varargin{5};
        writeOpt=varargin{6};
end

%Check if the solidName is a string
if ~ischar(solidName)
    error('solidName needs to be a string');
end

%% Get face normals
if isempty(N) %if face normals are missing
    [N,~]=patchNormal(F,V);
end

%% Write STL file in ASCII format

fid=fopen(fileName,writeOpt); %Append to existing file

fprintf(fid,['solid ',solidName,' \n']);
hw = waitbar(0,'Writing STL-file, please wait...');

%Start loop for all faces, vertices and normals
for qFace = 1:1:size(F,1)

    %Write face normals
    fprintf(fid,'  facet normal %6.6e %6.6e %6.6e\n',N(qFace,:));
    fprintf(fid,'    outer loop\n');
    fprintf(fid,'      vertex %6.6e %6.6e %6.6e\n',V(F(qFace,1),:));
    fprintf(fid,'      vertex %6.6e %6.6e %6.6e\n',V(F(qFace,2),:));
    fprintf(fid,'      vertex %6.6e %6.6e %6.6e\n',V(F(qFace,3),:));
    fprintf(fid,'    endloop\n');
    fprintf(fid,'  endfacet\n');

    %Increment waitbar
    waitbar(qFace/size(F,1),hw,['Writing STL-file ',num2str(round((qFace/size(F,1)).*100)),'%']);
end
fprintf(fid,['endsolid ',solidName,' \n']);
waitbar(1,hw,'Finished! Closing file.');
drawnow;
fclose(fid); %close file ID
close(hw); %close waitbar

%%
% _*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 r8r8_print ( a1, a2, title )

%*****************************************************************************80
%
%% R8R8_PRINT prints an R8R8.
%
%  Discussion:
%
%    An R8R8 is simply a pair of R8's, thought of as a single item.
%
%    A format is used which suggests a coordinate pair:
%
%  Example:
%
%    Center : ( 1.23, 7.45 )
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    29 October 2005
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, real A1, A2, the coordinates of the vector.
%
%    Input, string TITLE, a title.
%
  fprintf ( 1, '  %s : ( %12g, %12g )\n', title, a1, a2 );

  return
end


function [nlines] = dicomrt_nASCIIlines(filename)
% dicomrt_nASCIIlines(filename)
%
% Returns the number of lines in an ASCII file.
%
% Copyright (C) 2002 Emiliano Spezi (emiliano.spezi@physics.org)

fid=fopen(filename);
nlines = 0;
while 1
    if feof(fid)
        break;
    end
    nlines = nlines+1;
    fgetl(fid);
end
fclose(fid);