function Out = metastockread(fullpath)

% METASTOCKREAD Read metastock files (symbols index files: master, emaster, xmaster; data files: .dat and .mwd)
%
%   METASTOCKREAD Select with UIGETFILE the metastock file to read.
%                 maetastockread() remembers the last selected file.
%
%   METASTOCKREAD(FULLPATH) Read metastock file FULLPATH to output struct OUT.
%                           The FULLPATH input is a string enclosed in single quotes.
%
%   OUT = metastockread(...)
%       OUT is a "m by 1" NON-scalar structure, where "m" is the # of .dat/.mwd files in the same directory
%       of the symbol index files.
%
%       The (sub)scalar structure has the following fields:
%           - datNum : # of the .dat/.mwd file with the data                           UINT16
%           - symbol : security symbol                                                 CHAR
%           - name   : security name                                                   CHAR
%           - inDate : initial date of the data in datestr format 29 ('yyyy-mm-dd')    CHAR
%           - fiDate : final   date of the data in datestr format 29 ('yyyy-mm-dd')    CHAR
%           - freq   : time freq. of the data I (intraday), D (daily), W, M, Q or Y    CHAR
%           - idFreq : intraday time frequency in minutes                              UINT8
%           - data   : time series data with variable number of fields (columns)       DOUBLE
%
%       The data field columns follow the schema:
%
%       | Date/Time* | Open | High | Low | Close | Volume | OpenInterest |
%       * The date is represented in datenum format. The precision depends on the field idFreq.
%
%       If data has only 5 columns then Volume and OpenInterest columns are absent.
%
% Examples:
%
%     % Read metastock sample data supplied with the submission (unzip the MSdatasample.zip)
%     Out1 = metastockread;                                   % Chose any symbols index file with UIGETFILE
%
%     Out2 = metastockread('MSdatasample directory\Emaster'); % Read in data associated with the Emaster
%     Out3 = metastockread('MSdatasample directory\Xmaster'); % Read in data associated with the Xmaster
%     Out  = [Out2; Out3];                                    % Concatenate results
%
%     whos Out2
%     Name        Size              Bytes  Class     Attributes
%     Out1       255x1            1911591  struct
%
%     Out1(1,1)
%     ans =
%         datNum: 1
%         symbol: 'ASR'
%           name: 'A.S. ROMA'
%         inDate: '2010-01-04'
%         fiDate: '2010-07-09'
%           freq: 'D'
%         idFreq: 0
%           data: [127x7 double]
%
% Additional features:
% - FEX metastockread page
%
% See also: FREAD, TYPECAST, BITGET, DATESTR, DATENUM

% Author: Oleg Komarov (oleg.komarov@hotmail.it)
% Tested on R14SP3 (7.1) and on R2009b. In-between compatibility is assumed.
% Platform tested: WIN. If the fcn doesn't work on other platforms please report.
% 06 sep 2010 - Created
% 09 sep 2010 - Added link to FEX page and edit example.
% 03 aug 2011 - Added fix for buggy *.dat files as pointed out by Davide Dalmasso

% Use a global variable to remember last opened file
global alreadyOpenedFile

% IF no inputs
if nargin == 0
    % Select a symbols index file with uigetfile
    [filename, pathname] = uigetfile({'Emaster;Master;Xmaster','Metastock files'},'Load metastock file', alreadyOpenedFile);
    fullpath = [pathname filename];
    alreadyOpenedFile = fullpath;
    % No file has been selected
    if filename == 0
        disp([char(13) 'The user selected CANCEL.' char(13)])
        alreadyOpenedFile = [];
        return
    end
% IF one input
elseif nargin == 1
    % Check
    if ~ischar(fullpath)
        error('metastockread:strFullpath','FULLPATH should be a string.')
    end
% ELSE error
else
    error('metastockread:ninput','Too many input arguments.')
end

% Open fid
fid = fopen(fullpath,'r','b');
if fid == -1
    error('metastockread:invalidFullpath','Invalid FULLPATH. File not found.')
end

% Read symbols index file: MASTER, EMASTER or XMASTER
if     regexpi(fullpath,'\w*\\master')
    Out = readMaster(fid);
elseif regexpi(fullpath,'\w*\\emaster')
    Out = readEmaster(fid);
elseif regexpi(fullpath,'\w*\\xmaster')
    Out = readXmaster(fid);
else
    error('metastockread:namefile','The name of the file should be either MASTER, EMASTER or XMASTER.')
end

% Close fid
fclose(fid);

