function bss_struct = bsscreatestruct( bnds, n, m )
% BSSCREATESTRUCT   - Create a B-Spline series data structure.
%
% INPUTS:
%
%    bnds:
%       The lower and upper bounds for the estimation interval over which
%       the bspline series is to be created for evaluation. This is an NDIM
%       by 2 matrix, specifying the minimum and maximum values used to
%       create the B-spline series partition for each dimension.
%
%       NDIM must be >= 1.
%
%    n:
%       The nominal partition size or the number of subintervals to
%       use for the partition in each dimension. The partition size fro each
%       dimension does NOT include the extra subintervals that are created
%       based on the order of the B-splines that are used. This is a row
%       or column vector with NDIM elements. NDIM must
%       be 1, 2, or 3.
%
%       If empty or not present, the default value obtained from the
%       function bssdefaultpartitionsize() is used for each dimension.
%
%    m:
%       The order of the bsplines to be used. This must be an integer scalar
%       value greater than zero (M>=1).
%
%       If not present or empty, the default value is obtained from the
%       function bssdefaultorder().
%
% OUTPUTS:
%
%    bss_struct:
%
%       A B-Spline series data structure with the following fields.
%
%          m         - The order of the B-spline basis functions. This is a scalar
%                      value that is the same for all dimensions.
%          ndim      - The number of dimensions for the B-spline series.
%          npts      - The number of data points used to compute the coefficients.
%          bnds      - The estimation bounds for each dimension.
%          partition - The partition array containing fields X, N, NPART, and H.
%                      The fields represent the partition of the estimation interval
%                      for each dimension, with X defining the bounday points for
%                      the partition subintervals, N defining the nominal partition
%                      size, NPART defining the number of boundary points, and H
%                      defining the width of the subintervals for each dimension.
%          c         - The coefficient matrix for the B-spline series
%          pdf       - The PDF gridded interpolant for fast evaluations if
%                      not empty.
%          cdf       - The CDF gridded interpolant for fast evaluations if
%                      not empty.
%

%
% Copyright 2014-2023 Biometrics Northwest LLC
%

%
% Modified: 2023-02-17
% Author  : Kevin R. Gehringer
%
%    Modified to directly create empty fields for the PDF and CDF gridded
%    interpolants.
%
% Modified: 2022-12-18
% Author  : Kevin R. Gehringer
%
%    Removed unnecessary tests for the validity of the B-spline series
%    bounds, order, and partition.
%
%    Removed the output from the calls to bssbndsok(), bssorderok(), and
%    bsspartitionsizeok(). It no longer returns an output; it only signals
%    errors. If it returns everything is OK.
%
%    Fixed a cut and paste error in the B-spline order test: the test
%    was changed from isempty(n) to isempty(m).
%
% Modified: 2018-08-28
% Author  : Kevin R. Gehringer
%
%    Fixed a bug in the call to bsscheckmemory(). The argument was the
%    uninitialized variable 'ngrid' and shuold have been 'n'.
%
% Modified: 2016-09-14
% Author  : Kevin R. Gehringer
%
%   Changed the length of the coefficient array. The correct size was
%   NPART-1 = N+(M-1), not NPART = N+M. The coefficients go with the
%   intervals, specifically the index of the left end point, not with all
%   of the points in the partition.
%
% Modified: 2014-11-25
% Author  : Kevin R. Gehringer
%
%   Added a basic check of available memory to try and avoid paging if the
%   size of the structure will nearly fill or slightly overfill the
%   avaialble memory.
%
% Created: 2014-11-25
% Author : Kevin R. Gehringer
%

%
% define an initial value that is empty.
%
bss_struct = [];

%
% Check bounds for validity. The estimation bounds must:
%   exist
%   be nonempty
%   be numeric
%   be an NDIM by 2 matrix, where NDIM is 1, 2 or 3
%   and have bnds(i,1) < bnds(i,2) i = 1:NDIM
%
if ( ~exist('bnds','var') )
   error('The B-spline series estimation bounds were not supplied.');
end

bssbndsok(bnds);

ndim_b = size(bnds,1);

%
% Check the number of partition subintervals. At this point it may not
% exist or it could be empty.
%
if ( exist('n','var') )
   
   if ( ~isempty(n) )
   
      bsspartitionsizeok(n);
      
      if ( isrow(n) )
         n = n';
      end
      
   else
   
      n = ones(ndim_b,1) * bssdefaultpartitionsize;
   
   end
   
else
   
   n = ones(ndim_b,1) * bssdefaultpartitionsize;
   
end

ndim_n = size(n,1);

%
% Check to be sure that the dimensionality of the partition and the bounds
% agree.
%
if ( ndim_n ~= ndim_b )
   error('Dimension mismatch: estimation bounds (%d) is not equal to partition size (%d).', ndim_b, ndim_n);
end
ndim = ndim_n;

%
% Check the B-Spline order. At this point it may not exist or it could
% be empty.
%
if ( exist('m','var') )
   
   if ( ~isempty(m) )
      
      bssorderok(m);

   else
   
      m = bssdefaultorder;
   
   end

else
   
   m = bssdefaultorder;
   
end

%
% Check available memory to see if the structure will likely fit
%
bsscheckmemory(n,'bss');

%
% Compute the partition. 
%
partition = bsscreatepartition(bnds,n,m);

%
% Make the B-Spline series structure.
%
bss_struct.m         = m;
bss_struct.ndim      = ndim;
bss_struct.bnds      = bnds;
bss_struct.partition = partition;
bss_struct.npts      = 0;
bss_struct.pdf       = [];
bss_struct.cdf       = [];
if ( ndim == 1 )
   bss_struct.c = zeros(partition.npart-1,1);
else
   bss_struct.c = zeros((partition.npart-1)');
end
