% PARFRAC partial fraction representation of a transfer function.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% DESCRIPTION: PARFRAC partial fraction representation of a transfer function.
%
%                 R(1)       R(2)             R(n)
%      H(s)  =  -------- + -------- + ... + -------- + K(s)
%               s - P(1)   s - P(2)         s - P(n)
%
% SUPER CLASSES: ltpda_tf < ltpda_uoh < ltpda_uo < ltpda_obj
%
% CONSTRUCTOR:
%
%       r = parfrac()                    - creates an empty parfrac object
%       r = parfrac(res, poles, dir)     - construct from residuals, poles
%                                          and direct terms
%       r = parfrac(..., 'name')         - construct including name
%       r = parfrac(..., iunits, ounits) - include input and output units
%       r = parfrac(pl)                  - create a parfrac object from the
%                                          description given in the parameter list.
%       r = parfrac(pzm)                 - create a parfrac from a pzmodel.
%       r = parfrac(rat)                 - create a parfrac from a rational TF.
%
%
% The poles can be specified in a array or a cell as a real or complex number.
%
% Example:  r = parfrac([1 2+1i 2-1i], [6 1+3i 1-3i], []);
%
% <a href="matlab:utils.helper.displayMethodInfo('parfrac', 'parfrac')">Parameters Description</a>
%
% VERSION:     $Id: parfrac.m,v 1.57 2011/10/25 11:50:55 luigi Exp $
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

