function out = SPDFBREAKDOWNEPOCH16(epoch16)
%SPDFBREAKDOWNEPOCH16 converts the CDF_EPOCH16 time, picoseconds since
%             0000-01-01 to UTC date/time.
%
%   OUT = SPDFBREAKDOWNEPOCH16(epoch16) returns the UTC date/time from CDF_EPOCH16
%   time. OUT is an array with each row having ten (10) numerical values
%   for year, month, day, hour, minute, second, millisecond, microsecond,
%   nanosecond and picosecond..
%
%     epoch16            An array with each row having two (2) numerical
%                        value in CDF_EPOCH16 of mxDOUBLE_CLASS (double).
%
%   Examples:
%
%   % breakdown three CDF_EPOCH16 times to their UTC form.
%
%   data = spdfparseepoch16 (['2009-01-01T00:00:00.123456789123', ...
%                             '2009-01-01T12:00:00.987654321123', ...
%                             '2009-01-01T12:34:56.123456789123']);
%   out = SPDFBREAKDOWNEPOCH16(data)
%   ans =
%
%        2009     1     1     0      0     0    123    456    789  123
%        2009     1     1    12      0     0    987    654    321  123
%        2010     1     1    12     34    56    123    456    789  123
%
%
%   See also SPDFENCODEEPOCH16, SPDFPARSEEPOCH16, SPDFCOMPUTEEPOCH16


% HISTORY:
%   August 16, 2014  Mike Liu    The initial version.

%
% Process arguments.
%

if (nargin < 1)
    error('MATLAB:SPDFBREAKDOWNEPOCH16:inputArgumentCount', ...
          'SPDFBREAKDOWNEPOCH16 requires at least one input argument.')
end

if (nargout > 1)
    error('MATLAB:SPDFBREAKDOWNEPOCH16:outputArguments', ...
          'SPDFBREAKDOWNEPOCH16 requires only one output argument.')
end

if (~isa(epoch16,'numeric'))
    error('MATLAB:SPDFBREAKDOWNEPOCH16:inputArguments', ...
          'SPDFBREAKDOWNEPOCH16 requires the input to be in numeric.')
end

ss=size(epoch16);
if (ss(1) < 1 || ss(2) ~= 2)
    error('MATLAB:SPDFBREAKDOWNEPOCH16:inputArguments', ...
          'SPDFBREAKDOWNEPOCH16 requires each row to have just two (2) field.')
end

  out = spdfbreakdownepoch16c (epoch16);
end
function out = SPDFBREAKDOWNEPOCH(epoch)
%SPDFBREAKDOWNEPOCH converts the CDF_EPOCH time, milliseconds since
%             0000-01-01 to UTC date/time.
%
%   OUT = SPDFBREAKDOWNEPOCH(epoch) returns the UTC date/time from CDF_EPOCH
%   time. OUT is an array with each row having seven (7) numerical values
%   for year, month, day, hour, minute, second, millisecond.
%
%     epoch            A vector with each row having one (1) numerical
%                      value in CDF_EPOCH of mxDOUBLE_CLASS (double).
%
%   Examples:
%
%   % breakdown three CDF_EPOCH times to their UTC form.
%
%   data = spdfparseepoch (['2009-01-01T00:00:00.123', ...
%                           '2009-01-01T12:00:00.987', ...
%                           '2009-01-01T12:34:56.123']);
%   out = SPDFBREAKDOWNEPOCH(data)
%   ans =
%
%        2009     1     1     0      0     0    123
%        2009     1     1    12      0     0    987
%        2010     1     1    12     34    56    123
%
%
%   See also CDFEPOCH, SPDFENCODEEPOCH, SPDFPARSEEPOCH, SPDFCOMPUTEEPOCH


% HISTORY:
%   August 16, 2014  Mike Liu    The initial version.

%
% Process arguments.
%

if (nargin < 1)
    error('MATLAB:SPDFBREAKDOWNEPOCH:inputArgumentCount', ...
          'SPDFBREAKDOWNEPOCH requires at least one input argument.')
end

if (nargout > 1)
    error('MATLAB:SPDFBREAKDOWNEPOCH:outputArguments', ...
          'SPDFBREAKDOWNEPOCH requires only one output argument.')
end

ss=size(epoch);
if (ss(1) < 1 || ss(2) ~= 1)
    error('MATLAB:SPDFBREAKDOWNEPOCH:inputArguments', ...
          'SPDFBREAKDOWNEPOCH requires each row to have just one (1) field.')
end

  out = spdfbreakdownepochc (epoch);
end
function out = SPDFBREAKDOWNTT2000(tt2000)
%SPDFBREAKDOWNTT2000 converts the CDF TT2000 time, nanoseconds since
%             2000-01-01 12:00:00 to UTC date/time.
%
%   OUT = SPDFBREAKDOWNTT2000(tt2000) returns the UTC date/time from CDF TT2000
%   time. OUT is an array with each row having nine (9) numerical values
%   for year, month, day, hour, minute, second, millisecond, microsecond
%   and nanosecond.
%
%     tt2000             A vector with each row having one (1) numerical
%                        value in CDF TT2000 of mxINT64_CLASS (int64).
%
%   Examples:
%
%   % breakdown three CDF TT2000 times to their UTC form.
%
%   data = [ 284040066307456789; 284083267171654321; 315621362307456789];
%   out = SPDFBREAKDOWNTT2000(data)
%   ans =
%
%        2009     1     1     0      0     0    123    456    789
%        2009     1     1    12      0     0    987    654    321
%        2010     1     1    12     34    56    123    456    789
%
%
%   See also CDFTT2000, SPDFENCODETT2000, SPDFPARSETT2000, SPDFCOMPUTETT2000

% HISTORY:
%   August 16, 2014  Mike Liu    The initial version.

%
% Process arguments.
%

if (nargin < 1)
    error('MATLAB:SPDFBREAKDOWNTT2000:inputArgumentCount', ...
          'SPDFBREAKDOWNTT2000 requires at least one input argument.')
end

if (nargout > 1)
    error('MATLAB:SPDFBREAKDOWNTT2000:outputArguments', ...
          'SPDFBREAKDOWNTT2000 requires only one output argument.')
end

if (~isa(tt2000,'numeric'))
    error('MATLAB:SPDFBREAKDOWNTT2000:inputArguments', ...
          'SPDFBREAKDOWNTT2000 requires the input to be in numeric.')
end

ss=size(tt2000);
if (ss(1) < 1 || ss(2) ~= 1)
    error('MATLAB:SPDFBREAKDOWNTT2000:inputArguments', ...
          'SPDFBREAKDOWNTT2000 requires each row to have just one (1) field.')
end

if (~isa(tt2000, 'int64'))
  out = spdfbreakdowntt2000c (int64(tt2000(:)));
else
  out = spdfbreakdowntt2000c (tt2000);
end
function info = spdfcdfinfo(filename, varargin)
%SPDFCDFINFO Get details about a CDF file.
%   INFO = SPDFCDFINFO(FILE) gives information about a Common Data Format
%   (CDF) file.  INFO is a structure containing the following fields:
%
%     Filename             A string containing the name of the file
%
%     FileModDate          A string containing the modification date of
%                          the file
%
%     FileSize             An integer indicating the size of the file in
%                          bytes
%
%     Format               A string containing the file format (CDF)
%
%     FormatVersion        A string containing the version of the CDF
%                          library used to create the file
%
%     FileSettings         A structure containing the file settings
%                          describing the file
%
%     Subfiles             A cell array of filenames which contain the
%                          CDF file's data if it is a multifile CDF
%
%     Variables            A cell array containing details about the
%                          variables in the file (see below)
%
%     GlobalAttributes     A structure containing the global meta-data
%
%     VariableAttributes   A structure containing meta-data for the
%                          variables
%
%     LibVersion           A string containing the library version of 
%                          the CDF that is used to build the SPDFCDFinfo tool
%
%     PatchVersion         A string containing the patch version of 
%                          the MATLAB-CDF modules, e.g., spdfcdfinfo,
%                          spdfcdfread, spdfcdfwrite, and its release
%
%   Note: The CDF file name can be passed in as a null. In this case, INFO
%         contains only the CDF library version and MATLAB-CDF version.  
%
%   The "Variables" field contains a cell array of details about the
%   variables in the CDF file.  There are two presentations that can be
%   acquired. The original form is presented as the following:
%   each row represents a variable in the file.  The columns are:
%
%     (1) The variable's name as a string.
%
%     (2) The dimensions of the variable according to MATLAB's SIZE
%         function. 
%
%     (3) The number of records assigned for this variable.
%
%     (4) The variable's data type as it is stored in the CDF file.
%
%     (5) The record and dimension variance settings for the variable.
%         The value to the left of the slash designates whether values
%         vary by record; the values to the right designate whether
%         values vary at each dimension.
%
%     (6) The sparsity of the variables records.  Allowable values are
%         'Full', 'Sparse(padded)', and 'Sparse(previous)'.
%
%     (7) The compression of the variables.  Allowable values are
%         'None', 'RLE', 'HUFF', 'AHUFF', and 'GZIP.x' where x is the 
%         GZIP compression level.
%
%     (8) The blocking factors of the variables, if set.  A blocking factor is
%         the chunk of records to be pre-allocated for a standard variable when
%         a record being written does not already exist, or the number of
%         records to be compressed together for a compressed variable. For 
%         variables with large record size or number, providing a large 
%         blocking factor would have a significant impact on I/O performance
%         over the defaults.
%
%     (9) The pad values of the variables, if set. 
%    (10) The FILLVAL attribute entry values of the variables, if set. 
%    (11) The VALIDMIN attribute entry values of the variables, if set. 
%    (12) The VALIDMAX attribute entry values of the variables, if set. 
%
%   varinfo = spdfcdfinfo (FILE, 'VARIABLES', {'var1', 'var2', ...});
%   The optional 'VARIABLES' can be used to specify the variables that their
%   info is to be returned. The info includes only two varibale related fields:
%   Variables and VariableAttributes. No CDF info, e.g., Filename, Format, etc,
%   will be retrieved. A null is filled if a variable is not found in the CDF.
%
%   info = spdfcdfinfo (FILE, 'VARSTRUCT', TF);
%   The returned Variable field can also be presented in a structure form, if 
%   the option is provided with a true value.
%
%   The structure has the following fields, each one is a cell array.
%
%     Name                 A string containing the name of the variable
%
%     Dimensions           The dimensions of the variable
%
%     NumRecords           The number of written records for the variable
%
%     DataType             The data type of the variable
%
%     RecDimVariance       Record and dimensional variances of the variable
%
%     Sparseness           The sparseness of the variable
%
%     Compression          The Compression of the variablee
%
%     BlockingFactor       The blocking factor of the variable
%
%     PadValue             The pad value of the variable
%
%     FILLVAL              The FILLVAL attribute entry value for the variable
%
%     VALIDMIN             The VALIDMIN attribute entry value for the variable
%
%     VALIDMAX             The VALIDMAX attribute entry value for the variable
%
%   info = spdfcdfinfo (FILE, 'VALIDATE', TF);
%   This is to specify whether the CDF is to be validated when it's open. The
%   default is NOT to valdate the file so the processing can be faster. There
%   are two ways to set the data validation: setting the environment variable
%   CDF_VALIDATE to "yes" outside of the MATLAB environment, or using the
%   option 'VALIDATE' with true value when calling this module. If a CDF has 
%   been validated before, there is no need to validate it over and over again.
%
%   The "GlobalAttributes" and "VariableAttributes" structures contain a
%   field for each attribute.  Each field's name corresponds to the name
%   of the attribute, and the field contains a cell array containing the
%   entry values for the attribute.  For variable attributes, the first
%   column of the cell array contains the Variable names associated with
%   the entries, and the second contains the entry values.
%
%   NOTE: Attribute names which SPDFCDFINFO uses for field names in
%   "GlobalAttributes" and "VariableAttributes" may not match the names
%   of the attributes in the CDF file exactly.  Because attribute names
%   can contain characters which are illegal in MATLAB field names, they
%   may be translated into legal field names.  Illegal characters which
%   appear at the beginning of attributes are removed; other illegal
%   characters are replaced with underscores ('_').  If an attribute's
%   name is modified, the attribute's internal number is appended to the
%   end of the field name.  For example, '  Variable%Attribute ' might
%   become 'Variable_Attribute_013'.
%
%   To get the CDF library version that is used to build the current MATLAB
%   tool programs, e.g., spdfcdfread, spdfcdfwrite, spdfcdfinfo, you can
%   simply enter the command without providing a CDF file:
%   spdfcdfinfo()
%
%   Library version may differ from a CDF file's version. A CDF file is
%   assigned with the library version when it is created or modified.
%
%   Notes:
%
%     SPDFCDFINFO creates temporary files when accessing CDF files.  The
%     current working directory must be writable.
%
%
%   See also SPDFCDFREAD, SPDFCDFUPDATE, SPDFCDFWRITE, CDFEPOCH, CDFTT2000,
%            SPDFENCODEEPOCH, SPDFCOMPUTEEPOCH, SPDFPARSEEPOCH,
%            SPDFBREAKDOWNEPOCH, SPDFENCODEEPOCH16, SPDFCOMPUTEEPOCH16,
%            SPDFPARSEEPOCH16, SPDFBREAKDOWNEPOCH16, SPDFENCODETT2000,
%            SPDFCOMPUTETT2000, SPDFPARSETT2000, SPDFBREAKDOWNTT2000,
%            SPDFDATENUMTOEPOCH, SPDFDATENUMTOEPOCH16, SPDFDATENUMTOTT2000,
%            SPDFCDFLEAPSECONDSINFO

%
% HISTORY:
%   August 17, 2007   David Han      Modified to handle CDF_EPOCH16. Look for
%                                    'epoch16'.
%   July 17, 2009     Mike Liu       Added a new field 'LibVersion' for the
%                                    returned structure to show the library 
%                                    version. 'PatchVersion' will show the
%                                    patch version of the MATLAB release.
%   August 10, 2010   Mike Liu       Added INT8 and TT2000 data types.
%

%
% Process arguments.
%

%if (argin < 1)
%  if ~(nargin == 0) || ~(length(strtrim(filename)) == 0)
%    error('MATLAB:spdfcdfinfo:inputArguments', 'SPDFCDFINFO requires at least one input argument.')
%  end
%end

if ~(nargout == 1)
  if ~(nargin == 0) && ~(length(strtrim(filename)) == 0)
    error('MATLAB:spdfcdfinfo:outputArguments', 'SPDFCDFINFO requires one output argument.')
  end
end

% CDFlib creates temporary files in the current directory.  Make sure PWD is
% writable.
%[attrib_success, attrib_mode] = fileattrib(pwd);
%
%if (~attrib_mode.UserWrite)
%    error('Cannot create temporary files.  The current directory must be writable.')
%end

%
% Returned structure
%

info1.Filename = '';
info1.FileModDate = '';
info1.FileSize = '';
info1.Format = '';
info1.FormatVersion = '';
info1.FileSettings = [];
info1.FileSettings = {};
info1.Subfiles = {};
info1.Variables = {};
info1.GlobalAttributes = [];
info1.VariableAttributes = [];
info1.LibVersion = '';
info1.PatchVersion = '3.7.1.0';
info2.Variables = {};
info2.VariableAttributes = [];
args.VarStruct = false;
args.Variables = {};
args.Validate = false;
variables = false;

if (nargin == 0) || (length(strtrim(filename)) == 0)
    % Only for library info
    tmp = spdfcdfinfoc(' ');
    % Library version.
    theLibVersion = sprintf('%d.%d.%d', tmp.LibVersion.Version, ...
                                        tmp.LibVersion.Release, ...
                                        tmp.LibVersion.Increment);
    disp(['LibVersion: ',theLibVersion]);
    disp(['PatchVersion: ', info1.PatchVersion]);
else    
  % Parse arguments based on their number.
  if (nargin > 0)
    paramStrings = {'variables'
                    'validate'
                    'varstruct'};

    % For each pair
    for k = 1:2:length(varargin)
       param = lower(varargin{k});

       idx = strmatch(param, paramStrings);

       if (isempty(idx))
           msg = sprintf('Unrecognized parameter name "%s".', param);
           return
       elseif (length(idx) > 1)
           msg = sprintf('Ambiguous parameter name "%s".', param);
           return
       end

       switch (paramStrings{idx})
         case 'varstruct'
               varstruct = varargin{k + 1};

               if (islogical(varstruct))
                   args.VarStruct = varstruct;
               elseif (isnumeric(varstruct))
                   args.VarStruct = logical(varstruct);
               else
                   msg = 'The "varstruct" value must be a scalar logical.';
               end

         case 'validate'
               validate = varargin{k + 1};

               if (islogical(validate))
                   args.Validate = validate;
               elseif (isnumeric(validate))
                   args.Validate = logical(validate);
               else
                   msg = 'The "validate" value must be a scalar logical.';
               end

         case 'variables'
           if (k == length(varargin))
               msg = 'No variables specified.';
               return
           else
               args.Variables = varargin{k + 1};
               if (~iscell(args.Variables))
                   args.Variables = {args.Variables};
               end
               for p = 1:length(args.Variables)
                   if (~ischar(args.Variables{p}))
                       msg = 'All variable names must be strings.';
                       return
                   end
               end
	       if (length(args.Variables) > 0)
		 variables = true;
	       end
           end

       end  % switch
    end  % for
  end
  % Get full filename.
  fid = fopen(filename);

  %
  % Verify existence of filename.
  %
  if (fid == -1)
  
  % Look for filename with extensions.
      fid = fopen([filename '.cdf']);
    
      if (fid == -1)
          fid = fopen([filename '.CDF']);
      end
    
  end

  if (fid == -1)
      error('MATLAB:spdfcdfinfo:fileOpen', 'Couldn''t open file (%s).', filename)
  else
      filename = fopen(fid);
      fclose(fid);
  end
  if ~variables
    %
    % Record the file details.
    %
    d = dir(filename);
    % Set the positions of the fields.
    info1.Filename = d.name;
    info1.FileModDate = d.date;
    info1.FileSize = d.bytes;
    info1.Format = 'CDF';
  end
  % CDFlib's OPEN_ routine is flakey when the extension ".cdf" is used.
  % Strip the extension from the file before calling the MEX-file.

  if ((length(filename) > 4) && (isequal(lower(filename((end-3):end)), '.cdf')))
      filename((end-3):end) = '';
  end
  % Get the attribute, variable, and library details.

  tmp = spdfcdfinfoc(filename, args.VarStruct, args.Variables, args.Validate);

  if ~variables
    % Process file attributes.
    info1.FileSettings = parse_file_info(tmp.File);
    info1.FormatVersion = info1.FileSettings.Version;
    info1.FileSettings = rmfield(info1.FileSettings, 'Version');
    % Handle multifile CDF's.
    if isequal(info1.FileSettings.Format, 'Multifile')

      d = dir([filename '.v*']);
  
      for p = 1:length(d)
        info1.Subfiles{p} = d(p).name;
      end
    
    end
  end
  if ~args.VarStruct
     % Process variable table.
     vars = tmp.Variables;
     types = vars(:, 4);
     otypes = vars(:, 4);
     sp = vars(:, 6);
     pv = vars(:, 9);
     for p = 1:length(types)
       types{p} = find_datatype(types{p});
       sp{p} = find_sparsity(sp{p});
       pv{p} = find_padvalue(otypes{p}, pv{p});
     end
     vars(:, 4) = types;
     vars(:, 6) = sp;
     vars(:, 9) = pv;
     if ~variables
       info1.Variables = vars;
     else
       info2.Variables = vars;
     end
  else
     vars = tmp.Variables;
     types = vars.DataType;
     otypes = vars.DataType;
     sp = vars.Sparseness;
     pv = vars.PadValue;
     for p = 1:length(types)
       types{p} = find_datatype(types{p});
       sp{p} = find_sparsity(sp{p});
       pv{p} = find_padvalue(otypes{p}, pv{p});
     end
     vars.DataType = types;
     vars.Sparseness = sp;
     vars.PadValue = pv;
     if ~variables
       info1.Variables = vars;
     else
       info2.Variables = vars;
     end
  end
  % Assign rest.
  if ~variables
    info1.GlobalAttributes = tmp.GlobalAttributes;
  end
  if ~variables
    info1.VariableAttributes = tmp.VariableAttributes;
  else
    info2.VariableAttributes = tmp.VariableAttributes;
  end
  if ~variables
    info1.LibVersion = sprintf('%d.%d.%d', tmp.LibVersion.Version, ...
                                           tmp.LibVersion.Release, ...
                                           tmp.LibVersion.Increment);
  end
  if ~variables
    info = info1;
  else
    info = info2;
  end
end

function out = parse_file_info(in)

    out = in;

    % Format.
    if (in.Format == 2)
        out.Format = 'Multifile';
    else
        out.Format = 'Single-file';
    end

    % Encoding.
    out.Encoding = find_encoding(in.Encoding);

    % Majority.
    if (in.Majority == 1)
        out.Majority = 'Row';
    else
        out.Majority = 'Column';
    end

    % Version.
    out.Version = sprintf('%d.%d.%d', in.Version, in.Release, in.Increment);
    out = rmfield(out, {'Release', 'Increment'});

    % Compression.
    [comp_type, comp_param, comp_pct] = find_compression(in.Compression, ...
                                                  in.CompressionParam, ...
                                                  in.CompressionPercent);

    out.Compression = comp_type;
    out.CompressionParam = comp_param;
    out.CompressionPercent = comp_pct;

    % Checksum.
    if (in.Checksum == 1)
        out.Checksum = 'MD5';
    else
        out.Checksum = 'None';
    end

    % LeapSecondLastUpdated.
    if (in.LeapSecondLastUpdated >= 0)
      out.LeapSecondLastUpdated = in.LeapSecondLastUpdated;
    else
      out.LeapSecondLastUpdated = 'Not set';
    end

function str = find_datatype(num)
if (isempty(num))
  str = [];
else
  switch (num)
    case {1, 41}
      str = 'int8';
    case {2}
      str = 'int16';
    case {4}
      str = 'int32';
    case {8}
      str = 'int64';
    case {11}
      str = 'uint8';
    case {12}
      str = 'uint16';
    case {14}
      str = 'uint32';
    case {21, 44}
      str = 'single';
    case {22, 45}
      str = 'double';
    case {31}
      str = 'epoch';
    case {32}
      str = 'epoch16';
    case {33}
      str = 'tt2000';
    case {51, 52}
      str = 'char';
  end
end

function str = find_padvalue(num, value)
if (isempty(value)) str = [];
else
  switch (num)
    case {1, 41}
      str = int8(value);
    case {2}
      str = int16(value);
    case {11}
      str = uint8(value);
    case {12}
      str = uint16(value);
    case {21, 44}
      str = single(value);
    case {22, 45}
      str = double(value);
    case {51, 52}
      str = value;
    case {8, 33}
      str = int64(value);
    case {4}
      str = int32(value);
    case {14}
      str = uint32(value);
    case {31}
      str = double(value);
    case {32}
      str = value;
  end
end

function str = find_sparsity(num)
if (isempty(num))
  str = [];
else
  switch (num)
    case 0
      str = 'Full';
    case 1
      str = 'Sparse(padded)';
    case 2
      str = 'Sparse(previous)';
  end
end

function str = find_encoding(num)
if (isempty(num))
  str = [];
else
  switch (num)
    case 1
      str = 'Network';
    case 2
      str = 'Sun';
    case 3
      str = 'Vax';
    case 4
      str = 'DECStation';
    case 5
      str = 'SGI';
    case 6
      str = 'IBM-PC';
    case 7
      str = 'IBM-RS';
    case 8
      str = 'Host';
    case 9
      str = 'Macintosh';
    case 11
      str = 'HP';
    case 12
      str = 'NeXT';
    case 13
      str = 'Alpha OSF1';
    case 14
      str = 'Alpha VMS d';
    case 15
      str = 'Alpha VMS g';
    case 16
      str = 'Alpha VMS i';
    case 17
      str = 'ARM Little';
    case 18
      str = 'ARM Big';
    case 19
      str = 'IA64 VMS i';
    case 20
      str = 'IA64 VMS d';
    case 21
      str = 'IA64 VMS g';

  end
end



function [ctype, param, pct] = find_compression(ctype, param, pct)
if (isempty(ctype))
  ctype = [];
  param = [];
  pct = [];
else
  switch (ctype)
    case 0
      ctype = 'Uncompressed';
      param = '';
      pct = [];
    case 1
      ctype = 'Run-length encoding';
      if (param == 0)
          param = 'Encoding of zeros';
      else
          param = '';
      end
    case 2
      ctype = 'Huffman';
      if (param == 0)
          param = 'Optimal encoding trees';
      else
          param = '';
      end
    case 3
      ctype = 'Adaptive Huffman';
      if (param == 0)
          param = 'Optimal encoding trees';
      else
          param = '';
      end
    case 4
      ctype = 'Rice';
      param = '';
    case 5
      ctype = 'Gzip';
  end 
end
function SPDFCDFLEAPSECONDSINFO(varargin)
%SPDFCDFLEAPSECONDSINFO shows the information how the leap seconds is used by
%CDF.
%
%   SPDFCDFLEAPSECONDSINFO() displays the basic setup the CDF library uses to 
%   acquire the leap second table.
%
%   SPDFCDFLEAPSECONDSINFO('DUMP', TF) the 'DUMP' option will show the contents
%   of the leap second table, in addition to the basic information, if TF is
%   true.
%
%   See also CDFTT2000, SPDFENCODETT2000, SPDFCOMPUTETT2000, SPDFPARSETT2000,
%            SPDFBREAKDOWNTT2000.

% HISTORY:
%   August 16, 2011  Mike Liu    The initial version.

%
% Process arguments.
%

[args, msg] = parse_inputs(varargin{:});
if (~isempty(msg))
    error('MATLAB:SPDFCDFLEAPSECONDSINFO:badInputArguments', '%s', msg)
end

spdfcdfleapsecondsinfoc(args.Dump);

%%%
%%% Function parse_inputs
%%%

