function [OBJ,V,F,PE,DELTA,PDELTA,VOID,PRED,SMOOTH] = loglik(THIS,data,range,varargin)
% loglik  Evaluate minus the log-likelihood function in time or frequency domain.
%
% Full syntax
% ============
%
%     [OBJ,V,F,PE,DELTA,PDELTA] = loglik(M,D,RANGE,...)
%     [OBJ,V,F,PE,DELTA,PDELTA,~,PRED,SMOOTH] = loglik(M,D,RANGE,...)
%
% Syntax for fast one-off likelihood evaluation
% ==============================================
%
%     OBJ = loglik(M,D,RANGE,...)
%
% Syntax for repeated fast likelihood evaluations
% ================================================
%
%     % Initialise
%     loglik(M,D,RANGE,...,'persist=',true);
%
%     % Fast loglik for different parameter sets
%     M... = ...; % Change parameters.
%     L = loglik(M); % Evaluate likelihood.
%     ...
%     M... = ...; % Change parameters.
%     L = loglik(M); % Evaluated likelihood
%     ...
%     % etc.
%
% Input arguments
% ================
%
% * `M` [ model ] - Model object on which the likelihood of the input data
% will be evaluated.
%
% * `D` [ struct | cell ] - Input database or datapack from which the
% measurement variables will be taken.
%
% * `RANGE` [ numeric ] - Date range.
%
% Output arguments
% =================
%
% Test title
% -----------
%
% * `OBJ` [ numeric ] - Value of minus the log-likelihood function (or
% other objective function if specified in options).
%
% * `V` [ numeric ] - Estimated variance scale factor if the `'relative='`
% options is true; otherwise `V` is 1.
%
% * `F` [ numeric ] - Sequence of forecast MSE matrices for
% measurement variables.
%
% * `PE` [ struct ] - Database with prediction errors for measurement
% variables; exp of prediction errors for measurement variables declared as
% log-variables.
%
% * `DELTA` [ struct ] - Estimates of deterministic trend
% parameters specified in through the `'outoflik='` option.
%
% * `PDELTA` [ numeric ] - MSE matrix of the estimates of the `'outoflik='`
% parameters.
%
% * `PRED` [ struct | cell ] - Output database or datapack with the Kalman
% predictor data.
%
% * `SMOOTH` [ struct | cell ] - Output database or datapack with the
% Kalman smoother data.
%
% Options
% ========
%
% * `'persist='` [ `true` | *`false`* ] -- Pre-process and store the overhead
% (data and options) for subsequent fast calls.
%
% See help on [`model/filter`](model/filter) for other options available.
%
% Description
% ============
%
% The number of output arguments you request when calling `loglik` affects
% computational efficiency. Running the function with only the first output
% argument, i.e. the value of the likelihood function (minus the log of it,
% in fact), results in the fastest performance.
%
% Not specifying the last two output arguments,
% `PRED` and `SMOOTH`, if you really don't need them will speed up the
% function significantly because the prediction data will not have to be
% stored, and the smoother data will not be calculated at all.
%
% The `loglik` function returns identical results as the
% [`model/filter`](model/filter) function, only the output arguments are
% arranged in different ways.
%
% Example
% ========
%

% -IRIS Toolbox.
% -Copyright (c) 2007-2012 Jaromir Benes.

persistent DATA RANGE OPT LOGLIKOPT MLLFUNC Y ISTIMEDOMAIN;

% If loglik(m) is called without any further input arguments, the ones
% passed in last will be used.
if nargin == 1
    if isempty(DATA)
        utils.error('model', ...
            ['You must first initialise LOGLIK before ', ...
            'running it in fast mode with one input argument.']);
    end
else
    tune = [];
    if ~isempty(varargin) ...
            && (isstruct(varargin{1}) || isempty(varargin{1}))
        tune = varargin{1};
        varargin(1) = [];
    end
    RANGE = range;
    % Process `loglik` options.
    [OPT,varargin] = passvalopt('model.loglik',varargin{:});
    % Process `mykalman` and `myfdlik` options and initialise output data
    % handles.
    [LOGLIKOPT,THIS,MLLFUNC] = ...
        mypreloglik(THIS,RANGE,OPT.domain,tune,varargin{:});
    ISTIMEDOMAIN = strncmpi(LOGLIKOPT.domain,'t',1);
    % Get array of measurement variables.
    DATA = datarequest('y',THIS,data,RANGE,':',LOGLIKOPT);
    NARGOUT = nargout;
    Y = struct();
    dorequestoutp();
end

VOID = [];

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

[OBJ,V,delta,Y] = MLLFUNC(THIS,DATA,Y,LOGLIKOPT);
xrange = RANGE(1)-1 : RANGE(end);
[~,F,PE,PDELTA,PRED,SMOOTH] = mykalmanoutput(THIS,Y,xrange);

DELTA = struct();
if ~isempty(LOGLIKOPT.outoflik)
    for i = 1 : length(LOGLIKOPT.outoflik)
        namepos = LOGLIKOPT.outoflik(i);
        name = THIS.name{namepos};
        DELTA.(name) = permute(delta(1,i,:),[1,3,2]);
    end
end

if ~OPT.persist
    DATA = [];
    RANGE = [];
    OPT = [];
    LOGLIKOPT = [];
    Y = [];
end

% Nested functions.

%**************************************************************************
    function dorequestoutp()
        if NARGOUT >= 3 && ISTIMEDOMAIN
            Y.F = [];
        end
        if NARGOUT >= 4 && ISTIMEDOMAIN
            Y.pe = [];
        end
        if NARGOUT >= 6
            Y.Pdelta = [];
        end
        if NARGOUT >= 8 && ISTIMEDOMAIN
            Y.predmean = [];
            if ~LOGLIKOPT.meanonly && LOGLIKOPT.returnstd
                Y.predstd = [];
            end
            if ~LOGLIKOPT.meanonly && LOGLIKOPT.returnmse
                Y.predmse = [];
            end
        end
        if NARGOUT >= 9 && ISTIMEDOMAIN
            Y.smoothmean = [];
            if ~LOGLIKOPT.meanonly && LOGLIKOPT.returnstd
                Y.smoothstd = [];
            end
            if ~LOGLIKOPT.meanonly && LOGLIKOPT.returnmse
                Y.smoothmse = [];
            end
        end        
    end
% dorequestoutp().

end