classdef parfrac < ltpda_tf
  
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  %                            Property definition                            %
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  
  %---------- Public (read/write) Properties  ----------
  properties
  end
  
  %---------- Protected read-only Properties ----------
  properties (SetAccess = protected)
    res   = []; % residuals [R]
    poles = []; % poles (real or complex numbers) [P]
    pmul = []; % Represents the pole multiplicity
    dir   = 0; % direct terms [K]
  end
  
  %---------- Private Properties ----------
  properties (GetAccess = protected, SetAccess = protected)
  end
  
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  %                          Check property setting                           %
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  
  methods
    function set.res(obj, val)
      if ~isnumeric(val) && ~isempty(val)
        error('### The value for the property ''res'' must be a numeric array.');
      end
      if size(val,1) == 1
        val = val.';
      end
      obj.res = val;
    end
    function set.poles(obj, val)
      if ~(isnumeric(val) || iscell(val)) && ~isempty(val)
        error('### The value for the property ''poles'' must be a numeric array.');
      end
      if size(val,1) == 1
        val = val.';
      end
      obj.poles = val;
    end
    function set.dir(obj, val)
      if ~isnumeric(val) && ~isempty(val)
        error('### The value for the property ''dir'' must be a numeric array.');
      end
      if isempty(val)
        obj.dir = val;
      end      
      if size(val,1) == 1
        val = val.';
      end
      obj.dir = val;
    end
    function set.pmul(obj, val)
      if ~isnumeric(val) && ~isempty(val)
        error('### The value for the property ''pmul'' must be a numeric array.');
      end
      if size(val,1) == 1
        val = val.';
      end
      obj.pmul = val;
    end
  end
  
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  %                                Constructor                                %
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  
  methods
    function obj = parfrac(varargin)
      
      import utils.const.*
      utils.helper.msg(msg.OMNAME, 'running %s/%s', mfilename('class'), mfilename);
      
      % Collect all parfract objects
      [parfracs, invars, rest] = utils.helper.collect_objects(varargin(:), 'parfrac');
      
      if isempty(rest) && ~isempty(parfracs)
        % Do copy constructor and return
        utils.helper.msg(msg.OPROC1, 'copy constructor');
        obj = copy(parfracs, 1);
        for kk=1:numel(obj)
          obj(kk).addHistory(parfrac.getInfo('parfrac', 'None'), [], [], obj(kk).hist);
        end
        return
      end
      
      switch nargin
        case 0
          %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
          %%%%%%%%%%%% Zero inputs %%%%%%%%%%%%%%%
          %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
          utils.helper.msg(msg.OPROC1, 'empty constructor');
          obj.addHistory(parfrac.getInfo('parfrac', 'None'), plist(), [], []);
          
        case 1
          %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
          %%%%%%%%%%%% One inputs %%%%%%%%%%%%%%%
          %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
          
          if ischar(varargin{1})
            %%%%%%%%%%   pzm = parfract('foo.mat')   %%%%%%%%%%
            %%%%%%%%%%   pzm = parfract('foo.xml')   %%%%%%%%%%
            utils.helper.msg(msg.OPROC1, 'constructing from file %s', varargin{1});
            obj = fromFile(obj, varargin{1});
            
          elseif isstruct(varargin{1})
            %%%%%%%%%%   r = parfrac(struct)   %%%%%%%%%%
            utils.helper.msg(msg.OPROC1, 'constructing from struct');
            obj = fromStruct(obj, varargin{1});
            
          elseif isa(varargin{1}, 'rational')
            %%%%%%%%%%   r = parfrac(rational-object)   %%%%%%%%%%
            utils.helper.msg(msg.OPROC1, 'constructing from rational');
            obj = fromRational(obj, plist('rational', varargin{1}));
            
          elseif isa(varargin{1}, 'pzmodel')
            %%%%%%%%%%   r = parfrac(pzmodel-object)   %%%%%%%%%%
            utils.helper.msg(msg.OPROC1, 'constructing from pzmodel');
            obj = fromPzmodel(obj, plist('pzmodel', varargin{1}));
            
          elseif isa(varargin{1}, 'plist')
            %%%%%%%%%%   r = parfrac(plist)   %%%%%%%%%%
            pl       = varargin{1};
            
            % Selection of construction method
            if pl.isparam('filename')
              utils.helper.msg(msg.OPROC1, 'constructing from file %s', pl.find('filename'));
              obj = fromFile(obj, pl);
              
            elseif pl.isparam('hostname') || pl.isparam('conn')
              utils.helper.msg(msg.OPROC1, 'constructing from repository %s', pl.find('hostname'));
              obj = obj.fromRepository(pl);
              
            elseif pl.isparam('res') || pl.isparam('poles') || pl.isparam('dir')
              utils.helper.msg(msg.OPROC1, 'constructing from residuals/poles/direct');
              obj = fromResidualsPolesDirect(obj, pl);
              
            elseif pl.isparam('pzmodel')
              utils.helper.msg(msg.OPROC1, 'constructing from pole/zero model');
              obj = fromPzmodel(obj, pl);
              
            elseif pl.isparam('rational')
              utils.helper.msg(msg.OPROC1, 'constructing from rational object');
              obj = fromRational(obj, pl);
              
            elseif pl.isparam('built-in')
              utils.helper.msg(msg.OPROC1, 'constructing from built-in model');
              obj = fromModel(obj, pl);
              
            elseif pl.isparam('Plist')
              ipl = find(pl, 'Plist');
              obj = parfrac(ipl);
              
            else
              obj.setObjectProperties(pl);
              obj.addHistory(parfrac.getInfo('parfrac', 'None'), pl, [], []);
            end
          else
            error('### Unknown single argument constructor.');
          end
          
        case 2
          %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
          %%%%%%%%%%%%% Two inputs %%%%%%%%%%%%%%%
          %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
          if (isa(varargin{1}, 'database') || isa(varargin{1}, 'mpipeline.repository.RepositoryConnection')) && isnumeric(varargin{2})
            %%%%%%%%%%  f = parfrac(<database-object>, [IDs])   %%%%%%%%%%
            % parfrac(<database-object>, [IDs])
            utils.helper.msg(msg.OPROC1, 'retrieve from repository');
            obj = obj.fromRepository(plist('conn', varargin{1}, 'id', varargin{2}));
            
          elseif (isa(varargin{1}, 'parfrac') || isa(varargin{1}, 'rational')) && isa(varargin{2}, 'plist') && isempty(varargin{2}.params)
            %%%%%%%%%%  f = parfrac(parfrac-object, <empty plist>) %%%%%%%%%%
            obj = parfrac(varargin{1});
            
          elseif isa(varargin{1}, 'org.apache.xerces.dom.DeferredElementImpl') && ...
                 isa(varargin{2}, 'history')
            %%%%%%%%%%   obj = parfrac(DOM node, history-objects)   %%%%%%%%%%
            obj = fromDom(obj, varargin{1}, varargin{2});
            
          elseif isa(varargin{1}, 'ltpda_uoh') && isa(varargin{2}, 'plist')
            %%%%%%%%%%%   parfrac(<ltpda_uoh>-object, plist-object)   %%%%%%%%%%
            % always recreate from plist
            
            % If we are trying to load from file, and the file exists, do
            % that. Otherwise, copy the input object.
            if varargin{2}.isparam('filename')
              if exist(fullfile('.', find(varargin{2}, 'filename')), 'file')==2
                obj = parfrac(varargin{2});
              else
                obj = parfrac(varargin{1});
              end
            else
              obj = parfrac(varargin{2});
            end
          elseif isnumeric(varargin{1}) && isnumeric(varargin{2})
            % r = parfrac(num, den)
          utils.helper.msg(msg.OPROC1, 'constructing from residuals/poles/direct');
          pl = plist('res', varargin{1}, 'poles', varargin{2});
          obj = fromResidualsPolesDirect(obj, pl);
            
          else
            error('### Unknown 2 argument constructor.');
          end
          
        case 3
          %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
          %%%%%%%%%%%% Three inputs %%%%%%%%%%%%%%
          %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
          
          %%%%%%%%%%   r = parfrac(num, den, dir)   %%%%%%%%%%
          utils.helper.msg(msg.OPROC1, 'constructing from residuals/poles/direct');
          pl = plist('res', varargin{1}, 'poles', varargin{2}, 'dir', varargin{3});
          obj = fromResidualsPolesDirect(obj, pl);
          
        case 4
          %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
          %%%%%%%%%%%% Four inputs %%%%%%%%%%%%%%%
          %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
          
          %%%%%%%%%%   r = parfrac(num, den, dir, name)   %%%%%%%%%%
          utils.helper.msg(msg.OPROC1, 'constructing from residuals/poles/direct');
          pl = plist('res', varargin{1}, 'poles', varargin{2}, 'dir', varargin{3}, 'name', varargin{4});
          obj = fromResidualsPolesDirect(obj, pl);
          
        case 6
          %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
          %%%%%%%%%%%% five inputs %%%%%%%%%%%%%%%
          %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
          %%%%%%%%%%   pzm = parfrac(res, poles, dir, name, iunits, ounits)   %%%%%%%%%%
          utils.helper.msg(msg.OPROC1, 'constructing from residuals/poles/direct');
          pl = plist('res', varargin{1}, 'poles', varargin{2}, 'dir', varargin{3}, ...
            'name', varargin{4}, ...
            'iunits', varargin{5}, 'ounits', varargin{6});
          obj = fromResidualsPolesDirect(obj, pl);
          
        otherwise
          [parfracs, invars, rest] = utils.helper.collect_objects(varargin, 'parfrac');
          
          %%% Do we have a list of PARFRAC objects as input
          if ~isempty(parfracs) && isempty(rest)
            obj = parfrac(parfracs);
          else
            error('### Unknown number of arguments.');
          end
      end
      
    end % End constructor
    
  end
  
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  %                             Methods (protected)                           %
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  methods (Access = protected)
  end
  
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  %                            Methods (static)                               %
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  methods (Static)
    
    function mdls = getBuiltInModels(varargin)
      mdls = ltpda_uo.getBuiltInModels('parfrac');
    end
    
    function out = VEROUT()
      out = '$Id: parfrac.m,v 1.57 2011/10/25 11:50:55 luigi Exp $';
    end
    
    function ii = getInfo(varargin)
      ii = utils.helper.generic_getInfo(varargin{:}, 'parfrac');
    end
    
    function out = SETS()
      out = [SETS@ltpda_uoh, ...
        {'From Rational'},   ...
        {'From Pzmodel'},    ...
        {'From Residuals/Poles/Direct'}];
    end
    
    
    function plout = getDefaultPlist(set)
      persistent pl;
      persistent lastset;
      if exist('pl', 'var')==0 || isempty(pl) || ~strcmp(lastset, set)
        pl = parfrac.buildplist(set);
        lastset = set;
      end
      plout = pl;
    end
    
    function out = buildplist(set)
      
      if ~utils.helper.ismember(lower(parfrac.SETS), lower(set))
        error('### Unknown set [%s]', set);
      end
      
      out = plist();
      out = parfrac.addGlobalKeys(out);
      out = buildplist@ltpda_uoh(out, set);
      
      
      switch lower(set)
        case 'from rational'
          % rational
          p = param({'rational','Rational transfer-function model object to design from.'}, {1, {rational}, paramValue.OPTIONAL});
          out.append(p);
          % Iunits
          p = param({'iunits','The input units of the model.'}, paramValue.EMPTY_STRING);
          out.append(p);
          
          % Ounits
          p = param({'ounits','The output units of the model.'}, paramValue.EMPTY_STRING);
          out.append(p);
        case 'from pzmodel'
          % pzmodel
          p = param({'pzmodel','Pole/zero model object to design from.'}, {1, {pzmodel}, paramValue.OPTIONAL});
          out.append(p);
          % Iunits
          p = param({'iunits','The input units of the model.'}, paramValue.EMPTY_STRING);
          out.append(p);
          
          % Ounits
          p = param({'ounits','The output units of the model.'}, paramValue.EMPTY_STRING);
          out.append(p);
        case 'from residuals/poles/direct'
          
          % res
          p = param({'res','Residual terms.'}, paramValue.EMPTY_DOUBLE);
          out.append(p);
          
          % Poles
          p = param({'poles','Poles (real or complex numbers).'}, paramValue.EMPTY_DOUBLE);
          out.append(p);
          
          % Dir
          p = param({'dir','Direct terms.'}, paramValue.DOUBLE_VALUE(0));
          out.append(p);
          % Iunits
          p = param({'iunits','The input units of the model.'}, paramValue.EMPTY_STRING);
          out.append(p);
          
          % Ounits
          p = param({'ounits','The output units of the model.'}, paramValue.EMPTY_STRING);
          out.append(p);
      end
    end % function out = getDefaultPlist(varargin)
    
    function obj = initObjectWithSize(n,m)
      obj = parfrac.newarray([n m]);
    end
    
  end % End static methods
  
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  %                         Methods (static, private)                         %
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  
  methods (Static, Access=private)
  end % End static, private methods
  
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  %                         Methods (static, hidden)                          % 
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  
  methods (Static = true, Hidden = true)
    varargout = loadobj(varargin)
    varargout = update_struct(varargin);
  end
  
  methods (Hidden = true)
    varargout = attachToDom(varargin)
  end
  
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  %                             Methods (public)                              %
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  methods
    varargout = char(varargin)
    varargout = display(varargin)
    varargout = copy(varargin)
  end
  
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  %                            Methods (protected)                            %
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  methods (Access = protected)
    varargout = respCore(varargin)
    varargout = fromStruct(varargin)
    varargout = fromDom(varargin)
  end
  
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  %                           Methods (private)                               %
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  methods (Access = private)
    % Constructors
    varargout = fromResidualsPolesDirect(varargin)
    varargout = fromRational(varargin)
    varargout = fromPzmodel(varargin)
  end
  
