classdef (Abstract) idRecursivePolynomialModelEstimator < idRecursiveEstimator
    %

    %   Copyright 2015-2018 The MathWorks, Inc.
    
    %#codegen
    
    properties(Dependent,SetAccess=protected)
        A;
        B;
        C;
        D;
        F;
    end
    
    properties(Dependent)
        %InitialA The initial coefficients of the A polynomial
        %
        %   InitialA must be a row vector of length na+1, which you specify
        %   during initializing the estimator. The leading coefficient of
        %   InitialA must be 1. For instance:
        %      x = recursiveARX([2 1 1]); % na=2, nb=1, nk=1
        %      x.InitialA = [1 0.5 0.25];
        %   InitialA is specified in a similar manner for recursiveAR, 
        %   recursiveARMA and recursiveARMAX.
        InitialA;
        %InitialB The initial coefficients of the B polynomial
        %
        %   InitialB is a matrix with nu (number of inputs) rows and
        %   max(nb+nk,[],2) columns. i-th row of B0 must contain nk(i) zeros,
        %   followed by nb(i) parameter guesses. Entries beyond nk(i)+nb(i)
        %   are ignored. For instance:
        %      x = recursiveARX([2 2 3 1 1]); % na=2, nb=[2 3], nk=[1 1]
        %      x.InitialB = [0 1 0.5 0; 0 2 1.75 1];
        %   InitialB is specified in a similar manner for recursiveARMAX,
        %   recursiveOE and recursiveBJ.
        InitialB;
        %InitialC The initial coefficients of the C polynomial
        %
        %   InitialC must be a row vector of length nc+1, which you specify
        %   during initializing the estimator. The leading coefficient of
        %   InitialC must be 1. InitialC must also be a stable discrete-time
        %   polynomial (all roots in the unit disk, i.e.
        %   all(abs(roots(InitialC))<1)==true). For instance:
        %      x = recursiveARMAX([2 1 3 1]); % na=2, nb=1, nc=3, nk=1
        %      x.InitialC = [1 0.75 0.5 0.25];
        %   InitialC is specified in a similar manner for recursiveARMA and
        %   recursiveBJ.
        InitialC;
        %InitialD The initial coefficients of the D polynomial
        %
        %   InitialD must be a row vector of length nd+1, which you specify
        %   during initializing the estimator. The leading coefficient of
        %   InitialD must be 1. InitialD must also be a stable discrete-time
        %   polynomial (all roots in the unit disk, i.e.
        %   all(abs(roots(InitialD))<1)==true). For instance:
        %      x = recursiveBJ([1 1 1 2 1]); % na=1, nb=1, nc=1, nd=2, nk=1
        %      x.InitialD = [1 0.9 0.8];
        InitialD;
        %InitialF The initial coefficients of the F polynomial
        %
        %   InitialF must be a row vector of length nf+1, which you specify
        %   during initializing the estimator. The leading coefficient of
        %   InitialF must be 1. InitialF must also be a stable discrete-time
        %   polynomial (all roots in the unit disk, i.e.
        %   all(abs(roots(InitialF))<1)==true). For instance:
        %      x = recursiveOE([1 2 1]); % nb=1, nf=2, nk=1
        %      x.InitialF = [1 0.9 0.8];
        %   InitialF is specified in a similar manner for recursiveBJ.
        InitialF;
    end
    
    properties(Nontunable,Access=protected)
        na = uint16(0);
        nb = uint16(0);
        nc = uint16(0);
        nd = uint16(0);
        nf = uint16(0);
        nk = uint16(0);
    end
    
    properties(DiscreteState,SetAccess=protected)
        % Subclasses, such as recursiveARX, initialize these two discrete
        % states in their resetImpl() methods        
        PastMeasurements; %#ok<SOINITPROP>
        PastJacobianEstimates; %#ok<SOINITPROP>
    end
    
    methods(Abstract,Access=protected)
        validateOrdersPreSetImpl(this,orders);
        setOrdersImpl(this,orders);
    end
    
    methods
        function this = idRecursivePolynomialModelEstimator(varargin)
            this@idRecursiveEstimator(varargin{:});
        end
    end
    
    methods(Access=protected)        
        function validateInputsImpl(this,y,u)
            % validateInputsImpl Validate the I/O signals
            %
            % * Data type validation is performed in the base class
            % * Dimension validation is performed here
            
            % If using sample-based signals:
            % 1) y must be a scalar
            % 2) If not a time-series model, u must be a row or column
            % vector with NumberOfInputs elements
            %
            % If using frame-based signals
            % A) y must be a column vector
            % B) If not a time-series model, u must be a matrix with the
            % same number of rows with y. Number of col
            
            nInputs = this.getNumberOfInputs(this.nb);
            
            if this.IsUsingFrames_
                % Frame-based signals
                % A)
                coder.internal.assert(iscolumn(y),...
                    'Ident:simulink:recursiveEstimationFrameOutputNotColumn');
                % B)
                if nInputs~=0
                    % IO model
                    validateInputsImpl@idRecursiveEstimator(this,y,u);
                    % Dimension checks on u
                    coder.internal.assert(ismatrix(u),...
                        'Ident:simulink:recursiveEstimationFrameInputNotMatrix');
                    numelY = numel(y);
                    sizeU = size(u);
                    coder.internal.assert(...
                        isequal(sizeU,[numelY nInputs]),...
                        'Ident:simulink:recursiveEstimationFrameInputWrongDim',...
                        numelY, nInputs,...
                        sizeU(1), sizeU(2));
                else
                    % Time-series model
                    validateInputsImpl@idRecursiveEstimator(this,y);
                end
            else
                % Sample-based signals
                %
                % 1)
                coder.internal.assert(isscalar(y),...
                    'Ident:simulink:recursiveEstimationYDimensions');
                % 2)
                if nInputs~=0
                    % IO model
                    validateInputsImpl@idRecursiveEstimator(this,y,u);
                    % Dimension checks on u
                    coder.internal.assert(iscolumn(u),...
                        'Ident:simulink:recursiveEstimationUNotColumnVector');
                    coder.internal.assert(numel(u)==nInputs,...
                        'Ident:simulink:recursiveEstimationRPolyUDimensions',numel(u),nInputs);
                else
                    % Time-series model
                    validateInputsImpl@idRecursiveEstimator(this,y);
                end
            end            
        end
             
        function isInactive = isInactivePropertyImpl(this,propName)
            % Hide the unused properties of the superclass
            isInactive = isInactivePropertyImpl@idRecursiveEstimator(this,propName);
            if isInactive
                return;
            end
        end
        
        function s = saveObjectImpl(this)
            % Save the private properties, protected properties and states
            % when saving the system object
            
            % Call the base class method
            s = saveObjectImpl@idRecursiveEstimator(this);
            
            s.na = this.na;
            s.nb = this.nb;
            s.nc = this.nc;
            s.nd = this.nd;
            s.nf = this.nf;
            s.nk = this.nk;
            
            % Save the state only if object locked
            if isLocked(this)
                s.PastMeasurements = this.PastMeasurements;
                s.PastJacobianEstimates = this.PastJacobianEstimates;
            end
        end
        
        function loadObjectImpl(this,s,wasLocked)
            % Load the private properties, protected properties and states
            % when loading the system object
            
            % Load protected and private properties
            this.na = s.na;
            this.nb = s.nb;
            this.nc = s.nc;
            this.nd = s.nd;
            this.nf = s.nf;
            this.nk = s.nk;
            
            % Load the state only if object locked
            if wasLocked
                this.PastMeasurements = s.PastMeasurements;
                this.PastJacobianEstimates = s.PastJacobianEstimates;
            end
            
            % Call base class method to load public properties
            loadObjectImpl@idRecursiveEstimator(this,s,wasLocked);
        end
        
        function val = getNumberOfParameters(this)
            % Total number of estimated parameters. Return arg must be
            % uint32 to avoid overflow since na, nb, ... are uint16
            val = idRecursivePolynomialModelEstimator.staticGetNumberOfParameters(...
                this.na,this.nb,this.nc,this.nd,this.nf);
        end
        
        function setNumberOfParametersImpl(this,orders)
            % orders must have numeric, finite, real, non-negative elements
            validateattributes(orders,{'numeric'},...
                {'row','real','finite','integer',...
                'nonnegative','<=',controllib.internal.blocks.rls.unsignedIntegerBound},...
                '','Orders',1);
            
            % model type specific checks on orders
            validateOrdersPreSetImpl(this,orders);
            
            % model type specific implementation by subclasses
            setOrdersImpl(this,uint16(orders));
            
            % Consistency checks between na, nb, ...
            idRecursivePolynomialModelEstimator.validateOrdersPostSet(...
                this.na,this.nb,this.nc,this.nd,this.nf,this.nk);
        end
        
        function setInitialPolynomialCoefficients(this,whichPolynomials,...
                vararginPos,tunablePropNVPairs,firstNVIndex,varargin)
            % Set the initial values of the A, B, C, D, F polynomial
            % coefficients, as required by the model type. 
            %
            % Subclasses call this method, specifying whichPolynomials are
            % required to be set. These polynomial coefficients, if they
            % are specified as arguments, are in a certain order and
            % position in the arguments (in varargin). vararginPos captures
            % these positions.
            dataType = this.DataType;

            strIndex = strfind(whichPolynomials,'A');
            if ~isempty(strIndex)
                argPos = vararginPos(strIndex);
                if ~isempty(tunablePropNVPairs.InitialA) % specified in nv pairs
                    this.InitialA = cast(tunablePropNVPairs.InitialA,dataType);
                elseif firstNVIndex>argPos && ~isempty(varargin{argPos}) % specified as numeric arg
                    this.InitialA = cast(varargin{argPos},dataType);
                else % default value
                    this.InitialA = cast(idRecursivePolynomialModelEstimator.defaultACD(this.na),dataType);
                end
            end
            
            strIndex = strfind(whichPolynomials,'B');
            if ~isempty(strIndex)
                argPos = vararginPos(strIndex);
                if ~isempty(tunablePropNVPairs.InitialB) % specified in nv pairs
                    this.InitialB = cast(tunablePropNVPairs.InitialB,dataType);
                elseif firstNVIndex>argPos  && ~isempty(varargin{argPos}) % specified as numeric arg
                    this.InitialB = cast(varargin{argPos},dataType);
                else % default value
                    this.InitialB = cast(idRecursivePolynomialModelEstimator.defaultB(this.nb,this.nk),dataType);
                end
            end
            
            strIndex = strfind(whichPolynomials,'C');
            if ~isempty(strIndex)
                argPos = vararginPos(strIndex);
                if ~isempty(tunablePropNVPairs.InitialC)
                    this.InitialC = cast(tunablePropNVPairs.InitialC,dataType);
                elseif firstNVIndex>argPos  && ~isempty(varargin{argPos}) % specified as numeric arg
                    this.InitialC = cast(varargin{argPos},dataType);
                else % default value
                    this.InitialC = cast(idRecursivePolynomialModelEstimator.defaultACD(this.nc),dataType);
                end
            end
            
            strIndex = strfind(whichPolynomials,'D');
            if ~isempty(strIndex)
                argPos = vararginPos(strIndex);
                if ~isempty(tunablePropNVPairs.InitialD) % specified in nv pairs
                    this.InitialD = cast(tunablePropNVPairs.InitialD,dataType);
                elseif firstNVIndex>argPos  && ~isempty(varargin{argPos}) % specified as numeric arg
                    this.InitialD = cast(varargin{argPos},dataType);
                else % default value
                    this.InitialD = cast(idRecursivePolynomialModelEstimator.defaultACD(this.nd),dataType);
                end
            end
            
            strIndex = strfind(whichPolynomials,'F');
            if ~isempty(strIndex)
                argPos = vararginPos(strIndex);
                if ~isempty(tunablePropNVPairs.InitialF) % specified in nv pairs
                    this.InitialF = cast(tunablePropNVPairs.InitialF,dataType);
                elseif firstNVIndex>argPos  && ~isempty(varargin{argPos}) % specified as numeric arg
                    this.InitialF = cast(varargin{argPos},dataType);
                else % default value
                    nInputs = idRecursivePolynomialModelEstimator.getNumberOfInputs(this.nb);
                    this.InitialF = cast(idRecursivePolynomialModelEstimator.defaultF(this.nf,nInputs),dataType);
                end
            end
        end

        function [nontunableData,tunableData] = getNameValuePairOptionsSimulation(this,varargin)
            [nontunableData,tunableData] = idRecursivePolynomialModelEstimator.parseInputsSimulation(class(this),varargin{:});
        end
        
        function nontunableData = getNameValuePairOptionsCodegenNontunable(~,varargin)
            nontunableData = idRecursivePolynomialModelEstimator.parseInputsCodegenNontunable(varargin{:});
        end        
        
        function tunableData = getNameValuePairOptionsCodegenTunable(~,varargin)
            tunableData = idRecursivePolynomialModelEstimator.parseInputsCodegenTunable(varargin{:});
        end         
    end
    
    methods(Static,Access=protected)
        function l = codegenHelperGetListOfNVPairs()
            l = struct( ...
                'DataType', uint32(0), ...
                'ForgettingFactor', uint32(0), ...
                'AdaptationGain', uint32(0), ...
                'NormalizationBias', uint32(0), ...
                'ProcessNoiseCovariance', uint32(0),...
                'EstimationMethod', uint32(0),...
                'InitialParameterCovariance', uint32(0),...
                'EnableAdaptation', uint32(0),...
                'History', uint32(0),...
                'WindowLength', uint32(0),...
                'InputProcessing', uint32(0),...                
                'InitialA', uint32(0), ...
                'InitialB', uint32(0), ...
                'InitialC', uint32(0),...
                'InitialD', uint32(0),...
                'InitialF', uint32(0),...
                'InitialOutputs', uint32(0),...
                'InitialInputs', uint32(0));
        end
        
        function nontunableData = parseInputsCodegenNontunable(varargin)
            parms = idRecursivePolynomialModelEstimator.codegenHelperGetListOfNVPairs();
            
            popt = struct( ...
                'CaseSensitivity', false, ...
                'StructExpand',    true, ...
                'PartialMatching', false);
            
            % Specify the optional parameters and their defaults (Defaults
            % must be changed together with the ones in
            % parseInputsCodegen() method for consistency between ML/Codegen)
            optarg           = eml_parse_parameter_inputs(parms, popt, ...
                varargin{:});
            
            nontunableData = struct( ...
                'DataType', eml_get_parameter_value(optarg.DataType, [], varargin{:}), ...
                'EstimationMethod', eml_get_parameter_value(optarg.EstimationMethod,[], varargin{:}),...
                'InputProcessing', eml_get_parameter_value(optarg.InputProcessing, [], varargin{:}),...
                'History', eml_get_parameter_value(optarg.History, [], varargin{:}),...
                'WindowLength', eml_get_parameter_value(optarg.WindowLength, [], varargin{:}));
        end
        
        function tunableData = parseInputsCodegenTunable(varargin)
            parms = idRecursivePolynomialModelEstimator.codegenHelperGetListOfNVPairs();
            
            popt = struct( ...
                'CaseSensitivity', false, ...
                'StructExpand',    true, ...
                'PartialMatching', false);
            
            % Specify the optional parameters and their defaults (Defaults
            % must be changed together with the ones in
            % parseInputsCodegen() method for consistency between ML/Codegen)
            optarg           = eml_parse_parameter_inputs(parms, popt, ...
                varargin{:});
            
            tunableData = struct( ...
                'ForgettingFactor', eml_get_parameter_value(optarg.ForgettingFactor, idRecursiveEstimator.cDefaultForgettingFactor, varargin{:}), ...
                'AdaptationGain', eml_get_parameter_value(optarg.AdaptationGain, idRecursiveEstimator.cDefaultAdaptationGain, varargin{:}), ...
                'NormalizationBias', eml_get_parameter_value(optarg.NormalizationBias, idRecursiveEstimator.cDefaultNormalizationBias, varargin{:}), ...
                'ProcessNoiseCovariance', eml_get_parameter_value(optarg.ProcessNoiseCovariance, idRecursiveEstimator.cDefaultProcessNoiseCovariance, varargin{:}),...
                'InitialParameterCovariance', eml_get_parameter_value(optarg.InitialParameterCovariance, idRecursiveEstimator.cDefaultInitialParameterCovariance, varargin{:}),...
                'EnableAdaptation', eml_get_parameter_value(optarg.EnableAdaptation, idRecursiveEstimator.cDefaultEnableAdaptation, varargin{:}),...
                'InitialA', eml_get_parameter_value(optarg.InitialA, [], varargin{:}), ...
                'InitialB', eml_get_parameter_value(optarg.InitialB, [], varargin{:}), ...
                'InitialC', eml_get_parameter_value(optarg.InitialC, [], varargin{:}), ...
                'InitialD', eml_get_parameter_value(optarg.InitialD, [], varargin{:}), ...
                'InitialF', eml_get_parameter_value(optarg.InitialF, [], varargin{:}), ...
                'InitialInputs', eml_get_parameter_value(optarg.InitialInputs, [], varargin{:}), ...
                'InitialOutputs', eml_get_parameter_value(optarg.InitialOutputs, [], varargin{:}));
        end        
        
        
        
        function [nontunableData,tunableData] = parseInputsSimulation(fcnName,varargin)
            % Instantiate an input parser
            parser = inputParser;
            parser.FunctionName = fcnName;
            
            % Specify the optional parameters and their defaults (Defaults
            % must be changed together with the ones in
            % parseInputsCodegen() method for consistency between ML/Codegen)
            idRecursiveEstimator.inputParserSimulationAddCommonParameters(parser);
            addParameter(parser,'InitialA', []);
            addParameter(parser,'InitialB', []);
            addParameter(parser,'InitialC', []);
            addParameter(parser,'InitialD', []);
            addParameter(parser,'InitialF', []);
            addParameter(parser,'InitialOutputs', []);
            addParameter(parser,'InitialInputs', []);
            
            % Parse parameters
            parse(parser, varargin{:});
            
            % Package the results in two categories, based on if the
            % corresponding property is tunable. This is for compatibility
            % with the codegen NV pair processing method. Codegen cannot
            % detect constant (nontunable) property values in a struct if
            % the struct also contains non-constant fields.
            results = parser.Results;
            nontunableData = struct( ...
                'DataType', results.DataType,...
                'EstimationMethod', results.EstimationMethod,...
                'InputProcessing', results.InputProcessing,...
                'History', results.History,...
                'WindowLength', results.WindowLength);
            tunableData = struct( ...
                'ForgettingFactor', results.ForgettingFactor,...
                'AdaptationGain', results.AdaptationGain, ...
                'NormalizationBias', results.NormalizationBias, ...
                'ProcessNoiseCovariance', results.ProcessNoiseCovariance, ...
                'InitialParameterCovariance', results.InitialParameterCovariance, ...
                'EnableAdaptation', results.EnableAdaptation, ...
                'InitialA', results.InitialA,...
                'InitialB', results.InitialB,...
                'InitialC', results.InitialC,...
                'InitialD', results.InitialD,...
                'InitialF', results.InitialF,...
                'InitialOutputs', results.InitialOutputs,...
                'InitialInputs', results.InitialInputs);
        end
    end
    
    methods
        function val = idpoly(this)
            % idpoly Convert to polynomial model
            %
            % sys = idpoly(obj) creates a linear time-invariant polynomial
            % model sys with model coefficients from obj. obj is one of
            % the following objects: recursiveAR, recursiveARMA,
            % recursiveARX, recursiveARMAX, recursiveOE, recursiveBJ. The
            % resulting sys is of class idpoly.
            if isLocked(this)
                val = idpoly(this.A,this.B,this.C,this.D,this.F);
            else
                val = idpoly(this.InitialA,...
                    this.InitialB,...
                    this.InitialC,...
                    this.InitialD,...
                    this.InitialF);
            end
            if ~isempty(this.ParameterCovariance_)
                nParameters = getNumberOfParameters(this);
                val = setcov(val, ...
                    struct('R', inv(this.ParameterCovariance_),...
                    'T', speye(nParameters),...
                    'Free', true(nParameters,1)));
            end
        end
    end
    
    methods % set methods
        function set.InitialA(this,val)
            na_ = this.na;
            if isempty(coder.target)
                try 
                    idRecursivePolynomialModelEstimator.validateA(val,'InitialA',na_);
                    validateParameterDataType(this,val,'InitialA');
                catch ME
                    throw(ME)
                end
            else
                idRecursivePolynomialModelEstimator.validateA(val,'InitialA',na_);
                validateParameterDataType(this,val,'InitialA');
            end
            this.InitialParameters_(1:na_) = val(2:end).';
        end
        
        function set.InitialB(this,val)
            % Strip the leading and trailing zeros from the user-provided
            % matrix for the B polynomial's initial coefficients. Construct
            % a containing the initial values of the estimated parameters.
            nb_ = this.nb;
            nInputs = idRecursivePolynomialModelEstimator.getNumberOfInputs(nb_);
            if ~nInputs
                % nothing to do if there are no inputs
                return;
            end
            
            % Validate the data type and data dimensions
            nk_ = this.nk;
            if isempty(coder.target)
                try 
                    idRecursivePolynomialModelEstimator.validateB(val,'InitialB',nb_,nk_);
                    validateParameterDataType(this,val,'InitialB');
                catch ME
                    throw(ME)
                end
            else
                idRecursivePolynomialModelEstimator.validateB(val,'InitialB',nb_,nk_);
                validateParameterDataType(this,val,'InitialB');
            end
            
            % Extract the parameters to be estimated and place in the
            % parameters vector
            this.InitialParameters_(this.na+(1:sum(nb_,2,'native'))) = ...
                idRecursivePolynomialModelEstimator.extractParametersFromB(val,nb_,nk_).';
        end
        
        function set.InitialC(this,val)
            nc_ = this.nc;
            if isempty(coder.target)
                try 
                    idRecursivePolynomialModelEstimator.validateC(val,'InitialC',nc_);
                    validateParameterDataType(this,val,'InitialC');
                catch ME
                    throw(ME)
                end
            else
                idRecursivePolynomialModelEstimator.validateC(val,'InitialC',nc_);
                validateParameterDataType(this,val,'InitialC');
            end
            this.InitialParameters_(this.na+sum(this.nb,2)+(1:nc_)) = val(2:end).';
        end
        
        function set.InitialD(this,val)
            nd_ = this.nd;
            if isempty(coder.target)
                try 
                    idRecursivePolynomialModelEstimator.validateD(val,'InitialD',nd_);
                    validateParameterDataType(this,val,'InitialD');
                catch ME
                    throw(ME)
                end
            else
                idRecursivePolynomialModelEstimator.validateD(val,'InitialD',nd_);
                validateParameterDataType(this,val,'InitialD');
            end
            % Put the extracted values in the parameters vector
            this.InitialParameters_(this.na+sum(this.nb,2,'native')+...
                this.nc+(1:nd_)) = val(2:end).';
        end
        
        function set.InitialF(this,val)
            nb_ = this.nb;
            nInputs = idRecursivePolynomialModelEstimator.getNumberOfInputs(nb_);
            if ~nInputs
                % nothing to do if there are no inputs
                return;
            end
            
            nf_ = this.nf;
            if isempty(coder.target)
                try 
                    idRecursivePolynomialModelEstimator.validateF(val,'InitialF',nInputs,nf_);
                    validateParameterDataType(this,val,'InitialF');
                catch ME
                    throw(ME)
                end
            else
                idRecursivePolynomialModelEstimator.validateF(val,'InitialF',nInputs,nf_);
                validateParameterDataType(this,val,'InitialF');
            end

            % Extract the parameters to be estimated and place in the
            % parameters vector
            this.InitialParameters_(this.na+sum(nb_,2,'native')+...
                this.nc+this.nd+(1:sum(nf_,2,'native'))) = ...
                idRecursivePolynomialModelEstimator.extractParametersFromF(val,nf_).';
        end
    end
    
    methods % get methods
        function val = get.InitialA(this)
            val = idRecursivePolynomialModelEstimator.getParametersA(this.na,this.InitialParameters_);
        end
        
        function val = get.A(this)
            if ~isempty(this.Parameters_)
                val = idRecursivePolynomialModelEstimator.getParametersA(this.na,this.Parameters_);
            else
                val = [];
            end
        end
        
        function val = get.InitialB(this)
            val = idRecursivePolynomialModelEstimator.getParametersB(...
                this.na,this.nb,this.nk,this.InitialParameters_);
        end
        
        function val = get.B(this)
            if ~isempty(this.Parameters_)
                val = idRecursivePolynomialModelEstimator.getParametersB(...
                    this.na,this.nb,this.nk,this.Parameters_);
            else
                val = [];
            end
        end
        
        function val = get.InitialC(this)
            val = idRecursivePolynomialModelEstimator.getParametersC(...
                this.na,this.nb,this.nc,this.InitialParameters_);
        end
        
        function val = get.C(this)
            if ~isempty(this.Parameters_)
                val = idRecursivePolynomialModelEstimator.getParametersC(...
                    this.na,this.nb,this.nc,this.Parameters_);
            else
                val = [];
            end
        end
        
        function val = get.InitialD(this)
            val = idRecursivePolynomialModelEstimator.getParametersD(...
                this.na,this.nb,this.nc,this.nd,this.InitialParameters_);
        end
        
        function val = get.D(this)
            if ~isempty(this.Parameters_)
                val = idRecursivePolynomialModelEstimator.getParametersD(...
                    this.na,this.nb,this.nc,this.nd,this.Parameters_);
            else
                val = [];
            end
        end
        
        function val = get.InitialF(this)
            val = idRecursivePolynomialModelEstimator.getParametersF(...
                this.na,this.nb,this.nc,this.nd,this.nf,this.InitialParameters_);
        end
        
        function val = get.F(this)
            if ~isempty(this.Parameters_)
                val = idRecursivePolynomialModelEstimator.getParametersF(...
                    this.na,this.nb,this.nc,this.nd,this.nf,this.Parameters_);
            else
                val = [];
            end
        end
    end
    
    methods(Static,Access=private)
        function val = staticGetNumberOfParameters(na,nb,nc,nd,nf)
            % Return # number of estimated parameters
            val = uint32(na) + ...
                uint32(sum(nb,2)) + ...
                uint32(nc) + ...
                uint32(nd) + ...
                uint32(sum(nf,2));
        end
    end
    
    methods(Hidden,Static)
        function thetaB = extractParametersFromB(B,nb,nk)
            % Get the free parameters from the rows of the B polynomial
            % coefficient matrix. The k-th row of B contains:
            % 1) nk(k) fixed number of leading zeros for the i-th input
            % 2) nb(k) free parameters
            % 3) Trailing zeros for padding such that the all B polynomials
            % can be stored in a matrix
            %
            % Get 2) for each input:
            thetaB = zeros(sum(nb,2),1,'like',B);
            idx = cast(1,'like',nb);
            for kk=1:size(nb,2)
                % thetaB(idx+(1:nb(kk)-1)) = B(kk,nk(kk)+(1+nb(kk)));
                % idx = idx + nb(kk);
                for kk2=1:nb(kk)
                    thetaB(idx) = B(kk,nk(kk)+kk2);
                    idx = idx+1;
                end
            end
        end
        
        function thetaF = extractParametersFromF(F,nf)
            % Get the free parameters from the rows of the F polynomial
            % coefficient matrix. The k-th row of F contains
            % 1) F(k,1)=1
            % 2) nf(k) free parameters
            % 3) Trailing zeros for padding such that the all F polynomials
            % can be stored in a matrix
            %
            % Get 2) for each input:
            thetaF = zeros(sum(nf,2),1,'like',F);
            idx = cast(1,'like',nf);
            for kk=1:size(nf,2)
                % thetaF(idx+(0:nf(kk)-1)) = F(kk,2:nf(kk)+1);
                % dx = idx + nf(kk);
                for kk2=2:nf(kk)+1
                    thetaF(idx) = F(kk,kk2);
                    idx = idx+1;
                end
            end
        end
        
        function val = getNumberOfInputs(nb)
            % Calculate # of inputs in the estimated model based on nb
            if isscalar(nb) && nb(1)==0
                val = uint16(0); % time-series
            else
                val = uint16(size(nb,2));
            end
        end
        
        function val = getNumberOfOutputs(nb)
            % Calculate # of outputs in the estimated model based on nb
            val = uint16(size(nb,1));
        end        
        
        function A = getParametersA(na,allParams)
            coder.internal.prefer_const(na);            
            
            A = zeros(1,na+1,'like',allParams);
            A(1) = 1;
            if na>0
                A(2:na+1) = allParams(1:na);
            end
        end
        
        function B = getParametersB(na,nb,nk,allParams)
            coder.internal.prefer_const(na);
            coder.internal.prefer_const(nb);
            coder.internal.prefer_const(nk);
            
            nInputs = idRecursivePolynomialModelEstimator.getNumberOfInputs(nb);
            if nInputs>0
                B = zeros(nInputs,max(nb+nk),'like',allParams);
                cPos = na+1; % Current position (of param b0) in allParams
                for kk=1:nInputs % kk is input index
                    % Coder prefers the double for-loop for MISO: Cannot
                    % determine how to process the assignment
                    % B(kk,nk(kk)+1:nk(kk)+nb(kk)) = allParams(cPos:cPos+nb(kk)-1);
                    % cPos = cPos + nb(kk);
                    for kk2=1:nb(kk)
                        B(kk,nk(kk)+kk2) = allParams(cPos);
                        cPos = cPos + 1;
                    end
                end
            else
                B = cast([],'like',allParams);
            end
        end
        
        function C = getParametersC(na,nb,nc,allParams)
            coder.internal.prefer_const(na);
            coder.internal.prefer_const(nb);
            coder.internal.prefer_const(nc);            
            
            C = zeros(1,nc+1,'like',allParams);
            C(1) = 1;
            if nc>0
                c1PosInAllParams = na+sum(nb,2)+1;
                C(2:nc+1) = allParams(c1PosInAllParams:c1PosInAllParams+nc-1);
            end
        end
        
        function D = getParametersD(na,nb,nc,nd,allParams)
            coder.internal.prefer_const(na);
            coder.internal.prefer_const(nb);
            coder.internal.prefer_const(nc);
            coder.internal.prefer_const(nd);            
            
            D = zeros(1,nd+1,'like',allParams);
            D(1) = 1;
            if nd>0
                d1PosInAllParams = na+sum(nb,2)+nc+1;
                D(2:nd+1) = allParams(d1PosInAllParams:d1PosInAllParams+nd-1);
            end
        end
        
        function F = getParametersF(na,nb,nc,nd,nf,allParams)
            coder.internal.prefer_const(na);
            coder.internal.prefer_const(nb);
            coder.internal.prefer_const(nc);
            coder.internal.prefer_const(nd);
            coder.internal.prefer_const(nf);
            
            nInputs = idRecursivePolynomialModelEstimator.getNumberOfInputs(nb);
            % Edge case compatibility with idpoly:
            % if nInputs>0 -> nf=0, F=1
            % if nInputs==0 -> B=[], F=[]
            if nInputs>0
                F = zeros(nInputs,max(nf+1),'like',allParams);
                F(:,1) = 1;
                cPos = na+sum(nb,2)+nc+nd+1;  % Current position (of param f1) in allParams
                for kk=1:nInputs % kk is input index
                    % Coder prefers the double for-loop for MISO: Cannot
                    % determine how to process : operator
                    % F(kk,2:nf(kk)+1) = allParams(cPos+(0:nf(kk)-1));
                    % cPos = cPos + nf(kk);
                    for kk2=1:nf(kk)
                        F(kk,1+kk2) = allParams(cPos);
                        cPos = cPos + 1;
                    end
                end
            else
                F = cast([],'like',allParams);
            end
        end
        
        function ACD = defaultACD(nacd)
            ACD = [1 eps*ones(1,nacd)];
        end
        
        function B = defaultB(nb,nk)            
            nInputs = idRecursivePolynomialModelEstimator.getNumberOfInputs(nb);
            if nInputs>0
                B = zeros(nInputs,max(nb+nk));
                for kk=1:nInputs
                    % MATLAB Coder cannot process : operator when nb is a vector
                    % B(kk,nk(kk)+(1:nb(kk))) = eps;
                    for kk2=1:nb(kk)
                        B(kk,nk(kk)+kk2) = eps;
                    end
                end
            else
                B = [];
            end
        end
        
        function F = defaultF(nf,nInputs)
            if nInputs>0
                F = zeros(nInputs,max(nf+1));
                F(:,1) = 1;
                for kk=1:nInputs
                    % MATLAB Coder cannot process : operator when nf is a vector
                    % F(kk,2:nf(kk)+1) = eps;
                    for kk2=2:nf(kk)+1
                        F(kk,kk2) = eps;
                    end
                end
            else
               F = [];
            end
        end
        
        function validateA(A,propName,na)
            if nargin<=2
                validateattributes(A,{'numeric'},{'nonempty','real','finite','row'},'',propName);
            else
                % Also validate dimensions against na, if it's specified
                validateattributes(A,{'numeric'},{'numel',na+1,'nonempty','real','finite','row'},'',propName);
            end
            % The first element must be 1
            coder.internal.errorIf(A(1)~=1,...
                'Ident:simulink:recursiveEstimationPolyALeadingCoeffNot1');
        end
        
        function validateB(B,propName,nb,nk)
            % Dimension consistency checks between nb, nf, nk are done in
            % validateOrdersPostSet method. This ensures dimensions of B
            % and F are consistent.
            nInputs = idRecursivePolynomialModelEstimator.getNumberOfInputs(nb);
            if nInputs==0
                % TBD - Time-series model: B must be 0 or []
                coder.internal.assert(isempty(B) || (isscalar(B)&&B==0),...
                    'Ident:simulink:recursiveEstimationInitialBDimsInvalidTimeSeries');
                return;
            end
            
            if nargin<=2
                validateattributes(B,{'numeric'},{'2d','nonempty','real','finite'},'',propName);
            else
                % Validate dimensions of B against with nb, nk
                validateattributes(B,{'numeric'},{'size',[nInputs,max(nb+nk)],'nonempty','real','finite'},'',propName);
                % Validate first nk(rowIdx) elements in row 'rowIdx' is 0
                for inputIdx=1:nInputs
                    coder.internal.assert( all(B(inputIdx,1:nk(inputIdx))==0),...
                        'Ident:simulink:recursiveEstimationInitialBLeadingZerosInvalid',...
                        inputIdx,nk(inputIdx),inputIdx,nk(inputIdx));
                end
            end
        end
        
        function validateC(C,propName,nc)
            if nargin<=2
                validateattributes(C,{'numeric'},{'nonempty','real','finite','row'},'',propName);
            else
                % Validate dimensions against nc, if it's specified
                validateattributes(C,{'numeric'},{'numel',nc+1,'nonempty','real','finite','row'},'',propName);
            end
            % The first element must be 1
            coder.internal.errorIf(C(1)~=1,...
                'Ident:simulink:recursiveEstimationPolyCLeadingCoeffNot1');
            % C must be stable at all times
            coder.internal.assert(idRecursiveEstimationCheckBistritzCondition(C),...
                'Ident:simulink:recursiveEstimationPolyCUnstable');
        end
        
        function validateD(D,propName,nd)
            if nargin<=2
                validateattributes(D,{'numeric'},{'nonempty','real','finite','row'},'',propName);
            else
                % Validate dimensions against nd, if it's specified
                validateattributes(D,{'numeric'},{'numel',nd+1,'nonempty','real','finite','row'},'',propName);
            end
            % The first element must be 1
            coder.internal.errorIf(D(1)~=1,...
                'Ident:simulink:recursiveEstimationPolyDLeadingCoeffNot1');
            % D must be stable at all times
            coder.internal.assert(idRecursiveEstimationCheckBistritzCondition(D),...
                'Ident:simulink:recursiveEstimationPolyDUnstable');
        end
        
        function validateF(F,propName,nInputs,nf)
            % Dimension consistency checks between nb, nf, nk are done in
            % validateOrdersPostSet method. This ensures dimensions of B
            % and F are consistent.
            if nInputs==0
                % Time-series model: F must be 1 or []
                coder.internal.assert(isempty(F) || (isscalar(F)&&F==0),...
                    'Ident:simulink:recursiveEstimationInitialFDimsInvalidTimeSeries');
                return;
            end
            
            % IO model
            if nargin<=3
                validateattributes(F,{'numeric'},{'2d','nonempty','real','finite'},'',propName);
            else
                % Validate dimensions of F against with nf
                validateattributes(F,{'numeric'},{'size',[nInputs max(nf)+1],'nonempty','real','finite'},'',propName);
            end
            % First elements must be 1
            coder.internal.assert(all(F(:,1)==1),...
                'Ident:simulink:recursiveEstimationPolyFLeadingCoeffNot1');
            % All F must be stable at all times
            for inputIdx=1:nInputs
                coder.internal.assert(idRecursiveEstimationCheckBistritzCondition(F(inputIdx,:)),...
                    'Ident:simulink:recursiveEstimationPolyFUnstable');
            end
        end
        
        function validateOrdersPostSet(na,nb,nc,nd,nf,nk)
            % Validate the Orders property after it was set. This method
            % performs the composite checks between na, nb, nc, nd, nf, nk
            
            % Total number of estimated parameters must be >0 and <2^16-1
            numParams = idRecursivePolynomialModelEstimator.staticGetNumberOfParameters(na,nb,nc,nd,nf);
            coder.internal.assert(numParams>0,...
                'Ident:simulink:recursiveEstimationNumberOfParameters0');
            coder.internal.errorIf(numParams>controllib.internal.blocks.rls.unsignedIntegerBound,...
                'Ident:simulink:recursiveEstimationNumberOfParametersTooBig');
            
            % Dimensions of nb, nf, nk must match (Assume model is not MIMO)
            numelNb = numel(nb);
            coder.internal.assert(numelNb==numel(nk),...
                'Ident:simulink:recursiveEstimationNBNKDimMismatch');
            coder.internal.assert(numelNb==numel(nf),...
                'Ident:simulink:recursiveEstimationNBNFDimMismatch');
            
            % if nb is 0 for an input, nk and nf must be 0 as well
            for kk=1:numelNb
                coder.internal.errorIf(nb(kk)==0 && nk(kk)~=0,...
                    'Ident:simulink:recursiveEstimationNKNotZeroWhenNBIs');
                coder.internal.errorIf(nb(kk)==0 && nf(kk)~=0,...
                    'Ident:simulink:recursiveEstimationNFNotZeroWhenNBIs');
            end
        end
    end
    
    methods(Hidden)
        % Methods for internal testing
        function s = getProtectedProperties(this)
            % Get properties that has protected or private GetAccess
            m = metaclass(this);
            s = struct();
            for kk=1:numel(m.PropertyList)
                if ~strcmp(m.PropertyList(kk).GetAccess,'public')
                    s.(m.PropertyList(kk).Name) = this.(m.PropertyList(kk).Name);
                end
            end
        end
    end
end

% LocalWords:  MISO nontunable polynomial's na nb nk ARMA nc nd
