function svd_basis ( )

%*****************************************************************************80
%
%% MAIN is the main program for SVD_BASIS.
%
%  Discussion:
%
%    SVD_BASIS forms a basis from the SVD of a set of data vectors.
%
%    This program uses the singular value decomposition (SVD) to analyze
%    a set of data, and extract a number of dominant modes.
%
%    This program is intended as an intermediate application, in
%    the following situation:
%
%    A) a "high fidelity" or "high resolution" PDE solver is used
%       to determine many (say N = 500) solutions of a discretized
%       PDE at various times, or parameter values.  Each solution
%       may be regarded as an M vector.  Typically, each solution
%       involves an M by M linear system, greatly reduced in
%       complexity because of bandedness or sparsity.
%
%    B) This program is applied to extract L dominant modes from
%       the N solutions.  This is done using the singular value
%       decomposition of the M by N matrix, each of whose columns
%       is one of the original solution vectors.
%
%    C) a "reduced order model" program may then attempt to solve
%       a discretized version of the PDE, using the L dominant
%       modes as basis vectors.  Typically, this means that a dense
%       L by L linear system will be involved.
%
%    Thus, the program might read in 500 files, and write out
%    5 or 10 files of the corresponding size and "shape", representing
%    the dominant solution modes.
%
%    An option has been added to compute the average of the vectors
%    and subtract it before SVD processing.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    22 November 2011
%
%  Author:
%
%    John Burkardt
%
%  Reference:
%
%    Gal Berkooz, Philip Holmes, John Lumley,
%    The proper orthogonal decomposition in the analysis of turbulent flows,
%    Annual Review of Fluid Mechanics,
%    Volume 25, 1993, pages 539-575.
%
%    John Burkardt, Max Gunzburger, Hyung-Chun Lee,
%    Centroidal Voronoi Tessellation-Based Reduced-Order
%    Modelling of Complex Systems,
%    SIAM Journal on Scientific Computing,
%    Volume 28, Number 2, 2006, pages 459-484.
%
%    Lawrence Sirovich,
%    Turbulence and the dynamics of coherent structures, Parts I-III,
%    Quarterly of Applied Mathematics,
%    Volume XLV, Number 3, 1987, pages 561-590.
%
  clean = 0;

  timestamp ( );
  fprintf ( 1, '\n' );
  fprintf ( 1, 'SVD_BASIS\n' );
  fprintf ( 1, '  MATLAB version\n' );
  fprintf ( 1, '\n' );
  fprintf ( 1, '  Given a PDE for which:\n' );
  fprintf ( 1, '    C is the number of components of the solution \n' );
  fprintf ( 1, '      at any single point,\n' );
  fprintf ( 1, '    P is the number of points where a solution is given,\n' );
  fprintf ( 1, '    N is the number of solution vectors,\n' );
  fprintf ( 1, '    L is the number of modes to be extracted.\n' );
  fprintf ( 1, '\n' );
  fprintf ( 1, '  Then we let M = C*P be the abstract spatial dimension.\n' );
  fprintf ( 1, '\n' );
  fprintf ( 1, '  If requested, we compute the average solution,\n' );
  fprintf ( 1, '  subtract it from each solution, and save that\n' );
  fprintf ( 1, '  as mode #0.\n' );
  fprintf ( 1, '\n' );
  fprintf ( 1, '  Set up A, the M by N matrix of solution vectors,\n' );
  fprintf ( 1, '\n' );
  fprintf ( 1, '  Get A = U * S * V'', the singular value decomposition.\n' );
  fprintf ( 1, '\n' );
  fprintf ( 1, '  The first L columns of U are our dominant modes.\n' );
  fprintf ( 1, '\n' );
%
%  What is the basis size?
%
  basis_num = input ( '  How many basis vectors (L) are to be extracted? ' );

  fprintf ( 1, '\n' );
  fprintf ( 1, '  L = %d\n', basis_num );
%
%  Gather one or more "base" file names.
%
  data_file_base_num = 0;
  data_file_base_start = [];
  data_file_base_last = [];
  data_file_base = [];
  data_file_base_size = 0;

  while ( 1 )