end % End classdef

% From XML File
% -------------
%
%   Construct a parfrac by loading it from an XML file.
%
%   'filename' - construct a parfrac from a filename.
%                [default: empty string]
%
%                Example: plist('filename', 'parfrac1.xml')
%
% From MAT File
% -------------
%
%   Construct a parfrac by loading it from a MAT file.
%
%   'filename' - construct a parfrac from a filename.
%                [default: empty string]
%
%                Example: plist('filename', 'parfrac1.mat')
%
%
% From Repository
% ---------------
%
%   Construct a parfrac by retrieving it from an LTPDA repository.
%
%   'Hostname' - the repository hostname. Only those objects which
%                are parfracs are returned.
%                [default: 'localhost'];
%
%                Additional parameters:
%
%                'Database'   - The database name [default: 'ltpda']
%                'ID'         - A vector of object IDs. [default: []]
%                'CID'        - Retrieve all parfrac objects from a particular
%                               collection.
%                'Binary'     - Set to 'yes' to retrieve from stored binary
%                               representation (not always available).
%
% From Rational
% ------------
%
%   Construct a parfrac TF from a rational TF
%
%   'rational' - the rational model to convert
%                [default: empty rational object]
%
%                Example: rat = rational([1 2 3], [4 5 6 7], 'my rational')
%                         plist('rational', rat)
%
% From Pzmodel
% ------------
%
%   Construct a parfrac TF from a pzmodel
%
%   'pzmodel'  - the pzmodel to convert
%                [default: empty pole/zero model (pzmodel)]
%
%                Example: pzm = pzmodel(1, {1 2 3}, {4 5})
%                         plist('pzmodel', pzm)
%
% From Residuals/Poles/Direct
% --------------------------------
%
%   Construct a parfrac from arrays of coefficients
%
%   'res'      - vector of residuals [default: []]
%   'poles'    - vector/cell of real or complex numbers [default: []]
%   'dir'      - vector of direct terms [default: []]
%   'name'     - name of model [default: 'None']
%
% From Plist
% ----------
%
%   'Plist'    - construct from a plist. The value passed should be a plist
%                object. [default: empty plist]
%
%                Example: pzm = pzmodel(1, {1 2 3}, {4 5})
%                         pl  = plist('pzmodel', pzm)
%                         plist('PLIST', pl)
