function bssok = bssstructok( bss )
% BSSSTRUCTOK - Check a B-Spline series data structure for validity
%
% INPUTS:
%
%    bss:
%
%       A B-Spline series data structure which must have 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.
%          n         - The nominal uniform partition size for each
%                      dimension
%          npart     - The number of uniform subintervals in the partition for
%                      each dimension, including the extra subintervals that
%                      are created based on the order of the B-splines that
%                      are used.
%          bnds      - The estimation bounds for each dimension.
%          h         - The width of each uniform subinterval in the partition for
%                      each dimension.
%          partition - The partition for each dimension 1:NDIM stored as a
%                      vector with one field, x having length npart(i)+1, per
%                      element giving the vector of boundary points for the
%                      partition subintervals. The initial index is 1.
%          c         - The coefficient matrix for the B-spline series
%
%
% OUTPUTS:
%
%   bssok:
%
%      A logical flag indicating whether the input bss structure was valid
%      (true) or not (false).
%
%      A B-spline series structure is valid if it has all of the expected
%      fields and the fields have values that are either empty/zero or are
%      nonempty/nonzero and self consistent in terms of sizes and such.
%

%
% Copyright 2014-2018 Biometrics Northwest LLC
%

%
% Created: 2014-11-25
% Author : Kevin R. Gehringer
%

%
% The current fields expected for a B-spline series are below.
%
bss_fieldnames = {
    'm';
    'ndim';
    'bnds';
    'partition';
    'c';
    'npts';
   };

%
% Assume the structure is not OK to begin.
%
bssok = 0;

%
% Check to be sure that all of the fields are present.
%
fieldspresent = [
   isfield(bss,bss_fieldnames);
   isfield(bss.partition,'x');
   isfield(bss.partition,'h');
   isfield(bss.partition,'n');
   isfield(bss.partition,'npart');
   ];
if ( any(~fieldspresent) )
   error('At least one of the required fields for a B-spline series data structure was not present.');
end

%
% Now check for the consistency. If properly constructed, no field in the
% structure should be empty, all fields should be numeric, and all fields
% should have valid values that are consistent among the various related
% fields. We do not check the coefficient values unless NPTS is zero, and
% in that case all coefficient valuse C must be zero.
%
m         = bss.m;
ndim      = bss.ndim;
bnds      = bss.bnds;
partition = bss.partition;
c         = bss.c;
npts      = bss.npts;

%
% Check for empty fields
%
fieldsempty = [
   isempty(m);
   isempty(ndim);
   isempty(bnds);
   isempty(partition.n);
   isempty(partition.npart);
   isempty(partition.h);
   isempty(partition.x);
   isempty(c);
   isempty(npts);
   ];

if ( any(fieldsempty) )
   error('At least one of the required fields for a B-spline series data structure was empty.');
end

%
% check for numeric fields
%
fieldsnumeric = [
   isnumeric(m);
   isnumeric(ndim);
   isnumeric(bnds);
   isnumeric(partition.n);
   isnumeric(partition.npart);
   isnumeric(partition.h);
   isnumeric(partition.x);
   isnumeric(c);
   isnumeric(npts);
   ];

if ( any(~fieldsnumeric) )
   error('At least one of the required fields for a B-spline series data structure was not numeric.');
end

%
% Start checking the field values for consistency.
%

%
% Check the B-spline order: it must be at least 1.
%
if ( m < 1 )
   error('The B-spline series order must be an integer >= 1.');
end

%
% Check the number of dimensions: it must be between 1 and 3 inclusive.
%
if ( ndim < 1 || ndim > 3 )
   error('The number of dimensions used for the B-spline series must be between 1 and 3 and a value of %d was found for the estimation bounds.',ndim_b);
end

%
% Check the estimation interval bounds for each dimension
%
siz_bnds = size(bnds);
   
if ( siz_bnds(2) ~= 2 )
   error('The number of columns in the B-spline series estimation bounds was not equal to 2.');
end
   
ndim_b = siz_bnds(1);
if ( ndim_b < 1 || ndim_b > 3 )
   error('The number of dimensions used for the B-spline series bounds must be between 1 and 3 and a value of %d was found for the estimation bounds.',ndim_b);