%
%  Get the next base file name.
%
    fprintf ( 1, '\n' );
    fprintf ( 1, '  You specify a consecutive sequence of file names\n' );
    fprintf ( 1, '  by giving the first "base" file name.\n' );
    fprintf ( 1, '\n' );
    fprintf ( 1, '  If there are no more sequences to enter,\n' );
    fprintf ( 1, '  just hit RETURN.\n' );

    file_name = input ( '  Enter RETURN, or a new base file name: ' );

    if ( s_len_trim ( file_name ) <= 0 )
      fprintf ( 1, '\n' );
      fprintf ( 1, '  RETURN was entered.\n' );
      fprintf ( 1, '  Presumably, there are no more file sequences.\n' );
      break
    end

    file_name_length = length ( file_name );
    
    data_file_base_num = data_file_base_num + 1;
    data_file_base_start(data_file_base_num) = data_file_base_size + 1;
    data_file_base_last(data_file_base_num) = data_file_base_size + file_name_length;
    data_file_base = strcat ( data_file_base, file_name );
    data_file_base_size = data_file_base_size + file_name_length;

    fprintf ( 1, '\n' );
    fprintf ( 1, '  %6d:  "%s"\n', data_file_base_num, file_name );
%
%  For the very first base file, get the data sizes.
%
    if ( data_file_base_num == 1 )

      [ comp_num, node_num ] = r8mat_header_read ( file_name );

      dim_num = comp_num * node_num;

      fprintf ( 1, '\n' );
      fprintf ( 1, '  According to the first base file,\n' );
      fprintf ( 1, '  The number of solution components C =   %d\n', comp_num );
      fprintf ( 1, '  The number of solution points P =       %d\n', node_num );
      fprintf ( 1, '  The "size" of each solution M = (C*P) = %d\n', dim_num );
%
%  Idiocy check.  L must be less than or equal to M.
%
      if ( dim_num < basis_num )
        fprintf ( 1, '\n' );
        fprintf ( 1, 'SVD_BASIS - Fatal error!\n' );
        fprintf ( 1, '\n' );
        fprintf ( 1, '  M < L.\n' );
        fprintf ( 1, '\n' );
        fprintf ( 1, '  That is, the number of modes requested (L) is greater\n' );
        fprintf ( 1, '  than the spatial dimension (M).\n' );
        fprintf ( 1, '  Technically, the program could pad out the answer\n' );
        fprintf ( 1, '  with L-M zero vectors, but instead, we will stop\n' );
        fprintf ( 1, '  assuming you made an error, or a misapprehension.\n' );
        fprintf ( 1, '\n' );
        fprintf ( 1, 'SVD_BASIS:\n' );
        fprintf ( 1, '  Abnormal end of execution.\n' );
        fprintf ( 1, '\n' );
        timestamp;
        return
      end

    end

  end
%
%  Count all the data files.
%
  data_file_num = 0;

  for i = 1 : data_file_base_num

    start = data_file_base_start(i);
    last = data_file_base_last(i);
    data_file = data_file_base(start:last);

    while ( 1 )
      
      if ( ~file_exist ( data_file ) )
        break
      end

      data_file_num = data_file_num + 1;

      data_file = file_name_inc_nowrap ( data_file );

    end

  end

  if ( data_file_num == 0 )
    fprintf ( 1, '\n' );
    fprintf ( 1, 'SVD_BASIS - Fatal error!\n' );
    fprintf ( 1, '  There do not seem to be any solution files;\n' );
    fprintf ( 1, '  that is, files whose names are "incremented"\n' );
    fprintf ( 1, '  versions of the first file name.\n' );
    fprintf ( 1, '\n' );
    fprintf ( 1, '  The first file we looked for was "%s"\n', data_file );
    fprintf ( 1, '\n' );
    fprintf ( 1, 'SVD_BASIS:\n' );
    fprintf ( 1, '  Abnormal end of execution.\n' );
    fprintf ( 1, '\n' );
    timestamp;
  end

  fprintf ( 1, '\n' );
  fprintf ( 1, '  The number of data files N = %d\n', data_file_num );
