function [models, info] = slproglearn(source, getter, learnfunctor, varargin)
%SLPROGLEARN Performs Progressive Learning from sample source
%
% $ Syntax $
%   - models = slproglearn(source, getter, learnfunctor, ...)
%   - [models, info] = slproglearn(source, getter, learnfunctor, ...)   
%
% $ Arguments $
%   - source:       the source from which the data are fetched
%   - getter:       the functor to fetch data from source, in the form:
%                   data= f(source, ...)
%   - learnfunctor: the functor to construct/update models from data,
%                   it is given in the following form:
%                   models = f(models, data, ...)
%                   If the learning is recorded, it is like:
%                   [models, record] = f(models, data, ...)
%                   On initial construction, the input models is [].
%   - models:       The constructed models
%   
% $ Description $
%   - models = slproglearn(source, getter, learnfunctor, ...) 
%     construct models based on source, from which the samples are
%     fetched. It is assumed that the source can continuously offer
%     infinite number of samples. The construction is controlled by 
%     the following properties.
%     \*
%     \t   Table. Properties of Progressive Learning
%     \h       name        &            description             \\
%          'isrecorded'    &  Whether each iteration is recorded 
%                             (default = false)                 \\
%          'gtuner'        &  The functor to tune the getter, in the form:
%                             getter = f(getter, models, ...)   
%                             (default = {})                      \\
%          'cmpfunctor'    &  The functor to compare two set of models
%                             and judge whether discrepancies meet the
%                             criteria of convergence.
%                             It is given in the following form:
%                             isconverged = f(models1, models2, ...)
%                             This properties must be specified.     \\
%          'iter'          &  The iteration control properties for
%                             sliterproc, default = {}.
%          'verbose'       &  whether to show progress information 
%                             (default = true)                       \\
%          'initmodels'    &  The initial models, (default = [])
%     \*
%
%   - [models, info] = slproglearn(source, getter, learnfunctor, ...) also
%     returns the information of iterative process.
%
% $ History $
%   - Created by Dahua Lin, on Aug 31, 2006
%

%% Parse and verify input arguments

if nargin < 3
    raise_lackinput('slproglearn', 3);
end

opts.isrecorded = false;
opts.gtuner = [];
opts.cmpfunctor = [];
opts.iter = {};
opts.verbose = true;
opts.initmodels = [];
opts = slparseprops(opts, varargin{:});

if isempty(opts.cmpfunctor)
    error('sltoolbox:invalidarg', ...
        'You should specify a models comparison functor');
end


%% Main Skeleton

slsharedisp_attach('slproglearn', 'show', opts.verbose);

slsharedisp(opts, 'Progressive Learning from source');

objects = {source, opts.initmodels, getter};
iterfunctor = {@proglearn_iter, learnfunctor, opts};
cmpfunctor = {@proglearn_cmp, opts};
if nargout < 2
    objects = ...
        sliterproc(objects, iterfunctor, cmpfunctor, opts.isrecorded, opts.iter{:});
else
    [objects, info] = ...
        sliterproc(objects, iterfunctor, cmpfunctor, opts.isrecorded, opts.iter{:});
end

models = objects{2};

slsharedisp_detach();



%% Core Iteration function

function varargout = proglearn_iter(objects, learnfunctor, opts)
% objects = {source, models, getter}

% take input
source = objects{1};
models = objects{2};
getter = objects{3};

% fetch data
data = slevalfunctor(getter, source);

% learn models
if ~opts.isrecorded
    models = slevalfunctor(learnfunctor, models, data);
else
    [models, rec] = slevalfunctor(learnfunctor, models, data);
end

% tune getter
if ~isempty(opts.gtuner)
    getter = slevalfunctor(opts.gtuner, getter, models);
end

% make output
objects = {source, models, getter};
if ~opts.isrecorded
    varargout = {objects};
else
    varargout = {objects, rec};
end


function isconverged = proglearn_cmp(objects_prev, objects, opts)

models_prev = objects_prev{2};
models = objects{2};

isconverged = slevalfunctor(opts.cmpfunctor, models_prev, models);






