function [this,d,c,x,u,e,f,range] = estimate(this,varargin)
% estimate  Estimate FAVAR using static principal components.
%
% Syntax
% =======
%
%     [A,D,CC,F,U,E,CTF] = estimate(A,D,LIST,RANGE,[R,Q],...)
%     [A,X,CC,F,U,E,CTF] = estimate(A,X,RANGE,[R,Q],...)
%
% Input arguments
% ================
%
% * `A` [ FAVAR ] - Empty FAVAR object.
%
% * `D` [ struct ] - Input database.
%
% * `LIST` [ cellstr ] - List of series from the database on which the VAR
% will be estimated.
%
% * `X` [ tseries ] - Tseries objects with input data.
%
% * `RANGE` [ numeric ] - Estimation range.
%
% * `R` [ numeric ] - Selection criterion for the number of factors:
% Minimum requested roportion of input data volatility explained by the
% factors.
%
% * `Q` [ numeric ] - Selection criterion for the number of factors:
% Maximum number of factors.
%
% Output arguments
% =================
%
% * `A` [ FAVAR ] - Estimated FAVAR object.
%
% * `D` [ struct ] - Output database.
%
% * `X` [ tseries ] - Output tseries object.
%
% * `CC` [ tseries ] - Estimates of common components in the FAVAR
% observables.
%
% * `F` [ tseries ] - Estimates of factors.
%
% * `U` [ struct | tseries ] - Idiosyncratic residuals.
%
% * `E` [ tseries ] - Factor VAR residuals.
%
% * `CTF` [ tseries ] - Contributions of individual input series to the
% estimated factors.
%
% Options
% ========
%
% * `'cross='` [ *true* | false | numeric ] - Keep off-diagonal elements in
% the covariance matrix of idiosyncratic residuals; if false all
% cross-covariances are reset to zero; if a number between zero and one,
% all cross-covariances are multiplied by that number.
%
% * `'order='` [ numeric | *1* ] - Order of the VAR for factors.
%
% * `'output='` [ *'auto'* | 'dbase' | 'tseries' ] - Format of output data.
%
% * `'rank='` [ numeric | *`Inf`* ] - Restriction on the rank of the factor
% VAR residuals.
%
% Description
% ============
%
% Example
% ========
% -IRIS Toolbox.
% -Copyright (c) 2007-2012 Jaromir Benes.

%**************************************************************************

% Process input data.
[flag,y,range,ynames,inputformat,varargin] = ...
    VAR.inputdata(this,varargin{:});

% Invalid input data.
if ~flag
    FAVAR.error(5);
end

if isequal(inputformat,'dbase')
    this.ynames = ynames;
end
this.range = range;

% Parse required input arguments.
crit = varargin{1};
varargin(1) = [];

% Parse and validate options.
options = passvalopt('FAVAR.estimate',varargin{:});

% Determine format of output data.
if strcmpi(options.output,'auto')
    outputformat = inputformat;
else
    outputformat = options.output;
end

%**************************************************************************

% Standardise input data.
y0 = y;
[this,y] = standardise(this,y);

% Estimate static factors using principal components.
[x,this.C,u,this.Sigma,this.singval,sample,f] = ...
    FAVAR.pc(y,crit,options.method);

% Estimate VAR(p,q) on factors.
[this.A,this.B,this.Omega,this.T,this.U,e,this.fitted] = ...
    FAVAR.estimatevar(x,options.order,options.rank);
this.eigval = ordeig(this.T);

% Reduce or zero off-diagonal elements in the cov matrix of idiosyncratic
% residuals if requested.
this.cross = +options.cross;
if this.cross < 1
    index = logical(eye(size(this.Sigma)));
    this.Sigma(~index) = this.cross*this.Sigma(~index);
end

if nargout > 1
    ynames = get(this,'ynames');
    d = VAR.outputdata(this,outputformat,range,y0,[],ynames);
end

if nargout > 2
    % Common components.
    c = FAVAR.cc(this.C,x);
    c = FAVAR.destandardise(this.Mean,this.Std,c);
    c = VAR.outputdata(this,outputformat,range,c,[],ynames);
end

if nargout > 3
    % Factors.
    template = tseries();
    x = replace(template,permute(x,[2,1,3]),range(1));
end

if nargout > 4
    % Idiosyncratic residuals.
    u = FAVAR.destandardise(0,this.Std,u);
    u = VAR.outputdata(this,outputformat,range,u,[],ynames);
end

if nargout > 5
    % Residuals from the factor VAR.
    e = replace(template,permute(e,[2,1,3]),range(1));
end

if nargout > 6
    % Contributions to the factors.
    f = replace(template,permute(f,[2,1,3]),range(1));
end

if nargout > 7
    range = range(sample);
end

end