% Retrieve data files that reside on the same path as the symbols index file selected
pathname = fullpath(1:find(fullpath == '\',1,'last'));
pathname = strrep(pathname, '\','\\');

% Create list of filenames
if Out(1).datNum < 256; fmt = 'F%d.DAT\n';
else                    fmt = 'F%d.MWD\n'; end
names = dataread('string',sprintf([pathname fmt],cat(1,Out.datNum)),'%s','delimiter','\n');

% Files to process
numOut = numel(Out);
for n = 1:numOut

    % Open file
    fid = fopen(names{n},'r','b');

    % If file not found leave NaN and skip to next security
    if fid == -1
        Out(n).data = NaN;
        continue
    end
    % Retrieve the number of records
    fseek(fid,2,'bof');
    numRecs = double(typecast(fread(fid,2,'*uint8'),'uint16'));
    % Retrieve the number of fields
    fseek(fid,0,'eof');
    nfields = ftell(fid)/(numRecs*4);
    % Fix nfields in case .dat file is buggy: second last timestamp-entry
    % is repeated as last
    nfields = fix(nfields);

    % Skip header
    fseek(fid,nfields*4,'bof');

    % Retrieve all data at once; the FULL info set would be organized as follows:
    % | Date / Time | Open | High | Low | Close | Volume | OpenInterest |
    Out(n).data = reshape(mbf2ieee(fread(fid,(numRecs-1)*nfields*4,'*uint8')), nfields, numRecs-1).';

    % Format the date and time into one datenum column (the first)
    [d,m,y] = partNumDate(Out(n).data(:,1));
    if nfields == 8
        h = fix(Out(n).data(:,2)/1e4);
        m = fix(mod(Out(n).data(:,2)/1e2,1e2));
        Out(n).data = Out(n).data(:,[1 3:8]);
        Out(n).data(:,1) = datenummx(y,m,d,h,m,0);
    else
        Out(n).data(:,1) = datenummx(y,m,d);
    end

    % Close fid
    fclose(fid);
end
end

% ------------------------------------------------------------------------------------------------------------
% READMASTER  - Read master file
% ------------------------------------------------------------------------------------------------------------
function Out = readMaster(fid)
% Skip header (each record is 53 bytes long)
fseek(fid,53,'bof');
% Read in the whole file
tmp = fread(fid,[53,inf],'*uint8');
% Select and convert useful info
Out = struct('datNum', num2cell(uint16(tmp(1,:).'))         ,... % # of the .dat file with the data
             'symbol', cellstr(char(tmp(37:52,:).'))        ,... % security symbol
             'name'  , cellstr(char(tmp(8:23,:).'))         ,... % security name
             'inDate', fmtStrDate(mbf2ieee(tmp(26:29,:)))   ,... % initial date as 'yyyy-mm-dd' (datestr format 29)
             'fiDate', fmtStrDate(mbf2ieee(tmp(30:33,:)))   ,... % final   date as 'yyyy-mm-dd' (datestr format 29)
             'freq'  , cellstr(char(tmp(34,:).'))           ,... % time frequency of the data: I, D, M, W, Q, Y
             'idFreq', num2cell(tmp(35,:).'));                   % intraday time frequency in minutes
end

% ------------------------------------------------------------------------------------------------------------
% READEMASTER - Read emaster file
% ------------------------------------------------------------------------------------------------------------
function Out = readEmaster(fid)
% Skip header (each record is 150 bytes long)
fseek(fid,192,'bof');
% Read in the whole file
tmp = fread(fid,[192,inf],'*uint8');
% Select and convert useful info
Out = struct('datNum', num2cell(uint16(tmp(3,:).'))                                 ,...
             'symbol', cellstr(char(tmp(12:25,:).'))                                ,...
             'name'  , cellstr(char(tmp(33:48,:).'))                                ,...
             'inDate', fmtStrDate(typecast(reshape(tmp(65:68,:),[],1),'single'))    ,...
             'fiDate', fmtStrDate(typecast(reshape(tmp(73:76,:),[],1),'single'))    ,...
             'freq'  , cellstr(char(tmp(61,:).'))                                   ,...
             'idFreq', num2cell(tmp(63,:).'));

end

% ------------------------------------------------------------------------------------------------------------
% READXMASTER - Read xmaster file
% ------------------------------------------------------------------------------------------------------------
function Out = readXmaster(fid)
% Skip header (each record is 192 bytes long)
fseek(fid,150,'bof');
% Read in the whole file
tmp = fread(fid,[150,inf],'*uint8');
% Names (trim unuseful chars; observed behavior only in Xmaster)
names = cellfun(@(x) char(x(1:find(x == 0,1,'first'))), num2cell(tmp(17:39,:).',2),'un',0) ;
idx   = cellfun('isempty',names);
names(idx) = cellstr(char(tmp(17:39,idx).'));
% Select and convert useful info
Out = struct('datNum', num2cell(typecast(reshape(tmp(66:67,:),[],1),'uint16'))              ,...
             'symbol', cellstr(char(tmp(2:15,:).'))                                         ,...
             'name'  , names                                                                ,...
             'inDate', fmtStrDate(single(typecast(reshape(tmp(109:112,:),[],1),'uint32')))  ,...
             'fiDate', fmtStrDate(single(typecast(reshape(tmp(117:120,:),[],1),'uint32')))  ,...
             'freq'  , cellstr(char(tmp(63,:).'))                                           ,...
             'idFreq', num2cell(tmp(65,:).'));
end

% ------------------------------------------------------------------------------------------------------------
% MBF2IEEE - Converts MBF to IEEE
% ------------------------------------------------------------------------------------------------------------
function ieee = mbf2ieee(mbf)
% Convert a 32 bit Microsoft Basic Float into a IEEE double of the form (C)YYMMDD (ex: 990123 or 1010123)

% Layout of a MBF:
%
% MBF __________|__Byte 4 __|__Byte 3 __|__Byte 2 __|__Byte 1 __|
% Position bit__| 32 ... 24 | 23 ... 16 | 15 .. . 8 | 7 . . . 0 |
% Value in bit__| EEEE EEEE | XMMM MMMM | MMMM MMMM | MMMM MMMM |
%
% Components:
% X = sign bit
% E = 8 bit exponent
% M = 23 bit mantissa

% Cast into uint32 combining the 4 bytes and convert to double
mbf           = double(typecast(mbf(:),'uint32'));
mantissa_mask =   16777215;                                    % (2^24-1) bottom 24 bits holds the mantissa
exponent_mask = 4278190080;                                    % bitxor(2^32-1,mask1) top 8 bits holds the exponent
mantissa      = bitset  (bitand(mbf,mantissa_mask), 24)    ;   % restore hidden bit
exponent      = bitshift(bitand(mbf,exponent_mask),-24)-152;   % scale exponent
theSign       = double(bitget(mbf,24) == 0)*2 -1 ;             % +1 for zero or positive, -1 for negative
zeroVal       = double(mbf ~= 0) ;                             % 0 for zero values else +1
ieee          = zeroVal .* theSign .* 2.^exponent .* mantissa; % Convert to ieee
end

% ------------------------------------------------------------------------------------------------------------
% FORMATSTRDATE - Converts (C)YYMMDD or YYYYMMDD to datestring format 29
% ------------------------------------------------------------------------------------------------------------
function strdate = fmtStrDate(n)
% Add 1900 if is YYMMDD (ex: 990123 --> 1999-01-23) or CYYMMDD (ex:1010123 --> 2001-01-23) or leave YYYYMMDD (ex: 19990123)
strdate = sprintf('%d-%02d-%02d',[1900*(~fix(n/1e7)) + fix(n/1e4),fix(mod(n/1e2,1e2)),mod(n,1e2)].');
strdate = cellstr(reshape(strdate(:),10,[]).');
% Place 'none' if 0
none = n == 0;
strdate(none) = {'none'};
end

% ------------------------------------------------------------------------------------------------------------
% PARTNUMDATE - Parts (C)YYMMDD or YYYYMMDD to year YYYY, month MM and day DD
% ------------------------------------------------------------------------------------------------------------
function [d,m,y] = partNumDate(n)
% Day, month and year
d = mod(n,1e2);
m = fix(mod(n/1e2,1e2));
y = fix(n/1e4);
% If the number is supplied as YYMMDD (ex: 990123) or CYYMMDD (ex:1010123 --> 2001-01-23) instead of YYYYMMDD (ex: 19990123)
idx = ~fix(n/1e7);
y(idx) = y(idx) +1900;
end


function gpcf = gpcf_prod(varargin)
%GPCF_PROD  Create a product form covariance function
%
%  Description
%    GPCF = GPCF_PROD('cf', {GPCF_1, GPCF_2, ...})
%    creates a product form covariance function
%          GPCF = GPCF_1 .* GPCF_2 .* ... .* GPCF_N
%
%  See also
%    GP_SET, GPCF_*
%
% Copyright (c) 2009-2010, 2018 Jarno Vanhatalo
% Copyright (c) 2010 Aki Vehtari
% Copyright (c) 2014 Arno Solin and Jukka Koskenranta

% This software is distributed under the GNU General Public
% License (version 3 or later); please refer to the file
% License.txt, included with the software, for details.

  ip=inputParser;
  ip.FunctionName = 'GPCF_PROD';
  ip.addOptional('gpcf', [], @isstruct);
  ip.addParamValue('cf',[], @iscell);
  ip.parse(varargin{:});
  gpcf=ip.Results.gpcf;

  if isempty(gpcf)
    init=true;
    gpcf.type = 'gpcf_prod';
  else
    if ~isfield(gpcf,'type') && ~isequal(gpcf.type,'gpcf_prod')
      error('First argument does not seem to be a valid covariance function structure')
    end
    init=false;
  end

  if init || ~ismember('cf',ip.UsingDefaults)
    % Initialize parameters
    gpcf.cf = {};
    cfs=ip.Results.cf;
    if ~isempty(cfs)
      for i = 1:length(cfs)
        gpcf.cf{i} = cfs{i};
      end
    else
      error('At least one covariance function has to be given in cf');
    end
  end

  if init
    % Set the function handles to the subfunctions
    gpcf.fh.pak = @gpcf_prod_pak;
    gpcf.fh.unpak = @gpcf_prod_unpak;
    gpcf.fh.lp = @gpcf_prod_lp;
    gpcf.fh.lpg = @gpcf_prod_lpg;
    gpcf.fh.cfg = @gpcf_prod_cfg;
    gpcf.fh.cfdg = @gpcf_prod_cfdg;
    gpcf.fh.cfdg2 = @gpcf_prod_cfdg2;
    gpcf.fh.ginput = @gpcf_prod_ginput;
    gpcf.fh.ginput2 = @gpcf_prod_ginput2;
    gpcf.fh.ginput3 = @gpcf_prod_ginput3;
    gpcf.fh.ginput4 = @gpcf_prod_ginput4;
    gpcf.fh.cov = @gpcf_prod_cov;
    gpcf.fh.trcov  = @gpcf_prod_trcov;
    gpcf.fh.trvar  = @gpcf_prod_trvar;
    gpcf.fh.recappend = @gpcf_prod_recappend;
    gpcf.fh.cf2ss = @gpcf_prod_cf2ss;
  end

end

function [w, s, h] = gpcf_prod_pak(gpcf)
%GPCF_PROD_PAK  Combine GP covariance function parameters into one vector
%
%  Description
%    W = GPCF_PROD_PAK(GPCF, W) loops through all the covariance
%    functions and packs their parameters into one vector as
%    described in the respective functions. This is a mandatory
%    subfunction used for example in energy and gradient computations.
%
%  See also
%    GPCF_PROD_UNPAK

  ncf = length(gpcf.cf);
  w = []; s = {}; h=[];

  for i=1:ncf
    cf = gpcf.cf{i};
    [wi, si, hi] = cf.fh.pak(cf);
    w = [w wi];
    s = [s; si];
    h = [h hi];
  end
end

function [gpcf, w] = gpcf_prod_unpak(gpcf, w)
%GPCF_PROD_UNPAK  Sets the covariance function parameters into
%                 the structures
%
%  Description
%    [GPCF, W] = GPCF_PROD_UNPAK(GPCF, W) loops through all the
%    covariance functions and unpacks their parameters from W to
%    each covariance function structure. This is a mandatory
%    subfunction used for example in energy and gradient computations.
%
%  See also
%    GPCF_PROD_PAK
%
  ncf = length(gpcf.cf);

  for i=1:ncf
    cf = gpcf.cf{i};
    [cf, w] = cf.fh.unpak(cf, w);
    gpcf.cf{i} = cf;
  end

end

function lp = gpcf_prod_lp(gpcf)
%GPCF_PROD_LP  Evaluate the log prior of covariance function parameters
%
%  Description
%    LP = GPCF_PROD_LP(GPCF, X, T) takes a covariance function
%    structure GPCF and returns log(p(th)), where th collects the
%    parameters. This is a mandatory subfunction used for example
%    in energy computations.
%
%  See also
%    GPCF_PROD_PAK, GPCF_PROD_UNPAK, GPCF_PROD_LPG, GP_E

  lp = 0;
  ncf = length(gpcf.cf);
  for i=1:ncf
    cf = gpcf.cf{i};
    lp = lp + cf.fh.lp(cf);
  end

end

function lpg = gpcf_prod_lpg(gpcf)
%GPCF_PROD_LPG  Evaluate gradient of the log prior with respect
%               to the parameters.
%
%  Description
%    LPG = GPCF_PROD_LPG(GPCF) takes a covariance function
%    structure GPCF and returns LPG = d log (p(th))/dth, where th
%    is the vector of parameters. This is a mandatory subfunction
%    used for example in gradient computations.
%
%  See also
%    GPCF_PROD_PAK, GPCF_PROD_UNPAK, GPCF_PROD_LP, GP_G
  lpg = [];
  ncf = length(gpcf.cf);

  % Evaluate the gradients
  for i=1:ncf
    cf = gpcf.cf{i};
    lpg=[lpg cf.fh.lpg(cf)];
  end

end

function DKff = gpcf_prod_cfg(gpcf, x, x2, mask, i1)
%GPCF_PROD_CFG  Evaluate gradient of covariance function
%               with respect to the parameters.
%
%  Description
%    DKff = GPCF_PROD_CFG(GPCF, X) takes a covariance function
%    structure GPCF, a matrix X of input vectors and returns
%    DKff, the gradients of covariance matrix Kff = k(X,X) with
%    respect to th (cell array with matrix elements). This is a
%    mandatory subfunction used in gradient computations.
%
%    DKff = GPCF_PROD_CFG(GPCF, X, X2) takes a covariance
%    function structure GPCF, a matrix X of input vectors and
%    returns DKff, the gradients of covariance matrix Kff =
%    k(X,X2) with respect to th (cell array with matrix
%    elements). This subfunction is needed when using sparse
%    approximations (e.g. FIC).
%
%    DKff = GPCF_PROD_CFG(GPCF, X, [], MASK) takes a covariance
%    function structure GPCF, a matrix X of input vectors and
%    returns DKff, the diagonal of gradients of covariance matrix
%    Kff = k(X,X2) with respect to th (cell array with matrix
%    elements). This subfunction is needed when using sparse
%    approximations (e.g. FIC).
%
%    DKff = GPCF_PROD_CFG(GPCF, X, X2, [], i) takes a covariance
%    function structure GPCF, a matrix X of input vectors and
%    returns DKff, the gradients of covariance matrix Kff =
%    k(X,X2), or k(X,X) if X2 is empty, with respect to ith
%    hyperparameter. This subfunction is needed when using
%    memory save option in gp_set.
%
%  See also
%    GPCF_PROD_PAK, GPCF_PROD_UNPAK, GPCF_PROD_LP, GP_G

  [n, m] =size(x);
  ncf = length(gpcf.cf);

  DKff = {};

  if nargin==5
    % Use memory save option
    savememory=1;
    i3=0;
    for k=1:ncf
      % Number of hyperparameters for each covariance function
      cf=gpcf.cf{k};
      i3(k)=cf.fh.cfg(cf,[],[],[],0);
    end
    if i1==0
      % Return number of hyperparameters
      DKff=sum(i3);
      return
    end
    % Help indices
    i3=cumsum(i3);
    ind=find(cumsum(i3 >= i1)==1);
    if ind>1
      i1=[ind i1-i3(ind-1)];
    else
      i1=[ind i1];
    end
  else
    savememory=0;
  end

  % Evaluate: DKff{1} = d Kff / d magnSigma2
  %           DKff{2} = d Kff / d lengthScale
  % NOTE! Here we have already taken into account that the parameters are transformed
  % through log() and thus dK/dlog(p) = p * dK/dp

  % evaluate the gradient for training covariance
  if nargin == 2 || (isempty(x2) && isempty(mask))

    % evaluate the individual covariance functions
    for i=1:ncf
      cf = gpcf.cf{i};
      C{i} = cf.fh.trcov(cf, x);
    end

    % Evaluate the gradients
    ind = 1:ncf;
    DKff = {};
    if ~savememory
      i3=1:ncf;
    else
      i3=i1(1);
    end
    for i=i3
      cf = gpcf.cf{i};
      if ~savememory
        DK = cf.fh.cfg(cf, x);
      else
        DK = {cf.fh.cfg(cf,x,[],[],i1(2))};
      end

      CC = 1;
      for kk = ind(ind~=i)
        CC = CC.*C{kk};
      end
      for j = 1:length(DK)
        DKff{end+1} = DK{j}.*CC;
      end
    end

    % Evaluate the gradient of non-symmetric covariance (e.g. K_fu)
  elseif nargin == 3 || isempty(mask)
    if size(x,2) ~= size(x2,2)
      error('gpcf_prod -> _ghyper: The number of columns in x and x2 has to be the same. ')
    end

    % evaluate the individual covariance functions
    for i=1:ncf
      cf = gpcf.cf{i};
      C{i} = cf.fh.cov(cf, x, x2);
    end

    % Evaluate the gradients
    ind = 1:ncf;
    DKff = {};
    if ~savememory
      i3=1:ncf;
    else
      i3=i1(1);
    end
    for i=i3
      cf = gpcf.cf{i};
      if ~savememory
        DK = cf.fh.cfg(cf, x,x2);
      else
        DK = {cf.fh.cfg(cf,x,x2,[],i1(2))};
      end

      CC = 1;
      for kk = ind(ind~=i)
        CC = CC.*C{kk};
      end

      for j = 1:length(DK)
        DKff{end+1} = DK{j}.*CC;
      end
    end

    % Evaluate: DKff{1}    = d mask(Kff,I) / d magnSigma2
    %           DKff{2...} = d mask(Kff,I) / d lengthScale
  elseif nargin == 4 || nargin == 5

    % evaluate the individual covariance functions
    for i=1:ncf
      cf = gpcf.cf{i};
      C{i} = cf.fh.trvar(cf, x);
    end

    % Evaluate the gradients
    ind = 1:ncf;
    DKff = {};
    if ~savememory
      i3=1:ncf;
    else
      i3=i1(1);
    end
    for i=i3
      cf = gpcf.cf{i};
      if ~savememory
        DK = cf.fh.cfg(cf, x, [], 1);
      else
        DK = {cf.fh.cfg(cf, x, [], 1, i1(2))};
      end

      CC = 1;
      for kk = ind(ind~=i)
        CC = CC.*C{kk};
      end

      for j = 1:length(DK)
        DKff{end+1} = DK{j}.*CC;
      end
    end
  end
  if savememory
    DKff=DKff{1};
  end
end

function DKff = gpcf_prod_cfdg(gpcf, x, x2, dims)
%GPCF_SEXP_CFDG  Evaluate gradient of covariance function, of
%                which has been taken partial derivative with
%                respect to x, with respect to parameters.
%
%  Description
%    DKff = GPCF_SEXP_CFDG(GPCF, X) takes a covariance function
%    structure GPCF, a matrix X of input vectors and returns
%    DKff, the gradients of derivatived covariance matrix
%    dK(df,f)/dhyp = d(d k(X,X)/dx)/dhyp, with respect to the
%    parameters
%
%    Evaluate: DKff{1:m} = d Kff / d magnSigma2
%              DKff{m+1:2m} = d Kff / d lengthScale_m
%    m is the dimension of inputs. If ARD is used, then multiple
%    lengthScales. This subfunction is needed when using derivative
%    observations.
%
%         dims - is a vector of input dimensions with respect to which the
%                derivatives of the covariance function have been calculated
%                [by default dims=1:size(x,2)]
%
%
%    Note! When coding the derivatives of the covariance function, remember
%    to double check them. See gp_cov for lines of code to check the
%    matrices
%
%  See also
%    GPCF_SEXP_GINPUT

ncf = length(gpcf.cf);

[~, m] =size(x);
if nargin <3 || isempty(x2)
    x2=x;
end
if nargin < 4 || isempty(dims)
    %dims1 = 1:m;
    error('dims1 needs to be given')
end

% check whether there are any covariance functions that depend on dims
% covariate
ind = [];
DKff = {};
for i1 = 1:ncf
    if (~isfield(gpcf.cf{i1}, 'selectedVariables') || any(ismember(gpcf.cf{i1}.selectedVariables,dims)))
        ind(end+1) = i1;
    end
end
if ~isempty(ind)
    % Calculate derivative
    % calculate the individual covariance functions
    Ci = {};
    C = 1;
    for i1=1:ncf
        Ci{i1} = gpcf.cf{i1}.fh.cov(gpcf.cf{i1}, x, x2);
        C = C.*Ci{i1};
    end

    if ncf==2
        if any(ind==1)
            DKdx1 = gpcf.cf{1}.fh.cfdg(gpcf.cf{1}, x, x2, dims);
            DKx1 = gpcf.cf{1}.fh.ginput4(gpcf.cf{1}, x, x2, dims);
        else
            DKdx1{1} = 0;
            DKx1{1} = 0;
        end
        DK1 = gpcf.cf{1}.fh.cfg(gpcf.cf{1}, x, x2);
        if any(ind==2)
            DKdx2 = gpcf.cf{2}.fh.cfdg(gpcf.cf{2}, x, x2, dims);
            DKx2 = gpcf.cf{2}.fh.ginput4(gpcf.cf{2}, x, x2, dims);
        else
            DKdx2{1} = 0;
            DKx2{1} = 0;
        end
        DK2 = gpcf.cf{2}.fh.cfg(gpcf.cf{2}, x, x2);

        for i2 = 1:length(DK1)
            DKff{end+1} = DKdx1{i2}.*C./Ci{1} + DK1{i2}.*DKx2{1};
        end
        for i2 = 1:length(DK2)
            DKff{end+1} = DKdx2{i2}.*C./Ci{2} + DK2{i2}.*DKx1{1};
        end

    else
        error('not implemented yet')
        for i1=1:ncf

            DKdx = gpcf.cf{i1}.fh.cfdg(gpcf.cf{i1}, x, x2, dims);
            DK = gpcf.cf{i1}.fh.cfg(gpcf.cf{i1}, x, x2);

            gpcftmp = gpcf;
            gpcftmp.cf = {gpcftmp.cf(1:ncf~=i1)};
            % continue from here
            %DKx2 =
        end
    end
else
    DK = gpcf.fh.lpg(gpcf);
    for i2 = 1:length(DK)
        DKff{end+1} = 0;
    end
end

end

function DKff = gpcf_prod_cfdg2(gpcf, x, x2, dims1, dims2)
%GPCF_SEXP_CFDG  Evaluate gradient of covariance function, of
%                which has been taken partial derivative with
%                respect to x, with respect to parameters.
%
%  Description
%    DKff = GPCF_SEXP_CFDG(GPCF, X) takes a covariance function
%    structure GPCF, a matrix X of input vectors and returns
%    DKff, the gradients of derivatived covariance matrix
%    dK(df,f)/dhyp = d(d k(X,X)/dx)/dhyp, with respect to the
%    parameters
%
%    Evaluate: DKff{1:m} = d Kff / d magnSigma2
%              DKff{m+1:2m} = d Kff / d lengthScale_m
%    m is the dimension of inputs. If ARD is used, then multiple
%    lengthScales. This subfunction is needed when using derivative
%    observations.
%
%         dims - is a vector of input dimensions with respect to which the
%                derivatives of the covariance function have been calculated
%                [by default dims=1:size(x,2)]
%
%
%    Note! When coding the derivatives of the covariance function, remember
%    to double check them. See gp_cov for lines of code to check the
%    matrices
%
%  See also
%    GPCF_SEXP_GINPUT

ncf = length(gpcf.cf);

[~, m] =size(x);
if nargin <3 || isempty(x2)
    x2=x;
end
if nargin < 4 || isempty(dims1)
    %dims1 = 1:m;
    error('dims1 needs to be given')
end
if nargin < 5 || isempty(dims2)
    %dims2 = 1:m;
    error('dims2 needs to be given')
end

% NOTICE. AS OF NOW we assume that dims1 and dims2 are scalars

% check whether there are any covariance functions that depend on dims
% covariate
ind = [];
DKff = {};
for i1 = 1:ncf
    if (~isfield(gpcf.cf{i1}, 'selectedVariables') || any(ismember(gpcf.cf{i1}.selectedVariables,dims1)) || any(ismember(gpcf.cf{i1}.selectedVariables,dims2)))
        ind(end+1) = i1;
    end
end
if ~isempty(ind)
    % Calculate derivative
    % calculate the individual covariance functions
    Ci = {};
    C = 1;
    for i1=1:ncf
        Ci{i1} = gpcf.cf{i1}.fh.cov(gpcf.cf{i1}, x, x2);
        %C = C.*Ci{i1};
    end

    if ncf==2
        if any(ind==1)
            DKdx11 = gpcf.cf{1}.fh.cfdg(gpcf.cf{1}, x, x2, dims1);
            DKdx12 = gpcf.cf{1}.fh.cfdg(gpcf.cf{1}, x2, x, dims2);
            DKdx1_2 = gpcf.cf{1}.fh.cfdg2(gpcf.cf{1}, x, x2, dims1, dims2);
            DKx11 = gpcf.cf{1}.fh.ginput4(gpcf.cf{1}, x, x2, dims1);
            DKx12 = gpcf.cf{1}.fh.ginput4(gpcf.cf{1}, x2, x, dims2);
        else
            DKdx11{1} = 0;
            DKdx12{1} = 0;
            DKdx1_2{1} = 0;
            DKx11{1} = 0;
            DKx12{1} = 0;
        end
        if any(ind==2)
            DKdx21 = gpcf.cf{2}.fh.cfdg(gpcf.cf{2}, x, x2, dims1);
            DKdx22 = gpcf.cf{2}.fh.cfdg(gpcf.cf{2}, x2, x, dims2);
            DKdx2_2 = gpcf.cf{2}.fh.cfdg2(gpcf.cf{2}, x, x2, dims1, dims2);
            DKx21 = gpcf.cf{2}.fh.ginput4(gpcf.cf{2}, x, x2, dims1);
            DKx22 = gpcf.cf{2}.fh.ginput4(gpcf.cf{2}, x2, x, dims2);
        else
            DKdx21{1} = 0;
            DKdx22{1} = 0;
            DKdx2_2{1} = 0;
            DKx21{1} = 0;
            DKx22{1} = 0;
        end

        if dims1==dims2
            if any(ind==1)
                D2Kx1 = gpcf.cf{1}.fh.ginput2(gpcf.cf{1}, x, x2, dims1, dims2);
            else
                D2Kx1{1} = 0;
            end
            if any(ind==2)
                D2Kx2 = gpcf.cf{2}.fh.ginput2(gpcf.cf{2}, x, x2, dims1, dims2);
            else
                D2Kx2{1} = 0;
            end
        else
%             D2Kx1 = gpcf.cf{1}.fh.ginput3(gpcf.cf{1}, x, x2, dims1, dims2);
%             D2Kx2 = gpcf.cf{2}.fh.ginput3(gpcf.cf{2}, x, x2, dims1, dims2);
            if any(ind==1)
                D2Kx1 = gpcf.cf{1}.fh.ginput3(gpcf.cf{1}, x, x2, dims1, dims2);
            else
                D2Kx1{1} = 0;
            end
            if any(ind==2)
                D2Kx2 = gpcf.cf{2}.fh.ginput3(gpcf.cf{2}, x, x2, dims1, dims2);
            else
                D2Kx2{1} = 0;
            end
        end

        DK1 = gpcf.cf{1}.fh.cfg(gpcf.cf{1}, x, x2);
        DK2 = gpcf.cf{2}.fh.cfg(gpcf.cf{2}, x, x2);

        for i2 = 1:length(DK1)
            DKff{end+1} = DKdx1_2{i2}.*Ci{2} + DKdx11{i2}.*DKx22{1}' + DKdx12{i2}'.*DKx21{1} + DK1{i2}.*D2Kx2{1};
        end
        for i2 = 1:length(DK2)
            DKff{end+1} = DKdx2_2{i2}.*Ci{1} + DKdx21{i2}.*DKx12{1}' + DKdx22{i2}'.*DKx11{1} + DK2{i2}.*D2Kx1{1};
        end

    else
        error('not implemented yet')
        for i1=1:ncf

            DKdx = gpcf.cf{i1}.fh.cfdg(gpcf.cf{i1}, x, x2, dims);
            DK = gpcf.cf{i1}.fh.cfg(gpcf.cf{i1}, x, x2);

            gpcftmp = gpcf;
            gpcftmp.cf = {gpcftmp.cf(1:ncf~=i1)};
            % continue from here
            %DKx2 =
        end
    end
else
    DK = gpcf.fh.lpg(gpcf);
    for i2 = 1:length(DK)
        DKff{end+1} = 0;
    end
end

% [n, m] =size(x);
% ncf = length(gpcf.cf);
%
% if nargin <3 || isempty(x2)
%     x2=x;
% end
% if nargin < 4 || isempty(dims1)
%     %dims1 = 1:m;
%     error('dims1 needs to be given')
% end
% if nargin < 5 || isempty(dims2)
%     %dims2 = 1:m;
%     error('dims2 needs to be given')
% end
%
%
% % check whether there are any covariance functions that depend on dims
% % covariate
% ind = [];
% DKff = {};
% for i1 = 1:ncf
%     if (~isfield(gpcf.cf{i1}, 'selectedVariables') || any(ismember(gpcf.cf{i1}.selectedVariables,dims1)) || any(ismember(gpcf.cf{i1}.selectedVariables,dims2)))
%         ind(end+1) = i1;
%     end
% end
% if ~isempty(ind)
%     % Calculate derivative
%
%     % calculate the individual covariance functions
%     Ci = {};
%     C = 1;
%     for i1=1:ncf
%         Ci{i1} = gpcf.cf{i1}.fh.cov(gpcf.cf{i1}, x, x2);
%         C = C.*Ci{i1};
%     end
%
%     for i1=1:ncf
%         if any(i1 == ind)
%             DK = gpcf.cf{i1}.fh.cfdg2(gpcf.cf{i1}, x, x2, dims1, dims2);
%             for i2 = 1:length(DK)
%                 DKff{end+1} = DK{i2}.*C./Ci{i1};
%             end
%         else
%             DK = gpcf.cf{i1}.fh.lpg(gpcf.cf{i1});
%             for i2 = 1:length(DK)
%                 DKff{end+1} = 0;
%             end
%         end
%     end
% else
%     DK = gpcf.fh.lpg(gpcf);
%     for i2 = 1:length(DK)
%         DKff{end+1} = 0;
%     end
% end

end

function DKff = gpcf_prod_ginput(gpcf, x, x2, i1)
%GPCF_PROD_GINPUT  Evaluate gradient of covariance function with
%                  respect to x
%
%  Description
%    DKff = GPCF_PROD_GINPUT(GPCF, X) takes a covariance function
%    structure GPCF, a matrix X of input vectors and returns
%    DKff, the gradients of covariance matrix Kff = k(X,X) with
%    respect to X (cell array with matrix elements). This subfunction
%    is needed when computing gradients with respect to inducing
%    inputs in sparse approximations.
%
%    DKff = GPCF_PROD_GINPUT(GPCF, X, X2) takes a covariance
%    function structure GPCF, a matrix X of input vectors and
%    returns DKff, the gradients of covariance matrix Kff =
%    k(X,X2) with respect to X (cell array with matrix elements).
%    This subfunction is needed when computing gradients with
%    respect to inducing inputs in sparse approximations.
%
%    DKff = GPCF_PROD_GINPUT(GPCF, X, X2, i) takes a covariance
%    function structure GPCF, a matrix X of input vectors and
%    returns DKff, the gradients of covariance matrix Kff =
%    k(X,X2), or k(X,X) if X2 is empty, with respect to ith
%    covariate in X (cell array with matrix elements). This
%    subfunction is needed when using memory save option in
%    gp_set.
%
%  See also
%    GPCF_PROD_PAK, GPCF_PROD_UNPAK, GPCF_PROD_LP, GP_G

  [n, m] =size(x);

  if nargin==4
    % Use memory save option
    savememory=1;
    if i1==0
      % Return number of covariates
      if isfield(gpcf,'selectedVariables')
        DKff=length(gpcf.selectedVariables);
      else
        DKff=m;
      end
      return
    end
  else
    savememory=0;
  end
  % evaluate the gradient for training covariance
  if nargin == 2 || isempty(x2)

    ncf = length(gpcf.cf);

    % evaluate the individual covariance functions
    for i=1:ncf
      cf = gpcf.cf{i};
      C{i} = cf.fh.trcov(cf, x);
    end

    % Evaluate the gradients
    ind = 1:ncf;
    if ~savememory
      DKff=cellfun(@(a) zeros(n,n), cell(1,numel(x)), 'UniformOutput', 0);
    else
      DKff=cellfun(@(a) zeros(n,n), cell(1,n), 'UniformOutput', 0);
    end
    for i=1:ncf
      cf = gpcf.cf{i};
      if ~savememory
        DK = cf.fh.ginput(cf, x);
      else
        DK = cf.fh.ginput(cf, x, [], i1);
      end

      CC = 1;
      for kk = ind(ind~=i)
        CC = CC.*C{kk};
      end

      for j = 1:length(DK)
        DKff{j} = DKff{j} + DK{j}.*CC;
      end
    end

    % Evaluate the gradient of non-symmetric covariance (e.g. K_fu)
  elseif nargin == 3 ||  nargin == 4
    if size(x,2) ~= size(x2,2)
      error('gpcf_prod -> _ghyper: The number of columns in x and x2 has to be the same. ')
    end

    ncf = length(gpcf.cf);

    % evaluate the individual covariance functions
    for i=1:ncf
      cf = gpcf.cf{i};
      C{i} = cf.fh.cov(cf, x, x2);
    end

    % Evaluate the gradients
    ind = 1:ncf;
    if ~savememory
      DKff=cellfun(@(a) zeros(n,n), cell(1,numel(x)), 'UniformOutput', 0);
    else
      DKff=cellfun(@(a) zeros(n,n), cell(1,n), 'UniformOutput', 0);
    end
    for i=1:ncf
      cf = gpcf.cf{i};
      if ~savememory
        DK = cf.fh.ginput(cf, x, x2);
      else
        DK = cf.fh.ginput(cf, x, x2, i1);
      end

      CC = 1;
      for kk = ind(ind~=i)
        CC = CC.*C{kk};
      end

      for j = 1:length(DK)
        DKff{j} = DKff{j} + DK{j}.*CC;
      end
    end
  end

end

function [DKff, DKff1, DKff2]  = gpcf_prod_ginput2(gpcf, x, x2, dims, takeOnlyDiag)
%GPCF_PROD_GINPUT2  Evaluate gradient of covariance function with
%                   respect to both input variables x and x2 (in
%                   same dimension).
%
%  Description
%    DKff = GPCF_PROD_GINPUT2(GPCF, X, X2) takes a covariance
%    function structure GPCF, a matrix X of input vectors and
%    returns DKff, the gradients of twice derivatived covariance
%    matrix K(df,df) = dk(X1,X2)/dX1dX2 (cell array with matrix
%    elements). Input variable's dimensions are expected to be
%    same. The function returns also DKff1 and DKff2 which are
%    parts of DKff and needed with CFDG2. DKff = DKff1 -
%    DKff2. This subfunction is needed when using derivative
%    observations.
%
%    Note! When coding the derivatives of the covariance function, remember
%    to double check them. See gp_cov for lines of code to check the
%    matrices
%
%  See also
%    GPCF_PROD_GINPUT, GPCF_PROD_GINPUT2, GPCF_PROD_CFDG2
if nargin < 5
    takeOnlyDiag=[];
end

ncf = length(gpcf.cf);

% check whether there are any covariance functions that depend on dims
% covariate
ind = [];
for i1 = 1:ncf
    if (~isfield(gpcf.cf{i1}, 'selectedVariables') || any(ismember(gpcf.cf{i1}.selectedVariables,dims)))
        ind(end+1) = i1;
    end
end
% Calculate derivative
if ~isempty(ind)
    % evaluate the individual covariance functions
    C = 1;
    K = 0;
    for i1=1:ncf
        if ~isempty(takeOnlyDiag) && strcmp(takeOnlyDiag, 'takeOnlyDiag')
            Ci = gpcf.cf{i1}.fh.trvar(gpcf.cf{i1}, x);
        else
            Ci = gpcf.cf{i1}.fh.cov(gpcf.cf{i1}, x, x2);
        end
        C = C.*Ci;
        if any(i1 == ind)
            Dt = gpcf.cf{i1}.fh.ginput2(gpcf.cf{i1}, x, x2, dims, takeOnlyDiag);
            if length(Dt)>1
                error('gpcf_prod->ginput2: something is wrong')
            end
            K = K+Dt{1}./Ci;
        end
    end
    DKff = {C.*K};
else
    DKff = {0};
end

end

function DKff = gpcf_prod_ginput3(gpcf, x, x2, dims1, dims2)
%GPCF_PROD_GINPUT3  Evaluate gradient of covariance function with
%                   respect to both input variables x and x2 (in
%                   different dimensions).
%
%  Description
%    DKff = GPCF_PROD_GINPUT3(GPCF, X, X2) takes a covariance
%    function structure GPCF, a matrix X of input vectors and
%    returns DKff, the gradients of twice derivatived covariance
%    matrix K(df,df) = dk(X1,X2)/dX1dX2 (cell array with matrix
%    elements). The derivative is calculated in multidimensional
%    problem between input's observation dimensions which are not
%    same. This subfunction is needed when using derivative
%    observations.
%
%    ---- !!note this help text needs to be corrected !! ---
%    DKff is a cell array with the following elements:
%      DKff{1} = dk(X1,X2)/dX1_1dX2_2
%      DKff{2} = dk(X1,X2)/dX1_1dX2_3
%       ...
%      DKff{m-1} = dk(X1,X2)/dX1_1dX2_m
%      DKff{m} = dk(X1,X2)/dX1_2dX2_3
%       ...
%      DKff{m} = dk(X1,X2)/dX1_(m-1)dX2_m
%    where _m denotes the input dimension with respect to which the
%    gradient is calculated.
%     ---- clip ---
%
%    Note! When coding the derivatives of the covariance function, remember
%    to double check them. See gp_cov for lines of code to check the
%    matrices
%
%  See also
%    GPCF_PROD_GINPUT, GPCF_PROD_GINPUT2, GPCF_PROD_CFDG2

ncf = length(gpcf.cf);

% check whether there are any covariance functions that depend on dims
% covariate
ind = [];
for i1 = 1:ncf
    if (~isfield(gpcf.cf{i1}, 'selectedVariables') || any(ismember(gpcf.cf{i1}.selectedVariables,dims1)) || any(ismember(gpcf.cf{i1}.selectedVariables,dims2)) )
        ind(end+1) = i1;
    end
end
% Calculate derivative
if ~isempty(ind)
    % evaluate the individual covariance functions
    C = 1;
    K = 0;
    for i1=1:ncf
        Ci{i1} = gpcf.cf{i1}.fh.cov(gpcf.cf{i1}, x, x2);
        C = C.*Ci{i1};
    end
    for i1=1:ncf
        if any(i1 == ind)
            % double derivative of i'th covariance function
            Dt = gpcf.cf{i1}.fh.ginput3(gpcf.cf{i1}, x, x2, dims1, dims2);
            if length(Dt)>1
                error('gpcf_prod->ginput3: something is wrong')
            end
            K = K+Dt{1}./Ci{i1};
            % pairwise derivatives of i'th and j'th covariance functions
            Dti1 = gpcf.cf{i1}.fh.ginput4(gpcf.cf{i1}, x, x2, dims1);
            Dti2 = gpcf.cf{i1}.fh.ginput4(gpcf.cf{i1}, x2, x, dims2);
            for j1=i1+1:ncf
                if any(j1 == ind)
                    Dtj1 = gpcf.cf{j1}.fh.ginput4(gpcf.cf{j1}, x, x2, dims1);
                    Dtj2 = gpcf.cf{j1}.fh.ginput4(gpcf.cf{j1}, x2, x, dims2);

                    K = K + (Dti1{1}.*Dtj2{1}' + Dtj1{1}.*Dti2{1}')./Ci{i1}./Ci{j1};
                end
            end
        end
    end
    DKff = {C.*K};
else
    DKff = {0};
end

end

function DKff = gpcf_prod_ginput4(gpcf, x, x2, dims)
%GPCF_PROD_GINPUT4  Evaluate gradient of covariance function with
%                  respect to x. Simplified and faster version of
%                  sexp_ginput, returns full matrices.
%
%  Description
%    DKff = GPCF_PROD_GINPUT4takes a covariance function
%    structure GPCF, a matrix X of input vectors and returns
%    DKff, the gradients of covariance matrix Kff = k(X,X) with
%    respect to X (whole matrix). This subfunction is needed when
%    using derivative observations.
%
%    Note! When coding the derivatives of the covariance function, remember
%    to double check them. See gp_cov for lines of code to check the
%    matrices
%
%  See also
%    GPCF_PROD_GINPUT, GPCF_PROD_GINPUT2, GPCF_PROD_CFDG2

ncf = length(gpcf.cf);

% check whether there are any covariance functions that depend on dims
% covariate
ind = [];
for i1 = 1:ncf
    if (~isfield(gpcf.cf{i1}, 'selectedVariables') || any(ismember(gpcf.cf{i1}.selectedVariables,dims)))
        ind(end+1) = i1;
    end
end
% Calculate derivative
if ~isempty(ind)
    % evaluate the individual covariance functions
    C = 1;
    K = 0;
    for i1=1:ncf
        Ci = gpcf.cf{i1}.fh.cov(gpcf.cf{i1}, x, x2);
        C = C.*Ci;
        if any(i1 == ind)
            Dt = gpcf.cf{i1}.fh.ginput4(gpcf.cf{i1}, x, x2, dims);
            if length(Dt)>1
                error('gpcf_prod->ginput4: something is wrong')
            end
            K = K+Dt{1}./Ci;
        end
    end
    DKff = {C.*K};
else
    DKff = {0};
end

end

function C = gpcf_prod_cov(gpcf, x1, x2)
%GP_PROD_COV  Evaluate covariance matrix between two input vectors
%
%  Description
%    C = GP_PROD_COV(GP, TX, X) takes in covariance function of a
%    Gaussian process GP and two matrixes TX and X that contain
%    input vectors to GP. Returns covariance matrix C. Every
%    element ij of C contains covariance between inputs i in TX
%    and j in X. This is a mandatory subfunction used for example in
%    prediction and energy computations.
%
%
%  See also
%    GPCF_PROD_TRCOV, GPCF_PROD_TRVAR, GP_COV, GP_TRCOV

  if isempty(x2)
    x2=x1;
  end
  [n1,m1]=size(x1);
  [n2,m2]=size(x2);

  if m1~=m2
    error('the number of columns of X1 and X2 has to be same')
  end

  ncf = length(gpcf.cf);

  % evaluate the individual covariance functions
  C = 1;
  for i=1:ncf
    cf = gpcf.cf{i};
    C = C.*cf.fh.cov(cf, x1, x2);
  end
end

function C = gpcf_prod_trcov(gpcf, x)
%GP_PROD_TRCOV     Evaluate training covariance matrix of inputs
%
%  Description
%    C = GP_PROD_TRCOV(GP, TX) takes in covariance function of a
%    Gaussian process GP and matrix TX that contains training
%    input vectors. Returns covariance matrix C. Every element ij
%    of C contains covariance between inputs i and j in TX. This
%    is a mandatory subfunction used for example in prediction and
%    energy computations.
%
%  See also
%    GPCF_PROD_COV, GPCF_PROD_TRVAR, GP_COV, GP_TRCOV
  ncf = length(gpcf.cf);

  % evaluate the individual covariance functions
  C = 1;
  for i=1:ncf
    cf = gpcf.cf{i};
    C = C.*cf.fh.trcov(cf, x);
  end
end

function C = gpcf_prod_trvar(gpcf, x)
% GP_PROD_TRVAR     Evaluate training variance vector
%
%  Description
%    C = GP_PROD_TRVAR(GPCF, TX) takes in covariance function of
%    a Gaussian process GPCF and matrix TX that contains training
%    inputs. Returns variance vector C. Every element i of C
%    contains variance of input i in TX. This is a mandatory
%    subfunction used for example in prediction and energy computations.
%
%  See also
%    GPCF_PROD_COV, GP_COV, GP_TRCOV

  ncf = length(gpcf.cf);

  % evaluate the individual covariance functions
  C = 1;
  for i=1:ncf
    cf = gpcf.cf{i};
    C = C.*cf.fh.trvar(cf, x);
  end
end

function reccf = gpcf_prod_recappend(reccf, ri, gpcf)
%RECAPPEND  Record append
%
%  Description
%    RECCF = GPCF_PROD_RECAPPEND(RECCF, RI, GPCF) takes a
%    covariance function record structure RECCF, record index RI
%    and covariance function structure GPCF with the current MCMC
%    samples of the parameters. Returns RECCF which contains all
%    the old samples and the current samples from GPCF. This
%    subfunction is needed when using MCMC sampling (gp_mc).
%
%  See also
%    GP_MC, GP_MC->RECAPPEND

  if nargin == 2
    % Initialize the record
    reccf.type = 'gpcf_prod';

    % Initialize parameters
    ncf = length(ri.cf);
    for i=1:ncf
      cf = ri.cf{i};
      reccf.cf{i} = cf.fh.recappend([], ri.cf{i});
    end

    % Set the function handles
    reccf.fh.pak = @gpcf_prod_pak;
    reccf.fh.unpak = @gpcf_prod_unpak;
    reccf.fh.lp = @gpcf_prod_lp;
    reccf.fh.lpg = @gpcf_prod_lpg;
    reccf.fh.cfg = @gpcf_prod_cfg;
    reccf.fh.cov = @gpcf_prod_cov;
    reccf.fh.trcov  = @gpcf_prod_trcov;
    reccf.fh.trvar  = @gpcf_prod_trvar;
    reccf.fh.recappend = @gpcf_prod_recappend;
  else
    % Append to the record

    % Loop over all of the covariance functions
    ncf = length(gpcf.cf);
    for i=1:ncf
      cf = gpcf.cf{i};
      reccf.cf{i} = cf.fh.recappend(reccf.cf{i}, ri, cf);
    end
  end
end

function [F,L,Qc,H,Pinf,dF,dQc,dPinf,params] = gpcf_prod_cf2ss(gpcf,x)
%GPCF_PROD_CF2SS Convert the covariance function to state space form
%
%  Description
%    Convert the sum of two covariance functions to the corresponding
%    sum of two state space models. Details on how this is done can
%    be found in the reference:
%
%  References
%    Arno Solin and Simo Sarkka (2014). Explicit link between periodic
%    covariance functions and state space models. Accepted for
%    publication in Proceedings of the Seventeenth International
%    Conference on Artifcial Intelligence and Statistics (AISTATS 2014).
%

  % Check arguments
  if nargin < 2, x = []; end

  % Vector of function handles of conversion functions
  % from covariance functions to state space
  cf2ssvect = cell(length(gpcf.cf),1);
  for k = 1:length(gpcf.cf)

      % Initial function handle
      fh = @(y) gpcf.cf{k}.fh.cf2ss(gpcf.cf{k},x);

      % Deal with the periodic (deterministic) covariance function
      % as a special case
      if isequal(gpcf.cf{k}.type,'gpcf_periodic') && gpcf.cf{k}.decay == 0
        cf2ssvect{k} = @(y) cf2ss_periodicprod(fh);
      else
        cf2ssvect{k} = fh;
      end

  end

  % Return model matrices, derivatives and parameter information
  [F,L,Qc,H,Pinf,dF,dQc,dPinf,params] = ...
      cf_prod_to_ss(cf2ssvect);

end

function [F,L,Pinf1,H,Pinf,dF,dPinf,dPinf1,params] = cf2ss_periodicprod(fh)
% CF2SS_PERIODICPROD - Computes Qc values for periodic covariance production

 [F,L,Qc,H,Pinf,dF,dQc,dPinf,params] = fh();
 Pinf1 = Pinf; dPinf1 = dPinf;
end


function exact = p00_exact ( prob )

%*****************************************************************************80
%
%% P00_EXACT returns the exact integral for any problem.
%
%  Discussion:
%
%    This routine provides a "generic" interface to the exact integral
%    routines for the various problems, and allows a problem to be called
%    by number (PROB) rather than by name.
%
%    In some cases, the "exact" value of the integral is in fact
%    merely a respectable approximation.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    04 November 2009
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer PROB, the number of the desired test problem.
%
%    Output, real EXACT, the exact value of the integral.
%
  if ( prob == 1 )
    exact = p01_exact ( );
  elseif ( prob == 2 )
    exact = p02_exact ( );
  elseif ( prob == 3 )
    exact = p03_exact ( );
  elseif ( prob == 4 )
    exact = p04_exact ( );
  elseif ( prob == 5 )
    exact = p05_exact ( );
  elseif ( prob == 6 )
    exact = p06_exact ( );
  elseif ( prob == 7 )
    exact = p07_exact ( );
  elseif ( prob == 8 )
    exact = p08_exact ( );
  elseif ( prob == 9 )
    exact = p09_exact ( );
  elseif ( prob == 10 )
    exact = p10_exact ( );
  elseif ( prob == 11 )
    exact = p11_exact ( );
  elseif ( prob == 12 )
    exact = p12_exact ( );
  elseif ( prob == 13 )
    exact = p13_exact ( );
  elseif ( prob == 14 )
    exact = p14_exact ( );
  elseif ( prob == 15 )
    exact = p15_exact ( );
  elseif ( prob == 16 )
    exact = p16_exact ( );
  elseif ( prob == 17 )
    exact = p17_exact ( );
  elseif ( prob == 18 )
    exact = p18_exact ( );
  elseif ( prob == 19 )
    exact = p19_exact ( );
  elseif ( prob == 20 )
    exact = p20_exact ( );
  elseif ( prob == 21 )
    exact = p21_exact ( );
  elseif ( prob == 22 )
    exact = p22_exact ( );
  elseif ( prob == 23 )
    exact = p23_exact ( );
  elseif ( prob == 24 )
    exact = p24_exact ( );
  elseif ( prob == 25 )
    exact = p25_exact ( );
  elseif ( prob == 26 )
    exact = p26_exact ( );
  elseif ( prob == 27 )
    exact = p27_exact ( );
  elseif ( prob == 28 )
    exact = p28_exact ( );
  elseif ( prob == 29 )
    exact = p29_exact ( );
  elseif ( prob == 30 )
    exact = p30_exact ( );
  elseif ( prob == 31 )
    exact = p31_exact ( );
  elseif ( prob == 32 )
    exact = p32_exact ( );
  elseif ( prob == 33 )
    exact = p33_exact ( );
  elseif ( prob == 34 )
    exact = p34_exact ( );
  elseif ( prob == 35 )
    exact = p35_exact ( );
  elseif ( prob == 36 )
    exact = p36_exact ( );
  elseif ( prob == 37 )
    exact = p37_exact ( );
  elseif ( prob == 38 )
    exact = p38_exact ( );
  elseif ( prob == 39 )
    exact = p39_exact ( );
  elseif ( prob == 40 )
    exact = p40_exact ( );
  elseif ( prob == 41 )
    exact = p41_exact ( );
  elseif ( prob == 42 )
    exact = p42_exact ( );
  elseif ( prob == 43 )
    exact = p43_exact ( );
  elseif ( prob == 44 )
    exact = p44_exact ( );
  elseif ( prob == 45 )
    exact = p45_exact ( );
  elseif ( prob == 46 )
    exact = p46_exact ( );
  elseif ( prob == 47 )
    exact = p47_exact ( );
  elseif ( prob == 48 )
    exact = p48_exact ( );
  elseif ( prob == 49 )
    exact = p49_exact ( );
  elseif ( prob == 50 )
    exact = p50_exact ( );
  elseif ( prob == 51 )
    exact = p51_exact ( );
  elseif ( prob == 52 )
    exact = p52_exact ( );
  elseif ( prob == 53 )
    exact = p53_exact ( );
  elseif ( prob == 54 )
    exact = p54_exact ( );
  elseif ( prob == 55 )
    exact = p55_exact ( );
  elseif ( prob == 56 )
    exact = p56_exact ( );
  elseif ( prob == 57 )
    exact = p57_exact ( );
  elseif ( prob == 58 )
    exact = p58_exact ( );
  elseif ( prob == 59 )
    exact = p59_exact ( );
  elseif ( prob == 60 )
    exact = p60_exact ( );
  elseif ( prob == 61 )
    exact = p61_exact ( );
  elseif ( prob == 62 )
    exact = p62_exact ( );
  elseif ( prob == 63 )
    exact = p63_exact ( );
  elseif ( prob == 64 )
    exact = p64_exact ( );
  elseif ( prob == 65 )
    exact = p65_exact ( );
  elseif ( prob == 66 )
    exact = p66_exact ( );
  elseif ( prob == 67 )
    exact = p67_exact ( );
  elseif ( prob == 68 )
    exact = p68_exact ( );
  elseif ( prob == 69 )
    exact = p69_exact ( );
  elseif ( prob == 70 )
    exact = p70_exact ( );
  elseif ( prob == 71 )
    exact = p71_exact ( );
  elseif ( prob == 72 )
    exact = p72_exact ( );
  elseif ( prob == 73 )
    exact = p73_exact ( );
  elseif ( prob == 74 )
    exact = p74_exact ( );
  elseif ( prob == 75 )
    exact = p75_exact ( );
  elseif ( prob == 76 )
    exact = p76_exact ( );
  else
    fprintf ( 1, '\n' );
    fprintf ( 1, 'P00_EXACT - Fatal error!\n' );
    fprintf ( 1, '  Illegal problem number = %d\n', prob );
    error ( 'P00_EXACT - Fatal error!' );
  end

  return
end


function map = black2whiteColorMap(varargin)
%Linear white to black gray-scale color map

map = makeColorMap([0 0 0],[0.7 0.7 0.7],[1 1 1]);


function cs_install (do_pause)
%CS_INSTALL: compile and install CXSparse for use in MATLAB.
%   Your current working directory must be CXSparse/MATLAB in order to use this
%   function.
%
%   The directories
%
%       CXSparse/MATLAB/CSparse
%       CXSparse/MATLAB/Demo
%       CXSparse/MATLAB/UFget
%
%   are added to your MATLAB path (see the "pathtool" command to add these to
%   your path permanently, for future MATLAB sessions).
%
%   Next, the MATLAB CXSparse demo program, CXSparse/MATLAB/cs_demo is executed.
%   To run the demo with pauses so you can see the results, use cs_install(1).
%   To run the full MATLAB test programs for CXSparse, run testall in the
%   Test directory.
%
%   Example:
%       cs_install          % install and run demo with no pauses
%       cs_install(1)       % install and run demo with pauses
%
%   See also: cs_demo
%
%   Copyright 2006-2007, Timothy A. Davis.
%   http://www.cise.ufl.edu/research/sparse

fprintf ('Compiling and installing CXSparse\n') ;
if (nargin < 1)
    do_pause = 0 ;
end

if (do_pause)
    input ('Hit enter to continue: ') ;
end
addpath ([pwd filesep 'CSparse']) ;
addpath ([pwd filesep 'Demo']) ;

v = getversion ;
if (v >= 7.0)
    addpath ([pwd filesep 'UFget']) ;
else
    fprintf ('UFget not installed (MATLAB 7.0 or later required)\n') ;
end

cd ('CSparse') ;
cs_make (1) ;
cd ('../Demo') ;
cs_demo (do_pause)

%-------------------------------------------------------------------------------
function v = getversion
% determine the MATLAB version, and return it as a double.
v = sscanf (version, '%d.%d.%d') ;
v = 10.^(0:-1:-(length(v)-1)) * v ;


function scan_data = rdprfile(ufilename,plotmode,verbose)
%RDPRFILE Reads data from a profilometer data file
%
% DATA = RDPRFILE(UFILENAME,VERBOSE)
%
% RDPRFILE examines the header in a profilometer data file and attempts to
%  determine the type of data file. If a supported type is found, it then
%  uses the appropriate function to open the file and return the data.
%  Returns SCAN_DATA=[] if unable to determine the file type.
%
% Supported profilometers:
%  Dektak IIA
%  AlphaStep 200
%  Alphastep 500
%  Dektak 3ST
%  Veeco Dektak 8
%  Bruker Dektak XT
%  Tencor P-10
%
% Options:
% If VERBOSE == 0, no messages will be displayed.
% If VERBOSE == 1, the data summary will be displayed. (default)
% If VERBOSE == 2, the file header and data summary will be displayed.
% If PLOTMODE == 0, the data will not be displayed.
% if PLOTMODE == 1, the data will be displayed in a new figure. (default)
%
% See also dk2rd.m, as2rd.m, as5rd.m, dk3rd.m, dk8rd.m, dkXTrd.m, p10rd.m
%
% M.A. Hopcroft
%  mhopeng at gmail dot com
%
% The license governing use of this code is in the accompanying file
%  "license.txt".
%
versionstr='rdprfile 1.1';

%
% MH Jul2013
% v1.1  add Dektak XT
% MH Sep2012
% v1.0  works

% handle input arguments - set defaults
% default to verbose mode 1 (summary only)
if nargin < 3
    verbose = 1;
end

% default to plot mode 1
if nargin < 2
    plotmode = 1;
end

% get interactive if not being called by another program
if (nargout == 0)
    if (verbose >= 1 && nargin == 0)
        clc;
        fprintf(1, '\n\n  %s\n  %s\n\n', versionstr, 'Load profilometer data files');
    end
end

%%%%
%% open user's file
if nargin >= 1 % if the user already specified a file, use it
    userfilename=ufilename;
    % check to see if file exists
    cfid=fopen(userfilename);
    if cfid== -1
        if cfid== -1
            disp(pwd);
            fprintf(1,'ERROR: data file ["%s"] not found in current directory',userfilename);
            scan_data=[];
            return
        end
        fclose(cfid);
    end

else % if user did not specify the file name, ask for one
    [ufilename, ufilepath]=uigetfile({'.txt','.csv'},'Select the Profilometer data file');
    if isequal(ufilename,0)
        scan_data=[];
        return
    end
    userfilename=fullfile(ufilepath,ufilename);
    %userfilename=input(' Enter the name of the profilometer data file: ','s');
end

% open the file
userfile=fopen(userfilename);

% % %
%% read lines from the header until we have an answer
if (nargout == 0), fprintf(1,'\n '); end % screen formatting for interactive mode

%% read the first line in the file
linein1=fgetl(userfile);
while isempty(linein1), linein1=fgetl(userfile); end % handle spurious carriage returns
if verbose >= 2, fprintf(1,'%s\n',linein1); end

% is this a Dektak II file?
if strfind(linein1,'SLOAN DEKTAK II')
    fprintf(1,'rdprfile: This appears to be a data file from a Dektak IIA\n');
    fclose(userfile);
    scan_data=dk2rd(userfilename,plotmode,verbose);
    return
end

% is this a Dektak XT file?
if strfind(linein1,'Meta Data')
    fprintf(1,'rdprfile: This appears to be a data file from a Dektak XT\n');
    fclose(userfile);
    scan_data=dkXTrd(userfilename,plotmode,verbose);
    return
end

%% read another line
linein2=fgetl(userfile);
while isempty(linein2), linein2=fgetl(userfile); end % handle spurious carriage returns
if verbose >= 2, fprintf(1,'%s\n',linein2); end

% is this a AlphaStep 200 file?
if length(linein2)>=4 && strcmp(linein2(1:4),'VERT')
    fprintf(1,'rdprfile: This appears to be a data file from an AlphaStep 200\n');
    fclose(userfile);
    scan_data=as2rd(userfilename,plotmode,verbose);
    return
end

% is this a AlphaStep 500 file?
dbl1=strtrim(linein1);
dbl2=strtrim(linein2);
if strcmp(dbl1(1),'A') && strcmp(dbl2(1),'B')
    fprintf(1,'rdprfile: This appears to be a data file from an AlphaStep 500\n');
    fclose(userfile);
    scan_data=as5rd(userfilename,plotmode,verbose);
    return
end

% is this a Tencor P-10 file?
if strcmp(linein1(1),'*') && strfind(linein2,'data points')
    fprintf(1,'rdprfile: This appears to be a data file from a Tencor P-10\n');
    fclose(userfile);
    scan_data=p10rd(userfilename,plotmode,verbose);
    return
end

% is this a Dektak 3ST file?
if length(linein1)>=9 && strcmp(linein1(1:9),'DATA FILE') && length(linein2)>=9 && strcmp(linein2(1:9),'AUTO PROG')
    fprintf(1,'rdprfile: This appears to be a data file from a Dektak 3ST\n');
    fclose(userfile);
    scan_data=dk3rd(userfilename,plotmode,verbose);
    return
end

%% read more lines
linein3=fgetl(userfile);
while isempty(linein3), linein3=fgetl(userfile); end
if verbose >= 2, fprintf(1,'%s\n',linein3); end
linein4=fgetl(userfile);
while isempty(linein4), linein4=fgetl(userfile); end
if verbose >= 2, fprintf(1,'%s\n',linein4); end
linein5=fgetl(userfile);
while isempty(linein5), linein5=fgetl(userfile); end
if verbose >= 2, fprintf(1,'%s\n',linein5); end

% is this a Dektak 8 file?
if length(linein4)>=9 && strcmp(linein4(1:9),'DATA FILE') && length(linein5)>=4 && strcmpi(linein5(1:4),'Date')
    fprintf(1,'rdprfile: This appears to be a data file from an Dektak 8\n');
    fclose(userfile);
    scan_data=dk8rd(userfilename,plotmode,verbose);
    return
end

% fail
fprintf(1,'rdprfile: Unable to determine file type!\n');
scan_data=[];


function neqn = p01_neqn ( )

%*****************************************************************************80
%
%% P01_NEQN returns the number of equations for problem P01.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    17 February 2013
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Output, integer NEQN, the number of equations.
%
  neqn = 1;

  return
end


function [nWritten] = write_plx(filename, ch, freq, npw, n, ts, wave, units)
% write_plx: Write waveform data to a .plx file
%
% [nWritten] = write_plx(filename, ch, freq, npw, n, ts, wave, units)
%
% INPUT:
%   filename - if empty string, will use File Save dialog
%   ch - 1-based channel number that will be written to the .plx file
%   freq - the timestamp frequency in Hz
%   npw - number of points in each waveform
%   n - number of waveforms
%   ts - array of n timestamps (in seconds)
%   wave - array of waveforms [npw, n], raw a/d values
%   units - array of n unit assignments for each waveform, 0 = unsorted
% OUTPUT:
%   nWritten - number of waveforms successfully written to the output file
%
% This function will write a .plx file that consists of a single channel
% of spike data only. It is not intended to solve the general problem of
% writing out .plx files from Matlab. This code is intended to provide a
% starting point that can be customized as needed.
%
% Some of the limitations of this function include:
% - There is no facility to write multiple channels, slow a/d data, or event data.
% - Lots of assumptions are made (no comment, non-trodal, prethresh = npw/4,
% the header date is today, etc)
% - The count arrays in the header (TSCounts, WFCounts, EVCounts) are not
% filled (this is fixable with PlexUtil)
% - the gain for the channel is set to 1, which will result in the
% reconstructed voltages for the spikes to be different.
% - unit templates are not written out, they can be reconstructed with OFS
% - long timestamps are not supported
%
% Note that the PlexUtil program from Plexon can merge the output files
% from this routine together to form multi-channel files.
%
%

if(length(filename) == 0)
   [fname, pathname] = uigetfile('*.plx', 'Select a plx file');
	filename = strcat(pathname, fname);
end

pad256(1:256) = uint8(0);

% create the file and write the file header

plx_id = fopen(filename, 'w');
fwrite(plx_id, 1480936528, 'integer*4');    % 'PLEX' magic code
fwrite(plx_id, 101, 'integer*4');           % the version no.
fwrite(plx_id, pad256(1:128), 'char');      % placeholder for comment
fwrite(plx_id, freq, 'integer*4');          % timestamp frequency
fwrite(plx_id, 1, 'integer*4');             % no. of DSP channels
fwrite(plx_id, 0, 'integer*4');             % no. of event channels
fwrite(plx_id, 0, 'integer*4');             % no. of A/D (slow-wave) channels
fwrite(plx_id, npw, 'integer*4');           % no. points per waveform
fwrite(plx_id, npw/4, 'integer*4');         % (fake) no. pre-threshold points
[YR, MO, DA, HR, MI, SC] = datevec(now);    % current date & time
fwrite(plx_id, YR, 'integer*4');            % year
fwrite(plx_id, MO, 'integer*4');            % month
fwrite(plx_id, DA, 'integer*4');            % day
fwrite(plx_id, HR, 'integer*4');            % hour
fwrite(plx_id, MI, 'integer*4');            % minute
fwrite(plx_id, SC, 'integer*4');            % second
fwrite(plx_id, 0, 'integer*4');             % fast read (reserved)
fwrite(plx_id, freq, 'integer*4');          % waveform frequency
fwrite(plx_id, max(ts)*freq, 'double');     % last timestamp
fwrite(plx_id, pad256(1:56), 'char');       % should make 256 bytes

% now the count arrays (with counts of zero)

for i = 1:40
  fwrite(plx_id, pad256(1:130), 'char');    % first 20 are TSCounts, next 20 are WFCounts
end
for i = 1:8
  fwrite(plx_id, pad256(1:256), 'char');    % all of these make up EVCounts
end

% now the single PL_ChanHeader

% assume simple channel names
[DSPname, errmsg] = sprintf('DSP%d',ch);
[SIGname, errmsg] = sprintf('SIG%d',ch);

fwrite(plx_id, DSPname, 'char');
fwrite(plx_id, pad256(1:32-length(DSPname)));
fwrite(plx_id, SIGname, 'char');
fwrite(plx_id, pad256(1:32-length(SIGname)));
fwrite(plx_id, ch, 'integer*4');            % DSP channel number
fwrite(plx_id, 0, 'integer*4');             % waveform rate limit (not used)
fwrite(plx_id, ch, 'integer*4');            % SIG associated channel number
fwrite(plx_id, ch, 'integer*4');            % SIG reference  channel number
fwrite(plx_id, 1, 'integer*4');             % dummy for gain
fwrite(plx_id, 0, 'integer*4');             % filter off
fwrite(plx_id, -12, 'integer*4');           % (fake) detection threshold value
fwrite(plx_id, 0, 'integer*4');             % sorting method (dummy)
fwrite(plx_id, 0, 'integer*4');             % number of sorted units
for i = 1:10
  fwrite(plx_id, pad256(1:64), 'char');     % filler for templates (5 * 64 * short)
end
fwrite(plx_id, pad256(1:20), 'char');       % template fit (5 * int)
fwrite(plx_id, npw, 'integer*4');           % sort width (template only)
fwrite(plx_id, pad256(1:80), 'char');       % boxes (5 * 2 * 4 * short)
fwrite(plx_id, 0, 'integer*4');             % beginning of sorting window
fwrite(plx_id, pad256(1:128), 'char');      % comment
fwrite(plx_id, pad256(1:44), 'char');       % padding

% now the spike waveforms, each preceded by a PL_DataBlockHeader

for ispike = 1:n

  fwrite(plx_id, 1, 'integer*2');           % type: 1 = spike
  fwrite(plx_id, 0, 'integer*2');           % upper byte of 5-byte timestamp
  fwrite(plx_id, ts(ispike)*freq, 'integer*4');  % lower 4 bytes
  fwrite(plx_id, ch, 'integer*2');          % channel number
  fwrite(plx_id, units(ispike), 'integer*2');  % unit no. (0 = unsorted)
  fwrite(plx_id, 1, 'integer*2');           % no. of waveforms = 1
  fwrite(plx_id, npw, 'integer*2');         % no. of samples per waveform

  fwrite(plx_id, wave(ispike, 1:npw), 'integer*2');

end

nWritten = n;

fclose(plx_id);


function s = yjf_to_s_hebrew ( y, j, f )

%*****************************************************************************80
%
%% YJF_TO_S_HEBREW writes a Hebrew YJF date into a string.
%
%  Format:
%
%    AM YYYY/JJJ.FF
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    07 March 2013
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer Y, J, real F, the YJF date.
%
%    Output, string S, the representation of the date.
%
  s = sprintf ( 'AM %d/%f', y, j + f );

  return
end


function scimat2 = scimat_papillary_muscles(scimat, NPAPS)
% SCIMAT_PAPILLARY_MUSCLES  Extract the papillay muscles from a
% segmentation of the Left Ventricle's cavity.
%
% SCIMAT = scimat_papillary_muscles(SCIMAT)
%
%   This function extracts a segmentation of the papillary muscles from the
%   segmentation of a Left Ventricular cavity. In fact, the papillary
%   muscles are extraced, plus some bits of the wall that don't belong to
%   them, and instead of stopping at the chordae tendineae, the
%   segmentation continues and selects some trabeculations.
%
%   SCIMAT is the SCIMAT struct with the segmentation (see "help scimat"
%   for details).
%
% SCIMAT = scimat_papillary_muscles(SCIMAT, NPAPS)
%
%   NPAPS is a constant with the number of papillary muscles we want to
%   extract. By default, NPAPS=2.

% Author: Ramon Casero <rcasero@gmail.com>
% Copyright © 2010-2014 University of Oxford
% Version: 0.2.0
%
% University of Oxford means the Chancellor, Masters and Scholars of
% the University of Oxford, having an administrative office at
% Wellington Square, Oxford OX1 2JD, UK.
%
% This file is part of Gerardus.
%
% This program is free software: you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% (at your option) any later version.
%
% This program is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
% GNU General Public License for more details. The offer of this
% program under the terms of the License is subject to the License
% being interpreted in accordance with English Law and subject to any
% action against the University of Oxford being under the jurisdiction
% of the English Courts.
%
% You should have received a copy of the GNU General Public License
% along with this program.  If not, see <http://www.gnu.org/licenses/>.

% check arguments
narginchk(1, 2);
nargoutchk(0, 1);

% defaults
if (nargin < 2 || isempty(NPAPS))
    NPAPS = 2;
end

% remove the dummy dimension
scimat = scimat_squeeze(scimat);

% get a tight box around the LV segmentation
box = scimat_box(scimat);

% convert real world coordinates to indices
boxi = scimat_world2index(box', scimat.axis)';

% get data volume size
sz = size(scimat.data);

% create output segmentation volume
scimat2 = scimat;
scimat2.data(:) = 0;

%% Compute convex hull for the segmentation in each slice, and remove the
%% segmentation. This way, we get the papillary muscles together with some
%% rubbish where the convex hull fills in incorrectly a region outside the
%% cavity edge

% element for morphological operator
se1 = strel('disk', 1);

% loop every slice with at least one LV cavity voxel
for I = min(boxi(3,:)):max(boxi(3,:))

    % connected components filter (nlabs is the number of labels w/o counting
    % the background)
    [ im, nlabs ] = bwlabel(scimat.data(:, :, I));

    % get number of voxels in each label
    nvox = zeros(nlabs, 1);
    for L = 1:nlabs
        nvox(L) = sum(im(:) == L);
    end

    % we assume that the largest connected component is the cavity
    [ nvoxmax, idx ] = max(nvox);

    % it only makes sense to compute the convex hull if there are at least
    % three pixels
    if (nvoxmax >= 3)

        % we delete the rest of voxels; even if they are part of the cavity, we
        % don't want them interfering with the convex hull
        im = (im == idx);

        % get linear indices of cavity pixels
        idx = find(im);

        % convert linear index to multiple subscripts
        [ir, ic] = ind2sub(sz(1:2), idx);

        % compute the convex hull
        idxhull = convhull(ir, ic);

        % get all the pixels inside of convex hull
        hull = roipoly(im, ic(idxhull), ir(idxhull));

        % we are going to erode the hull polygon 1 pixel, to help in some
        % cases to avoid that the fringe of the next XOR operation are
        % connected between them
        hull = imerode(hull, se1);

        % put the new segmentation into the data volume
        scimat2.data(:, :, I) = bitxor(im, hull);

    end

end

%% Now we are going to get a guess at which bits are papillary muscle and
%% which bits are rubbish. For this, we start start at the middle slice,
%% because this one is likely to show the papillary muscles well. We assume
%% that the largest connected components belong to the papillary muscles,
%% and then look for the components that are touching the previous ones in
%% the next slice

% extract the middle slice
im = scimat2.data(:, :, round(mean(boxi(3,:))));

% compute connected components
[im, nlabs] = bwlabel(im);

% get number of voxels in each label
nvox = zeros(nlabs, 1);
for L = 1:nlabs
    nvox(L) = sum(im(:) == L);
end

% order connected components by decreasing area size
[nvox, idxlabs] = sort(nvox, 1, 'descend');

% the number of papillary muscles at the middle of the cavity may reduce as
% we move towards the end of the volume, and the muscles fuse together
npaps = NPAPS;

% keep only a number of largest components, each one assumed to belong to a
% papillary muscle
im2 = im*0;
for I = 1:npaps
    im2(im == idxlabs(I)) = I;
end

for I = round(mean(boxi(3,:)))+1:max(boxi(3,:))

    % previous slice
    im1 = im2;

    % initialize mask that we are going to use to discard pixels that are
    % far from the pixels in the previous slice
    im1mask = im1*0;

    % dilate each previous papillary muscle component to create a mask for
    % the region of interest
    for L = 1:npaps

        % isolate current component
        im1comp = im1 == L;

        % number of voxels in the component
        nvox = length(find(im1comp));

        % assuming a quasi circular shape, then the area is a=pi*r^2. Because
        % the area is equal to the number of pixels, we can compute the radius
        % of the component as r=sqrt(nvox/pi)
        rad = sqrt(nvox / pi);

        % we are going to allow the component to search for a new component
        % within an area that is the 25% dilation of the old component; but
        % first we do an erosion of 25% to remove spurious bits of
        % segmentation
        im1comp = imerode(im1comp, strel('disk', round(rad*0.25)));

        % dilate the previous component
        im1comp = imdilate(im1comp, strel('disk', round(rad*0.50)));

        % add dilated component to the mask
        im1mask = im1mask | im1comp;
    end

    % remove all pixels from the new slice that are outside of the mask
    scimat2.data(:, :, I) = scimat2.data(:, :, I) & im1mask;

    % compute connected components in the new slice
    [scimat2.data(:, :, I), nlabs2] = bwlabel(scimat2.data(:, :, I));

    % we need a temporal image to store the new papillary bits
    im2 = scimat2.data(:, :, I) * 0;

    % make sure that we are not in a slice without any segmented pixels
    if (nlabs2)

        % matrix to keep track of the intersection of each papillary bit
        % with every connected component
        sim = zeros(npaps, nlabs2);

        % intersect each papillary bit with each connected component bit
        for L = 1:npaps
            % compute how many pixels of the new component overlap the old
            % component
            for C = 1:nlabs2
                sim(L, C) = length(find((im1 == L) ...
                    & (scimat2.data(:, :, I) == C)));
            end
        end

        % for each papillary, select the connected component with the
        % largest intersection with the previous slice's bit
        for L = 1:npaps
            [foo, Lnew] = max(sim(L, :));
            im2(scimat2.data(:, :, I) == Lnew) = L;
        end

    end

    % overrite the segmentation volume with just the papillary bits
    scimat2.data(:, :, I) = im2;

    % recheck the number of components now in the slice (because the
    % papillary muscles can fuse together)
    [ foo, npaps ] = bwlabel(scimat2.data(:, :, I));

end

% keep only a number of largest components, each one assumed to belong to a
% papillary muscle
im2 = im*0;
for I = 1:NPAPS
    im2(im == idxlabs(I)) = I;
end

% the number of papillary muscles at the middle of the cavity may reduce as
% we move towards the end of the volume, and the muscles fuse together
npaps = NPAPS;

for I = round(mean(boxi(3,:))):-1:min(boxi(3,:))

    % previous slice
    im1 = im2;

    % initialize mask that we are going to use to discard pixels that are
    % far from the pixels in the previous slice
    im1mask = im1*0;

    % dilate each previous papillary muscle component to create a mask for
    % the region of interest
    for L = 1:npaps

        % isolate current component
        im1comp = im1 == L;

        % number of voxels in the component
        nvox = length(find(im1comp));

        % assuming a quasi circular shape, then the area is a=pi*r^2. Because
        % the area is equal to the number of pixels, we can compute the radius
        % of the component as r=sqrt(nvox/pi)
        rad = sqrt(nvox / pi);

        % we are going to allow the component to search for a new component
        % within an area that is the 25% dilation of the old component; but
        % first we do an erosion of 25% to remove spurious bits of
        % segmentation
        im1comp = imerode(im1comp, strel('disk', round(rad*0.25)));

        % dilate the previous component
        im1comp = imdilate(im1comp, strel('disk', round(rad*0.50)));

        % add dilated component to the mask
        im1mask = im1mask | im1comp;
    end

    % remove all pixels from the new slice that are outside of the mask
    scimat2.data(:, :, I) = scimat2.data(:, :, I) & im1mask;

    % compute connected components in the new slice
    [scimat2.data(:, :, I), nlabs2] = bwlabel(scimat2.data(:, :, I));

    % we need a temporal image to store the new papillary bits
    im2 = scimat2.data(:, :, I) * 0;

    % make sure that we are not in a slice without any segmented pixels
    if (nlabs2)

        % matrix to keep track of the intersection of each papillary bit
        % with every connected component
        sim = zeros(npaps, nlabs2);

        % intersect each papillary bit with each connected component bit
        for L = 1:npaps
            % compute how many pixels of the new component overlap the old
            % component
            for C = 1:nlabs2
                sim(L, C) = length(find((im1 == L) ...
                    & (scimat2.data(:, :, I) == C)));
            end
        end

        % for each papillary, select the connected component with the
        % largest intersection with the previous slice's bit
        for L = 1:npaps
            [foo, Lnew] = max(sim(L, :));
            im2(scimat2.data(:, :, I) == Lnew) = L;
        end

    end

    % overrite the segmentation volume with just the papillary bits
    scimat2.data(:, :, I) = im2;

    % recheck the number of components now in the slice (because the
    % papillary muscles can fuse together)
    [ foo, npaps ] = bwlabel(scimat2.data(:, :, I));

end


function [] = aps_spectrometer_PWV_modis(batchfile)
% aps_spectrometer_PWV_modis(batchfile)
% Scipt to load modis data, mask out clouds. The modis data is assumed to
% be structured in date folders. The batchfile contains the full path to the
% modis files in these folders. Note that the first line of the batchfile
% should read "files".
%

% INPUTS used:
% batchfile             A txt file containing the full path and file names of the
%                       meris data that needs to be processed. The first
%                       line of this file should read "files". The data
%                       should be structured in date folders.
% xlims                 Limits in the x-direction, either in degrees
% ylims                 Limits in the y-direction, either in degrees
% smpres                The output resolution, either in degrees
%                       Units needs to be consistend with xlims and ylims.
%
% By David Bekaert - University of Leeds
% August 2014

% setting the defaults and checking the input arguments
if nargin<1
    fprintf('aps_spectrometer_PWV_modis(batchfile) \n')
    error('myApp:argChk', ['Not enough input arguments...\n'])
end

smpres = getparm_aps('region_res',1);          % in degrees
xlims = getparm_aps('region_lon_range',1);
ylims = getparm_aps('region_lat_range',1);
stamps_processed = getparm_aps('stamps_processed',1);

%% the actual scripting
%bounds for all ifgms in degrees or in meters
xmin = xlims(1);
xmax = xlims(2);
ymin = ylims(1);
ymax = ylims(2);

% getting the number of files to be processed
files = char(textread(batchfile,'%s','headerlines',1));
ix_keep = [];
for counter=1:size(files,1)
    if isempty(strfind(files(counter,:),'recal'));
       ix_keep = [ix_keep; counter];
    end
end
files = files(ix_keep,:);
% exclude recalibrated files
ndates = size(files,1);

% loading the date information
if strcmp(stamps_processed,'y')
   ps = load(getparm_aps('ll_matfile',1));
   ifgs_dates = ps.day;
   fprintf('Stamps processed structure \n')
else
    ifgday_matfile = getparm_aps('ifgday_matfile',1);
    ifgs_dates = load(ifgday_matfile);
    ifgs_dates = ifgs_dates.ifgday;
    ifgs_dates = reshape(ifgs_dates,[],1);
    ifgs_dates = unique(ifgs_dates);
end

fprintf(['Lon range: ' num2str(xmin) ' -- '  num2str(xmax) ' degrees\n'])
fprintf(['Lat range: ' num2str(ymin) ' -- '  num2str(ymax) ' degrees\n'])
fprintf(['Output resolution is assumed to be ' num2str(smpres) ' degrees \n'])

% extracting the dates from the filenames
for k=1:ndates
    [path,filename_temp,ext_temp] = fileparts(files(k,:));
    clear filename_temp ext_temp
    [path_temp,date,ext_temp] = fileparts(path);
    clear path_temp ext_temp

    % save the paths as structures to allow for variable path lengths
    pathlist{k} = path;
    clear path
    % saving the date information
    datelist(k,:) =date;
    clear date
end

%start loop here to calculate atmos correction for each date
fprintf('Starting the masking and writing of the PWV for each SAR date \n')
for n = 1:ndates

    file = [files(n,:)];
    outfile_watervapor = [pathlist{n} filesep datelist(n,:) '_ZPWV_nointerp.xyz'];
    [lon_grid,lat_grid,watervap] = grdread2(file);          % g/cm^2 water vapour

    D = grdinfo2(file);
    mod_xmin = D(1);
    mod_xmax = D(2);
    mod_ymin = D(3);
    mod_ymax = D(4);
    mod_cols = length(lon_grid);
    mod_rows = length(lat_grid);
    clear D

    %% Calculate wet delay
    %mask out dodgy pixels
    grdwrite2(lon_grid,lat_grid,watervap,'tmp.grd')

    % downsample and output without interpolation
    grdsmp_cmd = ['grdsample -R',num2str(xmin),'/',num2str(xmax),'/',num2str(ymin),'/',num2str(ymax),' -I',num2str(smpres),' -F tmp.grd -Gtmp_smp.grd'];
    [a,b]=system(grdsmp_cmd);
    grd2xyz_cmd = ['grd2xyz -R',num2str(xmin),'/',num2str(xmax),'/',num2str(ymin),'/',num2str(ymax),' tmp_smp.grd -bo >' outfile_watervapor];
    [a,b]=system(grd2xyz_cmd);

    % load nointerp back in and write out
    nointfid = fopen(outfile_watervapor,'r');
    data_vector = fread(nointfid,'double');
    fclose(nointfid);
    % reshaping into the right n column matrix
    data = reshape(data_vector,3,[])';
    noint = data(:,3);
    xy = data(:,[1:2]);
    clear data data_vector

%     figure; scatter3(xy(:,1),xy(:,2),noint,15,noint,'filled'); view(0,90); axis equal; axis tight

    %output
    data_write = [xy noint]';
    fid = fopen(outfile_watervapor,'w');
    fwrite(fid,data_write,'double');
    fclose(fid);
    clear data_write

    fprintf([num2str(n) ' completed out of ' num2str(ndates) '\n'])
end

[a,b] = system('rm tmp.grd tmp.xyz tmp2.grd tmp_fil.grd tmp_smp.grd tmp_smp2.grd tmp_smp_fil.grd');


function [ h, hmin, hmax ] = p01_stepsize ( option )

%*****************************************************************************80
%
%% P01_STEPSIZE returns step sizes for problem 1.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    24 September 2008
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer OPTION, the option index.
%
%    Output, real H, HMIN, HMAX, suggested values for the
%    initial step, the minimum step, and the maximum step.
%
  h =    0.30000;
  hmin = 0.03125;
  hmax = 4.00000;

  return
end


function UFpages (figures, list)
%UFPAGES create web page for each matrix in UF Sparse Matrix Collection
% Usage: UFpages (figures, list)
%
% figures: 1 if figures are to be created, 0 otherwise
% list: list of matrix id's to process.  All matrices in the collection are
%   processed if not present.
%
% Example:
%
%   UFpages             % create all the pages
%   UFpages (0)         % create all the pages, but not the figures
%   UFpages (1,1:10)    % create pages for just matrices 1 to 10
%   UFpages (0,1:10)    % ditto, but do not create the figures
%
% See also UFpage, UFget.

% Copyright 2006-2007, Timothy A. Davis

if (nargin < 1)
    figures = 1 ;
end
index = UFget ;
if (nargin < 2)
    list = 1:length (index.nrows) ;
end
for i = list
    UFpage (i, index, figures) ;
end


function AdjustAxes(f,varargin)

%AdjustAxes - Adjust axes limits for all subplots
%
%  USAGE
%
%    AdjustAxes(f,<options>)
%
%    f              optional figure handle (default = gcf)
%    <options>      optional list of property-value pairs (see table below)
%
%    =========================================================================
%     Properties    Values
%    -------------------------------------------------------------------------
%     'x'           [min max], 'uniform' (same for all axes) or 'auto'
%     'y'           [min max], 'uniform' (same for all axes) or 'auto'
%     'xy'          [min max], 'uniform' (same for all axes) or 'auto'
%    =========================================================================
%
%  DEFAULTS
%
%    When no option is provided, all axes are automatically adjusted (same as
%    xlim('xy','uniform')). When 'x' is provided alone, y axes are left unchanged
%    and vice versa.
%

% Copyright (C) 2012 by Michaël Zugaro
%
% This program is free software; you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation; either version 3 of the License, or
% (at your option) any later version.

% Default values
xlims = [];
ylims = [];

% Optional parameter
if nargin < 1,
	f = gcf;
elseif ischar(f),
	varargin = {f,varargin{:}};
	f = gcf;
elseif ~ishandle(f),
	error('Incorrect value for ''f'' (type ''help AdjustAxes'' for details).');
end

% Parse parameter list
for i = 1:2:length(varargin),
  if ~ischar(varargin{i}),
    error(['Parameter ' num2str(i+1) ' is not a property (type ''help AdjustAxes'' for details).']);
  end
  switch(lower(varargin{i})),
    case 'x',
      xlims = lower(varargin{i+1});
      if ~isdvector(xlims,'<') && ~isstring_FMAT(xlims,'auto','uniform'),
        error('Incorrect value for property ''xlims'' (type ''help AdjustAxes'' for details).');
      end
    case 'y',
      ylims = lower(varargin{i+1});
      if ~isdvector(ylims,'<') && ~isstring_FMAT(ylims,'auto','uniform'),
        error('Incorrect value for property ''ylims'' (type ''help AdjustAxes'' for details).');
      end
    case 'xy',
      xlims = lower(varargin{i+1});
      ylims = lower(varargin{i+1});
      if ~isdvector(xlims,'<') && ~isstring_FMAT(xlims,'auto','uniform'),
        error('Incorrect value for property ''AdjustAxess'' (type ''help AdjustAxes'' for details).');
      end
    otherwise,
      error(['Unknown property ''' num2str(varargin{i}) ''' (type ''help AdjustAxes'' for details).']);
  end
end

if isempty(xlims) && isempty(ylims),
	xlims = 'uniform';
	ylims = 'uniform';
end

% Compute min and max limits for all subplots
sub = get(f,'children');
if strcmp(xlims,'uniform'),
	lims = [];
	for i = 1:length(sub),
		if strcmp(get(sub,'type'),'axes'),
			lims(end+1,:) = xlim(sub(i));
		end
	end
	if isempty(lims), return; end
	xlims = [min(lims(:,1)) max(lims(:,2))];
end
if strcmp(ylims,'uniform'),
	lims = [];
	for i = 1:length(sub),
		if strcmp(get(sub,'type'),'axes'),
			lims(end+1,:) = ylim(sub(i));
		end
	end
	if isempty(lims), return; end
	ylims = [min(lims(:,1)) max(lims(:,2))];
end

% Apply
for i = 1:length(sub),
	if strcmp(get(sub,'type'),'axes'),
		if strcmp(xlims,'auto'),
			xlim(sub(i),'auto');
		elseif ~isempty(xlims),
			xlim(sub(i),xlims);
		end
		if strcmp(ylims,'auto'),
			ylim(sub(i),'auto');
		elseif ~isempty(ylims),
			ylim(sub(i),ylims);
		end
	end
end


%kvocoASF 'Alternating Sequential Filter of type Opening(Closing(Opening()))'
% This MatLab function was automatically generated by a converter (KhorosToMatLab) from the Khoros vocoASF.pane file
%
% Parameters:
% InputFile: i 'Input Image ', required: 'input image'
% InputFile: str 'Struct Element', required: 'Structuring Element'
% Integer: n 'Number of open/close/opens', default: 5: 'number of open/close/opens'
% OutputFile: o 'Output Image ', required: 'resulting output image'
%
% Example: o = kvocoASF({i, str}, {'i','';'str','';'n',5;'o',''})
%
% Khoros helpfile follows below:
%
%  PROGRAM
% vocoASF - Alternating Sequential Filter of type Opening(Closing(Opening()))
%
%  DESCRIPTION
% .I vocoASF
% will do N successive open/close/opens by the same structuring element.
% Non-sequential filtering can be selected, so that no iteration is done. Otherwise, there will be iteration from 1 to n.
%
% The output image data type will be the same as that of the highest data type
% of the input image.
%
%
%
%  EXAMPLES
% vocoASF -i feath.xv -str b.str -n 5 -o outimage.xv
%
% Will do 5 successive open/close/opens (vocoASF) of the input image "feath.xv" by the structuring element "b.str" and the resulting image will be written to  "outimage.xv".
%
%  "SEE ALSO"
% vcast1(1)
%
%  RESTRICTIONS
% .I vocoASF
% can be defined for all data types supported by Khoros, but at the moment it has been implemented just for the bit and unsigned char types.
%
%  REFERENCES
%
%  COPYRIGHT
% Copyright (C) 1993-1997 Junior Barrera, Roberto Lotufo.  All rights reserved.
%

function varargout = kvocoASF(varargin)
if nargin ==0
  Inputs={};arglist={'',''};
elseif nargin ==1
  Inputs=varargin{1};arglist={'',''};
elseif nargin ==2
  Inputs=varargin{1}; arglist=varargin{2};
else error('Usage: [out1,..] = kvocoASF(Inputs,arglist).');
end
if size(arglist,2)~=2
  error('arglist must be of form {''ParameterTag1'',value1;''ParameterTag2'',value2}')
 end
narglist={'i', '__input';'str', '__input';'n', 5;'o', '__output'};
maxval={0,0,2,0};
minval={0,0,2,0};
istoggle=[0,0,0,0];
was_set=istoggle * 0;
paramtype={'InputFile','InputFile','Integer','OutputFile'};
% identify the input arrays and assign them to the arguments as stated by the user
if ~iscell(Inputs)
Inputs = {Inputs};
end
NumReqOutputs=1; nextinput=1; nextoutput=1;
  for ii=1:size(arglist,1)
  wasmatched=0;
  for jj=1:size(narglist,1)
   if strcmp(arglist{ii,1},narglist{jj,1})  % a given argument was matched to the possible arguments
     wasmatched = 1;
     was_set(jj) = 1;
     if strcmp(narglist{jj,2}, '__input')
      if (nextinput > length(Inputs))
        error(['Input ' narglist{jj,1} ' has no corresponding input!']);
      end
      narglist{jj,2} = 'OK_in';
      nextinput = nextinput + 1;
     elseif strcmp(narglist{jj,2}, '__output')
      if (nextoutput > nargout)
        error(['Output nr. ' narglist{jj,1} ' is not present in the assignment list of outputs !']);
      end
      if (isempty(arglist{ii,2}))
        narglist{jj,2} = 'OK_out';
      else
        narglist{jj,2} = arglist{ii,2};
      end

      nextoutput = nextoutput + 1;
      if (minval{jj} == 0)
         NumReqOutputs = NumReqOutputs - 1;
      end
     elseif isstr(arglist{ii,2})
      narglist{jj,2} = arglist{ii,2};
     else
        if strcmp(paramtype{jj}, 'Integer') & (round(arglist{ii,2}) ~= arglist{ii,2})
            error(['Argument ' arglist{ii,1} ' is of integer type but non-integer number ' arglist{ii,2} ' was supplied']);
        end
        if (minval{jj} ~= 0 | maxval{jj} ~= 0)
          if (minval{jj} == 1 & maxval{jj} == 1 & arglist{ii,2} < 0)
            error(['Argument ' arglist{ii,1} ' must be bigger or equal to zero!']);
          elseif (minval{jj} == -1 & maxval{jj} == -1 & arglist{ii,2} > 0)
            error(['Argument ' arglist{ii,1} ' must be smaller or equal to zero!']);
          elseif (minval{jj} == 2 & maxval{jj} == 2 & arglist{ii,2} <= 0)
            error(['Argument ' arglist{ii,1} ' must be bigger than zero!']);
          elseif (minval{jj} == -2 & maxval{jj} == -2 & arglist{ii,2} >= 0)
            error(['Argument ' arglist{ii,1} ' must be smaller than zero!']);
          elseif (minval{jj} ~= maxval{jj} & arglist{ii,2} < minval{jj})
            error(['Argument ' arglist{ii,1} ' must be bigger than ' num2str(minval{jj})]);
          elseif (minval{jj} ~= maxval{jj} & arglist{ii,2} > maxval{jj})
            error(['Argument ' arglist{ii,1} ' must be smaller than ' num2str(maxval{jj})]);
          end
        end
     end
     if ~strcmp(narglist{jj,2},'OK_out') &  ~strcmp(narglist{jj,2},'OK_in')
       narglist{jj,2} = arglist{ii,2};
     end
   end
   end
   if (wasmatched == 0 & ~strcmp(arglist{ii,1},''))
        error(['Argument ' arglist{ii,1} ' is not a valid argument for this function']);
   end
end
% match the remaining inputs/outputs to the unused arguments and test for missing required inputs
 for jj=1:size(narglist,1)
     if  strcmp(paramtype{jj}, 'Toggle')
        if (narglist{jj,2} ==0)
          narglist{jj,1} = '';
        end;
        narglist{jj,2} = '';
     end;
     if  ~strcmp(narglist{jj,2},'__input') && ~strcmp(narglist{jj,2},'__output') && istoggle(jj) && ~ was_set(jj)
          narglist{jj,1} = '';
          narglist{jj,2} = '';
     end;
     if strcmp(narglist{jj,2}, '__input')
      if (minval{jj} == 0)  % meaning this input is required
        if (nextinput > size(Inputs))
           error(['Required input ' narglist{jj,1} ' has no corresponding input in the list!']);
        else
          narglist{jj,2} = 'OK_in';
          nextinput = nextinput + 1;
        end
      else  % this is an optional input
        if (nextinput <= length(Inputs))
          narglist{jj,2} = 'OK_in';
          nextinput = nextinput + 1;
        else
          narglist{jj,1} = '';
          narglist{jj,2} = '';
        end;
      end;
     else
     if strcmp(narglist{jj,2}, '__output')
      if (minval{jj} == 0) % this is a required output
        if (nextoutput > nargout & nargout > 1)
           error(['Required output ' narglist{jj,1} ' is not stated in the assignment list!']);
        else
          narglist{jj,2} = 'OK_out';
          nextoutput = nextoutput + 1;
          NumReqOutputs = NumReqOutputs-1;
        end
      else % this is an optional output
        if (nargout - nextoutput >= NumReqOutputs)
          narglist{jj,2} = 'OK_out';
          nextoutput = nextoutput + 1;
        else
          narglist{jj,1} = '';
          narglist{jj,2} = '';
        end;
      end
     end
  end
end
if nargout
   varargout = cell(1,nargout);
else
  varargout = cell(1,1);
end
global KhorosRoot
if exist('KhorosRoot') && ~isempty(KhorosRoot)
w=['"' KhorosRoot];
else
if ispc
  w='"C:\Program Files\dip\khorosBin\';
else
[s,w] = system('which cantata');
w=['"' w(1:end-8)];
end
end
[varargout{:}]=callKhoros([w 'vocoASF"  '],Inputs,narglist);


function qwv_2d_test ( )

%*****************************************************************************80
%
%% QWV_2D_TEST tests the QWV_2D library.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    29 May 2014
%
%  Author:
%
%    John Burkardt
%
  timestamp ( );
  fprintf ( 1, '\n' );
  fprintf ( 1, 'QWV_2D_TEST:\n' );
  fprintf ( 1, '  MATLAB version\n' );
  fprintf ( 1, '  Test the QWV_2D library.\n' );

  qwv_2d_test01 ( );
  qwv_2d_test02 ( );
%
%  Terminate.
%
  fprintf ( 1, '\n' );
  fprintf ( 1, 'QWV_2D_TEST:\n' );
  fprintf ( 1, '  Normal end of execution.\n' );
  fprintf ( 1, '\n' );
  timestamp ( );

  return
end


function varargout = drawLine(lin, varargin)
%DRAWLINE Draw a straight line clipped by the current axis
%
%   drawLine(LINE);
%   Draws the line LINE on the current axis, by using current axis to clip
%   the line.
%
%   drawLine(LINE, PARAM, VALUE);
%   Specifies drawing options.
%
%   H = drawLine(...)
%   Returns a handle to the created line object. If clipped line is not
%   contained in the axis, the function returns -1.
%
%   Example
%     figure; hold on; axis equal;
%     axis([0 100 0 100]);
%     drawLine([30 40 10 20]);
%     drawLine([30 40 20 -10], 'Color', 'm', 'LineWidth', 2);
%     drawLine([-30 140 10 20]);
%
%   See also:
%   lines2d, createLine, drawEdge
%
%   ---------
%   author : David Legland
%   INRA - TPV URPOI - BIA IMASTE
%   created the 31/10/2003.
%

%   HISTORY
%   25/05/2004 add support for multiple lines (loop)
%   23/05/2005 add support for arguments
%   03/08/2010 bug for lines outside box (thanks to Reto Zingg)
%   04/08/2010 rewrite using clipLine
%   2011-10-11 add management of axes handle

% extract handle of axis to draw in
if isAxisHandle(lin)
    ax = lin;
    lin = varargin{1};
    varargin(1) = [];
else
    ax = gca;
end

% default style for drawing lines
if length(varargin) ~= 1
    varargin = [{'color', 'b'}, varargin];
end

% extract bounding box of the current axis
xlim = get(ax, 'xlim');
ylim = get(ax, 'ylim');

% clip lines with current axis box
clip = clipLine(lin, [xlim ylim]);
ok   = isfinite(clip(:,1));

% initialize result array to invalide handles
h = -1 * ones(size(lin, 1), 1);

% draw valid lines
h(ok) = plot(ax, clip(ok, [1 3])', clip(ok, [2 4])', varargin{:});

% return line handle if needed
if nargout > 0
    varargout = {h};
end


%% Intermediate Usage (for building complicated systems)
% This document builds upon <basic.html |Basic Usage|> and explains how to
% describe the problem in a more flexible way.

%% Example Code
% We examine a portion of the last code discussed in <basic.html |Basic Usage|>:
[E, H, obj_array, src_array] = maxwell_run(...
	'OSC', 1e-9, 1550, ...
	'DOM', {'vacuum', 'none', 1.0}, [-1100 1100; -1100 2600; 0 10], 10, BC.p, [100 100 0],...
	'OBJ', {'CRC/Ag', 'k'}, ...
		Box([-1100 -80; 0 1000; 0 10]), ...
		Box([80 1100; 0 1000; 0 10]), ...
	'SRCJ', PlaneSrc(Axis.y, -500, Axis.x), ...
	true);

%% Passing Variables as Arguments
% You can store any arugments of <../comp/maxwell_run.html |maxwell_run|> in
% variables and pass the variables instead of the arguments to |maxwell_run|.
% This increases the readability of the code.  For example, the meaning of the
% last logical argument |true| in the above code is hard to figure out from the
% code itself.  We can define a variable |inspect_only| and modify the code as:
inspect_only = true;
[E, H, obj_array, src_array] = maxwell_run(...
	{PARAMETER GROUPS}, ...
	inspect_only);

%%%
% Now, it is more obvious that |inspect_only = true| makes |maxwell_run| inpect
% the problem without solving it.
%
% Similarly, you can define a variable for the color code used for a material.
% Because the actual color of silver is closer to gray than black, let's define
% a RGB vector and substitute it for the color code 'k' used for the material
% |'CRC/Ag'|:
inspect_only = true;
gray = [0.5 0.5 0.5];  % [r g b]
[E, H, obj_array, src_array] = maxwell_run(...
	{OTHER PARAMETER GROUPS}, ...
	'OBJ', {'CRC/Ag', gray}, ...
		Box([-1100 -80; 0 1000; 0 10]), ...
		Box([80 1100; 0 1000; 0 10]), ...
	inspect_only);

%%%
% Upon execution, the above code generates the following figure describing the
% simulation domain:
%
% <<../img/intermediate_01.png>>
%
% Compare this with the last figure in <basic.html |Basic Usage|> and note that
% the two silver boxes are now drawn in gray.

%% Building Shapes Beforehand
% You can build the shapes of objects outside |maxwell_run|, store them in
% variables, and pass the variables to |maxwell_run|:
inspect_only = true;
shape1 = Box([-1100 -80; 0 1000; 0 10]);
shape2 = Box([80 1100; 0 1000; 0 10]);
gray = [0.5 0.5 0.5];  % [r g b]
[E, H, obj_array, src_array] = maxwell_run(...
	{OTHER PARAMETER GROUPS}, ...
	'OBJ', {'CRC/Ag', gray}, shape1, shape2, ...
	inspect_only);

%%%
% Defining shape variables before |maxwell_run| this way could be useful when,
% for example, you want to calculate the size of the shapes using complicated
% equations.
%
% You can even create an array of shapes and pass it as a single argument:
inspect_only = true;
shape1 = Box([-1100 -80; 0 1000; 0 10]);
shape2 = Box([80 1100; 0 1000; 0 10]);
shape_array = [shape1, shape2];
gray = [0.5 0.5 0.5];  % [r g b]
[E, H, obj_array, src_array] = maxwell_run(...
	{OTHER PARAMETER GROUPS}, ...
	'OBJ', {'CRC/Ag', gray}, shape_array, ...
	inspect_only);

%%%
% Passing an array of shapes as an argument can be useful when the number of
% shapes is not fixed.

%% Complete Code
% As shown above, |maxwell_run| allows flexible ways to construct arguments.  In
% fact, you can construct most arguments outside |maxwell_run|, store them in
% variables, and pass the variables to |maxwell_run|.  Below is a complete
% example that does so.
inspect_only = true;

% Geometric Parameters
L0 = 1e-9;  % length unit
wvlen = 1550;  % wavelength
dL = 10;  % grid size
Lpml = 10*dL;  % PML thickness
xn = -1000-Lpml; xp = 1000+Lpml;  % x boundaries
yn = -1000-Lpml; yp = 2500+Lpml;  % y boundaries
zn = 0; zp = dL;  % z boundaries
w = 160;  % slit width
h = 1000;  % metal thickness

% Materials and Shapes
vacuum = Material('vacuum', 'none', 1.0);
shape1 = Box([xn -w/2; 0 h; zn zp]);
shape2 = Box([w/2 xp; 0 h; zn zp]);
shape_array = [shape1, shape2];
gray = [0.5 0.5 0.5];  % [r g b]

% Source
y_src = -500;
polarization = Axis.x;
src = PlaneSrc(Axis.y, y_src, polarization);

% Solution
[E, H, obj_array, src_array] = maxwell_run(...
	'OSC', L0, wvlen, ...
	'DOM', vacuum, [xn xp; yn yp; zn zp], dL, BC.p, [Lpml Lpml 0],...
	'OBJ', {'CRC/Ag', gray}, shape_array, ...
	'SRCJ', src, ...
	inspect_only);

%%% See Also
% <basic.html |Basic Usage|>


function out = numColumns(f)
%NUMCOLUMNS   Number of columns (or rows) of a CHEBFUN quasimatrix.
%   NUMCOLUMNS(F) returns the number of columns of a column CHEBFUN, or the
%   number of rows of a row CHEBFUN. It is equivalent to min(size(f)).
%
% See also SIZE, NUMEL.

% Copyright 2017 by The University of Oxford and The Chebfun Developers.
% See http://www.chebfun.org/ for Chebfun information.

if ( isempty(f) )
    % An empty CHENFUN has no columns:
    out = 0;
elseif ( numel(f) == 1 )
    % Possible array-valued:
    out = size(f.funs{1}, 2);
else
    % A quasimatrix:
    out = numel(f);
end

end