function [args, msg] = parse_inputs(varargin)
% Set default values
args.Dump = false;
msg = '';
% Parse arguments based on their number.
if (nargin > 0)
    paramStrings = {'dump'};

    % For each pair
    for k = 1:2:length(varargin)
       param = lower(varargin{k});
       if (~ischar(param))
           msg = 'Parameter name must be a string.';
           return
       end

       idx = strmatch(param, paramStrings);

       if (isempty(idx))
           msg = sprintf('Unrecognized parameter name "%s".', param);
           return
       elseif (length(idx) > 1)
           msg = sprintf('Ambiguous parameter name "%s".', param);
           return
       end

       switch (paramStrings{idx})
       case 'dump'

           if (k == length(varargin))
               msg = 'No dump specified.';
               return
           else

               dump = varargin{k + 1};
               if (numel(dump) ~= 1)
                   msg = 'The "Dump" value must be a scalar logical.';
               end

               if (islogical(dump))
                   args.Dump = dump;
               elseif (isnumeric(dump))
                   args.Dump = logical(dump);
               else
                   msg = 'The "Dump" value must be a scalar logical.';
               end
           end

       end  % switch
    end  % for 
                   
end  % if (nargin > 1)


function [out, info] = spdfcdfread(filename, varargin)
%SPDFCDFREAD Read the data from a CDF file.
%   DATA = SPDFCDFREAD(FILE) reads all of the variables from each record of
%   FILE. Every piece of data from the CDF file is read and returned.
%
%   Note: To improve the performance and reduce the size of returned data,
%   especially working with large data files, the previously designated as
%   optional option 'Combinerecords' (with 'true' value) has been changed to be
%   the default. No need to specify this option, unless 'false' is requested.
%
%   DATA = SPDFCDFREAD(FILE, 'CombineRecords', TF, ...) combines all of the
%   records from each variable into a cell array with one row (1-by-N), where
%   N is the number of variables, if TF is true (the default). Each cell in
%   the cell array is either a scalar value or an array. Each cell may contain
%   a different number of data elements. For example, a single value(s) is
%   returned for a variable of non-record variant, while a multi-dimensional
%   array of data is returned for record-variant of scalar or dimensional
%   variables. For epoch data of CDF_EPOCH, CDF_EPOCH16 and CDF_TIME_TT2000
%   types, their values are automatically converted into MATLAB's datenum.
%   (To overwrite the conversion, use 'KeepEpochAsIs' option.)
%
%   If the option 'Combinerecords' is specified as false, DATA will be a cell
%   array of M-by-N, where M is the maximum number of record among the variables
%   and N is the number of variables. Each row corresponds to a record while
%   each column to a variable. This is the default output from MATLAB's
%   distributed CDFREAD module. The data from scalar variables are
%   imported into a column array.  Importing dimensional and string data
%   extends the dimensionality of the variable.  For example,
%   reading 1000 records of a 1-byte variable with dimensions of 20-by-30
%   yields a cell containing a 20-by-30-by-1000 UINT8 array. For data
%   from non-record variant variables, as their values never change from 
%   record to record, same value(s) will be repeated in all records.
%
%   DATA = SPDFCDFREAD(FILE, 'Records', RECNUMS, ...) reads particular
%   records from a CDF file.  RECNUMS is a vector of one or more
%   zero-based record numbers to read.  DATA is a cell array with
%   length(RECNUM) number of rows.  There are as many columns as
%   variables.
% 
%   DATA = SPDFCDFREAD(FILE, 'Variables', VARNAMES, ...) reads the variables
%   in the cell array VARNAMES from a CDF file.  DATA is a vector array
%   with length(VARNAMES) number of columns.  There is a row for each
%   record requested.
% 
%   DATA = SPDFCDFREAD(FILE, 'Slices', DIMENSIONVALUES, ...) reads specified
%   values from one variable in the CDF file.  The matrix DIMENSIONVALUES
%   is an m-by-3 array of "start", "interval", and "count" values.  The
%   "start" values are zero-based.
%
%   The number of rows in DIMENSIONVALUES must be less than or equal to
%   the number dimensions of the variable.  Unspecified rows are filled
%   with the values [0 1 N] to read every value from those dimensions.
% 
%   When using the 'Slices' parameter, only one variable can be read at a
%   time, so the 'Variables' parameter must be used.
% 
%   DATA = SPDFCDFREAD(FILE, 'ConvertEpochToDatenum', TF, ...) converts CDF 
%   epoch data values to MATLAB datenum if TF is true (the default if the 
%   'CombineRecords' is true). In this case, variable data will be presented in
%   an array of datenum. If TF is false (the default if 'CombineRecords' is
%   false), data of CDF_EPOCH type are wrapped in CDFEPOCH objects and
%   CDF_TIME_TT2000 in CDFTT2000 objects, which can hurt performance for large
%   datasets. For higher time resolution types as CDF_EPOCH16 and CDF_TIME_TT2000,
%   this option will cause the loss of sub-milliseconds resolution and not 
%   properly present the time at a leap second time.
%
%   DATA = SPDFCDFREAD(FILE, 'ConvertEpochToDatestr', TF, ...) converts CDF
%   epoch data values to MATLAB datestr if TF is true. This option is
%   similar to 'ConvertEpochToDatenum', with each CDF epoch returning as the 
%   form: dd-mmm-yyyy hh:mm:ss (all sub-milliseconds info is not displayed).
%   To display sub-milliseconds, use the 'CDFEpochToString' option.
%
%   DATA = SPDFCDFREAD(FILE, 'KeepEpochAsIs', TF, ...) whether to keep CDF 
%   Epoch data values as is.  If TF is set as true, no data conversion
%   from CDF epoch to MATLAB datenum is performed. The data values can
%   be written back to CDF later again without MATLAB datenum to CDF epoch
%   conversion. Each epoch will be kept as a double for CDF_EPOCH data, array
%   of doubles for CDF_EPOCH16 data, or an INT64 (mxINT64_CLASS) for
%   CDF_TIME_TT2000. If false, the default, all CDF epoch data will be converted
%   to MATLAB's datenum. CDF epoch values can be encoded, broken down, etc, by
%   epoch handling modules, e.g., spdfencodeepoch, spdfencodett2000, 
%   spdfbreakdownepoch, spdfbreakdowntt2000, etc.
%
%   DATA = SPDFCDFREAD(FILE, 'CDFEpochToString', TF, ...) whether to return 
%   CDF Epoch data values in strings, instead of numeric values. If TF
%   is set to true, each epoch data, by default, will be presented in the
%   form of dd-mon-yyyy hh:mm:ss.mmm for CDF_EPOCH, or
%   dd-mon-yyyy hh:mm:ss.mmm:uuu:nnn:ppp for CDF_EPOCH16, or
%   yyyy-mm-ddThh:mm:ss.mmmuuunnn for CDF_TIME_TT2000.
%
%   DATASTRUCT = SPDFCDFREAD(FILE, 'Structure', TF, ...) returns a structure
%   array (instead of a cell array) that contains the following
%   fields for each member (a variable) in the array if TF is true:
%       VariableName - variable name
%       Data - variable data in M-by-1 cell array without 'CombineRecords'
%              option, or a multi-dimensional array with 'CombineRecords'
%       Attributes - a structure that contains all the attributes that
%                    associated with this variable
%
%   DATA = SPDFCDFREAD(FILE, 'DATAONLY', TF, ...) specifies whether to simply 
%   just return the CDF variable data, without metadata, nor attribute info. 
%   This option provides the quick way of retrieving variable data as is, if
%   true, without any data conversion. The retrieved data will be in the same
%   form as the non-dataonly option, either a cell array for multiple
%   variables or a vector for a single variable. It works with these defined
%   options:
%    "CombineRecords", true, "KeepEpochAsis', true, "CDFEpochtoDatenum", false
%   without calling spdfcdfinfo for any info about the variables.
%   If the 'Variables' option is provided, only selected variables' data are
%   returned. Otherwise, all variables are retrieved. An empty matrix is
%   returned for the variable that is not found in the CDF. The dataonly option 
%   works faster as it only reads the data and it, unlike all other options,
%   will not call spdfcdfinfo to collect the vital CDF and variable info.
%   Such metadata and its variables' info can be acquired separately from
%   spdfcdfinfo call, if required.
%   
%   [DATA, INFO] = SPDFCDFREAD(FILE, ...) also returns details about the CDF
%   file in the INFO structure.
%
%   DATA = SPDFCDFREAD(FILE, 'VALIDATE', TF, ...) specifies whether to validate 
%   the CDF when it is open. The default is NOT to validate the file so its 
%   processing can be faster. There are two ways to set the data validation:
%   setting the environment variable CDF_VALIDATE to "yes" outside of the
%   MATLAB environment, or using the option 'VALIDATE' with true value when
%   calling this module. If a CDF has been validated before, there is no need to
%   validate it over and over again.
%
%   Notes:
%
%     SPDFCDFREAD creates temporary files when accessing CDF files.  The
%     current working directory must be writable.
%
%     Currently, it is not possible to provide a set of records to read
%     (using the 'Records' parameter) and to combine records (using the
%     'CombineRecords' parameter).
%
%     'ConvertEpochToDatenum', 'ConvertEpochToDatestr', 'KeepEpochAsIs' 
%     and 'CDFEpochToString' are mutually exclusive.
%
%   Examples:
%
%   % Read all of the data from example.cdf with the default of  true for
%   'Combinerecords' option, the most efficient way.
%
%   data = spdfcdfread('example');
%
%   % Read the same file as above and also return any CDF EPOCH or EPOCH16
%     variable data in the string form (dd-mon-yyyy hh:mm:ss.mmm for EPOCH, 
%     dd-mon-yyyy hh:mm:ss.mmm.uuu.nnn.ppp for EPOCH16 or 
%     yyyy-mm-ddThh:mm:ss.mmmuuunnn for CDF_TIME_TT2000) if they exist.
%
%   data = spdfcdfread('example', 'CDFEpochtoString', true);
%
%   % Read just the data from variable "Time".
%
%   data = spdfcdfread('example', 'Variable', {'Time'});
%
%   % Read the first value in the first dimension, the second value in
%   % the second dimension, the first and third values in the third
%   % dimension, and all of the values in the remaining dimension of
%   % the variable "multidimensional".  
%
%   data = spdfcdfread('example', 'Variable', {'multidimensional'}, ...
%                  'Slices', [0 1 1; 1 1 1; 0 2 2]);
%
%   % The example above is analogous to reading the whole variable 
%   % into a variable called "data" and then using matrix indexing, 
%   % as follows:
%
%   data = spdfcdfread('example', ...
%                  'Variable', {'multidimensional'});
%   data{1}(1, 2, [1 3], :)
%
%   % Displays the name of the variable being processed.
%
%   data = spdfcdfread('example', 'ShowProgress', true);
%
%   See also CDFEPOCH, CDFTT2000, SPDFCDFINFO, SPDFCDFWRITE, SPDFCDFUPDATE,
%            SPDFENCODEEPOCH, SPDFCOMPUTEEPOCH, SPDFBREAKDOWNEPOCH,
%            SPDFPARSEEPOCH, SPDFEPOCHTODATENUM, SPDFENCODEEPOCH16,
%            SPDFCOMPUTEEPOCH16, SPDFBREAKDOWNEPOCH16, SPDFPARSEEPOCH16,
%            SPDFEPOCH16TODATENUM, SPDFENCODETT2000, SPDFCOMPUTETT2000,
%            SPDFBREAKDOWNTT2000, SPDFPARSETT2000, SPDFDATENUMTOEPOCH,
%            SPDFDATENUMTOEPOCH16, SPDFDATENUMTOTT2000.

% HISTORY:
%   November 13, 2007  David Han    The following changes have been made to
%                                   spdfcdfreadc.c:
%                                     - Added a logic to read CDF_EPOCH and 
%                                       CDF_EPOCH16 data.
%                                     - Modified to return variable attributes
%                                       besides the variable data.
%                                     - Modified to read all the records in one 
%                                       read by default.
%
%                                   Removed the cdfepoch routine since it 
%                                      1) doesn't work with some CDF files
%                                      2) is no longer needed with the new
%                                          spdfcdfreadc.c 
%
%                                   No longer calls the find_records function
%                                   (since all the variable data records are
%                                   read in one read by default).
%                                     
%                                   spdfcdfread.m now returns a structure (instead
%                                   of just variable data) that contains the
%                                   following fields for each variable:
%
%                                       VariableName - variable name
%                                       Data - variable data
%                                       Attributes - a structure that contains
%                                                    all the attributes that 
%                                                    associated with this
%                                                    variable
%
%                                   Added the 'ShowProgress' option that 
%                                   displays the name of the variable being
%                                   processed.  This option can be useful if
%                                   a CDF file contains a lot of variables or
%                                   takes a long time to read all the 
%                                   variables.
%
%                     Mike Liu      Addendum... For the backward compatibility:
%                                      1) cdfepoch object is still supported, 
%                                         but it can't handle CDF_EPOCH16 data
%                                      2) data returned as a structure is now 
%                                         an option 
%
%   October 20, 2008  Mike Liu      The following change has been made to
%                                   spdfcdfreadc.c:
%                                     - Handled combining 'CombineRecords' and 
%                                       'ConvertEpochToDatenum' options 
%   February 4, 2009  Mike Liu      Added a dummy call to spdfcdfreadc after normal
%                                   calls. Previously, each spdfcdfreadc call
%                                   involves open/close the CDF file while 
%                                   reading a variable's data. Now, only the
%                                   first spdfcdfreadc call opens the file,
%                                   but it does not close the file anymore.
%                                   Instead, the dummy spdfcdfreadc call 
%                                   closes the file after all spdfcdfreadc
%                                   calls are done. 
%
%   February 23, 2009  Mike Liu     Added 'KeepEpochAsIs' option to keep
%                                   CDF epoch values in MATLAB. Added 
%                                   'ConvertEpochToDatestr' option. Not call 
%                                   convert_epoch anymore as CDF epoch values
%                                   to MATLAB datenum conversion is done in 
%                                   spdfcdfreadc (much faster) and it calls 
%                                   cdfepoch directly if needed.
%   September 9, 2011  Mike Liu     Changed to return UTC strings for
%                                   CDF_EPOCH16 as the default. Use 
%                                   epoch16todatenum to convert the strings
%                                   to MATLAB's datenum if
%                                   'ConvertEpochToDatenum' option is specified.
%   March 5, 2013  Mike Liu         Changed to 'KeepEpochAsIs' option to allow
%                                   returning CDF_EPOCH16 data into array of
%                                   doubles, instead of string.
%

%
% Process arguments.
%

if (nargin < 1)
    error('MATLAB:spdfcdfread:inputArgumentCount', ...
          'SPDFCDFREAD requires at least one input argument.')
end

if (nargout > 2)
    error('MATLAB:spdfcdfread:outputArguments', ...
          'SPDFCDFREAD requires two or fewer output argument.')
end

[args, msg, structure, show_progress] = parse_inputs(varargin{:});

if (args.DataOnly && (nargout == 2))
    error('MATLAB:spdfcdfread:outputArguments', ...
          'SPDFCDFREAD requires only one output argument for dataonly option.')
end

if (~isempty(msg))
    error('MATLAB:spdfcdfread:badInputArguments', '%s', msg)
end

validate_inputs(args);

if (~args.DataOnly) 
  if (args.CombineRecords)
    if (args.epochtodatenum == 1)
      args.ConvertEpochToDatestr = false;
      args.KeepEpochAsIs = false;
      args.CDFEpochToString = false;
    elseif (args.epochtodatestr == 1)
      args.ConvertEpochToDatenum = false;
      args.KeepEpochAsIs = false;
      args.CDFEpochToString = false;
    elseif (args.keepepoch == 1)
      args.ConvertEpochToDatenum = false;
      args.ConvertEpochToDatestr = false;
      args.CDFEpochToString = false;
    elseif (args.epochtostring == 1)
      args.ConvertEpochToDatenum = false;
      args.ConvertEpochToDatestr = false;
      args.KeepEpochAsIs = false;
    else
      args.ConvertEpochToDatenum = true;
      args.ConvertEpochToDatestr = false;
      args.KeepEpochAsIs = false;
      args.CDFEpochToString = false;
    end
  else
    if (args.epochtodatenum == 1)
      args.ConvertEpochToDatestr = false;
      args.KeepEpochAsIs = false;
      args.CDFEpochToString = false;
    elseif (args.epochtodatestr == 1)
      args.ConvertEpochToDatenum = false;
      args.KeepEpochAsIs = false;
      args.CDFEpochToString = false;
    elseif (args.keepepoch == 1)
      args.ConvertEpochToDatenum = false;
      args.ConvertEpochToDatestr = false;
      args.CDFEpochToString = false;
    elseif (args.epochtostring == 1)
      args.ConvertEpochToDatenum = false;
      args.ConvertEpochToDatestr = false;
      args.KeepEpochAsIs = false;
    else
      args.ConvertEpochToDatenum = false;
      args.ConvertEpochToDatestr = false;
      args.KeepEpochAsIs = false;
      args.CDFEpochToString = false;
    end
  end
end

%
% Verify existence of filename.
%

% Get full filename.
fid = fopen(filename);

if (fid == -1)
  
    % Look for filename with extensions.
    fid = fopen([filename '.cdf']);
    
    if (fid == -1)
        fid = fopen([filename '.CDF']);
    end
    
end

if (fid == -1)
    error('MATLAB:spdfcdfread:fileOpen', 'Couldn''t open file (%s).', filename)
else
    filename = fopen(fid);
    fclose(fid);
end

% CDFlib's OPEN_ routine is flakey when the extension ".cdf" is used.
% Strip the extension from the file before calling the MEX-file.

if ((length(filename) > 4) && (isequal(lower(filename((end-3):end)), '.cdf')))
    filename((end-3):end) = '';
end

if (args.DataOnly)
  out = spdfcdfreadc(filename, args.Variables, args.Records, ...
                     [], args.CombineRecords, ...
                     args.ConvertEpochToDatenum, structure, ...
                     args.KeepEpochAsIs, args.CDFEpochToString, ...
                     args.ConvertEpochToDatestr, args.DataOnly);
  if (numel(args.Variables) == 1)
    out = out{1};
  end
else

  %
  % Get information about the variables.
  %

  info = spdfcdfinfo(filename, 'VALIDATE', args.Validate);

  if (isempty(args.Variables))
    args.Variables = info.Variables(:, 1)';
  end

  % To make indexing info.Variables easier, reorder it to match the values in
  % args.Variables and remove unused values. Deblank variable list because
  % the intersection is based on the predicate of equality of strings.
  % Inconsistent trailing blanks in variable names from args and info may cause
  % inadvertent mismatch and consequent failure.
  [int, idx1, idx2] = intersect(deblank(args.Variables), ...
                                deblank(info.Variables(:, 1)));

  if (length(int) < length(args.Variables))
    
    % Determine which requested variables do not exist in the CDF.
    invalid = setdiff(args.Variables, int);
    
    msg = 'The following requested variables are not in this CDF:';
    msg = [msg sprintf('\n\t%s',invalid{:})];
    
    error('MATLAB:spdfcdfread:variableNotFound', '%s', msg)
    
  end

  % Remove unused variables.
  info.Variables = info.Variables(idx2, :);

  % Reorder the variables to match the order of args.Variables.
  [tmp, reorder_idx] = sort(idx1);
  info.Variables = info.Variables(reorder_idx, :);

  if (~structure) 
    if (isempty(args.Records))
      args.Records = find_records(info.Variables);
    elseif (any(args.Records < 0))
      error('MATLAB:spdfcdfread:recordNumber', 'Record values must be nonnegative.')
    end
  end

  %
  % Read each variable.
  %

  if (length(args.Variables) == 1)

    % Special case for single variable.
    if (info.Variables{3} == 0)
      out = [];
      return;
    end
    if (~isempty(args.Slices))
        [args.Slices, msg] = parse_slice_vals(args.Slices, info.Variables);
        if (~isempty(msg))
            error('MATLAB:spdfcdfread:sliceValue', '%s', msg)
        end
    else
        args.Slices = fill_slice_vals([], info.Variables);
    end
    [data, attrs] = spdfcdfreadc(filename, args.Variables{1}, args.Records, ...
                             args.Slices, args.CombineRecords, ...
                             args.ConvertEpochToDatenum, structure, ...
                             args.KeepEpochAsIs, args.CDFEpochToString, ...
                             args.ConvertEpochToDatestr, args.DataOnly);
    if (isequal(lower(info.Variables{4}), 'epoch16') && ...
        (args.KeepEpochAsIs)) data=transpose(data);
    end
    [dataX, dummy] = spdfcdfreadc('to_close', args.Variables{1}, args.Records, ...
                              args.Slices, args.CombineRecords, ...
                              args.ConvertEpochToDatenum, false, ...
                              args.KeepEpochAsIs, args.CDFEpochToString, ...
                              args.ConvertEpochToDatestr, args.DataOnly);

    if (~structure)
      if (isequal(lower(info.Variables{4}), 'tt2000'))
        if (args.CombineRecords || args.ConvertEpochToDatenum || ...
            args.KeepEpochAsIs || args.ConvertEpochToDatestr) 
          out = data;
        else
          if (length(data) > 1)
            for p = 1:length(data)
              if (length(data{p}) > 1)
                for q = 1:length(data{p})
                  databb(q) = cdftt2000(data{p}(q,1));
                end
                dataaa{p,1} = databb;
              else
                dataaa(p,1) = cdftt2000(data{p});
              end
            end
            out = dataaa;
          else
            out = cdftt2000(data);
          end
        end
      elseif (isequal(lower(info.Variables{4}), 'epoch'))
        if (args.ConvertEpochToDatenum || args.CDFEpochToString || ...
            args.KeepEpochAsIs || args.ConvertEpochToDatestr || ...
            args.CombineRecords)
          out = data;
        else
          if (~args.ConvertEpochToDatenum)
            %
            % None option - set up for cdfepoch object
            %
            if (length(data) > 1)
              for p = 1:length(data)
                if (length(data{p}) > 1)
                  for q = 1:length(data{p})
                    databb(q) = cdfepoch(data{p}(q,1));
                  end
                  dataaa{p,1} = databb;
                else
                  dataaa(p,1) = cdfepoch(data{p});
                end
              end
              out = dataaa;
            else
              out = cdfepoch(data);
            end
          else
            out = data;
          end
        end
      elseif (isequal(lower(info.Variables{4}), 'epoch16') && ...
              args.KeepEpochAsIs)
          out = transpose(data);
      else
        out = data;
      end
    else
      out.VariableName = args.Variables{1};
      out.Data = data;
      out.Attributes = attrs;
    end

  elseif ((~isempty(args.Slices)) && (length(args.Variables) ~= 1))

    error('MATLAB:spdfcdfread:sliceValue', 'Specifying variable slices requires just one variable.')

  else
    if (structure)
      % Regular reading.
      out1 = cell(length(args.Variables),3);
      for p = 1:length(args.Variables)
        if (show_progress)
            fprintf ('%d) Reading variable "%s"\n', p, args.Variables{p});
        end
        if (info.Variables{p, 3} == 0)
          continue;
        end
 
        args.Slices = fill_slice_vals([], info.Variables(p,:));
        out1{p,1} = args.Variables{p};
        [datax, attrs]  = spdfcdfreadc(filename, args.Variables{p}, args.Records, ...
                                   args.Slices, args.CombineRecords, ...
                                   args.ConvertEpochToDatenum, true, ...
                                   args.KeepEpochAsIs, ...
                                   args.CDFEpochToString, ...
                                   args.ConvertEpochToDatestr, args.DataOnly);
        if (isequal(lower(info.Variables{p, 4}), 'epoch16'))
          if (args.KeepEpochAsIs)
            out1{p,2} = transpose(datax);
          else
            out1{p,2} = datax;
          end
        else
          out1{p,2} = datax;
        end
        out1{p,3} = attrs;
      end
      [dataX, dummy]  = spdfcdfreadc('to_close', args.Variables{1}, args.Records, ...
                                args.Slices, args.CombineRecords, ...
                                args.ConvertEpochToDatenum, false, ...
                                args.KeepEpochAsIs, ... 
                                args.CDFEpochToString, ...
                                args.ConvertEpochToDatestr, args.DataOnly);

      % Change a cell array to an array of structures.
      fields = {'VariableName', 'Data', 'Attributes'};
      out = cell2struct(out1, fields, 2); 
      if (~structure)
        out = arrayfun(@(x)x.Data,out,'UniformOutput',false);
      end

    else

      if (args.CombineRecords)
          data = cell(1, length(args.Variables));
      else
          data = cell(length(args.Records), length(args.Variables));
      end

      for p = 1:length(args.Variables)
        if (show_progress)
            fprintf ('%d) Reading variable "%s"\n', p, args.Variables{p});
        end
        if (info.Variables{p, 3} == 0)
          continue;
        end

        args.Slices = fill_slice_vals([], info.Variables(p,:));

        if (info.Variables{p, 5}(1) == 'F')
% Non-record variant
            % Special case for variables which don't vary by record.
            [xdata, dummy] = spdfcdfreadc(filename, args.Variables{p}, 0, ...
                                      args.Slices, ...
                                      args.CombineRecords, ...
                                      args.ConvertEpochToDatenum, false, ...
                                      args.KeepEpochAsIs, ...
                                      args.CDFEpochToString, ...
                                      args.ConvertEpochToDatestr, args.DataOnly);
            if (args.CombineRecords)
              if (isequal(lower(info.Variables{p, 4}), 'epoch16'))
                if (args.KeepEpochAsIs)
                  data{p} = transpose(xdata);
                else
                  data{p} = xdata;
                end
              else
                data{p} = xdata;
              end
%              data{p} = xdata;
            else
              data(:,p) = repmat(xdata, length(args.Records), 1);
            end

        else