end

%
% Check to be sure that the B-spline series dimensionality agrees with the estimation bounds
%
if ( ndim ~= ndim_b )
   error('Dimension mismatch: The B-spline series dimension (%d) is not equal to the estimation bounds dimension (%d).',ndim, ndim_b);
end

%
%  Check bounds for validity: bnds(i,1) < bnds(i,2), i=1:NDIM.
%
width = bnds(:,2) - bnds(:,1);
row_ids = (1:ndim_b)';
width_nok = width <= 0;
if ( any(width_nok) )
   error('The estimation bounds for the B-Spline series were not valid for row %d: (%e , %e).\n', ...
   row_ids(width_nok),bnds(width_nok,1),bnds(width_nok,2));
end

%
% Get the number of dimensions from the partition size and check.
% Only value of 1, 2, or 3 are valid here.
%
ndim_n = size(partition.n,1);
if ( ndim_n <1 || ndim_n > 3 )
   error('The number of dimensions used for the B-spline series partition must be between 1 and 3 and a value of %d was found for the partition size.',ndim_n);
end

%
% Check to be sure that the B-spline series dimensionality agrees with the partition size
%
if ( ndim ~= ndim_n )
   error('Dimension mismatch: The B-spline series dimensions (%d) is not equal to nominal partition dimension (%d).',ndim, ndim_n);
end

%
% Check the partition size for each dimension. This must be at least 3 for
% each dimension: the two ends and one point in the middle.
%
row_ids = (1:length(partition.n))';
n_nok = partition.n < 3;
if ( any( n_nok ) )
   error('The partition size for the B-Spline series was not valid for row %d: %d < 3.\n', ...
   row_ids(width_nok),partition.n(n_nok));
end

%
% At this point M, BNDS, and the nominal partition sizes are all acceptable.
% Generate a partition to compare with the one provided and check whether
% they agree with each other.
%
tmppart = bsscreatepartition(bnds,partition.n,m);

if ( any(tmppart.npart ~= partition.npart) )
   error('The supplied B-spline series partition sizes did not agree with the values that were expected.');
end

if ( any(tmppart.h ~= partition.h) )
   error('The supplied B-spline series partition bin widths did not agree with the values that were expected.');
end

for i = 1:ndim
   if ( any(tmppart.x(1:partition.npart(i),i) ~= partition.x(1:partition.npart(i),i)) )
      error('The supplied B-spline series partition boundaries did not agree with the values that were expected for dimension %d.',i);
   end
end

%
% Check the number of data points, it should be >= 0 
%
if ( npts < 0 )
   error('The number of data points was negative and it must be >= 0.');
end

%
% Check the B-spline series coeffiecients for consistency
%
c_sz = size(c)';

ndim_c = length(c_sz);

if ( ndim_c <1 || ndim_c > 3 )
   error('The number of dimensions used for the B-spline series coefficients must be between 1 and 3 and a value of %d was found for the coefficients.',ndim_c);
end

%
% Check to be sure that the coefficient dimensionality agrees with the partition size
%
if ( ndim ~= ndim_c )
   error('Dimension mismatch: The B-spline series dimensions (%d) is not equal to nominal partition dimension (%d).',ndim, ndim_n);
end

if ( any(c_sz ~= partition.npart) )
   error('The size of the coefficients array did not agree with the supplied B-spline series partition sizes.');
end

%
% Check the coefficient values: they should sum to zero (0.0) if and only
% if NPTS == 0, otherwise the sum of the coefficients multiplied by the
% product of the partition bin widths H should sum to one (1.0).
%
c_sum = sum(c(:))*prod(partition.h);

if ( npts == 0 )
   if ( abs(c_sum) > sqrt(eps)  )
      error('The B-spline series coefficients did not sum to zero (0.0) and the number of data points was zero.')
   end
else
   if ( abs(1- c_sum) < sqrt(eps) )
      error('The B-spline series coefficients did not sum to one (1.0) and the number of data points was not zero.')
   end
   
end

%
% The B-spline data structure passed all tests
%
bssok = 1;
