% LINLSQSVD Linear least squares with singular value decomposition
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% DESCRIPTION: Linear least square problem with singular value
% decomposition
%
% ALGORITHM: % It solves the problem
%
%        Y = HX
%
% where X are the parameters, Y the measurements, and H the linear
% equations relating the two.
% It is able to perform linear identification of the parameters of a
% multichannel systems. The results of different experiments on the same
% system can be passed as input. The algorithm, thanks to the singular
% value decomposition, extract the maximum amount of information from each
% single channel and for each experiment. Total information is then
% combined to get the final result.
%            
% CALL: [a,Ca,Corra,Vu,bu,Cbu,Fbu,mse,dof,ppm] = utils.math.linlsqsvd(H1,...,HN,Y);
%       [a,Ca,Corra,Vu,bu,Cbu,Fbu,mse,dof,ppm] = utils.math.linlsqsvd(H1,...,HN,Y,errthres);
%       [a,Ca,Corra,Vu,bu,Cbu,Fbu,mse,dof,ppm] = utils.math.linlsqsvd(H1,...,HN,Y,errthres,knwpars);
% 
% If the experiment is 1 then H1,...,HN and Y are aos.
% If the experiments are M, then H1,...,HN and Y are Mx1 matrix objects
% with the aos relating to the given experiment in the proper position.
% 
% INPUT:
%               - Hi represent the columns of H
%               - Y represent the measurement set
%               - sThreshold it's a threshold for singular values. It is a
%               number, typically 1. It will remove singular values larger
%               than sThreshold which corresponds to removing svd parameters estimated
%               with an error larger than sThreshold.
%               - knwpars A struct array with the fields:
%                   pos - a number indicating the corresponding position of
%                     the parameter (corresponding column of H)
%                   value - the value for the parameter
%                   err - the uncertainty associated to the parameter
% 
% OUTPUT:
%   a:      params values
%   Ca:     fit covariance matrix for A
%   Corra:  fit correlation matrix for A
%   Vu:     is the complete conversion matrix
%   Cbu:    is the new variables covariance matrix
%   Fbu:    is the information matrix for the new variable
%   mse:    is the fit Mean Square Error
%   dof:    degrees of freedom for the global estimation
%   ppm:    number of svd parameters per measurements, provides also the
%   number of independent combinations of parameters per each singular
%   measurement. The coefficients of the combinations are then stored in Vu
% 
% 09-11-2010 L Ferraioli
%       CREATION
%
% <a href="matlab:utils.helper.displayMethodInfo('matrix', 'linfitsvd')">Parameter Sets</a>
%
% VERSION:     $Id: linlsqsvd.m,v 1.2 2011/03/11 09:28:26 luigi Exp $
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

function  [a,Ca,Corra,Vu,bu,Cbu,Fbu,mse,dof,ppm] = linlsqsvd(varargin)
  
  
  
  %%% get input params
  if isstruct(varargin{end})
    kwnpars   = varargin{end};
    if isnumeric(varargin{end-1})
      sThreshold = varargin{end-1};
      A = varargin{1:end-2};
    else
      A = varargin{1:end-1};
      sThreshold = [];
    end
  else
    kwnpars = [];
    if isnumeric(varargin{end})
      sThreshold = varargin{end};
      A = varargin{1:end-1};
    else
      A = varargin{:};
      sThreshold = [];
    end
  end
  
 
  %%% sort between one or multiple experiments
  exps = struct;
  
  if isa(A(1),'ao') % one experiment
    % Build matrices for lscov
    C = A(1:end-1);
    Y = A(end);

    H = C(:).y;
    y = Y.y;
    exps.fitbasis = H;
    exps.fitdata = y;
  elseif isa(A(1),'matrix') % multiple experiments
    % run over input objects and experiments
    for jj=1:numel(A(1).objs)
      C = [];
      for ii=1:numel(A)-1
        D = A(ii).objs(jj).y;
        % willing to work with columns
        if size(D,1)<size(D,2)
          D = D.';
        end
        C = [C D];
      end
      y = A(end).objs(jj).y;
      exps(jj).fitbasis = C;
      exps(jj).fitdata = y;
    end
  else
    error('Unknown input data type!')
  end
  
  %%% do fit
  if ~isempty(kwnpars) && isfield(kwnpars,'pos')
    if ~isempty(sThreshold)
      [a,Ca,Corra,Vu,bu,Cbu,Fbu,mse,dof,ppm] = utils.math.linfitsvd(exps,kwnpars,sThreshold);
    else
      [a,Ca,Corra,Vu,bu,Cbu,Fbu,mse,dof,ppm] = utils.math.linfitsvd(exps,kwnpars);
    end
  else
    if ~isempty(sThreshold)
      [a,Ca,Corra,Vu,bu,Cbu,Fbu,mse,dof,ppm] = utils.math.linfitsvd(exps,sThreshold);
    else
      [a,Ca,Corra,Vu,bu,Cbu,Fbu,mse,dof,ppm] = utils.math.linfitsvd(exps);
    end
  end
  
  
  
  
end
    