% Record variant
            [xdata, dummy] = spdfcdfreadc(filename, args.Variables{p}, ...
                                      args.Records, args.Slices, ...
                                      args.CombineRecords, ...
                                      args.ConvertEpochToDatenum, ... 
                                      false, ...
                                      args.KeepEpochAsIs, ...
                                      args.CDFEpochToString, ...
                                      args.ConvertEpochToDatestr, args.DataOnly);
            if (args.CombineRecords)
              if (isequal(lower(info.Variables{p, 4}), 'epoch16') && ...
                  args.KeepEpochAsIs)
                 data{p} = transpose(xdata);
              else
                data{p} = xdata;
              end
            else
              % M-by-N cell array....
              % Convert epoch data.
              if (isequal(lower(info.Variables{p, 4}), 'tt2000'))
                if (args.CombineRecords || args.ConvertEpochToDatenum || ...
                    args.KeepEpochAsIs || args.ConvertEpochToDatestr)
                  data(:,p) = xdata;
                else
                  for q = 1:length(xdata)
                    if (length(xdata{q}) > 1)
                      for r = 1:length(xdata{q})
                        databb(r,1) = cdftt2000(xdata{q}(r,1));
                      end
                      data{q,p} = databb;
                    else
                      data{q,p} = cdftt2000(xdata{q});
                    end
                  end
                end
              elseif (isequal(lower(info.Variables{p, 4}), 'epoch'))
                if (args.CDFEpochToString || args.KeepEpochAsIs || ...
                    args.CombineRecords || ...
                    args.ConvertEpochToDatenum || args.ConvertEpochToDatestr)
                  data(:,p) = xdata;
                elseif (~args.ConvertEpochToDatenum)
                  %
                  % None option case - set up to cdfepoch object
                  %
                  if (length(xdata) > 1)
                    for q = 1:length(xdata)
                      if (length(xdata{q}) > 1)
                        for r = 1:length(xdata{q})
                          databb(r,1) = cdfepoch(xdata{q}(r,1));
                        end
                        data{q,p} = databb;
                      else
                        data{q,p} = cdfepoch(xdata{q});
                      end
                    end
                  end
                else
                  data{1,p} = cdfepoch(xdata);
                end
              elseif (isequal(lower(info.Variables{p, 4}), 'epoch16'))
                if (args.KeepEpochAsIs)
                  data(:,p) = transpose(xdata);
                else
                  data(:,p) = xdata;
                end
              else
                data(:,p) = xdata;
              end
            end

        end

      end
      [dataX, dummy]  = spdfcdfreadc('to_close', args.Variables{1}, args.Records, ...
                               args.Slices, args.CombineRecords, ...
                               args.ConvertEpochToDatenum, false, ...
                               args.KeepEpochAsIs, args.CDFEpochToString, ...
                               args.ConvertEpochToDatestr, args.DataOnly);
      out = data;
    end
  end

end

%%%
%%% Function find_records
%%%

function records = find_records(var_details)

% Find which variables to consider.
rec_values = [var_details{:, 3}];
max_record = max(rec_values);

if (isempty(max_record))
  records = [];
else
  records = 0:(max_record - 1);
end



%%%
%%% Function parse_fill_vals
%%%

function [slices, msg] = parse_slice_vals(slices, var_details)

msg = '';

% Find the number of dimensions that the CDF recognizes.  This is given
% explicitly in the variance specification as the number of values to the
% right of the '/' (i.e., the length of the variance string minus two).
vary = var_details{5};
num_cdf_dims = size(vary, 2) - 2;


%
% Check the user-provided slice values.
%

if (num_cdf_dims < size(slices, 1))
    msg = sprintf(['Number of slice rows (%d) exceeds number of' ...
                   ' dimensions (%d) in CDF variable.'], ...
                  size(slices, 1), num_cdf_dims);
    return
end

if (any(slices(:,1) < 0))
    
    msg = 'Slice indices must be nonnegative.';
    return
    
elseif (any(slices(:,2) < 1))
    
    msg = 'Slice interval values must be positive.';
    return
    
elseif (any(slices(:,3) < 1))
    
    msg = 'Slice count values must be positive.';
    return
    
end

for p = 1:size(slices,1)
    
    % Indices are zero-based.
    max_idx = var_details{2}(p) - 1;
    last_requested = slices(p,1) + (slices(p,3) - 1) * slices(p,2);
    
    if (last_requested > max_idx)
        
        msg = sprintf(['Slice values for dimension %d exceed maximum' ...
                       ' index (%d).'], p, max_idx);
        return
        
    end
end


%
% Append unspecified slice values.
%
slices = fill_slice_vals(slices, var_details);



%%%
%%% Function fill_slice_vals
%%%

function slices = fill_slice_vals(slices, var_details)

dims = var_details{2};
vary = var_details{5};
num_cdf_dims = size(vary, 2) - 2;

if (num_cdf_dims > size(slices, 1))
    
    % Fill extra dimensions.
    for p = (size(slices, 1) + 1):(num_cdf_dims)
        slices(p, :) = [0 1 dims(p)];
    end
    
elseif (num_cdf_dims == 0)
    
    % Special case for scalar values.
    slices = [0 1 1];
    
end



%%%
%%% Function parse_inputs
%%%

function [args, msg, structure, show_progress] = parse_inputs(varargin)
% Set default values
show_progress = false;
structure = false;
args.CombineRecords = true;
args.ConvertEpochToDatenum = true;
args.ConvertEpochToDatestr = false;
args.KeepEpochAsIs = false;
args.CDFEpochToString = false;
args.DataOnly = false;
args.Validate = false;
args.Records = [];
args.Slices = [];
args.Variables = {};
args.epochtodatenum = 0;
args.epochtodatestr = 0;
args.keepepoch = 0;
args.epochtostring = 0;
msg = '';

% Parse arguments based on their number.
if (nargin > 0)
    paramStrings = {'variables'
                    'records'
                    'slices'
                    'convertepochtodatenum'
                    'convertepochtodatestr'
                    'keepepochasis'
                    'combinerecords'
                    'structure'
                    'cdfepochtostring'
                    'dataonly'
                    'validate'
                    'showprogress'};
    
    % For each pair
    for k = 1:2:length(varargin)
       param = lower(varargin{k});
       
            
       if (~ischar(param))
           msg = 'Parameter name must be a string.';
           return
       end

       idx = strmatch(param, paramStrings);
       
       if (isempty(idx))
           msg = sprintf('Unrecognized parameter name "%s".', param);
           return
       elseif (length(idx) > 1)
           msg = sprintf('Ambiguous parameter name "%s".', param);
           return
       end
    
       switch (paramStrings{idx})
       case 'variables'
           
           if (k == length(varargin))
               msg = 'No variables specified.';
               return
           else
               
               args.Variables = varargin{k + 1};
               
               if (~iscell(args.Variables))
                   args.Variables = {args.Variables};
               end
               
               for p = 1:length(args.Variables)
                   if (~ischar(args.Variables{p}))
                       msg = 'All variable names must be strings.';
                       return
                   end
               end
           end
           
       case 'records'
           
           if (k == length(varargin))
               msg = 'No records specified.';
               return
           else
               
               records = varargin{k + 1};
               
               if ((~isa(records, 'double')) || ...
                   (length(records) ~= numel(records)) || ...
                   (any(rem(records, 1))))
                   
                   msg = 'Record list must be a vector of integers.';
                   
               end
               args.Records = records;
               args.CombineRecords = false;	
           end
           
       case 'slices'
           
           if (k == length(varargin))
               msg = 'No slice values specified.';
               return
           else
               
               slices = varargin{k + 1};
               
               if ((~isa(slices, 'double')) || ...
                   (size(slices, 2) ~= 3) || ...
                   (~isempty(find(rem(slices, 1) ~= 0))))
                   
                   msg = 'Variable slice values must be n-by-3 array of integers.';
                   return
               end
               
               args.Slices = slices;
           end
           
       case 'convertepochtodatenum'
           
           if (k == length(varargin))
               msg = 'No epoch conversion value specified.';
               return
           else
               convert = varargin{k + 1};
               if (numel(convert) ~= 1)
                   msg = 'Epoch conversion value must be a scalar logical.';
               end
               
               if (islogical(convert))
                   args.ConvertEpochToDatenum = convert;
               elseif (isnumeric(convert))
                   args.ConvertEpochToDatenum = logical(convert);
               else
                   msg = 'Epoch conversion value must be a scalar logical.';
               end
               if (args.ConvertEpochToDatenum == 1)
                 args.epochtodatenum=1;
               end
           end

       case 'convertepochtodatestr'

           if (k == length(varargin))
               msg = 'No epoch conversion value specified.';
               return
           else
               convert = varargin{k + 1};
               if (numel(convert) ~= 1)
                   msg = 'Epoch conversion value must be a scalar logical.';
               end

               if (islogical(convert))
                   args.ConvertEpochToDatestr = convert;
               elseif (isnumeric(convert))
                   args.ConvertEpochToDatestr = logical(convert);
               else
                   msg = 'Epoch conversion value must be a scalar logical.';
               end
               if (args.ConvertEpochToDatestr == 1)
                 args.epochtodatestr=1;
               end
           end

       case 'combinerecords'
           
           if (k == length(varargin))
               msg = 'Missing "CombineRecords" value.';
               return
           else
               combine = varargin{k + 1};
               if (numel(combine) ~= 1)
                   msg = 'The "CombineRecords" value must be a scalar logical.';
               end
               
               if (islogical(combine))
                   args.CombineRecords = combine;
               elseif (isnumeric(combine))
                   args.CombineRecords = logical(combine);
               else
                   msg = 'The "CombineRecords" value must be a scalar logical.';
               end
           end

       case 'showprogress'
  
           if (k == length(varargin))
               msg = 'Missing "ShowProgress" value.';
               return
           else
               sp = varargin{k + 1};
               if (numel(sp) ~= 1)
                   msg = 'The "ShowProgress" value must be a scalar logical.';
               end
  
               if (islogical(sp))
                   show_progress = sp;
               elseif (isnumeric(sp))
                   show_progress = logical(sp);
               else
                   msg = 'The "ShowProgress" value must be a scalar logical.';
               end
           end
 
       case 'structure'
  
           if (k == length(varargin))
               msg = 'Missing "Structure" value.';
               return
           else
               sp = varargin{k + 1};
               if (numel(sp) ~= 1)
                   msg = 'The "Structure" value must be a scalar logical.';
               end
  
               if (islogical(sp))
                   structure = sp;
               elseif (isnumeric(sp))
                   structure = logical(sp);
               else
                   msg = 'The "Structure" value must be a scalar logical.';
               end
           end

       case 'keepepochasis'

           if (k == length(varargin))
               msg = 'No KeepEpochAsIs value specified.';
               return
           else
               keepasis = varargin{k + 1};
               if (numel(keepasis) ~= 1)
                   msg = 'KeepEpochAsIs value must be a scalar logical.';
               end
               if (islogical(keepasis))
                   args.KeepEpochAsIs = keepasis;
               elseif (isnumeric(keepasis))
                   args.KeepEpochAsIs = logical(keepasis);
               else
                   msg = 'KeepEpochAsIs value must be a scalar logical.';
               end
               if (args.KeepEpochAsIs ==1)
                 args.keepepoch=1;
               end
           end
 
       case 'dataonly'

           if (k == length(varargin))
               msg = 'No dataonly value specified.';
               return
           else
               dataonly = varargin{k + 1};
               if (numel(dataonly) ~= 1)
                   msg = 'dataonly value must be a scalar logical.';
               end
               if (islogical(dataonly))
                   args.DataOnly = dataonly;
               elseif (isnumeric(dataonly))
                   args.DataOnly = logical(dataonly);
               else
                   msg = 'dataonly value must be a scalar logical.';
               end
	       if (args.DataOnly)
		 args.CombineRecords = true;
		 args.KeepEpochAsIs = true;
		 args.ConvertEpochToDatenum = false;
	       end
           end
 
       case 'validate'

           if (k == length(varargin))
               msg = 'No validate value specified.';
               return
           else
               validate = varargin{k + 1};
               if (numel(validate) ~= 1)
                   msg = 'validate value must be a scalar logical.';
               end
               if (islogical(validate))
                   args.Validate = validate;
               elseif (isnumeric(validate))
                   args.Validate = logical(validate);
               else
                   msg = 'validate value must be a scalar logical.';
               end
           end
 
       case 'cdfepochtostring'

           if (k == length(varargin))
               msg = 'No CDFEpochToString value specified.';
               return
           else
               epochtostring = varargin{k + 1};
               if (numel(epochtostring) ~= 1)
                   msg = 'CDFEpochToString value must be a scalar logical.';
               end

               if (islogical(epochtostring))
                   args.CDFEpochToString = epochtostring;
               elseif (isnumeric(epochtostring))
                   args.CDFEpochToString = logical(epochtostring);
               else
                   msg = 'CDFEpochToString value must be a scalar logical.';
               end
               if (args.CDFEpochToString == 1)
                 args.epochtostring=1;
               end
           end
 
       end  % switch
    end  % for

end  % if (nargin > 1)



function epochs = convert_epoch(epoch_nums, convertToDatenum)
%CONVERT_EPOCH   Convert numeric epoch values to CDFEPOCH objects.

% Note: MATLAB datenums are the number of days since 00-Jan-0000, while the
%       CDF epoch is the number of milliseconds since 01-Jan-0000. 

% Convert values from milliseconds to MATLAB serial dates.
ml_nums = (epoch_nums ./ 86400000) + 1;

% Convert MATLAB serial dates to CDFEPOCH objects.
if (convertToDatenum)
    epochs = ml_nums;
else
    epochs = cdfepoch(ml_nums);
end



function validate_inputs(args)
%VALIDATE_INPUTS   Ensure that the mutually exclusive options weren't provided.

if ((args.CombineRecords) && (~isempty(args.Records)))
    error('MATLAB:spdfcdfread:combineRecordSubset', '%s\n%s', ...
          'You cannot currently combine a subset of records.', ...
          'Specify only one of ''CombineRecords'' and ''Records''.')
end

if ((args.epochtodatenum == 1) && (args.epochtodatestr == 1))
    error('MATLAB:spdfcdfread:Epochmutualexclusive', '%s\n', ...
          'Specify only one of ''ConvertEpochToDatenum'' and ''ConvertEpochToDatestr'' to true.')
end

if ((args.epochtodatenum == 1) && (args.keepepoch == 1))
    error('MATLAB:spdfcdfread:Epochmutualexclusive', '%s\n', ...
          'Specify only one of ''ConvertEpochToDatenum'' and ''KeepEpochAsIs'' to true.')
end

if ((args.epochtodatestr == 1) && (args.keepepoch == 1))
    error('MATLAB:spdfcdfread:Epochmutualexclusive', '%s\n', ...
          'Specify only one of ''ConvertEpochToDatestr'' and ''KeepEpochAsIs'' to true.')
end

if ((args.epochtostring == 1) && (args.keepepoch == 1))
    error('MATLAB:spdfcdfread:Epochmutualexclusive', '%s\n', ...
          'Specify only one of ''CDFEpochToString'' and ''KeepEpochAsIs'' to true.')
end

if ((args.epochtostring == 1) && (args.epochtodatenum == 1))
    error('MATLAB:spdfcdfread:Epochmutualexclusive', '%s\n', ...
          'Specify only one of ''CDFEpochToString'' and ''ConvertEpochToDatenum'' to true.')
end

if ((args.epochtostring == 1) && (args.epochtodatestr == 1))
    error('MATLAB:spdfcdfread:Epochmutualexclusive', '%s\n', ...
          'Specify only one of ''CDFEpochToString'' and ''ConvertEpochToDatestr'' to true.')
end

function spdfcdfupdate(filename, varargin)
%SPDFCDFUPDATE Update data in an existing CDF file.
% 
%   SPDFCDFUPDATE(FILE, 'VariableData', VARIABLELIST, ...) updates variable 
%   data in a CDF file whose name is specified by FILE.  VARIABLELIST is a 
%   cell array of ordered pairs, which are comprised of a CDF variable name 
%   (a string) and a corresponding value cell array. The value cell array 
%   is comprised of a number of cells, each cell consisting of a record number,
%   a dimension indices, as well as the new data value. The record number and 
%   dimension indices are all zero(0)-based. The number of dimension indices 
%   depends on the dimensionality of the variable. The form for entering
%   multiple value changes for variables is as follows:
%   {'varname1' [ {rec_no_1, {index1, index2, ...}, value1}; ...
%                 {rec_no_2, {index2, index2, ...}, value2}; ...
%                 ...
%               ] ...
%    'varname2' [ {rec_no_1, {index1, index2, ...}, value1}; ...
%                 {rec_no_2, {index2, index2, ...}, value2}; ...
%                 ...
%               ] ...
%    ...
%   }                 
%
%
%   SPDFCDFUPDATE(..., 'GlobalAttributes', GATTRIB, ...) updates the structure
%   GATTRIB as global meta-data for the CDF.  Each field of the
%   struct is the name of a global attribute.  The value of each
%   field contains the value of the attribute.  To update
%   multiple values for an attribute, the field value should be a
%   cell array.
%
%   SPDFCDFUPDATE(..., 'VariableAttributes', VATTRIB, ...) updates the
%   structure VATTRIB as variable meta-data for the CDF.  Each
%   field of the struct is the name of a variable attribute.  The
%   value of each field should be an mx2 cell array where m is the
%   number of variables with attributes.  The first element in the
%   cell array is the name of the variable, a string, and the 
%   second element, also a string, is the CDF specific data type of 
%   the entry, and the third element is the new entry value of the 
%   attribute for that variable.
%
%   SPDFCDFUPDATE(..., 'CDFCompression', TF, ...) resets the CDF compression.
%   The CDF is set to use GZIP compression if TF is true. If TF is
%   is false, the CDF is set to be a uncompressed file.
%
%   SPDFCDFUPDATE(..., 'CDFChecksum', TF, ...) resets the CDF checksum.
%   The CDF is set to use MD5 checksum if TF is true. If TF is
%   is false, the CDF is set not to use checksum.
%
%   SPDFCDFUPDATE(..., 'CDFLeapSecondLastUpdated', value, ...) resets the CDF's
%   leap second last updated date.  For CDFs created prior to V 3.6.0, this 
%   field is not set. It is set to indicate what leap second table this CDF is
%   based upon. The value, in YYYYMMDD form, must be a valid entry in the
%   currently used leap second table, or zero (0) if the table is not used. 
%   CDF will automatically fill the value with the last entry date in the leap
%   second table if this option is not specified. 
%
%   Notes:
%
%     SPDFCDFUPDATE only updates the data values for the existing variables.
%     For Epoch and Epoch16 data type variables, the updated values should
%     be in the string form, e.g., 01-Jan-2008 07:06:05.004 and 
%     01-Jan-2008 07:06:05.004:100:200:300:400, respectively, instead of
%     the double form as they are stored.
%
%   Examples:
%
%   %  Change data values for Longitude (a 1-dim of CDF_INT2 data type): 
%   %  4th element in Record 0 to 100, 3rd element in Record 1 to 200; and 
%   %  change data value for variable Latitude (a 2-dim of CDF_REAL4 data type) 
%   %  at index [0,0] in Record 10 to 20.2; and change record 2 for variable
%   %  Epoch (a 0-dim of CDF_EPOCH data type) to 01-Jan-2008 07:06:05.004 in 
%   %  the file 'example.cdf'. 
%
%      spdfcdfupdate('example', 'VariableData', {'Longitude' [{0,{3},100};{1,{2},200}] ...
%                                            'Latitude' [{10,{0,0},20.2}] ...
%                                            'Epoch' [{1,{0},'01-Jan-2008 07:06:05.004'}] ...
%                                           } ...
%               );
%   Or,
%      var1 = {'Longitude' [{0,{3},100};{1,{2},200}]};
%      var2 = {'Latitude' [{10,{0,0},20.2}]};
%      var3 = {'Epoch' [{1,{0},'01-Jan-2008 07:06:05.004'}]};
%      spdfcdfupdate('example', 'VariableData', [var1 var2 var3]);
%
%   Make sure that the data values are of the data type for the variables.
%
%   % Update the meta-data for the entry of variable attribute 'validmin' for
%   % variable 'Longitude' while updating its data values in the file 'example.cdf': 
%
%   varAttribStruct.validmin = {'Longitude' [10]};
%   spdfcdfupdate('example', 'VariableData', {'Longitude' [{0,{3},100};{1,{2},200}]}, ...
%                        'VariableAttributes', varAttribStruct);
%
%   % Update the global attribute, 'Name': the first entry to 'MyName', a CDF_CHAR 
%   % data type, and the second entry to 10, of data type CDF_INT2, in the 
%   % file 'example.cdf':
%
%   globalAttribStruct.Name = [{0, 'CDF_CHAR', 'MyName'},{1, 'CDF_INT2', 10}];
%   spdfcdfupdate('example', 'GlobalAttributes', globalAttribStruct);
%
%   % Set the CDF file, 'example.cdf', to use the GZIP compression:
%
%   spdfcdfupdate('example', 'cdfcompression', true);
%
%   % Set the CDF file, 'example.cdf', to use the MD5 checksum:
%
%   spdfcdfupdate('example', 'cdfchecksum', true);
%
%   See also SPDFCDFREAD, SPDFCDFWRITE, SPDFCDFINFO, CDFEPOCH, CDFTT2000,
%            SPDFENCODEEPOCH, SPDFCOMPUTEEPOCH, SPDFPARSEEPOCH,
%            SPDFBREAKDOWNEPOCH, SPDFENCODEEPOCH16, SPDFCOMPUTEEPOCH16,
%            SPDFPARSEEPOCH16, SPDFBREAKDOWNEPOCH16, SPDFENCODETT2000,
%            SPDFCOMPUTETT2000, SPDFPARSETT2000, SPDFBREAKDOWNTT2000,
%            SPDFDATENUMTOEPOCH, SPDFDATENUMTOEPOCH16, SPDFDATENUMTOTT2000,
%            SPDFCDFLEAPSECONDSINFO

% HISTORY:
%   January 13, 2009  Mike Liu      The new function was created.
%   August  23, 2013  Mike Liu      Added setting checksum operation.

%
% Process arguments.
%

if (nargin < 2)
    error('MATLAB:spdfcdfupdate:inputArgumentCount', ...
          'CDFWRITE requires at least two input arguments.')
end

% parse_inputs sorts out all of the input args.  Its return values:
%
% * args - an array of structs.  args.VarNames contains the names
%          of the variables to be updated to the CDF.  args.VarVals contains
%          the cell array for updating the data values.  
% * msg - an error message from parse_inputs that we pass on to the user.

[args, varAttribStruct, globalAttribStruct, exception] = parse_inputs(varargin{:});

if (~isempty(exception.msg))
    error(exception.id, '%s', exception.msg)
end

%
% Create a proper filename for the CDF
%

% See if there is an extension
[pname, fname, ext] = fileparts(filename);

% If there is an extension, then remove it before passing it to CDFlib.
if (~isempty(ext))
    if (~isempty(strfind(ext, 'cdf')))
        filename = fullfile(pname, fname);
    end
end

%
% Call the underlying spdfcdfupdatec function which calls the CDFlib
%

spdfcdfupdatec(filename, args.VarNames, args.VarRecs, args.VarIndices, ...
           args.VarDataVals, varAttribStruct, globalAttribStruct, ...
           args.isCDFCompressed, args.CDFchecksum, ...
           args.CDFleapsecondlastupdated);

%%%
%%% Function parse_inputs
%%%

function [args, varAttribStruct, globalAttribStruct, exception] = parse_inputs(varargin)

% Set default values
args.VarNames = {};
args.VarRecs = {};
args.VarIndices = {};
args.VarDataVals = {};
varcell = {};
varAttribStruct = struct([]);
globalAttribStruct = struct([]);
exception.msg = '';
exception.id = '';
args.isCDFCompressed = int32(0);
args.CDFchecksum = int32(0);
args.CDFleapsecondlastupdated = int32(-999);

% Parse arguments based on their number.
if (nargin > 0)
    
    paramStrings = {'variabledata'
                    'globalattributes'
                    'variableattributes'
                    'cdfcompression'
                    'cdfchecksum'
                    'cdfleapsecondlastupdated'};
    % For each pair
    for k = 1:2:length(varargin)
        param = lower(varargin{k});
            
        if (~ischar(param))
            exception.msg = 'Parameter name must be a string.';
            exception.id = 'MATLAB:spdfcdfupdate:paramNameNotString';
            return
        end
        
        idx = strmatch(param, paramStrings);
        
        if (isempty(idx))
            exception.msg = sprintf('Unrecognized parameter name "%s".', param);
            exception.id = 'MATLAB:spdfcdfupdate:unrecognizedParam';
            return
        elseif (length(idx) > 1)
            exception.msg = sprintf('Ambiguous parameter name "%s".', param);
             exception.id = 'MATLAB:spdfcdfupdate:ambiguousParam';
           return
        end
        
        switch (paramStrings{idx})
        case 'globalattributes'
           globalAttribStruct = varargin{k+1};
           if ~isstruct(globalAttribStruct)
               exception.msg = ['''GlobalAttributes''' ' must be a struct.'];
               exception.id = 'MATLAB:spdfcdfupdate:globalAttributesNotStruct';
               return
           end
           attribs = fieldnames(globalAttribStruct);
           
           % If the global attribute isn't a cell, then stuff it in one.
           for i = 1:length(attribs)
               attribVal = globalAttribStruct.(attribs{i});
               s = size(attribVal);
               if ~iscell(attribVal)
                   globalAttribStruct.(attribs{i}) = {attribVal};
               end
               % The global attribute struct may have more than one set of
               % entry id, data type and value.  However, there must only be
               % one associated value of the attribute for each entry,
               % hence the 3.
               if (s(2) == 3)
                   % Transpose it because CDFlib reads the arrays column-wise.
                   globalAttribStruct.(attribs{i}) = attribVal';   
               else
                   % We have ordered pairs.
                   globalAttribStruct.(attribs{i}) = reshape(globalAttribStruct.(attribs{i})(:),numel(globalAttribStruct.(attribs{i})(:))/3, 3);
               end
           end
        case 'variabledata'
           varcell = varargin{k+1};
           if ~iscell(varcell)
               exception.msg = ['''VariableData''' ' must be a cell.'];
               exception.id = 'MATLAB:spdfcdfupdate:variabledatanotcell';
               return
           end
           % First check that varcell meets all of our requirements
           args.VarNames = {varcell{1:2:end}};
           args.VarVals = {varcell{2:2:end}};

           if length(args.VarNames) ~= length(args.VarVals)
               exception.msg = 'All variable names must have a corresponding variable value cell array.';
               exception.id = 'MATLAB:spdfcdfupdate:variableWithoutValue';
               return
           end
           % args.VarVals
           % Check and make sure that all variable values are of the same
           % datatype, but ignore empties
           if ~isempty(args.VarVals)
               for i = 1:length(args.VarVals)
                   a = args.VarVals{i};
                   if (~iscell(a))
                     a = {a};
                   end
           	jj = numel(a)/length(a);
           	args.VarRecs{i} = {a{1:jj}};
           	args.VarIndices{i} = {a{jj+1:2*jj}};
           	args.VarDataVals{i} = {a{2*jj+1:3*jj}};
               end
           end
        case 'variableattributes'
           varAttribStruct = varargin{k+1};
           if ~isstruct(varAttribStruct)
               exception.msg = ['''VariableAttributes''' ' must be a struct.'];
               exception.id = 'MATLAB:spdfcdfupdate:variableAttributesNotStruct';
               return
           end
           attribs = fieldnames(varAttribStruct);
           % Check the VariableAttributes struct.
           for i = 1:length(attribs)
               % If the variable attribute isn't in a cell (because
               % it is scalar, then put it into a cell.
               attribVal = varAttribStruct.(attribs{i});
               s = size(attribVal);
               if ~iscell(attribVal)
                   varAttribStruct.(attribVal) = {attribVal};
               end
               % The variable attribute struct may have more than one 
               % variable-value pair.  However, there must only be
               % one associated value of the attribute for each variable,
               % hence the 2.
               if (s(2) == 2)
                   % Transpose it because CDFlib reads the arrays column-wise.
                   varAttribStruct.(attribs{i}) = attribVal';   
               else
                   % We have ordered pairs.
                   varAttribStruct.(attribs{i}) = reshape(varAttribStruct.(attribs{i})(:),numel(varAttribStruct.(attribs{i})(:))/2, 2);
               end
           end
        case 'cdfcompression'
           args.isCDFCompressed = varargin{k+1};
           if (numel(args.isCDFCompressed) ~= 1)
               exception.msg = 'CDF compression value must be a scalar logical.';
               exception.id = 'MATLAB:spdfcdfupdate:cdfcompression';
               return
           end

           if (islogical(args.isCDFCompressed))
               if (args.isCDFCompressed)
                 args.isCDFCompressed = int32(2);
               else
                 args.isCDFCompressed = int32(1);
               end
           elseif (isnumeric(args.isCDFCompressed))