%
%  Set up an array to hold all the data.
%
  point_num = data_file_num;

  fprintf ( 1, '\n' );
  fprintf ( 1, '  The data is stored in an M by N matrix A.\n' );
  fprintf ( 1, '\n' );
  fprintf ( 1, '  The "spatial" dimension M is   %d\n', dim_num );
  fprintf ( 1, '  The number of data points N is %d\n', point_num );
%
%  Read the data.
%
  l = 0;

  for ii = 1 : data_file_base_num

    start = data_file_base_start(i);
    last = data_file_base_last(i);
    data_file = data_file_base(start:last);

    while ( 1 )

      if ( ~file_exist ( data_file ) )
        break
      end

      l = l + 1;

      table = r8mat_data_read ( data_file, comp_num, node_num );

      k = 0;
      for j = 1 : node_num
        for i = 1 : comp_num
          k = k + 1;
          point(k,l) = table(i,j);
        end
      end

      data_file = file_name_inc_nowrap ( data_file );

    end

  end

  fprintf ( 1, '\n' );
  fprintf ( 1, '  The data has been read into the matrix A.\n' );
%
%----------------------------------------------------------------------------
%  Optionally, average the data, subtract the average from each entry,
%  and later save the average as vector #0.
%----------------------------------------------------------------------------
%
  fprintf ( 1, '\n' );
  fprintf ( 1, 'SVD_BASIS:\n' );
  fprintf ( 1, '  Averaging of data is optional.\n' );
  fprintf ( 1, '  The program can average the data vectors,\n' );
  fprintf ( 1, '  subtract it from each data vector,\n' );
  fprintf ( 1, '  and write out the data average vector as an\n' );
  fprintf ( 1, '  extra "mode 0" vector.\n' );
  fprintf ( 1, '\n' );
  fprintf ( 1, '  Do you want to compute and use the average? (''Y''/''N'')\n' );

  average_char = input ( '  Enter ''Y'' or ''N'': ' );

  if ( average_char == 'Y' || average_char == 'y' )
    average_normalization = 1;
    fprintf ( 1, '\n' );
    fprintf ( 1, '  The user has requested the average vector.\n' );
  else
    average_normalization = 0;
    fprintf ( 1, '\n' );
    fprintf ( 1, '  The user does not want the average vector.\n' );
  end

  if ( average_normalization )

    for i = 1 : dim_num
      point_average(i,1) = sum ( point(i,1:point_num) );
    end

    point_average(1:dim_num,1) = point_average(1:dim_num,1) / point_num;

    for i = 1 : dim_num
      point(i,1:point_num) = point(i,1:point_num) - point_average(i,1);
    end

  end
%
%----------------------------------------------------------------------------
%
%  Compute the SVD of A.
%
%----------------------------------------------------------------------------
%
  [ point, sval ] = singular_vectors ( dim_num, point_num, basis_num, point );
%
%----------------------------------------------------------------------------
%
%  "Clean" the output data.  We are having problems with some vectors
%  containing a few very tiny (and meaningless) values.
%
%----------------------------------------------------------------------------
%
  if ( clean )

    fprintf ( 1, '\n' );
    fprintf ( 1, '  Because the CLEAN option is on,\n' );
    fprintf ( 1, '  we will set very tiny vector entries to 0.\n' );

    tol = eps;

    for j = 1 : basis_num
      for i = 1 : dim_num
        if ( abs ( point(i,j) ) < tol )
          point(i,j) = 0.0;
        end
      end
    end
  end
