function [nb,B,nk,nInputs] = ...
    validatePolynomialB(data,nk,modelStructure,...
    initialEstimateSource)
% validatePolynomialB Validate the initial B polynomial or its number of
% coefficients
%
%   [nb,B,nk,nInputs] = validatePolynomialB(data,nk,modelStructure,...
%                            initialEstimateSource)
%
%   Inputs:
%     data                  - Initial B(q) guess or the number of free
%                             parameters in B(q), i.e. nb
%     nk                    - Fixed number of leading zeros in B(q)
%     modelStructure        - 'ARX','ARMAX' etc.
%     initialEstimateSource - 'None', 'Internal', 'External'
%
%   Outputs:
%     nb      - Number of free parameters in B(q)
%     nk      - Fixes number of leading zeros in B(q)
%     B       - Initial B(q) polynomials
%     nInputs - Number of inputs to the system

%   Copyright 2013-2018 The MathWorks, Inc.

switch initialEstimateSource
    case {1,3} % None or External
        % User is specifying nb, nk. 'data' has nb
        [nb,B,nk,nInputs] = localValidateNbNk(data,nk,modelStructure);
    case 2 % Internal: Extract nb, nk, nInputs from data
        [nb,B,nk,nInputs] = validateInitialB(data);
        nk = localValidateNk(nk,modelStructure,nInputs);
end

assert(all(nb<intmax('uint16')));
assert(all(nk<intmax('uint16')));
nb = uint16(nb);
nk = uint16(nk);
end

function [nb,B,nk,nInputs] = localValidateNbNk(data,nk,modelStructure)
% localValidateNbNk Validate nb, nk
%
% User specifies nb, nk if:
% * History='Finite' 
% OR
% * History='Infinite' AND (InitialEstimateSource='None' or ='External')
nb = data;
validateattributes(nb,...
    {'numeric'},...
    {'nonempty','real','finite','integer','nonnegative','<',controllib.internal.blocks.rls.unsignedIntegerBound()},...
    '',...
    getString(message('Ident:simulink:recursiveEstimationMaskNumberOfParametersB')));
if any(nb==0)
    error(message('Ident:simulink:recursiveEstimationNoInputs'));
end
nInputs = idRecursivePolynomialModelEstimator.getNumberOfInputs(nb);
nk = localValidateNk(nk,modelStructure,nInputs);
B = eps*ones(sum(nb,2),1);
end


function nk = localValidateNk(nk,modelStructure,nInputs)
% validateNk Validate the number of leading coefficients in the B polynomial
switch modelStructure
    case {'ar','arma'} % time-series
        nk = 0;
        return;
    case 'arx'
        minnk = 0;
        enforceSISO = false();
    case 'armax'
        minnk = 0;
        enforceSISO = true();
    case 'oe'
        minnk = 1;
        enforceSISO = true();
    case 'bj'
        minnk = 1;
        enforceSISO = true();
    otherwise
        assert(false());
end
validateattributes(nk,...
    {'numeric'},...
    {'row','real','finite','integer','nonnegative','<',controllib.internal.blocks.rls.unsignedIntegerBound()},...
    '',...
    getString(message('Ident:simulink:recursiveEstimationMaskNk')));
if enforceSISO && ~isscalar(nk)
    error(message('Ident:simulink:recursiveEstimationNkNotScalar'));
end
if any(nk<minnk)
    error(message('Ident:simulink:recursiveEstimationNkLowerBound',minnk));
end
if numel(nk)~=nInputs
    error(message('Ident:simulink:recursiveEstimationNkWrongNumel',nInputs,numel(nk)))
end
end

function [nb,B,nk,nInputs] = validateInitialB(b0)
% validateInitialB Validate the initial B polynomial guess from the
% dialog
if isempty(b0)
    % Autoregressive model
    nInputs = 0;
    nb = 0;
    nk = 0;
    B = [];
    return;
end

% Store all estimated B(q) parameters in a cell containing column vectors.
% cell2mat(B) will return a column vector of parameters to be estimated.
b0FieldName = getString(message('Ident:simulink:recursiveEstimationMaskInitialB'));
if iscell(b0) && isrow(b0)
    nInputs = numel(b0);
    B = cell(nInputs,1);
    nb = zeros(1,nInputs);
    nk = nb;
    for kk=1:nInputs
        [nb(kk),B{kk},nk(kk)] = validateIndividualBPolynomial(b0{kk},false(),b0FieldName);
    end
elseif ~iscell(b0) && ismatrix(b0)
    nInputs = size(b0,1);
    B = cell(nInputs,1);
    nb = zeros(1,nInputs);
    nk = nb;
    for kk=1:nInputs
        [nb(kk),B{kk},nk(kk)] = validateIndividualBPolynomial(b0(kk,:),true(),b0FieldName);
    end
else
    % A column cell array (which would be multiple outputs) or a 3D array
    % for instance
    error(message('Ident:simulink:recursiveEstimationPolyBInvalid'));
end
B = cell2mat(B);
end

function [nb,B,nk] = validateIndividualBPolynomial(bp,stripTrailingZeros,b0FieldName)
% validateIndividualBPolynomial Validate the B polynomial for each input of
% the estimated model.
validateattributes(bp,{'numeric'},{'nonempty','row','real','finite'},'',b0FieldName)
if all(bp==0)
    error(message('Ident:simulink:recursiveEstimationPolyBCannotContainZeroRow'));
end
nk = find(bp,1)-1;
if stripTrailingZeros
    % b0 specified as matrix, ignore leading&trailing zeros in the current
    % row
    lastParameterIndex = find(bp,1,'last');
else
    % b0 specified as cell array, only ignore leading zeros in the current
    % row
    lastParameterIndex = numel(bp);
end
nb = lastParameterIndex-nk; % The leading 0s are fixed during estimation
B = bp(nk+1:lastParameterIndex); % Strip out the leading 0s, store the rest of the initial guess
B = B(:);
end

% LocalWords:  nb nk arma Autoregressive
