# v_paramsetch

## PURPOSE

V_PARAMSETCH update and check parameter values p=(d,q,m,c,t)

## SYNOPSIS

function p=v_paramsetch(d,q,m,c,t)

## DESCRIPTION

    V_PARAMSETCH update and check parameter values p=(d,q,m,c,t)
     Usage: (1) function x=func(y,q)
                d=struct('a',1,'b',2,'c',3);   % default parameters
                p=v_paramsetch(d,q);           % update selected parameters

            (2) function x=func(y,q)
                d=struct('a',1,'b',2,'c',3);   % default parameters
                c={'p.a>0 && p.a<5','Message 1';'p.b>0','b must be positive'};
                p=v_paramsetch(d,q,'E',c);     % check parameter ranges

            (3) t={'a','description of parameter a';'c','and of parameter c'}
                p=v_paramsetch(d,q,'l',c,t);   % list values with optional descrpitions
                                               % '-','=','*','+' indicates default, unchanged, updated and new fields

      Inputs:
           d  default parameter structure
           q  new parameter values either a struct or alternatively matrix with
              each row being the value of the variables in the same order as the
              fields of d. If q is a matrix then all updated values will be row
              vectors with the same number of elements.
           m  mode string: any combination of the following
               'a' include additional fields in q that are not in d
               'A' additional fields in q constitute an error
               'e' print errors (but don't exit unless 'E' given as well)
               'E' exit if there are any errors
               'l' list fields and their values (default if no output)
           c  cell array with parameter checking conditions (use p for structure name) and error
                   message; one row per check. All results of command c{*,1) must be true.
                   e.g. {'p.a>3' 'parameter a must be > 3'; 'p.b<2' 'parameter b must be < 2'}
           t  cell array with descriptive text for each field in a new row. Either in
              the form t(:,*)={'field' 'description'} or a single column of
              descriptions in the same order as the fields of d

     Outputs:
           p  output parameter structure

     Bugs/Suggestions:
     (1) When printing include "=" to show an updated parameter has not changed (might waste a lot of computation)
     (2) If input d has few entries, it would be faster to loop through d rather than q

## CROSS-REFERENCE INFORMATION

This function calls:

This function is called by:

- v_fxrapt V_FXRAPT RAPT pitch tracker [FX,VUV]=(S,FS,M,Q)

## SOURCE CODE

    0001 function p=v_paramsetch(d,q,m,c,t)
    0002 %V_PARAMSETCH update and check parameter values p=(d,q,m,c,t)
    0003 % Usage: (1) function x=func(y,q)
    0004 %            d=struct('a',1,'b',2,'c',3);   % default parameters
    0005 %            p=v_paramsetch(d,q);           % update selected parameters
    0006 %
    0007 %        (2) function x=func(y,q)
    0008 %            d=struct('a',1,'b',2,'c',3);   % default parameters
    0009 %            c={'p.a>0 && p.a<5','Message 1';'p.b>0','b must be positive'};
    0010 %            p=v_paramsetch(d,q,'E',c);     % check parameter ranges
    0011 %
    0012 %        (3) t={'a','description of parameter a';'c','and of parameter c'}
    0013 %            p=v_paramsetch(d,q,'l',c,t);   % list values with optional descrpitions
    0014 %                                           % '-','=','*','+' indicates default, unchanged, updated and new fields
    0015 %
    0016 %  Inputs:
    0017 %       d  default parameter structure
    0018 %       q  new parameter values either a struct or alternatively matrix with
    0019 %          each row being the value of the variables in the same order as the
    0020 %          fields of d. If q is a matrix then all updated values will be row
    0021 %          vectors with the same number of elements.
    0022 %       m  mode string: any combination of the following
    0023 %           'a' include additional fields in q that are not in d
    0024 %           'A' additional fields in q constitute an error
    0025 %           'e' print errors (but don't exit unless 'E' given as well)
    0026 %           'E' exit if there are any errors
    0027 %           'l' list fields and their values (default if no output)
    0028 %       c  cell array with parameter checking conditions (use p for structure name) and error
    0029 %               message; one row per check. All results of command c{*,1) must be true.
    0030 %               e.g. {'p.a>3' 'parameter a must be > 3'; 'p.b<2' 'parameter b must be < 2'}
    0031 %       t  cell array with descriptive text for each field in a new row. Either in
    0032 %          the form t(:,*)={'field' 'description'} or a single column of
    0033 %          descriptions in the same order as the fields of d
    0034 %
    0035 % Outputs:
    0036 %       p  output parameter structure
    0037 %
    0038 % Bugs/Suggestions:
    0039 % (1) When printing include "=" to show an updated parameter has not changed (might waste a lot of computation)
    0040 % (2) If input d has few entries, it would be faster to loop through d rather than q
    0041
    0042 %      Copyright (C) Mike Brookes 2017
    0043 %      Version: $Id: v_paramsetch.m 10865 2018-09-21 17:22:45Z dmb $
    0044 %
    0045 %   VOICEBOX is a MATLAB toolbox for speech processing.
    0046 %   Home page: http://www.ee.ic.ac.uk/hp/staff/dmb/voicebox/voicebox.html
    0047 %
    0048 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    0049 %   This program is free software; you can redistribute it and/or modify
    0050 %   it under the terms of the GNU Lesser General Public License as published by
    0051 %   the Free Software Foundation; either version 3 of the License, or
    0052 %   (at your option) any later version.
    0053 %
    0054 %   This program is distributed in the hope that it will be useful,
    0055 %   but WITHOUT ANY WARRANTY; without even the implied warranty of
    0056 %   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    0057 %   GNU Lesser General Public License for more details.
    0058 %
    0059 %   You can obtain a copy of the GNU Lesser General Public License from
    0060 %   https://www.gnu.org/licenses/ .
    0061 %
    0062 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    0063 p=d;                                            % initialize output structure to the default values
    0064 numerr=0;                                       % initialize error count
    0065 dn=fieldnames(d);                               % list of parameter default fields
    0066 ndn=length(dn);                                 % number of default parameter fields
    0067 nq=0;                                           % default value for size(q,1)
    0068 % handle common case efficiently with 1 or 2 input arguments and an output argument
    0069 if nargin<=2 && nargout>0
    0070     if nargin==2 && numel(q)>0                  % if update argument exists
    0071         if isstruct(q)                          % if update argument is a structure
    0072             qn=fieldnames(q);                   % field names to update
    0073             nq=length(qn);                      % number of fields to update
    0074             if nq<ndn                           % relatively few entries in q
    0075                 for i=1:nq                      % loop through list of fields to update
    0076                     fi=qn{i};                   % get field name to update
    0077                     if isfield(p,fi);           % is this an existing field ?
    0078                         p.(fi)=q.(fi);          % set new field value
    0079                     end
    0080                 end
    0081             else                                % more entries in q than in d
    0082                 for i=1:ndn                     % loop through list of existing fields
    0083                     fi=dn{i};                   % get field name to potentially update
    0084                     if isfield(q,fi);           % is there a replacement value for this field ?
    0085                         p.(fi)=q.(fi);          % set new field value
    0086                     end
    0087                 end
    0088             end
    0089         else                                    % else update argument is a matrix
    0090             nq=size(q,1);                       % number of fields to update
    0091             for i=1:min(nq,ndn)
    0092                 p.(dn{i})=q(i,:);
    0093             end
    0094         end
    0095     end
    0096 else                                            % we have >2 arguments or else no output arguments
    0097     % sort out input arguments
    0098     if nargin<5
    0099         t={'' ''};                              % define an empty description array
    0100         if nargin<4
    0101             c=cell(0,2);                        % define an empty check condition array
    0102             if nargin<3
    0103                 m='';                           % define an empty check mode string
    0104             end
    0105         end
    0106     end
    0107     % now update the selected fields
    0108     adderr=any(m=='A');                         % new fields constitute an error
    0109     addnew=any(m=='a');                         % new fields should be added into p
    0110     printerr=any(m=='e');                       % print error messages
    0111     numerr=0;                                   % initialize count of errors
    0112     if numel(q)>0                               % if update argument is non-empty
    0113         if isstruct(q)                          % if update argument is a structure
    0114             qn=fieldnames(q);                   % field names to update
    0115             nq=length(qn);                      % number of fields to update
    0116             if addnew                           % we are including all fields in q(n)
    0117                 for i=1:nq                      % loop through list of fields to update
    0118                     fi=qn{i};                   % get field name to update
    0119                     p.(fi)=q.(fi);              % set new field value
    0120                 end
    0121             else
    0122                 for i=1:length(qn)              % loop through list of fields to update
    0123                     fi=qn{i};                   % get field name to update
    0124                     if isfield(p,fi)            % if this is an existing field ...
    0125                         p.(fi)=q.(fi);          % set new field value
    0126                     elseif adderr               % if this is an non-existant field ...
    0127                         numerr=numerr+1;        % increment error count
    0128                         if printerr
    0129                             fprintf(2,'Unknown parameter: %s\n',fi);
    0130                         end
    0131                     end
    0132                 end
    0133             end
    0134         else                                    % else update argument is a matrix
    0135             nq=size(q,1);                       % number of fields to update
    0136             for i=1:min(nq,ndn)
    0137                 p.(dn{i})=q(i,:);
    0138             end
    0139             if nq>ndn
    0140                 numerr=numerr+nq-ndn;
    0141                 if printerr
    0142                     if  nq==ndn+1
    0143                         fprintf(2,'1 extra parameter specified\n');
    0144                     else
    0145                         fprintf(2,'%d extra parameters specified\n',nq-ndn);
    0146                     end
    0147                 end
    0148             end
    0149         end
    0150     end
    0151     % Apply parameter checks
    0152     if numel(c)>0                               % check if any checks are specified
    0153         for i=1:size(c,1)                       % one check per row of c
    0154             if ~all(eval(c{i,1}),'all')               % perform the check
    0155                 numerr=numerr+1;
    0156                 if size(c,1)==1
    0157                     fprintf(2,'Parameter check failed: %s\n',c{i});
    0158                 else
    0159                     fprintf(2,c{i,2});
    0160                     fprintf(2,'\n');
    0161                 end
    0162             end
    0163         end
    0164     end
    0165     % print out a list of the parameters if requested
    0166     if ~nargout || any(m=='l')
    0167            fprintf('Key: - default value, = updated to default value, * updated to new value, + additional field\n');
    0168         pn=fieldnames(p);
    0169         nf=length(pn);                          % length of updated structure
    0170         st=size(t);
    0171         for i=1:nf                              % loop through field in output p
    0172             fi=pn{i};                           % field name
    0173             vi=p.(fi);                          % field value
    0174             if i>ndn                            % if this field was added to structure d
    0175                 cat='+';                        % new field
    0176             else
    0177                 if nq>0 && isstruct(q)          % if update argument is a structure
    0178                     if isfield(q,fi)            % was this field updated ?
    0179                         if isequal(p.(fi),d.(fi))
    0180                             cat='=';            % updated to existing value
    0181                         else
    0182                             cat='*';            % updated to new value
    0183                         end
    0184                     else                        % not an updated field
    0185                         cat='-';                % not updated
    0186                     end
    0187                 else                            % if update argument is a matrix
    0188                     if i>nq                     % beyond list of updated fields
    0189                         cat='-';                % not updated
    0190                     else
    0191                         if isequal(p.(fi),d.(fi))
    0192                             cat='=';            % updated to existing value
    0193                         else
    0194                             cat='*';            % updated to new value
    0195                         end
    0196                     end
    0197                 end
    0198             end
    0199             if st(2)>1                          % description are field-based
    0200                 jti=find(strcmp(fi,t(:,1)),1);  % find description for this field
    0201                 if ~isempty(jti)
    0202                     jti=t{jti,2};               % description string
    0203                 end
    0204             elseif i<=st(1)
    0205                 jti=t{i,1};                     % description string
    0206             else
    0207                 jti=[];                         % empty description
    0208             end
    0209             if isnumeric(vi) && length(vi)==numel(vi) && isreal(vi) % can print vector on one line
    0210                 fit=fi;
    0211                 if size(vi,1)>1
    0212                     fit=[fi '''']; % transposed vector
    0213                 end
    0214                 fprintf('%3d%c %s:',i,cat,fit);
    0215                 fprintf(' %g',vi);
    0216                 if isempty(jti)
    0217                     fprintf('\n');
    0218                 else
    0219                     fprintf(' = %s\n',jti);
    0220                 end
    0221             elseif numel(vi)<6
    0222                 fprintf('%3d%c %s:',i,cat,fi);
    0223                 if isempty(jti)
    0224                     fprintf('\n');
    0225                 else
    0226                     fprintf(' %s:\n',jti);
    0227                 end
    0228                 disp(vi);
    0229             else
    0230                 fprintf('%3d%c %s: [large]',i,cat,fi);
    0231                 if isempty(jti)
    0232                     fprintf('\n');
    0233                 else
    0234                     fprintf(' = %s\n',jti);
    0235                 end
    0236             end
    0237         end
    0238     end
    0239     if numerr>0 && any(m=='E')
    0240         error('%d error%c in parameter specification',numerr,(' '+(numerr>1)*('s'-' ')));
    0241     end
    0242 end