%
%----------------------------------------------------------------------------
%
%  Write the first L left singular vectors (columns of U) to files.
%
%----------------------------------------------------------------------------
%
  fprintf ( 1, '\n' );
  fprintf ( 1, 'SVD_BASIS:\n' );
  fprintf ( 1, '  Ready to write the left singular vectors to files.\n' );
  fprintf ( 1, '\n' );
  fprintf ( 1, '  Do you want comments in the header of the file?\n' );
  fprintf ( 1, '  (These begin with the "#" character.) (''Y''/''N'')\n' );

  comment_char = input ( '  Enter ''Y'' or ''N'': ' );

  if ( comment_char == 'Y' || comment_char == 'y' )
    comment = 1;
  else
    comment = 0;
  end

  basis_file = 'svd_000.txt';

  if ( average_normalization )

    fprintf ( 1, '\n' );
    fprintf ( 1, '  Writing average file "%s".\n', basis_file );

    average_value = 0.0;
    
    table = reshape ( point_average(1:dim_num,1), comp_num, node_num );
    r8mat_write ( basis_file, comp_num, node_num, table );

  end

  for j = 1 : basis_num

    basis_file = file_name_inc_nowrap ( basis_file );

    if ( j == 1 )
      fprintf ( 1, '\n' );
      fprintf ( 1, '  Writing first file "%s".\n', basis_file );
    end

    if ( j == basis_num )
      fprintf ( 1, '  Writing last file  "%s"\n', basis_file );
    end

    table = reshape ( point(1:dim_num,j), comp_num, node_num );
    r8mat_write ( basis_file, comp_num, node_num, table );

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

  return
end
function column_num = file_column_count ( input_file_name )

%*****************************************************************************80
%
%% FILE_COLUMN_COUNT counts the columns in the first line of a file.
%
%  Discussion:
%
%    The file is assumed to be a simple text file.
%
%    Most lines of the file are presumed to consist of COLUMN_NUM words,
%    separated by spaces.  There may also be some blank lines, and some 
%    comment lines, which have a "#" in column 1.
%
%    The routine tries to find the first non-comment non-blank line and
%    counts the number of words in that line.
%
%    If all lines are blanks or comments, it goes back and tries to analyze
%    a comment line.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license. 
%
%  Modified:
%
%    21 February 2004
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, string INPUT_FILE_NAME, the name of the file.
%
%    Output, integer COLUMN_NUM, the number of columns in the file.
%
  FALSE = 0;
  TRUE = 1;
%
%  Open the file.
%
  input_unit = fopen ( input_file_name );

  if ( input_unit < 0 ) 
    fprintf ( 1, '\n' );
    fprintf ( 1, 'FILE_COLUMN_COUNT - Error!\n' );
    fprintf ( 1, '  Could not open the file "%s".\n', input_file_name );
    error ( 'FILE_COLUMN_COUNT - Error!' );
  end
%
%  Read one line, but skip blank lines and comment lines.
%  Use FGETL so we drop the newline character!
%
  got_one = FALSE;

  while ( 1 )

    line = fgetl ( input_unit );

    if ( line == -1 )
      break;
    end

    if ( s_len_trim ( line ) == 0 )

    elseif ( line(1) == '#' )

    else
      got_one = TRUE;
      break;
    end

  end

  fclose ( input_unit );

  if ( got_one == FALSE ) 
    fprintf ( 1, '\n' );
    fprintf ( 1, 'FILE_COLUMN_COUNT - Warning!\n' );
    fprintf ( 1, '  The file does not seem to contain any data.\n' );
    column_num = -1;
    return;
  end

  column_num = s_word_count ( line );

  return
end
function value = file_exist ( file_name )

%*****************************************************************************80
%
%% FILE_EXIST reports whether a file exists.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    29 October 2004
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, character FILE_NAME, the name of the file.
%
%    Output, logical FILE_EXIST, is TRUE if the file exists.
%
  fid = fopen ( file_name );

  if ( fid == -1 ) 
    value = 0;
  else
    fclose ( fid );
    value = 1;
  end

  return
end
function file_name = file_name_inc_nowrap ( file_name )