%              args.isCDFCompressed = logical(args.isCDFCompressed);
               if (logical(args.isCDFCompressed))
                 args.isCDFCompressed = int32(2);
               else
                 args.isCDFCompressed = int32(1);
               end
           else
               exception.msg = 'CDF compression value must be a scalar logical.';
               exception.id = 'MATLAB:spdfcdfupdate:cdfcompression';
               return
           end
        case 'cdfchecksum'
           args.CDFchecksum = varargin{k+1};
           if (numel(args.CDFchecksum) ~= 1)
               exception.msg = 'CDF checksum value must be a scalar logical.';
               exception.id = 'MATLAB:spdfcdfupdate:cdfchecksum';
               return
           end
           if (islogical(args.CDFchecksum))
               if (args.CDFchecksum)
                 args.CDFchecksum = int32(2);
               else
                 args.CDFchecksum = int32(1);
               end
           end
        case 'cdfleapsecondlastupdated'
           args.CDFleapsecondlastupdated = varargin{k+1};
           if (isnumeric(args.CDFleapsecondlastupdated))
               args.CDFleapsecondlastupdated = int32(args.CDFleapsecondlastupdated);
           else
               exception.msg = 'CDF leapsecondlastupdated value must be a numeric value (in YYYYMMDD form).';
               exception.id = 'MATLAB:spdfcdfupdate:cdfleapsecondlastupdated';
               return
           end
        end % switch
    end  % for
    
end

function spdfcdfwrite(filename, varcell, varargin)
%SPDFCDFWRITE Write data to a CDF file.
% 
%   SPDFCDFWRITE(FILE, VARIABLELIST, ...) writes out a CDF file whose name
%   is specified by FILE.  VARIABLELIST is a cell array of ordered
%   pairs, which are comprised of a CDF variable name (a string) and
%   the corresponding CDF variable value.  To write out multiple records
%   for a variable, there are two ways of doing it. One way is putting the
%   variable values in a cell array, where each element in the cell array
%   represents a record. Another way, the better one, is to place the
%   values in a vector (single or multi-dimensional) with the option
%   'RecordBound' being specified.  
%
%   SPDFCDFWRITE(..., 'PadValues', PADVALS) writes out pad values for given
%   variable names.  PADVALS is a cell array of pairs of a variable name (a
%   string) and a corresponding pad value.  Pad values are the default value
%   associated with the variable when an out-of-bounds record is accessed.
%   Variable names that appear in PADVALS must be in VARIABLELIST.
%
%   SPDFCDFWRITE(..., 'GlobalAttributes', GATTRIB, ...) writes the structure
%   GATTRIB as global meta-data for the CDF.  Each field of the
%   struct is the name of a global attribute.  The value of each
%   field contains the value of the attribute.  To write out
%   multiple values for an attribute, the field value should be a
%   cell array. 
%
%   If there is a master CDF that has all the meta-data that the new CDF needs,
%   then SPDFCDFINFO module can be used to retrieve the infomation. The
%   'GlobalAttributes' field from the returned structure can be
%   passed in for the GATTRIB.
%
%   In order to specify a global attribute name that is illegal in
%   MATLAB, create a field called "CDFAttributeRename" in the 
%   attribute struct.  The "CDFAttribute Rename" field must have a value
%   which is a cell array of ordered pairs.  The ordered pair consists
%   of the name of the original attribute, as listed in the 
%   GlobalAttributes struct and the corresponding name of the attribute
%   to be written to the CDF.
%
%   SPDFCDFWRITE(..., 'VariableAttributes', VATTRIB, ...) writes the
%   structure VATTRIB as variable meta-data for the CDF.  Each
%   field of the struct is the name of a variable attribute.  The
%   value of each field should be an Mx2 cell array where M is the
%   number of variables with attributes.  The first element in the
%   cell array should be the name of the variable and the second
%   element should be the value of the attribute for that variable.
%
%   If there is a master CDF that has all the meta-data that the new CDF needs,
%   then SPDFCDFINFO module can be used to retrieve the infomation. The 
%   'VariableAttributes' field from the returned structure can be passed
%   in for the VATTRIB.
%   Note: For string variable attributes, they can be either a single string
%         or a cell of strings. 
%
%   In order to specify a variable attribute name that is illegal in
%   MATLAB, create a field called "CDFAttributeRename" in the 
%   attribute struct.  The "CDFAttribute Rename" field must have a value
%   which is a cell array of ordered pairs.  The ordered pair consists
%   of the name of the original attribute, as listed in the 
%   VariableAttributes struct and the corresponding name of the attribute
%   to be written to the CDF.   If you are specifying a variable attribute
%   of a CDF variable that you are re-naming, the name of the variable in
%   the VariableAttributes struct must be the same as the re-named variable.
%
%   SPDFCDFWRITE(..., 'WriteMode', MODE, ...) where MODE is either 'overwrite'
%   or 'append' indicates whether or not the specified variables or attributes
%   should be appended to the CDF if the file already exists.  The 
%   default is 'overwrite', indicating that SPDFCDFWRITE will not append
%   variables and attributes.
%
%   SPDFCDFWRITE(..., 'Format', FORMAT, ...) where FORMAT is either 'multifile'
%   or 'singlefile' indicates whether or not the data is written out
%   as a multi-file CDF.  In a multi-file CDF, each variable is stored
%   in a *.vN file where N is the number of the variable that is
%   written out to the CDF.  The default is 'singlefile', which indicates
%   that SPDFCDFWRITE will write out a single file CDF.  When the 'WriteMode'
%   is set to 'Append', the 'Format' option is ignored, and the format
%   of the pre-existing CDF is used.
%
%   SPDFCDFWRITE(..., 'Version', VERSION, ...) where VERSION is a string which 
%   specifies the version of the CDF library to use in writing the file.
%   The default option is to use the latest version of the library 
%   (currently version 3.2+), and has to be specified '3.0'.  The 
%   other available version is version 2.7 ('2.7').  Note that 
%   versions of MATLAB before R2006b will not be able to read files 
%   which were written with CDF versions greater than 3.0.
%
%   SPDFCDFWRITE(..., 'RecordBound', RECBNDVARS) specifies data values in arrays
%   (1-D or multi-dimensional) are to be written into "records" for the given
%   variable. RECBNDVARS is a cell array of variable names. The M-by-N array
%   data will create M rows (records), while each row having N elements. For an
%   M-by-1 (column) or 1-by-M (row) vector, it will create M records, each 
%   being a scalar. For a 3-D array M-by-N-by-R, R records will be written,
%   and each record with M-by-N elements. Without this option, an array of 
%   M-by-N will be written into a single record of 2-dimensions. See sample
%   codes for its usage.
%
%   SPDFCDFWRITE(..., 'Vardatatypes', VARDATATYPE) specifies the variable's
%   data types. By default, this module uses each variable's passed data to
%   determine its corresponding CDF data type. While it is fine for the most
%   cases, this will not work for the CDF epoch types, i.e., CDF_EPOCH (a double),
%   CDF_EPOCH16 (an array of 2 doubles) and CDF_TIME_TT2000 (an int64). This
%   option can be used to address such issue. VARDATATYPE is a cell array of
%   variable names and their respective data types (in string).
%
%   The following table shows the valid type strings, either in CDF defined
%   forms, or alternatively in the forms presented at column 4 in the Variables
%   field of the structure returned from a SPDFCDFINFO module call to an
%   existing CDF or master CDF.  
%       type             CDF Types
%       -----            ---------
%       int8             CDF_INT1 or CDF_BYTE
%       int16            CDF_INT2
%       int32            CDF_INT4
%       int64            CDF_INT8
%       uint8            CDF_UINT1
%       uint16           CDF_UINT2
%       uint32           CDF_UINT4
%       single           CDF_FLOAT or CDF_REAL4
%       double           CDF_DOUBLE or CDF_REAL8
%       epoch            CDF_EPOCH
%       epoch16          CDF_EPOCH16
%       tt2000           CDF_TIME_TT2000
%       char             CDF_CHAR or CDF_UCHAR
%
%   Note: Make sure variable's data match to the defined type.
%
%   SPDFCDFWRITE(..., 'VarCompress', COMPRESSVARS) specifies which variables
%   will be compressed by what compression method (and level for GZIP).
%   COMPRESSVARS is a cell array of variable names and their respective
%   compression methods. For examples, to set the compression for 'var1' and
%   'var2' with AHUFF and GZIP.6 respectively, COMPRESSVARS should be given
%   as {'var1', 'ahuff', 'var2', 'gzip.6'}. GZIP.6 compression provides the
%   best compression rate and performance.
%
%   If there is a master CDF that has all the same variable info as the new CDF,
%   then SPDFCDFINFO module can be used to retrieve the infomation. The 
%   'Variables' field from the returned structure contain the compression info
%   (at element 7) for each variable,  Set up a cell to use such compression
%   info.
%
%   SPDFCDFWRITE(..., 'CDFCompress', COMPRESSVALUE) specifies the CDF will be
%   compressed at the file level by what compression method (and level for GZIP).
%   COMPRESSVALUE is a string of a valid compression method and level.
%
%   If there is a master CDF that can be used to define the new CDF's settings,
%   then SPDFCDFINFO module can be used to retrieve the infomation. The 
%   'Compression' and 'CompressionParam' fields from the returned structure's
%   'FileSettings' structure field contain the compression info. Combining these 
%   fields will make a valid compression. See the example.
%   Alternatively, COMPRESSVALUE can be provided as one of the following strings:
%   'none', 'gzip.x', 'rle' (Run-length encoding), 'huff' (Huffman), 'ahuff'
%   (Adaptive Huffman), where 'x' in 'gzip.x' is the level of 1-9 (6 being the
%   preferable). 
%
%   SPDFCDFWRITE(..., 'VarSparse', SPARSEVARS) specifies which variables have
%   sparse records. SPARSEVARS is a cell array of orderly pairs of a variable
%   name and its respective value. The valid value should be 'full',
%   'Sparse(padded)' or 'Sparse(previous)' (the 6th column from Variables field
%   from spdfcdfinfo).
%   Value 'full' is the default if a variable does not have the sparse records.
%   For examples, to set the sparse record for 'var1' and 'var2' with 'full'
%   and 'Sparse(previous)', SPARSEVARS should be specified as {'var1', 'full',
%   'var2', 'Sparse(padded)'}. (Actually, there is no need for 'var1'.) 
%   Variable's record data should be provided in cell form for the sparse record
%   variables. Data for any virtual records should be presented as []s. See the
%   sample code for its usage.  
%
%   SPDFCDFWRITE(..., 'BlockingFactor', BFVARS) specifies the blocking factor
%   for the variables. The blocking factor is the number of variable records
%   that will be pre-allocated when a new record is to be written. The default 
%   value could be too small for variables that have large record size or
%   number. A large blocking factor (up to a variable's maximum record number)
%   can make access to the full variable data more efficient as less blocks
%   are involved (thus less I/Os). It can also make the file less fragmented.
%   BFVARS is a cell array of pairs of variable name and its respective value.
%   The value should be a numeric.
%
%   If there is a master CDF that has all the same variable info as the new CDF,
%   then SPDFCDFINFO module can be used to retrieve the infomation. The 
%  'Variables' field from the returned structure contain the record sparseness
%   info (at element 6) for each variable,  Set up a cell to use such sparseness
%   info.
%
%   SPDFCDFWRITE(..., 'ConvertDatenumToEpoch', TF, ...) converts MATLAB datenum
%   values to CDF epoch data if TF is true. This option works with the
%   variable(s) that is of CDF_EPOCH type in a CDF. 
%   There are two ways to write data for epoch variable(s) of CDF_EPOCH into
%   a CDF. First, uses cdfepoch objects, each of which is from a datenum,
%   datestr, or even cdfepoch object, to pass data to spdfcdfwrite: 
%      SPDFCDFWRITE(..., {'Epoch',cdfepoch([...], ...)}, ...) 
%   This option is time and space consuming if large datasets are involved. 
%   The second way uses 'ConvertDatenumToEpoch':
%     SPDFCDFWRITE(..., {'Epoch',[...], ...}, 'ConvertDatenumToEpoch', TF, ...) 
%   If TF is set to true, the passed data are assumed as MATLAB datenum
%   values and a data conversion to CDF epoch values is performed. 
%   Setting it to false (the default), All data will be considered already in
%   CDF_EPOCH form and will be filled, as is, to
%   CDF_EPOCH data type variable(s). The CDF_EPOCH data need to be numeric of
%   mxDouble_CLASS (double).
%
%   SPDFCDFWRITE(..., 'ConvertDatenumToTT2000', TF, ...) converts MATLAB datenum
%   values to CDF TT2000 data if TF is true. This option works with the
%   variable(s) that is of CDF_TIME_TT2000 type in a CDF.
%   There are two ways to write data for epoch variable (s) of CDF_TIME_TT2000
%   into a CDF. First, uses cdftt2000 objects, each of which is from a datenum,
%   datestr, or even cdftt2000 object, to pass data to spdfcdfwrite:
%      SPDFCDFWRITE(..., {'Epoch',cdftt2000([...], ...)}, ...)
%   This option is also time and space consuming if large datasets are involved.
%   The second way uses 'ConvertDatenumToTT2000':
%     SPDFCDFWRITE(..., {'Epoch',[...], ...}, 'ConvertDatenumToTT2000', TF, ...)
%   If TF is set to true, the passed data are assumed as MATLAB datenum
%   values and a data conversion to CDF TT2000 values is performed.
%   Setting it to false (the default), All data will be considered already in
%   CDF_TIME_TT2000 form and will be filled, as is, to
%   CDF_TIME_TT2000 data type variable(s). The CDF TT2000 values needs to be
%   numeric of mxINT64_CLASS (int64).
%
%   SPDFCDFWRITE(..., 'Checksum', CHECKSUMVAL, ...) specifies whether the output
%   CDF should have its checksum computed. The valid values are 'MD5' or 'none'.
%   The default is 'none'.
%
%   SPDFCDFWRITE(..., 'CDFLeapSecondLastUpdated', value, ...) resets the CDF's
%   leap second last updated date.  For CDFs created prior to V 3.6.0, this 
%   field is not set. It is set to indicate what leap second table this CDF is
%   based upon. The value, in YYYYMMDD form, must be a valid entry in the
%   currently used leap second table, or zero (0) if the table is not used. 
%   CDF will automatically fill the value with the last entry date in the leap
%   second table if this option is not specified. 
%
%   SPDFCDFWRITE(..., 'Singleton', VARS, ...) indicates whether to keep the
%   singleton dimension(s) passed in from the multi-dimensional data. VARS is
%   a cell array of variable names, indicating each variable's singleton 
%   dimension(s) is to be kept.
%   For example, variable with data dimensions like 10x1x100 will be written
%   as 2-dimensions (10x1) for 100 records if the record bound is specified.
%   For a row (1-by-M) or column (M-by-1) vector, the variable data will be
%   written as 2-dimension as is, unless the recordbound is specified.  
%   The default setting is to have all singleton dimension(s) removed.
%   The above 10x1x100 variable will be written as 1-dimension
%   (with 10 elements).  
%
%   Notes:
%
%     SPDFCDFWRITE creates temporary files when writing CDF files.  Both the
%     target directory for the file and the current working directory
%     must be writable.
%
%     To maximize performance, specify the 'ConvertDatenumToEpoch' 
%     parameter with true (nonzero) value while providing datenum values
%     for 'Epoch' variable. 
%
%     CDF epoch is the number of milliseconds since 1-Jan-0000 0h:0m:0s:000ms.
%     CDF TT2000 is the number of nanoseconds since 1-Jan-2000
%                12h:0m:0s:000000000ns with leap seconds included.
%
%
%   Examples:
%
%   % Write out a file 'example.cdf' containing a variable 'Longitude'
%   % with a single record (row) of a vector with 361 elements:
%
%   spdfcdfwrite('example', {'Longitude', 0:360});
%
%   % Write out a file 'example.cdf', containing a variable 'Longitude'
%   % with the value [0:360] (one record of 361 values), and with a variable
%   % attribute of 'validmin' with the value 10:
%
%   varAttribStruct.validmin = {'Longitude' [10]};
%   spdfcdfwrite('example', {'Longitude' 0:360}, ...
%                'VariableAttributes', varAttribStruct);
%
%   % Write out a file 'example.cdf' containing variables 'Longitude'
%   % and 'Latitude' with the variable 'Longitude' being a Record-bound
%   % (361 records to be written)::
%
%   spdfcdfwrite('example', {'Longitude', (0:360), 'Latitude', 10:20}, ...
%                'RecordBound', {'Longitude'});
%
%   % These two commands should write out the data values identically:
%      SPDFCDFWRITE('example', {'Epoch', num2cell(1:100)}, ...
%                   'epochiscdfepoch', true);
%      SPDFCDFWRITE('example', {'Epoch', (1:100)'}, ...
%                   'recordbound', {'Epoch'}, ...
%                   'epochiscdfepoch', true);
%
%   % Write out a file 'example.cdf' containing variables 'Longitude'
%   % and 'Latitude' with the variable 'Latitude' having a pad value
%   % of 10 for all out-of-bounds records that are accessed. The
%   % 'Longitude' variable has one record (row) of a vector with 361 elements
%   % 'Latitude' has one record of a vector with 11 elements.
%
%   spdfcdfwrite('example', {'Longitude', (0:360)', 'Latitude', (10:20)'}, ...
%                'RecordBound', {'Latitude', 'Longitude'}, ...
%                'PadValues', {'Latitude', 10});
%
%   % Write out a file 'example.cdf', with multiple rows of time series data.
%   % Each row has a time and a sampled data, both being scalar. 
%   % The following sample shows 100 records (rows): epoch is of CDF_EPOCH 
%   % data type and Samples of CDF_DOUBLE. 
%   % The epoch starts from 2010-01-01T00:00:00.000 with 1 second stride.
%   % Each sampled value starts from 0.0, with 5.0 stride. 
%
%   epoch=utc2cdfepoch(2010,1,1,0,0,0,0);
%   epochvalues = (epoch+[0:99]*1000)';
%   values = ([0:99]*5.0)';
%   spdfcdfwrite('example', {'Epoch', epochvalues, 'Samples', values}, ...
%                'EpochisCDFEpoch', true, ...
%                'RecordBound', {'Epoch', 'Samples'});
%
%   % 'EpochisCDFEpoch' option is needed as 'Epoch' is to be created of
%   % CDF_EPOCH type.
%
%   % Alternatively, the same result can be accomplished by using the 
%   % 'EpochType' option:
%
%   spdfcdfwrite('example', {'Epoch', epochvalues, 'Samples', values}, ...
%                'EpochType', {'Epoch'}, ...
%                'RecordBound', {'Epoch', 'Samples'});
%
%   % Alternatively, the same result can be accomplished by making the epoch
%   % vector to cell. No 'RECORDBOUND' option is needed.
%   epoch=utc2cdfepoch(2010,1,1,0,0,0,0);
%   epochvalues = epoch+[0:99]*1000;
%   epochscell = num2cell(epochvalues);
%   values = num2cell([0:99]*5.0);
%   spdfcdfwrite('example', {'Epoch', epochvalues, 'Samples', values}, ...
%                'EpochType', {'Epoch'});
%
%   % Write out a file 'example.cdf', with single or multiple rows of
%   % vectorized data. Variable 'one0' will have one record with 5 elements.
%   % Variable 'one1' has five records, each record having 1 value. Variable
%   % 'two0' has one 5-by-2 record, while Variable 'two2' has five (5) 
%   % records, each record having 2 elements. Variable 'three0' has a
%   % single 3-D (3-by-2-by-2) record, while Variable 'three3' has two (2)
%   % records, each record being a 3-by-2 matrix. 
%
%   data0=1:5;
%   data1=data0';
%   data2=[10 20;30 40;50 60;70 80;90 100];
%   data2a=data2+100;
%   data3=[1 2;3 4;5 6];
%   data3(:,:,2)=[11 22; 33 44; 55 66];
%   data3a=data3+100;
%   spdfcdfwrite('example',{'one0',data0,'one1',data1,'two0',data2, ...
%                'two2',data2a,'three0',data3,'three3',data3a}, ...          
%                'recordbound',{'one1','two2','three3'});
%
%   % For writing out two variables: 'Epoch' of CDF_EPOCH type, and
%   % 'Sample' of CDF_DOUBLE type. Four records are written for each. Epoch's
%   % record is a scalar, while Sample's is an 1-D with 4 elements.
%
%   epoch=utc2cdfepoch(2010,1,1,0,0,0,0);
%   epochvalues = (epoch+[0:3]*1000)';
%   value = [0:3]*5.0;
%   values = [value; value+10; value+20; value+30];
%   spdfcdfwrite('example', {'Epoch', epochvalues, 'Sample', values}, ...
%                'EpochType', {'Epoch'}, ...
%                'RecordBound', {'Epoch', 'Sample'});
%
%   % Write out a file 'example.cdf', with 100 MATLAB datenum values,
%   % starting from 2010-01-01T00:00:00.000 with 1 second stride, 
%   % into variable: 'Epoch' of CDF_EPOCH type. The first record has a date:
%   %  01-Jan-2010 00:00:00.000, the second record:
%   %  01-Jan-2010 00:00:01.000, the third record:
%   %  01-Jan-2010 00:00:02.000, etc.
%   % 'Epoch' has a pad value of 01-Jan-0000 00:00:00.001.
%
%   datenum1=datenum(2010,1,1,0,0,0);
%   datenumvalues = (datenum1+[0:99]/86400)';
%   spdfcdfwrite('example', {'Epoch', datenumvalues}, ...
%                'ConvertDatenumToEpoch', true, ...
%                'RecordBound', {'Epoch'}, ...
%                'PadValue', {'Epoch', 1});
%
%   % Write out a file 'example.cdf', with three records for the variable
%   % 'Epoch' of CDF_TIME_TT2000 type. The first record has a date:
%   % 2010-10-10T01:02:03.456, the second record: 2010-11-11T02:04:06.789
%   % and the third record: 2010-12-12T03:04:05.123.
%
%   dates = datenum([2010 10 10 1 2 3.456; 2010 11 11 2 4 6.789; ...
%                    2010 12 12 3 4 5.123]);
%   spdfcdfwrite('example', {'Epoch', dates'}, ...
%                'ConvertDatenumToTT2000', true, ...
%                'RecordBound', {'Epoch'});
%
%   % Write out a file 'example.cdf', with 6 records for the variable 
%   % 'Epoch', which will be of CDF_TIME_TT2000 data type. The data
%   $ crosses over a leap second. Convert the epoch in UTC string
%   $ to their TT2000 values before write out to the CDF file.
%
%   time = {'2008-12-31T23:59:58.123456789'; ...
%           '2008-12-31T23:59:59.123456789'; ...
%           '2008-12-31T23:59:60.123456789'; ...
%           '2009-01-01T00:00:00.123456789'; ...
%           '2009-01-01T00:00:01.123456789'; ...
%           '2009-01-01T00:00:02.123456789'};
%   values = spdfparsett2000(time);          
%   spdfcdfwrite('example', {'Epoch', values}, ...
%                'EpochType', {'Epoch'}, ...
%                'RecordBound', {'Epoch'});
%
%   % Write out a file 'sample.cdf', with two variables. One variable, 'var2'.
%   % is compressed with GZIP.6.
%
%   var1data=......;
%   var2data=......;
%   spdfcdfwrite('sample',{'Epoch',var1data,'var2',var2data}, ...
%                'recordbound',{'Epoch','var2'}, ...
%                'varcompress',{'var2','gzip.6'});
%
%   % Write out a file 'sparse.cdf', with a variable that has sparse records.
%   % The variable 'one' only has two (2) physical data: at record 0 and
%   % record 4. 
%
%   spdata={[123 321];[];[];[];[-321 -123]};
%   spdfcdfwrite('sparse',{'one',spdata}, ...
%                'varsparse', {'one','Sparse(previous)'});
%
%   % Write out a file 'real.cdf', based on the master cdf, which provides the
%   % file settings info, i.e., checksum, CDF file level compression, as well as
%   % meta-data for all of the global and variable attribute information.  It
%   % also provides the variable spec, e.g., data type, record variance, record
%   % sparseness, blocking factor, pad value and compression, for each variable.
%   % 'Recordbound' option is used for specifying record-variant (RV) variables.
%   % Among the variables, Variable number 15, a single floating point array, has
%   % sparse records [at record 1 and 5]. Its data has to be in the cell array.
%
%   info=spdfcdfinfo('master.cdf');
%   for p = 1:length(info.Variables(:,1))
%     compress{(2*p)-1} = info.Variables(p,1); 	% Variable name
%     compress{2*p} = info.Variables(p,7);	% Variable compression
%     sparse{(2*p)-1} = info.Variables(p,1);	% Variable name
%     sparse{2*p} = info.Variables(p,6);	% Variable sparseness
%     bf{2*p-1} = info.Variables{p,1};		% Variable name
%     bf{2*p} = info.Variables{p,8};		% Variable blocking factor
%     pad{2*p-1} = info.Variables{p,1};		% Variable name
%     pad{2*p} = info.Variables{p,9};		% Variable pad value
%     datatypes{2*p-1} = info.Variables{p,1};	% Variable name
%     datatypes{2*p} = info.Variables{p,4};	% Variable data type
%   end
%   rbvars = {info.Variables{:,1}};		% Variable names for recordbound
%   for p = length(vars):-1:1
%     if (strncmpi(info.Variables{p,5},'f',1)==1)	% NRV variable
%       rbvars(:,p)=[]; 	  		% Remove it
%     end
%   end
%   if isnumeric(info.FileSettings.CompressionParam) % A number for Gzip parameter 
%     cdfcompress=strcat(info.FileSettings.Compression, '.', ... % Make it 'gzip.x'
%                        num2str(info.FileSettings.CompressionParam));
%   else
%     cdfcompress=strcat(info.FileSettings.Compression, '.', ... % None or non-gzip
%                        info.FileSettings.CompressionParam);
%   end
%
%   % fill data
%   for p = 1:length(info.Variables(:,1))
%     varsdata{2*p-1} = info.Variables(p,1);
%     if (p == 15)				% A sparse record variable 
%       var15data={single([123 321]);[];[];[];single([-321 -123])};
%       varsdata{(2*15)} = var15data;		% Sparse record data
%     else
%       varsdata{(2*p)} = [...];		% Normal data
%     end
%   end
%   spdfcdfwrite('real',varsdata, ...
%                'GlobalAttributes', info.GlobalAttributes, ...	% Global attributes
%                'VariableAttributes', info.VariableAttributes, ... %Variable attributes
%                'RecordBound', rbvars, ...			% Var record bound
%                'varcompress',compress, ...			% Var compression 
%                'varsparse', sparse, ...			% Var sparseness
%                'blockingfactor', bf, ...			% Var blocking factors 
%                'padvalues', pad, ...				% Var Pad values
%                'cdfcompress',cdfcompress, ...			% CDF compression
%                'checksum', info.FileSettings.Checksum, ... 	% Checksum
%                'VarDatatypes', datatypes);			% Var data types
%
%   Note: The compatible data types between MATLAB and CDF are as follows:
%         MATLAB                  CDF
%         ------                --------
%         int8                  CDF_BYTE or CDF_INT1
%         int16                 CDF_INT2
%         int32                 CDF_INT4
%         int64                 CDF_INT8 or CDF_TIME_TT2000
%         uint8                 CDF_UINT1
%         uint16                CDF_UINT2
%         uint32                CDF_UINT4
%         single                CDF_FLOAT
%         double                CDF_DOUBLE or CDF_EPOCH or CDF_EPOCH16
%         char/string           CDF_UCHAR or CDF_CHAR
%
%   See also SPDFCDFREAD, SPDFCDFUPDATE, SPDFCDFINFO, CDFEPOCH, CDFTT2000,
%            SPDFENCODEEPOCH, SPDFCOMPUTEEPOCH, SPDFPARSEEPOCH,
%            SPDFBREAKDOWNEPOCH, SPDFENCODEEPOCH16, SPDFCOMPUTEEPOCH16,
%            SPDFPARSEEPOCH16, SPDFBREAKDOWNEPOCH16, SPDFENCODETT2000,
%            SPDFCOMPUTETT2000, SPDFPARSETT2000, SPDFBREAKDOWNTT2000,
%            SPDFDATENUMTOEPOCH, SPDFDATENUMTOEPOCH16, SPDFDATENUMTOTT2000,
%            SPDFCDFLEAPSECONDSINFO

% HISTORY:
%   February 12, 2009  Mike Liu     The following changes have been made to
%                                   spdfcdfwritec.c:
%                                     - Added parameter 'RecordBound'.
%                                     - Added parameter 'ConvertDatenumToEpoch'.
%                                     - Added a logic to check CDF_EPOCH and 
%                                       CDF_EPOCH16 data.

%
% Process arguments.
%

if (nargin < 2)
    error('MATLAB:spdfcdfwrite:inputArgumentCount', ...
          'SPDFCDFWRITE requires at least two input arguments.')
end

% parse_inputs sorts out all of the input args.  Its return values:
%
% * args - an array of structs.  args.VarNames contains the names
%          of the variables to be written to the CDF.  args.VarVals contains
%          the corresponding values.  args.PadVals contains corresponding pad
%          values. args.ConvertDatenum indicates whether to convert
%          MATLAB datenum to CDF epoch values. args.RecBnd contains variables
%          that are for Record-bound. args.EpochIsCDFEpoch is a flag
%          indicating whether datenum to cdf epoch conversion is needed.
% * isAppending - whether or not to delete this file or if we need to
%                 append to the file
% * isMultifile - whether or not to write out as a multi-file CDF
% * CDFversion - which version of the CDF library to use
% * varAttribStruct - a struct containing the variable attributes
% * globalAttribStruct - a struct containing the global CDF attributes
% * msg - an error message from parse_inputs that we pass on to the user.

[args, isAppending, isMultifile, CDFversion, varAttribStruct, ...
 globalAttribStruct, exception] = parse_inputs(varcell, varargin{:});

if (~isempty(exception.msg))
    error(exception.id, '%s', exception.msg)
end

%
% Create a proper filename for the CDF
%

% See if there is an extension
[pname, fname, ext] = fileparts(filename);

% If there is an extension, then remove it before passing it to CDFlib.
if (~isempty(ext))
    if (~isempty(strfind(ext, 'cdf')))
        filename = fullfile(pname, fname);
    end
end

%
% Call the underlying spdfcdfwritec function which calls the CDFlib
%

spdfcdfwritec(filename, args.VarNames, args.VarVals, args.PadVals, ...
          globalAttribStruct, varAttribStruct, isAppending, ...
          isMultifile, CDFversion, args.ConvertDatenum, args.RecBnd, ...
          args.EpochIsCDFEpoch, args.TT2000, args.ConvertDatenum2, ...
          args.EpochTp, args.VarCompVals, args.SparseVarVals, ...
          args.BFVarVals, args.DTVarVals, args.MD5, args.CDFComp, ...
          args.CDFleapsecondlastupdated, args.SingletonVars);

%%%
%%% Function parse_inputs
%%%

function [args, isAppending, isMultifile, CDFversion, varAttribStruct, ...
          globalAttribStruct, exception] = parse_inputs(varcell, varargin)

% Set default values
args.PadVals = {};
args.RecBnd = {};
args.VarCompVals = {};
args.SparseVarVals = {};
args.BFVarVals = {};
args.EpochTp = {};
args.EpochIsCDFEpoch = false;
args.TT2000 = false;
args.ConvertDatenum = false;
args.ConvertDatenum2 = false;
args.MD5 = false;
args.CDFComp = '';
args.CDFleapsecondlastupdated = int32(-999);
isAppending = 0;
isMultifile = 0;
varAttribStruct = struct([]);
globalAttribStruct = struct([]);
% The following value indicates no version preference.
CDFversion = -1.0;

exception.msg = '';
exception.id = '';

% First check that varcell meets all of our requirements
args.VarNames = {varcell{1:2:end}};
args.VarVals = {varcell{2:2:end}};
% Wrap the scalars non-empties in cell arrays.
for i = 1:length(args.VarVals)
%     if ~isempty(args.VarVals{i}) && (ischar(args.VarVals{i}) || (numel(args.VarVals{i}) == 1))
     if ~isempty(args.VarVals{i}) && ischar(args.VarVals{i})
         args.VarVals{i} = {args.VarVals{i}};    
     end
end

if length(args.VarNames) ~= length(args.VarVals)
    exception.msg = 'All variable names must have a corresponding variable value.';
    exception.id = 'MATLAB:spdfcdfwrite:variableWithoutValue';
    return
end

% Check and make sure that all variable values are of the same
% datatype, but ignore empties
if ~isempty(args.VarVals)
    for i = 1:length(args.VarVals)
        a = args.VarVals{i};
        if iscell(a)
            nonEmpties = {a{~cellfun('isempty',a)}};
            if iscell(nonEmpties) && ~isempty(nonEmpties)
                dtype = class(nonEmpties{1});
                if ~all(cellfun('isclass',nonEmpties,dtype))
                    exception.msg = 'All record values for a given variable must be of the same type.';    
                    exception.id = 'MATLAB:spdfcdfwrite:inconsistentRecordTypes';
                end
            end
%        else
            % If it isn't a cell array, then it is an array and
            % all elements are of the same type. 
%            args.VarVals{i} = (args.VarVals{i});
        end
    end
end

args.PadVals = cell(1,length(args.VarNames));
args.RecBnd = cell(1,length(args.VarNames));
args.EpochTp = cell(1,length(args.VarNames));
args.VarCompVals = cell(1,length(args.VarNames));
args.SparseVarVals = cell(1,length(args.VarNames));
args.BFVarVals = cell(1,length(args.VarNames));
args.DTVarVals = cell(1,length(args.VarNames));
args.SingletonVars = cell(1,length(args.VarNames));

% Parse arguments based on their number.
if (nargin > 0)
    
    paramStrings = {'padvalues'
                    'varcompress'
                    'cdfcompress'
                    'varsparse'
                    'blockingfactor'
                    'vardatatypes'
                    'globalattributes'
                    'variableattributes'
                    'writemode'
                    'format'
                    'recordbound'
                    'convertdatenumtoepoch'
                    'convertdatenumtott2000'
                    'epochiscdfepoch'
                    'version'
                    'tt2000'
                    'checksum'
                    'cdfleapsecondlastupdated'
                    'epochtype'
                    'singleton'};
    
    % For each pair
    for k = 1:2:length(varargin)
        param = lower(varargin{k});
            
        if (~ischar(param))
            exception.msg = 'Parameter name must be a string.';
            exception.id = 'MATLAB:spdfcdfwrite:paramNameNotString';
            return
        end
        
        idx = strmatch(param, paramStrings);
        
        if (isempty(idx))
            exception.msg = sprintf('Unrecognized parameter name "%s".', param);
            exception.id = 'MATLAB:spdfcdfwrite:unrecognizedParam';
            return
        elseif (length(idx) > 1)
            exception.msg = sprintf('Ambiguous parameter name "%s".', param);
             exception.id = 'MATLAB:spdfcdfwrite:ambiguousParam';
           return
        end
        
        switch (paramStrings{idx})
        case 'padvalues'
           padCell = varargin{k+1};
           % If we weren't passed an even pair, then a variable
           % name or value was left out.
           if rem(length(padCell), 2)
               exception.msg = ['Number of variables to write out with ' ...
                      'padding does not match number of pad values.'];
               exception.id = 'MATLAB:spdfcdfwrite:paddingMismatch';
               return
           end
           vars = {padCell{1:2:end}};
           padVals = {padCell{2:2:end}};
           % Check that vars are in the list above.
           if ~iscellstr(vars)
               exception.msg = 'All variable names 1 must be strings.';
               exception.id = 'MATLAB:spdfcdfwrite:varNameNotString';
               return
           end
           if ~all(ismember(vars, args.VarNames))
               exception.msg = ['Variables listed in the PadValues ' ...
                      'cell must be on the list of variables ' ...
                      'to save.'];
               exception.id = 'MATLAB:spdfcdfwrite:notSavingVarForPadValue';
               return
           end
           for i = 1:length(padVals)
               padVal = padVals{i};
               if (isempty(padVal)) 
                   continue
               end
               if isnumeric(padVal) || ischar(padVal) || isa(padVal,'cdfepoch') || ...
                  isa(padVal,'cdftt2000')
                   args.PadVals{strcmp(args.VarNames,vars{i})} = padVal;
               else
                   exception.msg = 'Pad values must be numbers, strings, cdfepoch or cdftt2000.';
                   exception.id = 'MATLAB:spdfcdfwrite:badPadValue';
                   return
               end
           end
        case 'varcompress'
           varCompCell = varargin{k+1};
           % If we weren't passed an even pair, then a variable
           % name or value was left out.
           if rem(length(varCompCell), 2)
               exception.msg = ['Number of variables to compress ' ...
                      'does not match number of compression values.'];
               exception.id = 'MATLAB:spdfcdfwrite:compressingMismatch';
               return
           end
	   if iscell(varCompCell{1})
	       vars = varCompCell{1:2:end};
	   else
               vars = {varCompCell{1:2:end}};
	   end
           varCompVals = {varCompCell{2:2:end}};
           % Check that vars are in the list above.
           if ~iscellstr(vars)
               exception.msg = 'All variable names 2 must be strings.';
               exception.id = 'MATLAB:spdfcdfwrite:varNameNotString';
               return
           end
           if ~all(ismember(vars, args.VarNames))
               exception.msg = ['Variables listed in the VarCompValues ' ...
                      'cell must be on the list of variables ' ...
                      'to save.'];
               exception.id = 'MATLAB:spdfcdfwrite:notSavingVarForVarCompValue';
               return
           end
           for i = 1:length(varCompVals)
               varCompVal = varCompVals{i};
               if (isempty(varCompVal))
                   continue
               end
               if ischar(varCompVal) ...
                   args.VarCompVals{strcmp(args.VarNames,vars{i})} = varCompVal;
               else
                   exception.msg = 'Compression 3 must be strings.';
                   exception.id = 'MATLAB:spdfcdfwrite:badVarCompValue';
                   return
               end
           end
       case 'globalattributes'
           globalAttribStruct = varargin{k+1};
           if ~isstruct(globalAttribStruct)
               exception.msg = ['''GlobalAttributes''' ' must be a struct.'];
               exception.id = 'MATLAB:spdfcdfwrite:globalAttributesNotStruct';
               return
           end
           attribs = fieldnames(globalAttribStruct);
           
           % If the global attribute isn't a cell, then stuff it in one.
           for i = 1:length(attribs)
               attribVal = globalAttribStruct.(attribs{i});
               if ~iscell(attribVal)
                   globalAttribStruct.(attribs{i}) = {attribVal};
               end
           end
        case 'variableattributes'
           varAttribStruct = varargin{k+1};
           if ~isstruct(varAttribStruct)
               exception.msg = ['''VariableAttributes''' ' must be a struct.'];
               exception.id = 'MATLAB:spdfcdfwrite:variableAttributesNotStruct';
               return
           end
           attribs = fieldnames(varAttribStruct);
           
           % Check the VariableAttributes struct.
           for i = 1:length(attribs)
               % If the variable attribute isn't in a cell (because
               % it is scalar, then put it into a cell.
               attribVal = varAttribStruct.(attribs{i});
               s = size(attribVal);
               if ~iscell(attribVal)
                   varAttribStruct.(attribVal) = {attribVal};
               end
               % The variable attribute struct may have more than one
               % variable per attribute.  However, there must only be
               % one associated value of the attribute for each variable,
               % hence the 2.
               if (s(2) == 2)
                   % Transpose it because CDFlib reads the arrays column-wise.
                   varAttribStruct.(attribs{i}) = attribVal';   
               else
                   % We have ordered pairs.
                   varAttribStruct.(attribs{i}) = reshape(varAttribStruct.(attribs{i})(:),numel(varAttribStruct.(attribs{i})(:))/2, 2);
               end
               
%                % Don't forget to ignore the "CDFAttributeRename" attribute
%                completeSet = {args.VarNames{:} 'CDFAttributeRename'};
%                tmpVar = varAttribStruct.(attribs{i});
%                varsWithAttributes = {tmpVar{1,:}};
%                if ~all(ismember(varsWithAttributes, completeSet))
%                    exception.msg = ['Variables listed in the VariableAttributes ' ...
%                           'struct must be on the list of variables ' ...
%                           'to save.'];
%                    return
%                end               
           end
        case 'writemode'
            isAppending = varargin{k+1};
            if strcmpi(isAppending, 'overwrite')
                isAppending = 0;
            elseif strcmpi(isAppending, 'append')
                isAppending = 1;
            else
                exception.msg = ['''WriteMode''' ' must be either ' '''overwrite''' ... 
                       ' or ' '''append'''];
                exception.id = 'MATLAB:spdfcdfwrite:badWriteModeValue';
                return
            end
        case 'format'
            isMultifile = varargin{k+1};
            if strcmpi(isMultifile, 'singlefile')
                isMultifile = 0;
            elseif strcmpi(isMultifile, 'multifile')
                isMultifile = 1;
            else
                exception.msg = ['''Format''' ' must be either ' '''singlefile''' ... 
                       ' or ' '''multifile'''];
                exception.id = 'MATLAB:spdfcdfwrite:badFormatValue';
                return
            end
        case 'cdfcompress'
            compression = varargin{k+1};
            args.CDFComp = find_compression(compression);
        case 'version'
            version = varargin{k+1};
            if ischar(version) && ...
                    (strcmp(version,'2.7') || strcmp(version,'3.0'))
                CDFversion = str2num(version);
            else
                exception.msg = '''Version'' must be either ''2.7'' or ''3.0''';
                exception.id = 'MATLAB:spdfcdfwrite:badVersionValue';
                return
            end
        case 'recordbound'
           RecBndCell = varargin{k+1};
           % Check that vars are in the list above.
           if ~iscellstr(RecBndCell)
               exception.msg = 'All variable names for recordbound must be strings.';
               exception.id = 'MATLAB:spdfcdfwrite:varNameNotString';
               return
           end
           if ~all(ismember(RecBndCell, args.VarNames))
               exception.msg = ['Variables listed in the RECORDBOUND ' ...
                      'cell must be on the list of variables ' ...
                      'to save.'];
               exception.id = 'MATLAB:spdfcdfwrite:notSavingVarForRecBnd';
               return
           end
           for i = 1:length(RecBndCell)
               RecBndVar = RecBndCell{i};
               args.RecBnd{strcmp(args.VarNames,RecBndCell{i})} = RecBndVar;
           end
        case 'checksum'
           if (k == length(varargin))
               msg = 'Missing "checksum" value.';
               return
           else
               checksum = varargin{k + 1};
               if ~isstr(checksum)
                 exception.msg = 'Checksum value must be a string.';
                 exception.id = 'MATLAB:spdfcdfwrite:checksumvalue';
                 return
               end
               args.MD5 = logical(find_checksum(checksum));
           end
        case 'cdfleapsecondlastupdated'
           args.CDFleapsecondlastupdated = varargin{k+1};
           if (isnumeric(args.CDFleapsecondlastupdated))
               args.CDFleapsecondlastupdated = int32(args.CDFleapsecondlastupdated);
           else
               exception.msg = 'CDF leapsecondlastupdated value must be a numeric value (in YYYYMMDD form).';
               exception.id = 'MATLAB:spdfcdfwrite:cdfleapsecondlastupdated';
               return
           end
        case 'varsparse'
           SparseVarsCell = varargin{k+1};
           % If we weren't passed an even pair, then a variable
           % name or value was left out.
           if rem(length(SparseVarsCell), 2)
               exception.msg = ['Number of variables to sparse records ' ...
                      'does not match number of their values.'];
               exception.id = 'MATLAB:spdfcdfwrite:sparserecordMismatch';
               return
           end
           vars = {SparseVarsCell{1:2:end}};
           sparseVals = {SparseVarsCell{2:2:end}};
           % Check that vars are in the list above.
           if ~iscellstr(vars)
               exception.msg = 'All variable names must be strings.';
               exception.id = 'MATLAB:spdfcdfwrite:varNameNotString';
               return
           end
           if ~all(ismember(vars, args.VarNames))
               exception.msg = ['Variables listed in the SparseValues ' ...
                      'cell must be on the list of variables ' ...
                      'to save.'];
               exception.id = 'MATLAB:spdfcdfwrite:notSavingVarForSparseValue';
               return
           end
           for i = 1:length(sparseVals)
               sparseVal = sparseVals{i};
               if (isempty(sparseVal))
                   continue
               end
               if ischar(sparseVal)
                   args.SparseVarVals{strcmp(args.VarNames,vars{i})} = sparseVal;
               else
                   exception.msg = 'Sparse Record must be strings.';
                   exception.id = 'MATLAB:spdfcdfwrite:badSparseValue';
                   return
               end
           end
        case 'blockingfactor'
           BFVarsCell = varargin{k+1};
           % If we weren't passed an even pair, then a variable
           % name or value was left out.
           if rem(length(BFVarsCell), 2)
               exception.msg = ['Number of variables to blocking factors ' ...
                      'does not match number of their values.'];
               exception.id = 'MATLAB:spdfcdfwrite:blockingfactorMismatch';
               return
           end
           vars = {BFVarsCell{1:2:end}};
           bfVals = {BFVarsCell{2:2:end}};
           % Check that vars are in the list above.
           if ~iscellstr(vars)
               exception.msg = 'All variable names must be strings.';
               exception.id = 'MATLAB:spdfcdfwrite:varNameNotString';
               return
           end
           if ~all(ismember(vars, args.VarNames))
               exception.msg = ['Variables listed in the BFValues ' ...
                      'cell must be on the list of variables ' ...
                      'to save.'];
               exception.id = 'MATLAB:spdfcdfwrite:notSavingVarForBFValue';
               return
           end
           for i = 1:length(bfVals)
               bfVal = bfVals{i};
               if (isempty(bfVal))
		   continue
	       end
               if isnumeric(bfVal)
                   args.BFVarVals{strcmp(args.VarNames,vars{i})} = int32(bfVal);
               else
                   exception.msg = 'Blocking factor must be numeric.';
                   exception.id = 'MATLAB:spdfcdfwrite:badBlockingFactor';
                   return
               end
           end
        case 'vardatatypes'
           DTVarsCell = varargin{k+1};
           % If we weren't passed an even pair, then a variable
           % name or value was left out.
           if rem(length(DTVarsCell), 2)
               exception.msg = ['Number of variables to blocking factors ' ...
                      'does not match number of their values.'];
               exception.id = 'MATLAB:spdfcdfwrite:blockingfactorMismatch';
               return
           end
           vars = {DTVarsCell{1:2:end}};
           dtVals = {DTVarsCell{2:2:end}};
           % Check that vars are in the list above.
           if ~iscellstr(vars)
               exception.msg = 'All variable names must be strings.';
               exception.id = 'MATLAB:spdfcdfwrite:varNameNotString';
               return
           end
           if ~iscellstr(dtVals)
               exception.msg = 'All variable data types must be strings.';
               exception.id = 'MATLAB:spdfcdfwrite:vardatatypeNotString';
               return
           end
           if ~all(ismember(vars, args.VarNames))
               exception.msg = ['Variables listed in the DTValues ' ...
                      'cell must be on the list of variables ' ...
                      'to save.'];
               exception.id = 'MATLAB:spdfcdfwrite:notSavingVarForDTValue';
               return
           end
           for i = 1:length(dtVals)
               dtVal = dtVals{i};
               if (~isempty(dtVal))
                   args.DTVarVals{strcmp(args.VarNames,vars{i})} = ...
                                           int32(find_datatype(dtVal));
               end
           end
        case 'epochtype'
           EpochTpCell = varargin{k+1};
           % Check that vars are in the list above.
           if ~iscellstr(EpochTpCell)
               exception.msg = 'All variable names 7 must be strings.';
               exception.id = 'MATLAB:spdfcdfwrite:varNameNotString';
               return
           end
           if ~all(ismember(EpochTpCell, args.VarNames))
               exception.msg = ['Variables listed in the EPOCHTYPE ' ...
                      'cell must be on the list of variables ' ...
                      'to save.'];
               exception.id = 'MATLAB:spdfcdfwrite:notSavingVarForEpochTp';
               return
           end
           for i = 1:length(EpochTpCell)
               EpochTpVar = EpochTpCell{i};
               args.EpochTp{strcmp(args.VarNames,EpochTpCell{i})} = EpochTpVar;
           end
        case 'convertdatenumtoepoch'
           if (k == length(varargin))
               msg = 'No datenum conversion value specified.';
               return
           else
               convert = varargin{k + 1};
               if (numel(convert) ~= 1)
                   msg = 'Datenum conversion value must be a scalar logical.';
               end

               if (islogical(convert))
                   args.ConvertDatenum = convert;
               elseif (isnumeric(convert))
                   args.ConvertDatenum = logical(convert);
               else
                   msg = 'Datenum conversion value must be a scalar logical.';
               end
           end
        case 'convertdatenumtott2000'
           if (k == length(varargin))
               msg = 'No datenum conversion value specified.';
               return
           else
               convert = varargin{k + 1};
               if (numel(convert) ~= 1)
                   msg = 'Datenum conversion value must be a scalar logical.';
               end

               if (islogical(convert))
                   args.ConvertDatenum2 = convert;
               elseif (isnumeric(convert))
                   args.ConvertDatenum2 = logical(convert);
               else
                   msg = 'Datenum conversion value must be a scalar logical.';
               end
           end
        case 'epochiscdfepoch'
           if (k == length(varargin))
               msg = 'No EpochIsCDFEpoch value specified.';
               return
           else
               noconvert = varargin{k + 1};
               if (numel(noconvert) ~= 1)
                   msg = 'EpochIsCDFEpoch value must be a scalar logical.';
               end
               if (islogical(noconvert))
                   args.EpochIsCDFEpoch = noconvert;
               elseif (isnumeric(noconvert))
                   args.EpochIsCDFEpoch = logical(noconvert);
               else
                   msg = 'Datenum conversion value must be a scalar logical.';
               end
           end
        case 'tt2000'
           if (k == length(varargin))
               msg = 'No TT2000 value specified.';
               return
           else
               noconvert = varargin{k + 1};
               if (numel(noconvert) ~= 1)
                   msg = 'TT2000 value must be a scalar logical.';
               end
               if (islogical(noconvert))
                   args.TT2000 = noconvert;
               elseif (isnumeric(noconvert))
                   args.TT2000 = logical(noconvert);
               else
                   msg = 'TT2000 conversion value must be a scalar logical.';
               end
           end
        case 'singleton'
           if (k == length(varargin))
               msg = 'No variables specified for Singleton.';
               return
           else
             singletonVars = varargin{k+1};
             if ~iscellstr(singletonVars)
               exception.msg = 'Singleton variable names must be a cell of strings.';
               exception.id = 'MATLAB:spdfcdfwrite:varNameNotString';
               return
             end 
             % Check that vars are in the list above.
             for i = 1:length(singletonVars)
               singletonVar = singletonVars{i};
               args.SingletonVars{strcmp(args.VarNames,singletonVar)} = singletonVar;
             end
           end
        end  % switch
    end  % for
    
    % Do a sanity check on the sizes of what we are passing back
    if ~isequal(length(args.VarNames), length(args.VarVals), ...
                length(args.PadVals))
        exception.msg = 'Number of variable names, values, and pad values do not match.';
        exception.id = 'MATLAB:spdfcdfwrite:sanityCheckMismatch';
        return    
    end
    if ~isequal(length(args.VarNames), length(args.VarVals))
        exception.msg = 'Number of variable names and values do not match.';
        exception.id = 'MATLAB:spdfcdfwrite:sanityCheckMismatch';
        return    
    end
%    validate_inputs(args);

end  % if (nargin > 1)

function validate_inputs(args)
%VALIDATE_INPUTS   Ensure that the mutually exclusive options weren't provided.
%
if ((args.TT2000) && (args.EpochIsCDFEpoch))
    error('MATLAB:spdfcdfwrite:TT2000', '%s\n%s', ...
          'You cannot currently specify these two options.', ...
          'Specify only one of ''TT2000'' and ''EpochIsCDFEpoch''.')
end

if ((args.TT2000) && (args.ConvertDatenum))
    error('MATLAB:spdfcdfwrite:TT2000', '%s\n%s', ...
          'You cannot currently specify these two options.', ...
          'Specify only one of ''TT2000'' and ''ConvertDatenumToEpoch''.')
end

if ((args.ConvertDatenum) && (args.ConvertDatenum2))
    error('MATLAB:spdfcdfwrite:convertdatenum', '%s\n%s', ...
          'You cannot currently specify these two options.', ...
          'Specify only one of ''ConvertDatenumToEpoch'' and ''ConvertDatenumToTT2000''.')
end
if (((args.epochtype) && (args.EpochIsCDFEpoch)) || ...
    ((args.epochtype) && (args.TT2000)))
    error('MATLAB:spdfcdfwrite:epochtype', '%s\n%s', ...
          'You cannot currently specify these two options.', ...
          'Specify only one of ''TT2000'', ''EpochIsCDFEpoch'' and ''EpochType''.')
end

function num = find_datatype(str)
if (strcmpi(str,'double') == 1 || strcmpi(str,'cdf_double') == 1 || ...
    strcmpi(str,'cdf_real8') == 1)
  num = 45;
elseif (strcmpi(str,'single') == 1 || strcmpi(str,'cdf_float') == 1 || ...
        strcmpi(str,'cdf_real4') == 1)
  num = 44;
elseif (strcmpi(str,'int8') == 1 || strcmpi(str,'cdf_int1') == 1 || ...
        strcmpi(str,'cdf_byte') == 1)
  num = 1;
elseif (strcmpi(str,'int16') == 1 || strcmpi(str,'cdf_int2') == 1)
  num = 2;
elseif (strcmpi(str,'int32') == 1 || strcmpi(str,'cdf_int4') == 1)
  num = 4;
elseif (strcmpi(str,'int64') == 1 || strcmpi(str,'cdf_int8') == 1)
  num = 8;
elseif (strcmpi(str,'uint8') == 1 || strcmpi(str,'cdf_uint1') == 1)
  num = 11;
elseif (strcmpi(str,'uint16') == 1 || strcmpi(str,'cdf_uint2') == 1)
  num = 12;
elseif (strcmpi(str,'uint32') == 1 || strcmpi(str,'cdf_uint4') == 1)
  num = 14;
elseif (strcmpi(str,'epoch') == 1 || strcmpi(str,'cdf_epoch') == 1)
  num = 31;
elseif (strcmpi(str,'epoch16') == 1 || strcmpi(str,'cdf_epoch16') == 1)
  num = 32;
elseif (strcmpi(str,'tt2000') == 1 || strcmpi(str,'cdf_time_tt2000') == 1)
  num = 33;
elseif (strcmpi(str,'char') == 1 || strcmpi(str,'cdf_char') == 1 || ...
        strcmpi(str,'cdf_uchar') == 1)
  num = 52;
else
  error('MATLAB:spdfcdfwrite:enteredvardatatype', '%s:%s\n', ...
          'One of the entered variable data types is not valid. ', str);
end

function num = find_checksum(str)
if (strcmpi(str,'none') == 1)
  num = 0;
elseif (strcmpi(str,'md5') == 1)
  num = 1;
else
  error('MATLAB:spdfcdfwrite:checksumvalue', '%s:%s\n', ...
          'The checksum value is not valid. ', str);
end

function compress = find_compression(str)
compress = '';
lstr=lower(str);
item1=findstr(lstr, 'uncompressed');
item2=findstr(lstr, 'none');
if ~isempty(item1) || ~isempty(item2)
   compress = 'none';
else
  item1=findstr(lstr, 'gzip');
  if ~isempty(item1)
     len = length(lstr);
     if (isstrprop(lstr(len),'digit') && len < 7)
       level = str2num(lstr(len));
       if (level > 0 && level < 10) 
         if (len == 6)
           compress = lstr;
         elseif (len == 5)
           compress = strcat(lstr(1,4), ',', lstr(5));
         end
       end
     end
  else
    item1=findstr(lstr, 'run-length');
    item2=findstr(lstr, 'rle');
    if ~isempty(item1) || ~isempty(item2)
       compress = 'rle';
    else
      item1=findstr(lstr, 'adaptive');
      item2=findstr(lstr, 'ahuff');
      if ~isempty(item1) || ~isempty(item2)
        compress = 'ahuff';
      else
        item1=findstr(lstr, 'huffman');
        item2=findstr(lstr, 'huff');
        if ~isempty(item1) || ~isempty(item2)
          compress = 'huff';
        end
      end
    end
  end
end
if (length(compress) == 0)
  error('MATLAB:spdfcdfwrite:compression', '%s:%s\n', ...
          'The compression value is not valid. ', str);
end




function out = SPDFCOMPUTEEPOCH16(datetime)
%SPDFCOMPUTEEPOCH16 converts the UTC date/time components to CDF_EPOCH16 time,
%              in nanoseconds since 2000-01-01 12:00:00 with leap seconds.
%
%   OUT = SPDFCOMPUTEEPOCH16(datetime) returns the CDF_EPOCH16 time. OUT is
%   an array of N by 2 double values of mxDOUBLE_CLASS (double), each row
%   having two double values..
%
%     datetime             An array with each row having ten (10) numerical
%                          values for year, month, day, hour, minute, second,
%                          millisecond, microsecond and nanosecond.
%
%   Examples:
%
%   % Compute two UTC times into CDF_EPOCH16.
%
%   data = [2009 01 01  0 0 0 123 456 789 123;
%           2009 01 01 12 0 0 987 654 321 987];
%   out = SPDFCOMPUTEEPOCH16(data);
%
%   SPDFENCODEEPOCH16(out) will show:
%   ans = 
%
%       '01-JAN-2009 00:00:00.123456789123'
%       '01-JAN-2009 12:00:00.987654321987'
%
%
%   See also SPDFENCODEEPOCH16, SPDFPARSEEPOCH16, SPDFBREAKDOWNEPOCH16

% HISTORY:
%   August 16, 2011  Mike Liu    The initial version.

%
% Process arguments.
%

if (nargin < 1)
    error('MATLAB:SPDFCOMPUTEEPOCH16:inputArgumentCount', ...
          'SPDFCOMPUTEEPOCH16 requires at least one input argument.')
end

if (nargout > 1)
    error('MATLAB:SPDFCOMPUTEEPOCH16:outputArguments', ...
          'SPDFCOMPUTEEPOCH16 requires only one output argument.')
end

if (~isa(datetime,'numeric'))
    error('MATLAB:SPDFCOMPUTEEPOCH16:inputArguments', ...
          'SPDFCOMPUTEEPOCH16 requires the input to be in numeric.')
end

ss=size(datetime);
if (ss(1) < 1 || ss(2) ~= 10)
    error('MATLAB:SPDFCOMPUTEEPOCH16:inputArguments', ...
          'SPDFCOMPUTEEPOCH16 requires each row to have ten (10) date/time fields.')
end

out = spdfcomputeepoch16c (datetime);

function out = SPDFCOMPUTEEPOCH(datetime)
%SPDFCOMPUTEEPOCH converts the UTC date/time components to CDF_EPOCH time,
%              in milliseconds since 00-01-01.
%
%   OUT = SPDFCOMPUTEEPOCH(datetime) returns the CDF_EPOCH time. OUT is
%   a vector of double values of mxDOUBLE_CLASS (double).
%
%     datetime             An array with each row having seven (7) numerical
%                          values for year, month, day, hour, minute, second,
%                          millisecond.
%
%   Examples:
%
%   % Compute two UTC times into CDF_EPOCH.
%
%   data = [2009 01 01  0 0 0 123;
%           2009 01 01 12 0 0 987];
%   out = SPDFCOMPUTEEPOCH(data);
%
%  SPDFENCODEEPOCH(out) will show:
%   ans = 
%
%       '01-JAN-2009 00:00:00.123'
%       '01-JAB-2009 12:00:00.987'
%
%
%   See also CDFEPOCH, SPDFENCODEEPOCH, SPDFPARSEEPOCH, SPDFBREAKDOWNEPOCH

% HISTORY:
%   August 16, 2011  Mike Liu    The initial version.

%
% Process arguments.
%

if (nargin < 1)
    error('MATLAB:SPDFCOMPUTEEPOCH:inputArgumentCount', ...
          'SPDFCOMPUTEEPOCH requires at least one input argument.')
end

if (nargout > 1)
    error('MATLAB:SPDFCOMPUTEEPOCH:outputArguments', ...
          'SPDFCOMPUTEEPOCH requires only one output argument.')
end

if (~isa(datetime,'numeric'))
    error('MATLAB:SPDFCOMPUTEEPOCH:inputArguments', ...
          'SPDFCOMPUTEEPOCH requires the input to be in numeric.')
end

ss=size(datetime);
if (ss(1) < 1 || ss(2) ~= 7)
    error('MATLAB:SPDFCOMPUTEEPOCH:inputArguments', ...
          'SPDFCOMPUTEEPOCH requires each row to have seven (7) date/time fields.')
end

out = spdfcomputeepochc (datetime);

function out = SPDFCOMPUTETT2000(datetime)
%SPDFCOMPUTETT2000 converts the UTC date/time components to CDF TT2000 time,
%              in nanoseconds since 2000-01-01 12:00:00 with leap seconds.
%
%   OUT = SPDFCOMPUTETT2000(datetime) returns the CDF TT2000 time. OUT is
%   a vector of integer values of mxINT64_CLASS (int64).
%
%     datetime             An array with each row having nine (9) numerical
%                          values for year, month, day, hour, minute, second,
%                          millisecond, microsecond and nanosecond.
%
%   Examples:
%
%   % Compute two UTC times into CDF TT2000.
%
%   data = [2009 01 01  0 0 0 123 456 789;
%           2009 01 01 12 0 0 987 654 321];
%   out = SPDFCOMPUTETT2000(data);
%
%   SPDFENCODETT2000(out) will show:
%   ans = 
%
%       '2009-01-01T00:00:00.123456789'
%       '2009-01-01T12:00:00.987654321'
%
%
%   See also CDFTT2000, SPDFENCODETT2000, SPDFPARSETT2000, SPDFBREAKDOWNTT2000

% HISTORY:
%   August 16, 2011  Mike Liu    The initial version.

%
% Process arguments.
%

if (nargin < 1)
    error('MATLAB:SPDFCOMPUTETT2000:inputArgumentCount', ...
          'SPDFCOMPUTETT2000 requires at least one input argument.')
end

if (nargout > 1)
    error('MATLAB:SPDFCOMPUTETT2000:outputArguments', ...
          'SPDFCOMPUTETT2000 requires only one output argument.')
end

if (~isa(datetime,'numeric'))
    error('MATLAB:SPDFCOMPUTETT2000:inputArguments', ...
          'SPDFCOMPUTETT2000 requires the input to be in numeric.')
end

ss=size(datetime);
if (ss(1) < 1 || ss(2) ~= 9)
    error('MATLAB:SPDFCOMPUTETT2000:inputArguments', ...
          'SPDFCOMPUTETT2000 requires each row to have nine (9) date/time fields.')
end

out = spdfcomputett2000c (datetime);

function epoch16 = spdfdatenumtoepoch16(varargin)
%SPDFDATENUMtoEPOCH16 Convert MATLAB's datenum to CDF_EPOCH16 values.
%
%    E = SPDFDATENUMtoEPOCH16(DATE) convert a DATE, a valid string (datestr) or
%        number (datenum) representing a date, to CDF_EPOCH16 value.
%
%    Note that a CDF_EPOCH16 is the number of picoseconds since 
%    1-Jan-0000 while MATLAB datenums are the number of days since 0-Jan-0000.
%
%    See also SPDFEPOCH16TODATENUM, SPDFCOMPUTEEPOCH16, SPDFENCODEEPOCH16.
%             SPDFPARSEEPOCH16, SPDFBREAKDOWNEPOCH16.

if (nargin > 1)
    error('MATLAB:SPDFDATENUMtoEPOCH16:SPDFDATENUMtoEPOCH16:tooManyInput', ...
          'Only one argument is allowed.');
elseif (nargin < 1)
    error('MATLAB:SPDFDATENUMtoEPOCH16:SPDFDATENUMtoEPOCH16:tooFewInput', ...
          'Only one argument is allowed.');
else
    input = varargin{1};
end

if iscellstr(input)
    input = char(input);
end

if ~ischar(input) & ~isnumeric(input)
    error('MATLAB:SPDFDATENUMtoEPOCH16:SPDFDATENUMtoEPOCH16:badInputs', ...
          'Input must be a number, string, cellstr.');
end

if ischar(input)
    % If the input is a string, then you have to convert

    % Convert to MATLAB datenum.  If this bombs out, an invalid
    % datestr was passed to datenum.
    n = datenum(input);
else
    % It's numeric, so if it's a matrix, go element by element
    % and convert each and then reshape.
    n = input(:);
end

epoch16 = spdfdatenumtoepoch16c(n);

function epoch = spdfdatenumtoepoch(varargin)
%SPDFDATENUMtoEPOCH Convert MATLAB's datenum to CDF_EPOCH values.
%
%    E = SPDFDATENUMtoEPOCH(DATE) convert a DATE, a valid string (datestr) or
%        number (datenum) representing a date, to CDF_EPOCH value.
%
%    Note that a CDF epoch is the number of milliseconds since 
%    1-Jan-0000 while MATLAB datenums are the number of days since 0-Jan-0000.
%
%    See also CDFEPOCH, SPDFEPOCHTODATENUM, SPDFCOMPUTEEPOCH, SPDFENCODEEPOCH.
%             SPDFPARSEEPOCH, SPDFBREAKDOWNEPOCH.

if (nargin > 1)
    error('MATLAB:SPDFDATENUMtoEPOCH:SPDFDATENUMtoEPOCH:tooManyInput', ...
          'Only one argument is allowed.');
elseif (nargin < 1)
    error('MATLAB:SPDFDATENUMtoEPOCH:SPDFDATENUMtoEPOCH:tooFewInput', ...
          'Only one argument is allowed.');
else
    input = varargin{1};
end

if iscellstr(input)
    input = char(input);
end

if ~ischar(input) & ~isnumeric(input)
    error('MATLAB:SPDFDATENUMtoEPOCH:SPDFDATENUMtoEPOCH:badInputs', ...
          'Input must be a number, string, cellstr.');
end

if ischar(input)
    % If the input is a string, then you have to convert

    % Convert to MATLAB datenum.  If this bombs out, an invalid
    % datestr was passed to datenum.
    n = datenum(input);
else
    % It's numeric, so if it's a matrix, go element by element
    % and convert each and then reshape.
    n = input(:);
end

epoch = spdfdatenumtoepochc(n);

function tt2000 = spdfdatenumtott2000(varargin)
%SPDFDATENUMtoTT2000 Convert MATLAB's datenum to CDF_TIME_TT2000 values.
%
%    E = SPDFDATENUMtoTT2000(DATE) convert a DATE, a valid string (datestr) or
%        number (datenum) representing a date, to CDF TT2001 value.
%
%    Note that a CDF TT2000 is the number of nanoseconds since 
%    1-Jan-0000T12:00:00 with leap seconds included and that MATLAB
%    datenums are the number of days since 0-Jan-0000.
%
%    See also CDFTT2000, SPDFTT2000TODATENUM, SPDFCOMPUTETT2000, SPDFENCODETT2000,
%             SPDFPARSETT2000, SPDFBREAKDOWNTT2000.


if (nargin > 1)
    error('MATLAB:SPDFDATENUMtoTT2000:SPDFDATENUMtoTT2000:tooManyInput', ...
          'Only one argument is allowed.');
elseif (nargin < 1)
    error('MATLAB:SPDFDATENUMtoTT2000:SPDFDATENUMtoTT2000:tooFewInput', ...
          'Only one argument is allowed.');
else
    input = varargin{1};
end

if iscellstr(input)
    input = char(input);
end

if ~ischar(input) & ~isnumeric(input)
    error('MATLAB:SPDFDATENUMtoTT2000:SPDFDATENUMtoTT2000:badInputs', ...
          'Input must be a number, string, cellstr.');
end

if ischar(input)
    % If the input is a string, then you have to convert

    % Convert to MATLAB datenum.  If this bombs out, an invalid
    % datestr was passed to datenum.
    n = datenum(input);
else
    % It's numeric, so if it's a matrix, go element by element
    % and convert each and then reshape.
    n = input(:);
end

tt2000 = spdfdatenumtott2000c(n);

function out = SPDFENCODEEPOCH16(epoch, varargin)
%SPDFENCODEEPOCH16 encodes an epoch of CDF_EPOCH16 data type, an double array value
%
%   OUT = SPDFENCODEEPOCH16(epoch) 
%         Returns a UTC string(s).
%
%     epoch                An epoch
%
%   The encoded epoch string will have the following ISO 8601 format:
%          yyyy-mm-ddThh:mm:ss.mmmuuunnnppp
%          e.g., "2000-01-01T12:34:56.123456789999"
%   Originally, it was in this form:
%          dd-mmm-yyyy hh:mm:ss.mmm.uuu.nnn.ppp
%          e.g., "01-Jan-2000 12:34:56.123.456.789.999"
%
%   OUT = SPDFENCODEEPOCH16(epoch, 'Format', FORMAT) encodes the UTC
%   string into the specified format. FORMAT is a number from 0 to 4.
%   FORMAT:
%     0: dd-mmm-yyyy hh:mm:ss.mmm.uuu.nnn.ppp 
%        e.g., "01-JAN-2000 12:34:56.123.456.789.999"
%     1: yyyymmdd.ddddddd e.g., "20000101.1200000"
%     2: yyyymmddhhmmss e.g., "20000101123456"
%     3: yyyy-mm-ddThh:mm:ss.mmm.uuu.nnn.pppZ
%        e.g., "2000-01-01T12:34:56.123.456.789.999Z"
%     4: yyyy-mm-ddThh:mm:ss.mmmuuunnnppp
%        e.g., "2000-01-01T12:34:56.123456789999"
%   where mmm is milliseconds,
%   where uuu is microseconds,
%   where nnn is nanoseconds,
%   where ppp is picoseconds.
%
%   Examples:
%
%   % Acquire 'Epoch' variable data as is (two double values) from 'sample' CDF
%   % and encode the epoch values.
%
%   epochs = spdfcdfread('Sample', 'Variables', {'Epoch'}, 
%                        "KeepEpochAsIs", true);
%   spdfencodeepoch16(epochs)
%
%   See also SPDFBREAKDOWNEPOCH16, SPDFCOMPUTEEPOCH16, SPDFPARSEEPOCH16

% HISTORY:
%   March 5, 2013  Mike Liu    The initial version.

if (nargin < 1)
    error('MATLAB:SPDFENCODEEPOCH16:inputArgumentCount', ...
          'SPDFENCODEEPOCH16 requires at least one input argument.')
end

if (nargout > 1)
    error('MATLAB:SPDFENCODEEPOCH16:outputArguments', ...
          'SPDFENCODEEPOCH16 requires only one output argument.')
end

[args, msg] = parse_inputs(varargin{:});
if (~isempty(msg))
    error('MATLAB:SPDFENCODEEPOCH:badInputArguments', '%s', msg)
end
out = spdfencodeepoch16c(epoch, args.Format);
%%%
%%% Function parse_inputs
%%%

function [args, msg] = parse_inputs(varargin)
% Set default values
args.Format = int32(4);
msg = '';
% Parse arguments based on their number.
if (nargin > 0)
    paramStrings = {'format'};

    % For each pair
    for k = 1:2:length(varargin)
       param = lower(varargin{k});
       if (~ischar(param))
           msg = 'Parameter name must be a string.';
           return
       end

       idx = strmatch(param, paramStrings);

       if (isempty(idx))
           msg = sprintf('Unrecognized parameter name "%s".', param);
           return
       elseif (length(idx) > 1)
           msg = sprintf('Ambiguous parameter name "%s".', param);
           return
       end

       switch (paramStrings{idx})
       case 'format'

           if (k == length(varargin))
               msg = 'No format specified.';
               return
           else
               format = varargin{k + 1};

               if (~isa(format, 'double') || ~isscalar(format))
                   msg = 'Format must be a single integer.';
                   return;
               end

               if (int32(format) < 0 || int32(format) > 4)
                 msg = sprintf('format value "%d" is out or 0-4 range.', format);
                 return;

               end
               args.Format = int32(format);
           end
       end  % switch
    end  % for

end  % if (nargin > 1)

function out = SPDFENCODEEPOCH(epoch, varargin)
%SPDFENCODEEPOCH encodes an epoch of CDF_EPOCH data type, a double value or
%cdfepoch object..
%
%   OUT = SPDFENCODEEPOCH(epoch) 
%         Returns a UTC string.
%
%     epoch                An epoch
%
%   The encoded epoch string will have the following ISO 8601 format:
%       yyyy-mm-ddThh:mm:ss.mmm, e.g., "2000-01-01T12:34:56.123"
%   Originally, it was in this form:
%       dd-mmm-yyyy hh:mm:ss.mmm, e.g., "01-Jan-2000 12:34:56.123"
%
%   OUT = SPDFENCODEEPOCH(epoch, 'Format', FORMAT) encodes the UTC
%   string into the specified format. FORMAT is a number from 0 to 4.
%   FORMAT:
%     0: dd-mmm-yyyy hh:mm:ss.mmm, e.g., "01-JAN-2000 12:34:56.123"
%     1: yyyymmdd.ddddddd, e.g., "20000101.1200000"
%     2: yyyymmddhhmmss, e.g., "20000101123456"
%     3: yyyy-mm-ddThh:mm:ss.mmmZ, e.g., "2000-01-01T12:34:56.123Z"
%     4: yyyy-mm-ddThh:mm:ss.mmm, e.g., "2000-01-01T12:34:56.123"
%   where mmm is milliseconds.
%   Format: 0 is the only allowed form for cdfepoch object.
%
%   Note: If the epoch values come from spdfcdfread function call, the values
%         can be in one of the three forms: in cdfepoch object, in MATLAB
%         datenum (the default), or in their original CDF_EPOCH 
%         data via an extra 'keepepochasis' option. This function works for
%         cdfepoch objects or the data retrieved with 'keepepochasis' option.
%         For datenum values, use MATLAB's datestr instead.
%
%   Examples:
%
%   % Encode epoch from date/time: 2012-10-10T10:10:10.010Z:
%
%   utc = '2012-10-10T10:10:10.010';
%   epoch = UTC2CDFEpoch(utc);
%   SPDFENCODEEPOCH(epoch)
%   ans =
%       '10-Oct-2012 10:10:10.010'
%   SPDFENCODEEPOCH(epoch, 'format', 3) 
%   ans =
%       '2012-10-10T10:10:10.010Z'
%
%   % Acquire 'Epoch' variable data as is (double values) from 'sample' CDF
%   % and encode the epoch values.
%
%   epochs = spdfcdfread('Sample', 'Variables', {'Epoch'}, 'KEEPEPOCHASIS', true);
%   spdfencodeepoch(epochs)
%
%   See also CDFEPOCH, SPDFBREAKDOWNEPOCH, SPDFCOMPUTEEPOCH, SPDFPARSEEPOCH

% HISTORY:
%   August 16, 2011  Mike Liu    The initial version.
%   October 16, 2018  Mike Liu   The default format is now 4 (from 0).

if (nargin < 1)
    error('MATLAB:SPDFENCODEEPOCH:inputArgumentCount', ...
          'SPDFENCODEEPOCH requires at least one input argument.')
end

if (nargout > 1)
    error('MATLAB:SPDFENCODEEPOCH:outputArguments', ...
          'SPDFENCODEEPOCH requires only one output argument.')
end

[args, msg] = parse_inputs(varargin{:});
if (~isempty(msg))
    error('MATLAB:SPDFENCODEEPOCH:badInputArguments', '%s', msg)
end
if (isa(epoch,'cdfepoch'))
  if (length(epoch) > 1)
    for p = 1:length(epoch)
      if (~isa(epoch, 'cell'))
        dataaa(p,1) = datestr((todatenum(epoch(p,1))), 0);
      else
        if (length(epoch{p}) > 1)
          for q = 1:length(epoch{p})
            dataaa(q) = datastr((todatenum(epoch{p}(q,1))), 0);
          end
        else
          dataaa(p,1) = datastr((todatenum(epoch{p})), 0);
        end
      end
    end
    out = dataaa;
  end
else
  out = spdfencodeepochc(epoch, args.Format);
end
%%%
%%% Function parse_inputs
%%%

function [args, msg] = parse_inputs(varargin)
% Set default values
args.Format = int32(4);
msg = '';
% Parse arguments based on their number.
if (nargin > 0)
    paramStrings = {'format'};

    % For each pair
    for k = 1:2:length(varargin)
       param = lower(varargin{k});
       if (~ischar(param))
           msg = 'Parameter name must be a string.';
           return
       end

       idx = strmatch(param, paramStrings);

       if (isempty(idx))
           msg = sprintf('Unrecognized parameter name "%s".', param);
           return
       elseif (length(idx) > 1)
           msg = sprintf('Ambiguous parameter name "%s".', param);
           return
       end

       switch (paramStrings{idx})
       case 'format'

           if (k == length(varargin))
               msg = 'No format specified.';
               return
           else
               format = varargin{k + 1};

               if (~isa(format, 'double') || ~isscalar(format))
                   msg = 'Format must be a single integer.';
                   return;
               end

               if (int32(format) < 0 || int32(format) > 4)
                 msg = sprintf('format value "%d" is out or 0-4 range.', format);
                 return;

               end
               args.Format = int32(format);
           end
       end  % switch
    end  % for

end  % if (nargin > 1)

function out = SPDFENCODETT2000(tt2000, varargin)
%SPDFENCODETT2000 encodes the CDF epoch data in TT2000 data type to UTC
%strings.
%
%   OUT = SPDFENCODETT2000(tt2000) returns the CDF epoch in string. OUT is
%   a cell of strings.
%
%     tt2000               A single or vector of numerical values of
%                          CDF_TIME_TT2000 (an mxINT64_CLASS) data type.
%
%   The encoded epoch string will have the following format:
%       yyyy-mm-ddThh:mm:ss.mmmuuunnn, e.g., "2000-01-01T12:34:56.123456789"
%
%   OUT = SPDFENCODETT2000(tt2000, 'Format', FORMAT) encodes the UTC
%   string into the specified format. FORMAT is a number from 0 to 4.
%   FORMAT:
%     0: dd-mmm-yyyy hh:mm:ss.mmmuuunnn, e.g., "01-JAN-2000 12:34:56.123456789"
%     1: yyyymmdd.dddddddddd, e.g., "20000101.1200000000"
%     2: yyyymmddhhmmss, e.g., "20000101123456"
%     3: yyyy-mm-ddThh:mm:ss.mmmuuunnn, e.g., "2000-01-01T12:34:56.123456789"
%     4: yyyy-mm-ddThh:mm:ss.mmmuuunnnZ, e.g., "2000-01-01T12:34:56.123456789Z"
%   where mmmuuunnn is milliseconds, microseconds and nanoseconds.
%   Format 3 is the default (as ISO 8601) if this option is not provided.
%
%   Examples:
%
%   % Read all of the data from the same file, the most efficient way,
%   % and encode the Variable Epoch, of CDF_TIME_TT2000 data type, to UTC
%   % string.
%
%   data = spdfcdfread('example', 'Variables', {'Epoch'}, "KEEPEPOCHASIS", true);
%   out = SPDFENCODETT2000(data);
%
%
%   See also CDFTT2000, SPDFPARSETT2000, SPDFCOMPUTETT2000, SPDFBREAKDOWNTT2000,

% HISTORY:
%   August 16, 2011  Mike Liu    The initial version.
%   October 6, 2018  Mike Liu    Added format 4.

%
% Process arguments.
%

if (nargin < 1)
    error('MATLAB:SPDFENCODETT2000:inputArgumentCount', ...
          'SPDFENCODETT2000 requires at least one input argument.')
end

if (nargout > 1)
    error('MATLAB:SPDFENCODETT2000:outputArguments', ...
          'SPDFENCODETT2000 requires only one output argument.')
end

[args, msg] = parse_inputs(varargin{:});
if (~isempty(msg))
    error('MATLAB:SPDFENCODETT2000:badInputArguments', '%s', msg)
end
if (isa(tt2000,'cdftt2000'))
  if (length(tt2000) > 1)
    for p = 1:length(tt2000)
      if (~isa(tt2000, 'cell'))
        dataaa(p,1) = spdfencodett2000c(int64(todatenum(tt2000(p,1))), args.Format);
      else
        if (length(tt2000{p}) > 1)
          for q = 1:length(tt2000{p})
            dataaa(q) = spdfencodett2000c(int64(todatenum(tt2000{p}(q,1))), args.Format);
          end
        else
          dataaa(p,1) = spdfencodett2000c(int64(todatenum(tt2000{p})), args.Format);
        end
      end
    end
    out = dataaa;
  end
elseif (isa(tt2000,'double'))
  out = datestr(tt2000);
else
  out = spdfencodett2000c(tt2000, args.Format);
end
%%%
%%% Function parse_inputs
%%%

function [args, msg] = parse_inputs(varargin)
% Set default values
args.Format = int32(3);
msg = '';
% Parse arguments based on their number.
if (nargin > 0)
    paramStrings = {'format'};

    % For each pair
    for k = 1:2:length(varargin)
       param = lower(varargin{k});
       if (~ischar(param))
           msg = 'Parameter name must be a string.';
           return
       end

       idx = strmatch(param, paramStrings);

       if (isempty(idx))
           msg = sprintf('Unrecognized parameter name "%s".', param);
           return
       elseif (length(idx) > 1)
           msg = sprintf('Ambiguous parameter name "%s".', param);
           return
       end

       switch (paramStrings{idx})
       case 'format'

           if (k == length(varargin))
               msg = 'No format specified.';
               return
           else

               format = varargin{k + 1};

               if (~isa(format, 'double') || ~isscalar(format))
                   msg = 'Format must be a single integer.';
                   return;
               end

               if (int32(format) < 0 || int32(format) > 4)
                 msg = sprintf('format value "%d" is out of 0-4 range.', format);
                 return;
               end
               args.Format = int32(format);
           end
       end  % switch
    end  % for 
                   
end  % if (nargin > 1)


function out = SPDFEPOCH16toDATENUM(epoch16)
%SPDFEPOCH16toDATENUM converts the time in UTC string (returned from
%                 spdfcdfread) or date/time values for CDF_EPOCH16 to
%                 MATLAB datenum
%
%   OUT = SPDFEPOCH16toDATENUM(epoch16) returns MATLAB datenum.
%   OUT a column vector of numerical values of MATLAB date numbers.
%
%     epoch16               A vector or cell of UTC string or an M-by-10 matrix
%                           containing M full or partial date vectors for
%                           year, month, day, hour, minute, second, millisecond,
%                           microsecond, nanosecond and picosecond, in that 
%                           order.
%
%   Note:
%     The valid epoch string should have one of the following forms:
%     1. dd-mmm-yyyy hh:mm:ss.mmm.uuu.nnn.ppp (length of 36), e.g.,
%       "01-JAN-2000 12:00:00.123.456.789.000"
%     2. yyyymmdd.ddddddddddddddd (length of 24), e.g.,
%       "20000101.120000000000000"
%     3. yyyymmddhhmmss (length of 14), e.g.,
%       "20000101120000"
%     4. yyyy-mm-ddThh:mm:ss.mmmuuunnnppp (length of 32), e.g.,
%       "2000-01-01T12:00:00.123456789000"
%     where mmmuuunnnppp is for milliseconds, microseconds, nanoseconds and
%                           picoseconds.
%
%   Examples:
%
%   % Read all the variable data in a CDF file. Among them, the variable of 
%     CDF_EPOCH16 data type is returned in UTC strings. Convert the strings
%     to MATLAB datenum. The variable is at index of 17 from the output of
%     1 by 20 array of cells from spdfcdfread, the efficient way. 
%
%   data = spdfcdfread('test');
%   epoch = data(1,17);
%   datenums = SPDFEPOCH16toDATENUM(epoch);
%
%   % Similar to the above example. But, read in the data into 40 by 20 array
%     of cells by spdfcdfread. Convert the variable of CDF_EPOCH16 data type in
%     UTC strings and convert them to MATLAB datenum.
%
%   data = spdfcdfread('test', 'Combinerecords', false);
%   epoch = data(:,17);
%   datenums = SPDFEPOCH16toDATENUM(epoch);
%
%   % Convert the UTC strings in vector to MATLAB datenum.
%
%   epoch1 = ['2009-01-01T00:00:00.123456789000';
%             '2009-01-01T12:00:00.123456789000'];
%   datenums = SPDFEPOCH16toDATENUM(epoch1);
%
%   % Convert the date/times in matrix to MATLAB datenum.
%
%   epoch2 = [2009 01 01 00 00 00 123 456 789 000;
%             2009 01 01 12 00 00 123 456 789 000];
%   datenums = SPDFEPOCH16toDATENUM(epoch2);
%
%   See also SPDFCDFREAD.


% HISTORY:
%   August 16, 2011  Mike Liu    The initial version.

%
% Process arguments.
%

if (nargin < 1)
    error('MATLAB:SPDFEPOCH16toDATENUM:inputArgumentCount', ...
          'SPDFEPOCH16toDATENUM requires at least one input argument.')
end

if (nargout > 1)
    error('MATLAB:SPDFEPOCH16toDATENUM:outputArguments', ...
          'SPDFEPOCH16toDATENUM requires only one output argument.')
end
s = size(epoch16);
if (iscell(epoch16(1,1)))
  if (s(1) == 1)
    epoch16 = epoch16{1,1};
  else
    if (iscellstr(epoch16))
      epoch16 = char(epoch16);
    else
      if (s(2) == 1)
        for x=1:s(1)
        epoch16(x,1) = epoch16{x,1};
        end
      end
    end
  end
end
if (iscell(epoch16))
  epoch16 = cell2mat(epoch16);
end
if (isnumeric(epoch16))
  s = size(epoch16);
  if (s(2) ~= 10)
    error('MATLAB:SPDFEPOCH16toDATENUM:inputArgumentvector', ...
          'SPDFEPOCH16toDATENUM requires an M by 10 vector for date/time fields.')
  end
  dates=epoch16(:,1:6);
  for p =1:s(1)
    dates(p,6) = epoch16(p,6) + epoch16(p,7)/1000 + epoch16(p,8)/1000000 + ...
                 epoch16(p,9)/1000000000 + epoch16(p,10)/1000000000000;
  end
  out = datenum(dates); 
else
  dates = spdfparseepoch16c(epoch16);
  out = datenum(dates);
end
function out = SPDFEPOCH16UnixTime(time, varargin)
%SPDFEPOCH16UnixTime converts the CDF_EPOCH16 time (picoseconds since 
%                  01-01-0000 00:00:00.000) to unix time (seconds from 
%                  01-01-1970 00:00:00.000) or vice verse.
%
%   OUT = SPDFEPOCH16UnixTime(time, 'TOEPOCH16', TF) returns converted time(s).
%   OUT a column vector of numerical values of converted times.
%
%     time         A vector or scalar of time(s), based on CDF_EPOCH16 data 
%                  type or unix time. 
%
%   The option 'TOEPOCH16' is specified to true if the time conversion is from
%   unix time to CDF_EPOCH16. If false, or not specified, the conversion is
%   from CDF_EPOCH16 to unix time.
%
%   Note: Unix time to CDF_EPOCH16 might not be properly converted at 
%         sub-microseconds portion.
%
%   Examples:
%
%   % Convert a CDF_EPOCH16 data, a scalar at 
%     01-01-2000 00:00:00.123.456.789.000 to a unix
%     time value.
%
%   epoch = spdfcomputeepoch16([2000,1,1,0,0,0,123,456,789,000]);
%   unixtime = SPDFEPOCH16UnixTime(epoch);
%
%   % Convert CDF_EPOCH16 data, a vector of
%     01-01-2000 00:00:00.123.456.789.000 and 
%     02-02-2001 01:01:01.456.789.012.345 to unix time values.
%
%   epochs = spdfcomputeepoch16([2000,1,1,0,0,0,123,456,789,0;
%                                2001,2,2,1,1,1,456,789,012,345]);
%   unixtimes = SPDFEPOCH16UnixTime(epochs);
%
%   % Convert a unix time to CDF_EPOCH16 epoch. 
%
%   epoch = SPDFEPOCH16UnixTime(unixtime, 'TOEPOCH16', true);
%
%   % Convert a vector of unix times to CDF_EPOCH16 epochs.
%
%   epochs = SPDFEPOCH16UnixTime(unixtimes, 'TOEPOCH16', true);
%

% HISTORY:
%   November 22, 2018  Mike Liu    The initial version.

%
% Process arguments.
%

if (nargin < 1)
    error('MATLAB:SPDFEPOCH16UnixTime:inputArgumentCount', ...
          'SPDFEPOCH16UnixTime requires at least one input argument.')
end

if (nargout > 1)
    error('MATLAB:SPDFEPOCH16UnixTime:outputArguments', ...
          'SPDFEPOCH16UnixTime requires only one output argument.')
end

[args, msg] = parse_inputs(varargin{:});

if (~isempty(msg))
    error('MATLAB:spdfepochunixtime:badInputArguments', '%s', msg)
end
if (args.toepoch == 0)
  time = transpose(time);
end
out = spdfepoch16unixtimec(time, args.toepoch);
if (args.toepoch == 1)
  out = transpose(out);
end

end

%%%
%%% Function parse_inputs
%%%

function [args, msg] = parse_inputs(varargin)
% Set default values
args.toepoch = int32(0);
msg = '';
% Parse arguments based on their number.
if (nargin > 0)
    paramStrings = {'toepoch16'};

    % For each pair
    for k = 1:2:length(varargin)
       param = lower(varargin{k});
       idx = strmatch(param, paramStrings);
       if (isempty(idx))
           msg = sprintf('Unrecognized parameter name "%s".', param);
           return
       elseif (length(idx) > 1)
           msg = sprintf('Ambiguous parameter name "%s".', param);
           return
       end
       switch (paramStrings{idx})
         case 'toepoch16'
           if (k == length(varargin))
               msg = 'No epoch conversion value specified.';
               return
           else
               convert = varargin{k + 1};
               if (numel(convert) ~= 1)
                   msg = 'Conversion value must be a scalar logical.';
               end
               if (islogical(convert))
                   if (convert)
                     args.toepoch = int32(1);
                   else
                     args.toepoch = int32(0);
                   end
               elseif (isnumeric(convert))
                   if (convert > 0)
                     args.toepoch = int32(1);
                   else
                     args.toepoch = int32(0);
                   end
               else
                   msg = 'Epoch conversion value must be a scalar logical.';
               end
           end
       end  % switch
    end  % for

end  % if (nargin > 1)

end
function out = SPDFEPOCHtoDATENUM(epoch)
%SPDFEPOCHtoDATENUM converts the time in UTC string (returned from spdfcdfread) or
%               date/time values for CDF_EPOCH to MATLAB datenum
%
%   OUT = SPDFEPOCHtoDATENUM(epoch) returns MATLAB datenum.
%   OUT a column vector of numerical values of MATLAB date numbers.
%
%     epoch               A vector or cell of UTC string or an M-by-7 matrix
%                           containing M full or partial date vectors for
%                           year, month, day, hour, minute, second, millisecond,
%                           in that order.
%
%   Note:
%     The valid epoch string should have one of the following forms:
%     1. dd-mmm-yyyy hh:mm:ss.lll (length of 24), e.g.,
%       "01-JAN-2000 12:00:00.123"
%     2. yyyymmdd.ddddddd (length of 16), e.g.,
%       "20000101.1200000"
%     3. yyyymmddhhmmss (length of 14), e.g.,
%       "20000101120000"
%     4. yyyy-mm-ddThh:mm:ss.lll (length of 23), e.g.,
%       "2000-01-01T12:00:00.123"
%
%   Examples:
%
%   % Read all the variable data in a CDF file. Among them, the variable of 
%     CDF_EPOCH data type is returned in UTC strings. Convert the strings
%     to MATLAB datenum. The variable is at index of 17 from the output of
%     1 by 20 array of cells from spdfcdfread, the efficient way. 
%
%   data = spdfcdfread('test', 'KeepEpochAsIs', true);
%   epoch = data(1,17);
%   datenums = SPDFEPOCHtoDATENUM(epoch);
%
%   % Similar to the above example. But, read in the data into 40 by 20 array
%     of cells by spdfcdfread. Convert the variable of CDF_EPOCH data type in
%     UTC strings and convert them to MATLAB datenum.
%
%   data = spdfcdfread('test');
%   epoch = data(:,17);
%   datenums = SPDFEPOCHtoDATENUM(epoch);
%
%   % Convert the UTC strings in vector to MATLAB datenum.
%
%   epoch1 = ['2009-01-01T00:00:00.123';
%             '2009-01-01T12:00:00.123'];
%   datenums = SPDFEPOCHtoDATENUM(epoch1);
%
%   % Convert the date/times in matrix to MATLAB datenum.
%
%   epoch2 = [2009 01 01 00 00 00 123;
%             2009 01 01 12 00 00 123];
%   datenums = SPDFEPOCHtoDATENUM(epoch2);
%
%   See also SPDFCDFREAD.

% HISTORY:
%   August 16, 2011  Mike Liu    The initial version.

%
% Process arguments.
%

if (nargin < 1)
    error('MATLAB:SPDFEPOCHtoDATENUM:inputArgumentCount', ...
          'SPDFEPOCHtoDATENUM requires at least one input argument.')
end

if (nargout > 1)
    error('MATLAB:SPDFEPOCHtoDATENUM:outputArguments', ...
          'SPDFEPOCHtoDATENUM requires only one output argument.')
end

if (isa(epoch, 'cdfepoch'))
  error('MATLAB:SPDFEPOCHtoDATENUM:inputobject', ...
        'SPDFEPOCHtoDATENUM can not handle input data in cdfepoch object class.')
end

s = size(epoch);
if (iscell(epoch(1,1)))
  if (s(1) == 1)
    epoch = epoch{1,1};
  else
    if (iscellstr(epoch))
      epoch = char(epoch);
    else
      if (s(2) == 1)
        for x=1:s(1)
        epoch(x,1) = epoch{x,1};
        end
      end
    end
  end
end
if (iscell(epoch))
  epoch = cell2mat(epoch);
end
if (isnumeric(epoch))
  s = size(epoch);
  if (s(2) == 1)
    out = datenum(epoch./86400000.0+1);
  else
    if (s(2) ~= 7)
      error('MATLAB:SPDFEPOCHtoDATENUM:inputArgumentvector', ...
            'SPDFEPOCHtoDATENUM requires an M by 7 vector for date/time fields.')
    end
    dates=epoch(:,1:6);
    for p =1:s(1)
      dates(p,6) = epoch(p,6) + epoch(p,7)/1000.0;
    end
    out = datenum(dates); 
  end
else
  dates = spdfparseepochc(epoch);
  out = datenum(dates);
end
function out = SPDFEPOCHUnixTime(time, varargin)
%SPDFEPOCHUnixTime converts the CDF_EPOCH time (milliseconds since 
%                  01-01-0000 00:00:00.000) to unix time (seconds from 
%                  01-01-1970 00:00:00.000) or vice verse.
%
%   OUT = SPDFEPOCHUnixTime(time, 'TOEPOCH', TF) returns converted time(s).
%   OUT a column vector of numerical values of converted times.
%
%     time         A vector or scalar of time(s), based on CDF_EPOCH data type
%                  or unix time. 
%
%   The option 'TOEPOCH' is specified to true if the time conversion is from
%   unix time to CDF_EPOCH. If false, or not specified, the conversion is
%   from CDF_EPOCH to unix time.
%
%   Examples:
%
%   % Convert a CDF_EPOCH data, a scalar at 01-01-2000 00:00:00.123 to a unix
%     time value.
%
%   epoch = spdfcomputeepoch([2000,1,1,0,0,0,123]);
%   unixtime = SPDFEPOCHUnixTime(epoch);
%
%   % Convert CDF_EPOCH data, a vector at 01-01-2000 00:00:00.123 and 
%     02-02-2001 01:01:01.456 to unix time values.
%
%   epochs = spdfcomputeepoch([2000,1,1,0,0,0,123; 2001,2,2,1,1,1,456]);
%   unixtimes = SPDFEPOCHUnixTime(epochs);
%
%   % Convert a unix time to CDF_EPOCH epoch. 
%
%   epoch = SPDFEPOCHUnixTime(unixtime, 'TOEPOCH', true);
%
%   % Convert a vector of unix times to CDF_EPOCH epochs.
%
%   epochs = SPDFEPOCHUnixTime(unixtimes, 'TOEPOCH', true);
%

% HISTORY:
%   November 22, 2018  Mike Liu    The initial version.

%
% Process arguments.
%

if (nargin < 1)
    error('MATLAB:SPDFEPOCHUnixTime:inputArgumentCount', ...
          'SPDFEPOCHUnixTime requires at least one input argument.')
end

if (nargout > 1)
    error('MATLAB:SPDFEPOCHUnixTime:outputArguments', ...
          'SPDFEPOCHUnixTime requires only one output argument.')
end

[args, msg] = parse_inputs(varargin{:});

if (~isempty(msg))
    error('MATLAB:spdfepochunixtime:badInputArguments', '%s', msg)
end

out = spdfepochunixtimec(time, args.toepoch);

end

%%%
%%% Function parse_inputs
%%%

function [args, msg] = parse_inputs(varargin)
% Set default values
args.toepoch = int32(0);
msg = '';
% Parse arguments based on their number.
if (nargin > 0)
    paramStrings = {'toepoch'};

    % For each pair
    for k = 1:2:length(varargin)
       param = lower(varargin{k});
       idx = strmatch(param, paramStrings);
       if (isempty(idx))
           msg = sprintf('Unrecognized parameter name "%s".', param);
           return
       elseif (length(idx) > 1)
           msg = sprintf('Ambiguous parameter name "%s".', param);
           return
       end
       switch (paramStrings{idx})
         case 'toepoch'
           if (k == length(varargin))
               msg = 'No epoch conversion value specified.';
               return
           else
               convert = varargin{k + 1};
               if (numel(convert) ~= 1)
                   msg = 'Conversion value must be a scalar logical.';
               end
               if (islogical(convert))
                   if (convert)
                     args.toepoch = int32(1);
                   else
                     args.toepoch = int32(0);
                   end
               elseif (isnumeric(convert))
                   args.toepoch = int32(convert);
               else
                   msg = 'Epoch conversion value must be a scalar logical.';
               end
           end
       end  % switch
    end  % for

end  % if (nargin > 1)

end
function out = SPDFPARSEEPOCH16(epoch16)
%SPDFPARSEEPOCH16 converts the epoch in UTC string to CDF_EPOCH16
%data type.
%
%   OUT = SPDFPARSEEPOCH16(epoch16) returns the CDF epoch in CDF_EPOCH16 type.
%   OUT an array of numerical values of mxDOUBLE_CLASS (double).
%
%     epoch16               A cell or vector of UTC string
%
%   Note: the valid epoch string must be one of the following forms:
%      0: dd-mmm-yyyy hh:mm:ss.mmm.uuu.nnn.ppp, e.g., "01-JAN-2010 12:00:00.000.000.000.000"
%      1: yyyymmdd.ddddddddddddddd, e.g., "20100101.120000000000000"
%      2: yyyymmddhhmmss, e.g., "20100101120000"
%      3: yyyy-mm-ddThh:mm:ss.mmm.uuu.nnn.pppZ, e.g., "2010-01-01T12:00:00.000.000.000.000Z"
%         where mmm is milliseconds, uuu microseconds, nnn nanoseconds,
%         ppp picoseconds.
%      4: yyyy-mm-ddThh:mm:ss.mmmuuunnnppp, e.g., "2010-01-01T12:00:00.000000000000"
%         where mmm is milliseconds, uuu microseconds, nnn nanoseconds,
%         ppp picoseconds.
%
%   Examples:
%
%   % Convert the UTC strings in cell to CDF_EPOCH16 and write it to Epoch
%   % variable of CDF CDF_EPOCH16 data type in a CDF.
%
%   utcs = {'2009-01-01T00:00:00.123'; '2009-01-01T12:00:00.123'};
%   epoch16 = SPDFPARSEEPOCH16(utcs);
%   SPDFCDFWRITE('example', {'Epoch', epoch16}, ...
%            'recordbound', {'Epoch'});
%
%   See also SPDFENCODEEPOCH16, SPDFCOMPUTEEPOCH16, SPDFBREAKDOWNEPOCH16

% HISTORY:
%   August 16, 2014  Mike Liu    The initial version.

%
% Process arguments.
%

if (nargin < 1)
    error('MATLAB:SPDFPARSEEPOCH16:inputArgumentCount', ...
          'SPDFPARSEEPOCH16 requires at least one input argument.')
end

if (nargout > 1)
    error('MATLAB:SPDFPARSEEPOCH16:outputArguments', ...
          'SPDFPARSEEPOCH16 requires only one output argument.')
end

out = spdfparseepoch16c(epoch16);

function out = SPDFPARSEEPOCH(epoch)
%SPDFPARSEEPOCH converts the epoch in UTC string to CDF_EPOCH
%data type.
%
%   OUT = SPDFPARSEEPOCH(epoch) returns the CDF epoch in EPOCH type.
%   OUT a vector of numerical values of mxDOUBLE_CLASS (double).
%
%     epoch               A cell or vector of UTC string
%
%   Note: the valid epoch string must be one of the following forms:
%      0: dd-mmm-yyyy hh:mm:ss.mmm, e.g., "01-JAN-2010 12:00:00.000"
%      1: yyyymmdd.ddddddd, e.g., "20100101.1200000"
%      2: yyyymmddhhmmss, e.g., "20100101120000"
%      3: yyyy-mm-ddThh:mm:ss.mmmZ, e.g., "2010-01-01T12:00:00.000Z"
%         where mmm is milliseconds.
%      4: yyyy-mm-ddThh:mm:ss.mmm, e.g., "2010-01-01T12:00:00.000"
%         where mmm is milliseconds.
%
%   Examples:
%
%   % Convert the UTC strings in cell to CDF_EPOCH and write it to Epoch
%   % variable of CDF CDF_EPOCH data type in a CDF.
%
%   utcs = {'2009-01-01 00:00:00.123'; '2009-01-01 12:00:00.123'};
%   epoch = SPDFPARSEEPOCH(utcs);
%   SPDFCDFWRITE('example', {'Epoch', epoch}, ...
%            'recordbound', {'Epoch'});
%
%   See also CDFEPOCH, SPDFENCODEEPOCH, SPDFCOMPUTEEPOCH, SPDFBREAKDOWNEPOCH

% HISTORY:
%   August 16, 2014  Mike Liu    The initial version.

%
% Process arguments.
%

if (nargin < 1)
    error('MATLAB:SPDFPARSEEPOCH:inputArgumentCount', ...
          'SPDFPARSEEPOCH requires at least one input argument.')
end

if (nargout > 1)
    error('MATLAB:SPDFPARSEEPOCH:outputArguments', ...
          'SPDFPARSEEPOCH requires only one output argument.')
end

out = spdfparseepochc(epoch);

function out = SPDFPARSETT2000(tt2000)
%SPDFPARSETT2000 converts the CDF epoch in UTC string to CDF_TIME_TT2000
%data type.
%
%   OUT = SPDFPARSETT2000(tt2000) returns the CDF epoch in TT2000 data type.
%   OUT a vector of numerical values of mxINT64_CLASS (int64).
%
%     tt2000               A cell or vector of TT2000 UTC string
%
%   Note: the valid epoch string should be one of the following forms:
%      0: dd-mmm-yyyy hh:mm:ss.mmmuuunnn, e.g., "01-JAN-2010 12:00:00.000000000"
%      1: yyyymmdd.dddddddddd, e.g., "20100101.1200000000"
%      2: yyyymmddhhmmss, e.g., "20100101120000"
%      3: yyyy-mm-ddThh:mm:ss.mmmuuunnn, e.g., "2010-01-01T12:00:00.000000000"
%         where mmmuuunnn is for milliseconds, microseconds and nanoseconds.
%      4: yyyy-mm-ddThh:mm:ss.mmmuuunnnZ, e.g., "2010-01-01T12:00:00.000000000Z"
%         where mmmuuunnn is for milliseconds, microseconds and nanoseconds.
%
%   Examples:
%
%   % Convert the UTC strings in cell to TT2000 and write it to Epoch
%   % variable of CDF TT2000 data type in a CDF.
%
%   utcs = {'2009-01-01T00:00:00.123456789'; '2009-01-01T12:00:00.123456789'};
%   tt2000 = SPDFPARSETT2000(utcs);
%   SPDFCDFWRITE('example', {'Epoch', tt2000}, 'TT2000', true, ...
%            'recordbound', {'Epoch'});
%
%   See also CDFTT2000, SPDFENCODETT2000, SPDFCOMPUTETT2000, SPDFBREAKDOWNTT2000

% HISTORY:
%   August 16, 2011  Mike Liu    The initial version.

%
% Process arguments.
%

if (nargin < 1)
    error('MATLAB:SPDFPARSETT2000:inputArgumentCount', ...
          'SPDFPARSETT2000 requires at least one input argument.')
end

if (nargout > 1)
    error('MATLAB:SPDFPARSETT2000:outputArguments', ...
          'SPDFPARSETT2000 requires only one output argument.')
end

out = spdfparsett2000c(tt2000);

function epoch = spdfdatenumtoepoch(varargin)
%SPDFDATENUMtoEPOCH Convert MATLAB's datenum to CDF_EPOCH values.
%
%    E = SPDFDATENUMtoEPOCH(DATE) convert a DATE, a valid string (datestr) or
%        number (datenum) representing a date, to CDF_EPOCH value.
%
%    Note that a CDF epoch is the number of milliseconds since 
%    1-Jan-0000 while MATLAB datenums are the number of days since 0-Jan-0000.
%
%    See also CDFEPOCH, SPDFEPOCHTODATENUM, SPDFCOMPUTEEPOCH, SPDFENCODEEPOCH.
%             SPDFPARSEEPOCH, SPDFBREAKDOWNEPOCH.

if (nargin > 1)
    error('MATLAB:SPDFDATENUMtoEPOCH:SPDFDATENUMtoEPOCH:tooManyInput', ...
          'Only one argument is allowed.');
elseif (nargin < 1)
    error('MATLAB:SPDFDATENUMtoEPOCH:SPDFDATENUMtoEPOCH:tooFewInput', ...
          'Only one argument is allowed.');
else
    input = varargin{1};
end

if iscellstr(input)
    input = char(input);
end

if ~ischar(input) & ~isnumeric(input)
    error('MATLAB:SPDFDATENUMtoEPOCH:SPDFDATENUMtoEPOCH:badInputs', ...
          'Input must be a number, string, cellstr.');
end

if ischar(input)
    % If the input is a string, then you have to convert

    % Convert to MATLAB datenum.  If this bombs out, an invalid
    % datestr was passed to datenum.
    n = datenum(input);
else
    % It's numeric, so if it's a matrix, go element by element
    % and convert each and then reshape.
    n = input(:);
end

epoch = spdfdatenumtoepochc(n);

function out = SPDFTT2000toDATENUM(tt2000)
%SPDFTT2000toDATENUM converts the time in UTC string (returned from spdfcdfread)
%                or date/time values for CDF_TIME_TT2000 to MATLAB datenum
%
%   OUT = SPDFTT2000toDATENUM(tt2000) returns MATLAB datenum.
%   OUT a column vector of numerical values of MATLAB date numbers.
%
%     tt2000               A vector or cell of UTC string or an either
%                          M-by-1 matrix of CDF_TIME_TT2000 values or
%                          M-by-9 matrix containing M rows, each with date/time
%                          fields for year, month, day, hour, minute, second,
%                          millisecond, microsecond, and nanosecond, in that
%                          order.
%
%   Note:
%     The valid tt2000 string should have one of the following forms:
%     1. dd-mmm-yyyy hh:mm:ss.mmmuuunnn (length of 30), e.g.,
%       "01-JAN-2000 12:00:00.123456789"
%     2. yyyymmdd.dddddddddd (length of 19), e.g.,
%       "20000101.1200000000"
%     3. yyyymmddhhmmss (length of 14), e.g.,
%       "20000101120000"
%     4. yyyy-mm-ddThh:mm:ss.mmmuuunnn (ISO 8601, length of 29), e.g.,
%       "2000-01-01T12:00:00.123456789"
%     where mmmuuunnn is milliseconds, microseconds and nanoseconds.
%
%   Examples:
%
%   % Read all the variable data in a CDF file. Among them, the variable of 
%     CDF_TIME_TT2000 data type, at index of 17, is returned. Convert the
%     variable of CDF_TIME_TT2000 data type in cdftt2000 objects to MATLAB
%     datenum.
%
%   data = spdfcdfread('test','KeepEpochAsIs',true);
%   tt2000 = data(1,17);
%   datenums = SPDFTT2000toDATENUM(tt2000);
%
%   % Convert the UTC strings in vector to MATLAB datenum.
%
%   tt2000 = ['2009-01-01T00:00:00.123456789';
%             '2009-01-01T12:00:00.123456789'];
%   datenums = SPDFTT2000toDATENUM(tt2000);
%
%   % Convert the date/times in matrix to MATLAB datenum.
%
%   tt2000 = [2009 01 01 00 00 00 123 456 789;
%             2009 01 01 12 00 00 123 456 789];
%   datenums = SPDFTT2000toDATENUM(tt2000);
%
%   See also CDFTT2000, SPDFENCODETT2000, SPDFCOMPUTETT2000, SPDFPARSETT2000

% HISTORY:
%   August 16, 2011  Mike Liu    The initial version.

%
% Process arguments.
%

if (nargin < 1)
    error('MATLAB:SPDFTT2000toDATENUM:inputArgumentCount', ...
          'SPDFTT2000toDATENUM requires at least one input argument.')
end

if (nargout > 1)
    error('MATLAB:SPDFTT2000toDATENUM:outputArguments', ...
          'SPDFTT2000toDATENUM requires only one output argument.')
end

if (isa(tt2000, 'cdftt2000'))
  error('MATLAB:SPDFTT2000toDATENUM:inputobject', ...
        'SPDFTT2000toDATENUM can not handle input data in cdftt2000 object class.')
end

s = size(tt2000);
if (iscell(tt2000(1,1)))
  if (s(1) == 1)
    tt2000 = tt2000{1,1};
  else
    if (iscellstr(tt2000))
      tt2000 = char(tt2000);
    else
      if (s(2) == 1)
        for x=1:s(1)
        tt2000(x,1) = tt2000{x,1};
        end
      end
    end
  end
end
if (iscell(tt2000))
  tt2000 = cell2mat(tt2000);
end
if (isnumeric(tt2000))
  s = size(tt2000);
  if (s(2) == 1)
    dates = spdfbreakdowntt2000(tt2000);
    dates3 = dates(:, 1:6);
    for p =1:s(1)
      dates3(p,6) = dates(p,6) + dates(p,7)/1000 + dates(p,8)/1000000 + ...
                    dates(p,9)/1000000000;
    end
    out = datenum(dates3);
  else
    if (s(2) ~= 9)
      error('MATLAB:SPDFTT2000toDATENUM:inputArgumentvector', ...
            'SPDFTT2000toDATENUM requires an M by 9 vector for date/time fields.')
    end
    dates=tt2000(:,1:6);
    for p =1:s(1)
      dates(p,6) = tt2000(p,6) + tt2000(p,7)/1000 + tt2000(p,8)/1000000 + ...
                   tt2000(p,9)/1000000000;
    end
    out = datenum(dates); 
  end
else
  dates = spdfparsett2000(tt2000);
  dates2 = spdfbreakdowntt2000(dates);
  dates3 = dates2(:, 1:6);
  for p =1:s(1)
    dates3(p,6) = dates2(p,6) + dates2(p,7)/1000 + dates2(p,8)/1000000 + ...
                  dates2(p,9)/1000000000;
  end
  out = datenum(dates3);
end
function out = SPDFTT2000UnixTime(time, varargin)
%SPDFTT2000UnixTime converts the CDF_TIME_TT2000 time (nanoseconds since 
%                  2000-01-01 12:00:00.000 with leap seconds) to unix time
%                  (seconds from 1970-01-01 00:00:00.000) or vice verse.
%
%   OUT = SPDFTT2000UnixTime(time, 'TOTT2000', TF) returns converted time(s).
%   OUT a column vector of numerical values of converted times.
%
%     time         A vector or scalar of time(s), based on CDF_TIME_TT2000 data type
%                  or unix time. 
%
%   The option 'TOTT2000' is specified to true if the time conversion is from
%   unix time to CDF_TIME_TT2000. If false, or not specified, the conversion is
%   from CDF_TIME_TT2000 to unix time.
%
%   Note: Since unix time does not include leap seconds, the time conversion will
%         not be properly handled if a time falls on a leap second. The TT2000
%         time has a higher time resolution, sub-milliseonds might not be preserved
%         in unix time.
%
%   Examples:
%
%   % Convert a CDF_TIME_TT2000 data, a scalar at 2000-01-01 00:00:00.123 to a unix
%     time value.
%
%   epoch = spdfcomputett2000([2000,1,1,0,0,0,123,0,0]);
%   unixtime = SPDFTT2000UnixTime(epoch);
%
%   % Convert CDF_TIME_TT2000 data, a vector at 2000-01-01-2000 00:00:00.123 and 
%     2001-02-02 01:01:01.456 to unix time values.
%
%   epochs = spdfcomputett2000([2000,1,1,0,0,0,123,0,0; 2001,2,2,1,1,1,456,0,0]);
%   unixtimes = SPDFTT2000UnixTime(epochs);
%
%   % Convert a unix time to CDF_TIME_TT2000 epoch. 
%
%   epoch = SPDFTT2000UnixTime(unixtime, 'TOTT2000', true);
%
%   % Convert a vector of unix times to CDF_TIME_TT2000 epochs.
%
%   epochs = SPDFTT2000UnixTime(unixtimes, 'TOTT2000', true);
%

% HISTORY:
%   November 22, 2018  Mike Liu    The initial version.

%
% Process arguments.
%

if (nargin < 1)
    error('MATLAB:SPDFTT2000UnixTime:inputArgumentCount', ...
          'SPDFTT2000UnixTime requires at least one input argument.')
end

if (nargout > 1)
    error('MATLAB:SPDFTT2000UnixTime:outputArguments', ...
          'SPDFTT2000UnixTime requires only one output argument.')
end

[args, msg] = parse_inputs(varargin{:});

if (~isempty(msg))
    error('MATLAB:spdftt2000unixtime:badInputArguments', '%s', msg)
end

out = spdftt2000unixtimec(time, args.toepoch);

end

%%%
%%% Function parse_inputs
%%%

function [args, msg] = parse_inputs(varargin)
% Set default values
args.toepoch = int32(0);
msg = '';
% Parse arguments based on their number.
if (nargin > 0)
    paramStrings = {'tott2000'};

    % For each pair
    for k = 1:2:length(varargin)
       param = lower(varargin{k});
       idx = strmatch(param, paramStrings);
       if (isempty(idx))
           msg = sprintf('Unrecognized parameter name "%s".', param);
           return
       elseif (length(idx) > 1)
           msg = sprintf('Ambiguous parameter name "%s".', param);
           return
       end
       switch (paramStrings{idx})
         case 'tott2000'
           if (k == length(varargin))
               msg = 'No epoch conversion value specified.';
               return
           else
               convert = varargin{k + 1};
               if (numel(convert) ~= 1)
                   msg = 'Conversion value must be a scalar logical.';
               end
               if (islogical(convert))
                   if (convert)
                     args.toepoch = int32(1);
                   else
                     args.toepoch = int32(0);
                   end
               elseif (isnumeric(convert))
                   args.toepoch = int32(convert);
               else
                   msg = 'Epoch conversion value must be a scalar logical.';
               end
           end
       end  % switch
    end  % for

end  % if (nargin > 1)

end
function epochObj = cdfepoch(varargin)
%CDFEPOCH Construct epoch object for CDF export.
%
%    E = CDFEPOCH(DATE) constructs a cdfepoch object where DATE is
%    a valid string (datestr) or number (datenum) representing a
%    date.  DATE may also be a cdfepoch object.
%
%    CDFEPOCH objects should be constructed to create EPOCH data in CDF's.
%    using SPDFCDFWRITE.  Note that a CDF epoch is the number of milliseconds
%    since 1-Jan-0000 and that MATLAB datenums are the number of days
%    since 0-Jan-0000.
%
%    See also SPDFCDFWRITE, DATENUM, SPDFCDFREAD, SPDFCDFINFO.

if (nargin == 0)
    s.date = [];
    epochObj = class(s, 'cdfepoch');
    return;
elseif (nargin > 1)
    error('MATLAB:cdfepoch:cdfepoch:tooManyInputs', ...
          'Too many input arguments.');
else
    input = varargin{1};
end

if isa(input,'cdfepoch')
    epochObj = input;
    return;
end

if iscellstr(input)
    input = char(input);
end

if ~ischar(input) & ~isnumeric(input)
    error('MATLAB:cdfepoch:cdfepoch:badInputs', ...
          'Input must be a number, string, cellstr, or cdfepoch object.');
end

% Initialize in case passed empty
s.date = [];

if ischar(input)
    % If the input is a string, then you have to convert
    cdfepochs = [];

    % Convert to MATLAB datenum.  If this bombs out, an invalid
    % datestr was passed to datenum.
    n = datenum(input);
else
    % It's numeric, so if it's a matrix, go element by element
    % and convert each and then reshape.
    n = input(:);
end

s = struct('date',num2cell((n - 1) * 24 * 3600000)');
s = s';

if isnumeric(input) & ~isempty(input)
    s = reshape(s, size(input));
end

epochObj = class(s, 'cdfepoch');

if isnumeric(input) & ~isempty(input)
    s = reshape(s, size(input));
end
function disptt2000(obj)
%DISPTT2000   DISP for CDFTT2000 object.

% If obj is not scalar, then just display the size
s = size(obj);
if ~isequal(s,[1 1])
    disp(sprintf(['     [%dx%d cdftt2000]'], s(1), s(2)));
else
    disp( [spdfencodett2000(obj)]);
end
function out = UTC2CDFEPOCH16(UTC,month,day,hour,minute,second,milsec,micsec,nansec,picsec)
%UTC2CDFEPOCH16 converts a UTC date/time in string or components to CDF_EPOCH16
%
%   There are two forms of this function:
%
%   OUT = UTC2CDFEPOCH16(UTC) 
%         Parses a single CDF epoch string and returns a CDF_EPOCH16
%         data type.
%
%     UTC               A UTC string
%
%   Note: the valid epoch string should be one of the following forms:
%      0: dd-mmm-yyyy hh:mm:ss.llluuunnn, e.g., "01-JAN-2010 12:00:00.000000000"
%      1: yyyymmdd.dddddddddd, e.g., "20100101.1200000000"
%      2: yyyymmddhhmmss, e.g., "20100101120000"
%      3: yyyy-mm-ddThh:mm:ss.llluuunnn, e.g., "2010-01-01T12:00:00.000000000"
%         where lll as milliseconds, uuu as microseconds and nnn as nanoseconds.
%   Or,
%
%   OUT = UTC2CDFEPOCH16(year,month,day,hour,minute,second,milsec,micsec,nansec,picsec) 
%         Compute the CDF epoch in CDF_EPOCH16 data type.
%
%     year, month, day, hour,       Integer form for date/time components
%     minute, second, milsec,  
%     micsec, nansec, picsec
%
%   Examples:
%
%   % Write 100 epoch records, starting from 2009-01-01T00:00:00.123456789 with
%   % one (1) second stride, to a CDF.
%
%   utc = '2009-01-01T00:00:00.123456789';
%   epoch = UTC2CDFEPOCH16(utc);
%   epochs = epoch+[0:99]*1000;
%   SPDFCDFWRITE('example', {'Epoch', epochs}, 'EPOCHArrayisCDFEpoch', true, ...
%            'recordbound', {'Epoch'});
%
%   % Alternatively, the sample can be entered as
%
%   epoch = UTC2CDFEPOCH16(2010,1,1,0,0,0,0,0,0,0);
%   epochs = epoch:[0:99]*1000;
%   SPDFCDFWRITE('example', {'Epoch', epochs}, 'EPOCHArrayisCDFEpoch', true, ...
%            'recordbound', {'Epoch'});
%
%   See also SPDFCDFWRITE, SPDFCDFREAD, CDFTT2000.

% HISTORY:
%   August 16, 2011  Mike Liu    The initial version.

%
% Process arguments.
%

if (nargin ~= 1 && nargin ~= 10)
    error('MATLAB:UTC2CDFEPOCH16:inputArgumentCount', ...
          'UTC2CDFEPOCH16 requires one input string or ten date/time components.')
end

if (nargout > 1)
    error('MATLAB:UTC2CDFEPOCH16:outputArguments', ...
          'UTC2CDFEPOCH16 requires only one output argument.')
end

if (nargin == 1 && ischar(UTC))
  out = spdfparseepoch16(UTC);
elseif (nargin == 10) 
  out = spdfcomputeepoch16([UTC,month,day,hour,minute,second,milsec,micsec,nansec,picsec]);
else
  error('MATLAB:UTC2CDFEPOCH16:input', ...
        'UTC2CDFEPOCH16 input does not meet requirements.')
end
end
function out = UTC2CDFEPOCH(UTC,month,day,hour,minute,second,milsec,micsec)
%UTC2CDFEPOCH converts a UTC date/time in string or components to CDF_EPOCH
%
%   There are two forms of this function:
%
%   OUT = UTC2CDFEPOCH(UTC) 
%         Parses a single CDF epoch string and returns a CDF_EPOCH
%         data type.
%
%     UTC               A UTC string
%
%   Note: the valid epoch string should be one of the following forms:
%      0: dd-mmm-yyyy hh:mm:ss.lll, e.g., "01-JAN-2010 12:00:00.000"
%      1: yyyymmdd.ddddddd, e.g., "20100101.1200000"
%      2: yyyymmddhhmmss, e.g., "20100101120000"
%      3: yyyy-mm-ddThh:mm:ss.lll, e.g., "2010-01-01T12:00:00.000"
%         where lll as milliseconds.
%   Or,
%
%   OUT = UTC2CDFEPOCH(year,month,day,hour,minute,second,milsec) 
%         Compute the CDF epoch in CDF_EPOCH data type.
%
%     year, month, day, hour,       Integer form for date/time components
%     minute, second, milsec.  
%
%   Examples:
%
%   % Write 100 epoch records, starting from 2009-01-01T00:00:00.123456789 with
%   % one (1) second stride, to a CDF.
%
%   utc = '2009-01-01T00:00:00.123';
%   epoch = UTC2CDFEPOCH(utc);
%   epochs = epoch+[0:99]*1000;
%   SPDFCDFWRITE('example', {'Epoch', epochs}, 'EPOCHArrayisCDFEpoch', true, ...
%            'recordbound', {'Epoch'});
%
%   % Alternatively, the sample can be entered as
%
%   epoch = UTC2CDFEPOCH(2010,1,1,0,0,0,0);
%   epochs = epoch:[0:99]*1000;
%   SPDFCDFWRITE('example', {'Epoch', epochs}, 'EPOCHArrayisCDFEpoch', true, ...
%            'recordbound', {'Epoch'});
%
%   See also SPDFCDFWRITE, SPDFCDFREAD, CDFEPOCH.

% HISTORY:
%   August 16, 2011  Mike Liu    The initial version.

%
% Process arguments.
%

if (nargin ~= 1 && nargin ~= 7)
    error('MATLAB:UTC2CDFEPOCH:inputArgumentCount', ...
          'UTC2CDFEPOCH requires one input string or seven date/time components.')
end

if (nargout > 1)
    error('MATLAB:UTC2CDFEPOCH:outputArguments', ...
          'UTC2CDFEPOCH requires only one output argument.')
end

if (nargin == 1 && ischar(UTC))
  out = spdfparseepoch(UTC);
elseif (nargin == 7) 
  out = spdfcomputeepoch([UTC,month,day,hour,minute,second,milsec]);
else
  error('MATLAB:UTC2CDFEPOCH:input', ...
        'UTC2CDFEPOCH input does not meet requirements.')
end
end
function out = UTC2CDFTT2000(UTC,month,day,hour,minute,second,milsec,micsec,nansec)
%UTC2CDFTT2000 converts a UTC date/time in string or components to CDF_TIME_TT2000
%
%   There are two forms of this function:
%
%   OUT = UTC2CDFTT2000(UTC) 
%         Parses a single CDF epoch string and returns a CDF_TIME_TT2000
%         data type.
%
%     UTC               A UTC string
%
%   Note: the valid epoch string should be one of the following forms:
%      0: dd-mmm-yyyy hh:mm:ss.llluuunnn, e.g., "01-JAN-2010 12:00:00.000000000"
%      1: yyyymmdd.dddddddddd, e.g., "20100101.1200000000"
%      2: yyyymmddhhmmss, e.g., "20100101120000"
%      3: yyyy-mm-ddThh:mm:ss.llluuunnn, e.g., "2010-01-01T12:00:00.000000000"
%         where lll as milliseconds, uuu as microseconds and nnn as nanoseconds.
%   Or,
%
%   OUT = UTC2CDFTT2000(year,month,day,hour,minute,second,milsec,micsec,nansec) 
%         Compute the CDF epoch in CDF_TIME_TT2000 data type.
%
%     year, month, day, hour,       Integer form for date/time components
%     minute, second, milsec,  
%     micsec, nansec
%
%   Examples:
%
%   % Write 100 epoch records, starting from 2009-01-01T00:00:00.123456789 with
%   % one (1) second stride, to a CDF.
%
%   utc = '2009-01-01T00:00:00.123456789';
%   epoch = UTC2CDFTT2000(utc);
%   epochs = epoch+[0:99]*1000;
%   SPDFCDFWRITE('example', {'Epoch', epochs}, 'EPOCHArrayisCDFEpoch', true, ...
%            'recordbound', {'Epoch'});
%
%   % Alternatively, the sample can be entered as
%
%   epoch = UTC2CDFTT2000(2010,1,1,0,0,0,0,0,0);
%   epochs = epoch:[0:99]*1000;
%   SPDFCDFWRITE('example', {'Epoch', epochs}, 'EPOCHArrayisCDFEpoch', true, ...
%            'recordbound', {'Epoch'});
%
%   See also SPDFCDFWRITE, SPDFCDFREAD, CDFTT2000.

% HISTORY:
%   August 16, 2011  Mike Liu    The initial version.

%
% Process arguments.
%

if (nargin ~= 1 && nargin ~= 9)
    error('MATLAB:UTC2CDFTT2000:inputArgumentCount', ...
          'UTC2CDFTT2000 requires one input string or nine date/time components.')
end

if (nargout > 1)
    error('MATLAB:UTC2CDFTT2000:outputArguments', ...
          'UTC2CDFTT2000 requires only one output argument.')
end

if (nargin == 1 && ischar(UTC))
  out = spdfparsett2000(UTC);
elseif (nargin == 9) 
  out = spdfcomputett2000([UTC,month,day,hour,minute,second,milsec,micsec,nansec]);
else
  error('MATLAB:UTC2CDFTT2000:input', ...
        'UTC2CDFTT2000 input does not meet requirements.')
end
end