%*****************************************************************************80
%
%% FILE_NAME_INC_NOWRAP increments a partially numeric filename.
%
%  Discussion:
%
%    It is assumed that the digits in the name, whether scattered or
%    connected, represent a number that is to be increased by 1 on
%    each call.  Non-numeric letters of the name are unaffected..
%
%    If the (nonempty) name contains no digits, or all the digits are
%    9, then the empty string is returned.
%
%    If the empty string is input, the routine stops.
%
%  Example:
%
%      Input            Output
%      -----            ------
%      'a7to11.txt'     'a7to12.txt'  (typical case.  Last digit incremented)
%      'a7to99.txt'     'a8to00.txt'  (last digit incremented, with carry.)
%      'a8to99.txt'     'a9to00.txt'
%      'a9to99.txt'     ' '
%      'cat.txt'        ' '
%      ' '              STOP!
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    22 November 2011
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, string FILE_NAME, the string to be incremented.
%
%    Output, string FILE_NAME, the incremented string.
%
  lens = s_len_trim ( file_name );

  if ( lens <= 0 )
    fprintf ( 1, '\n' );
    fprintf ( 1, 'FILE_NAME_INC_NOWRAP - Fatal error!\n' );
    fprintf ( 1, '  The input filename is empty.\n' );
    error ( 'FILE_NAME_INC_NOWRAP - Fatal error!' );
  end

  change = 0;
  carry = 0;

  for i = lens : -1 : 1

    c = file_name(i);

    if ( '0' <= c && c <= '9' )

      change = change + 1;

      if ( c == '9' )
        c = '0';
        carry = 1;
      else
        c = c + 1;
        carry = 0;
      end
    
      file_name(i) = c;

      if ( c ~= '0' )
        return
      end

    end

  end
%
%  Unsatisfied carry.  The input digits were all 9.  Return blank.
%
  if ( carry == 1 )
    file_name = ' ';
  end
%
%  No digits were found.  Return blank.
%
  if ( change == 0 )
    file_name = ' ';
  end

  return
end
function row_num = file_row_count ( input_file_name )

%*****************************************************************************80
%
%% FILE_ROW_COUNT counts the number of row records in a file.
%
%  Discussion:
%
%    Each input line is a "RECORD".
%
%    The records are divided into three groups:
%    
%    * BLANK LINES (nothing but blanks)
%    * COMMENT LINES (begin with a '#')
%    * DATA RECORDS (anything else)
%
%    The value returned by the function is the number of data records.
%
%    By the way, if the MATLAB routine FGETS is used, instead of
%    FGETL, then the variable LINE will include line termination 
%    characters, which means that a blank line would not actually
%    have zero characters.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license. 
%
%  Modified:
%
%    31 December 2006
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, string INPUT_FILE_NAME, the name of the input file.
%
%    Output, integer ROW_NUM, the number of rows found. 
%
  input_unit = fopen ( input_file_name );

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

  blank_num = 0;
  comment_num = 0;
  row_num = 0;
  
  record_num = 0;

  while ( 1 )

    line = fgetl ( input_unit );

    if ( line == -1 )
      break;
    end

    record_num = record_num + 1;
    record_length = s_len_trim ( line );
    
    if ( record_length <= 0 )
      blank_num = blank_num + 1;
    elseif ( line(1) == '#' )
      comment_num = comment_num + 1;
    else
      row_num = row_num + 1;
    end

  end

  fclose ( input_unit );

  return
end
function table = r8mat_data_read ( input_filename, m, n )

%*****************************************************************************80
%
%% R8MAT_DATA_READ reads data from an R8MAT file.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    27 January 2006
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, string INPUT_FILENAME, the name of the input file.
%
%    Input, integer M, N, the number of rows and columns of data.
%
%    Output, real TABLE(M,N), the point coordinates.
%
  table = [];
%
%  Build up the format string for reading M real numbers.
%
  string = ' ';

  for i = 0 : m
    string = strcat ( string, ' %f' );
  end

  input_unit = fopen ( input_filename );

  if ( input_unit < 0 ) 
    fprintf ( 1, '\n' );
    fprintf ( 1, 'R8MAT_DATA_READ - Error!\n' );
    fprintf ( 1, '  Could not open the file.\n' );
    error ( 'R8MAT_DATA_READ - Error!' );
    return;
  end

  i = 0;

  while ( i < n )

    line = fgets ( input_unit );

    if ( line == -1 )
      break;
    end

    if ( line(1) == '#' )

    elseif ( s_len_trim ( line ) == 0 )
      
    else

      [ x, count ] = sscanf ( line, string );

      if ( count == m )
        i = i + 1;
        table(1:m,i) = x(1:m);
      end

    end

  end

  fclose ( input_unit );

  return
end
function [ m, n ] = r8mat_header_read ( input_filename )

%*****************************************************************************80
%
%% R8MAT_HEADER_READ reads the header from an R8MAT file.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    22 October 2004
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, string INPUT_FILENAME, the name of the input file.
%
%    Output, integer M, the spatial dimension.
%
%    Output, integer N, the number of points.
%
  m = file_column_count ( input_filename );

  if ( m <= 0 )
    fprintf ( 1, '\n' );
    fprintf ( 1, 'R8MAT_HEADER_READ - Fatal error!\n' );
    fprintf ( 1, '  There was some kind of I/O problem while trying\n' );
    fprintf ( 1, '  to count the number of data columns in\n' );
    fprintf ( 1, '  the file %s.\n', input_filename );
  end

  n = file_row_count ( input_filename );

  if ( n <= 0 )
    fprintf ( 1, '\n' );
    fprintf ( 1, 'R8MAT_HEADER_READ - Fatal error!\n' );
    fprintf ( 1, '  There was some kind of I/O problem while trying\n' );
    fprintf ( 1, '  to count the number of data rows in\n' );
    fprintf ( 1, '  the file %s\n', input_filename );
  end

  return
end
function r8mat_write ( output_filename, m, n, table )

%*****************************************************************************80
%
%% R8MAT_WRITE writes an R8MAT file.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    09 August 2009
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, string OUTPUT_FILENAME, the output filename.
%
%    Input, integer M, the spatial dimension.
%
%    Input, integer N, the number of points.
%
%    Input, real TABLE(M,N), the points.
%

%
%  Open the file.
%
  output_unit = fopen ( output_filename, 'wt' );

  if ( output_unit < 0 ) 
    fprintf ( 1, '\n' );
    fprintf ( 1, 'R8MAT_WRITE - Error!\n' );
    fprintf ( 1, '  Could not open the output file.\n' );
    error ( 'R8MAT_WRITE - Error!' );
  end
%
%  Write the data.
%
%  For greater precision, try:
%
%     fprintf ( output_unit, '  %24,16f', table(i,j) );
%
  for j = 1 : n
    for i = 1 : m
      fprintf ( output_unit, '  %14f', table(i,j) );
    end
    fprintf ( output_unit, '\n' );
  end
%
%  Close the file.
%
  fclose ( output_unit );

  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 [ u, s ] = singular_vectors ( m, n, basis_num, a )

%*****************************************************************************80
%
%% SINGULAR_VECTORS computes the desired singular values.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    15 May 2007
%
%  Author:
%
%    John Burkardt
%
%  Reference:
%
%    Edward Anderson, Zhaojun Bai, Christian Bischof, Susan Blackford,
%    James Demmel, Jack Dongarra, Jeremy Du Croz, Anne Greenbaum,
%    Sven Hammarling, Alan McKenney, Danny Sorensen,
%    LAPACK User's Guide,
%    Third Edition,
%    SIAM, 1999,
%    LC: QA76.73.F25L36
%
%  Parameters:
%
%    Input, integer M, the number of spatial dimensions.
%
%    Input, integer N, the number of data points.
%
%    Input, integer BASIS_NUM, the number of basis vectors to be extracted.
%
%    Input, real A(M,N), the matrix whose singular values 
%    are to be computed.  
%
%    Output, real U(M,BASIS_NUM), the first BASIS_NUM left singular vectors.
%
%    Output, real S(BASIS_NUM), the first BASIS_NUM singular values.
%
  fprintf ( 1, '\n' );
  fprintf ( 1, 'SINGULAR_VECTORS\n' );
  fprintf ( 1, '  For an MxN matrix A in general storage,\n' );
  fprintf ( 1, '  The LAPACK routine DGESVD computes the\n' );
  fprintf ( 1, '  singular value decomposition:\n' );
  fprintf ( 1, '\n' );
  fprintf ( 1, '    A = U * S * V''\n' );
  fprintf ( 1, '\n' );

  [ u, s, v ] = svd ( a );
%
%  Copy out the first BASIS_NUM singular values.
%
  s = s(1:basis_num);
%
%  Copy out the first BASIS_NUM singular vectors.
%
  u = u(1:m,1:basis_num);

  fprintf ( 1, '\n' );
  fprintf ( 1, '  The leading singular values:\n' );
  fprintf ( 1, '\n' );

  for i = 1 : basis_num
    fprintf ( 1, '  %4d  %16f\n', i, s(i) );
  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
