function grid = GetSearchGrid(model, material, fix, fixedvals)
% Returns a list of parameter combinations for the specified model
% for the GridSearch function to use in search for the combination
% that best fits a set of measurements.
%
% grid=GetSearchGrid(model, material, fix, fixedvals)
% returns a list of parameter combinations for the model that
% are realistic for a particular material type.
%
% model is a string specifying the model.
%
% material is a string specifying the type of material, which
% determines the range of each parameter in the grid.
% Options are:
% invivo
% invivopreterm
% invivowhitematter
% fixedwhitematter
%
% fix is a list of binary numbers specifying which
% model parameters have fixed values.  By default the
% list is all zeros so no parameters are fixed.
%
% fixedvals is an array the same size as fix that specifies
% the fixed values of any fixed parameters.  Entries in
% fixedvals in locations where fix has value zero are not
% used.
%
% author: Daniel C Alexander (d.alexander@ucl.ac.uk)
%         Gary Hui Zhang     (gary.zhang@ucl.ac.uk)
%

if(nargin<3)
    fix=zeros(6);
    fixedvals = zeros(6);
end

if(strcmp(material, 'invivo'))
    fs = [0.0 0.25 0.5 0.75 1.0];
    dpars = [13.0 17.0 21.0]*1E-10;
    disos = [10.0 20.0 30.0 50.0]*1E-10;
    Rs=[0.5 1 2 4]*1E-6;
    irfracs=[0 0.1 0.2 0.3];
    fisos=[0.0 0.25 0.5 0.75 1.0];
    kappas = [0.5 1 2 4 8];
    fic = [0.3 0.5 0.7];
elseif(strcmp(material, 'exvivo'))
    fs = [0.0 0.25 0.5 0.75 1.0];
    dpars = [3.0 4.5 6.0 7.5]*1E-10;
    disos = [5.0 10.0 15.0]*1E-10;
    Rs=[1 2 4 8]*1E-6;
    irfracs=[0 0.1 0.2 0.3];
    fisos=[0.0 0.25 0.5 0.75 1.0];
    kappas = [0.5 1 2 4 8];
    fic = [0.3 0.5 0.7];
elseif(strcmp(material, 'invivopreterm'))
    fs = [0.0 0.1 0.2 0.3];
    dpars = [13.0 17.0 21.0]*1E-10;
    disos = [10.0 20.0 30.0 50.0]*1E-10;
    Rs=[1 2 4 8]*1E-6;
    irfracs=[0 0.1 0.2 0.3];
    fisos=[0.0 0.25 0.5 0.75 1.0];
    kappas = [0.5 1 2 4 8];
    fic = [0.3 0.5 0.7];
elseif(strcmp(material, 'invivowhitematter'))
    fs = [0.5 0.7 0.9];
    dpars = [10.0 13.0 15.0 17.0 19.0 21.0 23.0 25.0]*1E-10;
    disos = [10.0 20.0 30.0 50.0]*1E-10;
    Rs=[1 2 4 8]*1E-6;
    irfracs=[0 0.1 0.2 0.3];
    fisos=[0 0.2 0.4];
    kappas = [4 8 16 32 64 128];
    fic = [0.3 0.5 0.7];
elseif(strcmp(material, 'postmortemwhitematter'))
    fs = [0.5 0.7 0.9];
    dpars = [2.0 3.0 4.0 5.0 6.0]*1E-10;
    disos = [5.0 10.0 15.0]*1E-10;
    Rs=[1 2 4 8]*1E-6;
    irfracs=[0 0.1 0.2 0.3];
    fisos=[0 0.2 0.4];
    kappas = [4 8 16 32 64 128];
    fic = [0.3 0.5 0.7];
else
    error(['Unknown material: ', tissue]);
end

% Adjust for fixed parameters.  The first two parameters are the same for
% all models.
if(fix(1))
    fs = [fixedvals(1)];
end
if(fix(2))
    dpars = [fixedvals(2)];
end

if(strcmp(model, 'CylSingleRadTortGPD'))
    if(fix(3))
        Rs = [fixedvals(3)];
    end

    numCombs = length(Rs)*length(fs)*length(dpars);
    grid = zeros(3, numCombs);
    ind = 1;
    for i=1:length(Rs)
        for j=1:length(fs)
            for k=1:length(dpars)
                pars = [fs(j) dpars(k) Rs(i)];
                grid(:,ind) = pars;
                ind = ind + 1;
            end
        end
    end
elseif(strcmp(model, 'CylSingleRadGPD'))
    if(fix(4))
        Rs = [fixedvals(4)];
    end

    numCombs = length(Rs)*length(fs)*length(dpars);
    grid = zeros(4, numCombs);
    ind = 1;
    for i=1:length(Rs)
        for j=1:length(fs)
            for k=1:length(dpars)
                % Set dperp using the standard tortuosity model for
                % randomly placed cylinders unless fixed.
                dperp = dpars(k)*(1-fs(j));
                if(fix(3))
                    dperp = fixedvals(3);
                end
                pars = [fs(j) dpars(k) dperp Rs(i)];
                grid(:,ind) = pars;
                ind = ind + 1;
            end
        end
    end
elseif(strcmp(model, 'CylSingleRadTortIsoGPD'))
    if(fix(3))
        Rs = [fixedvals(3)];
    end
    if(fix(4))
        fisos = [fixedvals(4)];
    end

    numCombs = length(Rs)*length(fs)*length(dpars)*length(fisos);
    grid = zeros(4, numCombs);
    ind = 1;
    for i=1:length(Rs)
        for j=1:length(fs)
            for k=1:length(dpars)
                for l=1:length(fisos)
                    pars = [fs(j) dpars(k) Rs(i) fisos(l)];
                    grid(:,ind) = pars;
                    ind = ind + 1;
                end
            end
        end
    end
elseif(strcmp(model, 'CylSingleRadIsoV_GPD') || strcmp(model, 'CylSingleRadIsoV_GPD_B0'))
    if(fix(4))
        Rs = [fixedvals(4)];
    end
    if(fix(5))
        fisos = [fixedvals(5)];
    end
    if(fix(6))
        disos = [fixedvals(6)];
    end

    numCombs = length(Rs)*length(fs)*length(dpars)*length(fisos)*length(disos);
    grid = zeros(6, numCombs);
    ind = 1;
    for i=1:length(Rs)
        for j=1:length(fs)
            for k=1:length(dpars)
                for l=1:length(fisos)
                    for m=1:length(disos)
                        % Set dperp using the standard tortuosity model for
                        % randomly placed cylinders unless fixed.
                        dperp = dpars(k)*(1-fs(j));
                        if(fix(3))
                            dperp = fixedvals(3);
                        end
                        pars = [fs(j) dpars(k) dperp Rs(i) fisos(l) disos(m)];
                        grid(:,ind) = pars;
                        ind = ind + 1;
                    end
                end
            end
        end
    end
elseif(strcmp(model, 'CylSingleRadTortIsoV_GPD') || strcmp(model, 'CylSingleRadTortIsoV_GPD_B0'))
    if(fix(3))
        Rs = [fixedvals(3)];
    end
    if(fix(4))
        fisos = [fixedvals(4)];
    end
    if(fix(5))
        disos = [fixedvals(5)];
    end

    numCombs = length(Rs)*length(fs)*length(dpars)*length(fisos)*length(disos);
    grid = zeros(5, numCombs);
    ind = 1;
    for i=1:length(Rs)
        for j=1:length(fs)
            for k=1:length(dpars)
                for l=1:length(fisos)
                    for m=1:length(disos)
                        pars = [fs(j) dpars(k) Rs(i) fisos(l) disos(m)];
                        grid(:,ind) = pars;
                        ind = ind + 1;
                    end
                end
            end
        end
    end
elseif(strcmp(model, 'CylSingleRadIsoDotGPD'))
    if(fix(4))
        Rs = [fixedvals(4)];
    end
    if(fix(5))
        irfracs = [fixedvals(5)];
    end
    numCombs = length(Rs)*length(fs)*length(dpars)*length(irfracs);
    grid = zeros(5, numCombs);
    ind = 1;
    for i=1:length(Rs)
        for j=1:length(fs)
            for k=1:length(dpars)
                for l=1:length(irfracs)
                    dperp = dpars(k)*(1-fs(j));
                    pars = [fs(j) dpars(k) dperp Rs(i) irfracs(l)];
                    grid(:,ind) = pars;
                    ind = ind + 1;
                end
            end
        end
    end
elseif(strcmp(model, 'CylSingleRadIsoResTortIsoV_GPD') || strcmp(model, 'CylSingleRadIsoResTortIsoV_GPD_B0')...
        || strcmp(model, 'CylSingleRadIsoStickTortIsoV_GPD') || strcmp(model, 'CylSingleRadIsoStickTortIsoV_GPD_B0')...
        || strcmp(model, 'CylSingleRadIsoSphereTortIsoV_GPD') || strcmp(model, 'CylSingleRadIsoSphereTortIsoV_GPD_B0')...
        || strcmp(model, 'CylSingleRadIsoDotTortIsoV_GPD') || strcmp(model, 'CylSingleRadIsoDotTortIsoV_GPD_B0'))
    if(fix(3))
        Rs = [fixedvals(3)];
    end
    if(fix(4))
        irfracs = [fixedvals(4)];
    end
    if(fix(5))
        fisos = [fixedvals(5)];
    end
    if(fix(6))
        disos = [fixedvals(6)];
    end

    numCombs = length(Rs)*length(fs)*length(dpars)*length(fisos)*length(disos);
    grid = zeros(6, numCombs);
    ind = 1;
    for i=1:length(Rs)
        for j=1:length(fs)
            for k=1:length(dpars)
                for l=1:length(fisos)
                    for m=1:length(disos)
                        for n=1:length(irfracs)
                            pars = [fs(j) dpars(k) Rs(i) irfracs(n) fisos(l) disos(m)];
                            grid(:,ind) = pars;
                            ind = ind + 1;
                        end
                    end
                end
            end
        end
    end
elseif(strcmp(model, 'Stick'))
    numCombs = length(fs)*length(dpars);
    grid = zeros(3, numCombs);
    ind = 1;
    for i=1:length(fs)
        for j=1:length(dpars)
            % Set dperp using the standard tortuosity model for
            % randomly placed cylinders unless fixed.
            dperp = dpars(j)*(1-fs(i));
            if(fix(3))
                dperp = fixedvals(3);
            end
            pars = [fs(i) dpars(j) dperp];
            grid(:,ind) = pars;
            ind = ind + 1;
        end
    end
elseif(strcmp(model, 'StickIsoV_B0'))
    if(fix(4))
        fisos = [fixedvals(4)];
    end
    if(fix(5))
        disos = [fixedvals(5)];
    end

    numCombs = length(fs)*length(dpars)*length(fisos)*length(disos);
    grid = zeros(5, numCombs);
    ind = 1;
    for i=1:length(fs)
        for j=1:length(dpars)
            for k=1:length(fisos)
                for l=1:length(disos)
                    % Set dperp using the standard tortuosity model for
                    % randomly placed cylinders unless fixed
                    dperp = dpars(j)*(1-fs(i));
                    if(fix(3))
                        dperp = fixedvals(3);
                    end
                    pars = [fs(i) dpars(j) dperp fisos(k) disos(l)];
                    grid(:,ind) = pars;
                    ind = ind + 1;
                end
            end
        end
    end
elseif(strcmp(model, 'StickTortIsoV_B0'))
    if(fix(3))
        fisos = [fixedvals(3)];
    end
    if(fix(4))
        disos = [fixedvals(4)];
    end

    numCombs = length(fs)*length(dpars)*length(fisos)*length(disos);
    grid = zeros(4, numCombs);
    ind = 1;
    for i=1:length(fs)
        for j=1:length(dpars)
            for k=1:length(fisos)
                for l=1:length(disos)
                    pars = [fs(i) dpars(j) fisos(k) disos(l)];
                    grid(:,ind) = pars;
                    ind = ind + 1;
                end
            end
        end
    end
elseif(strcmp(model, 'WatsonStick') || strcmp(model, 'WatsonSHStick'))
    numCombs = length(fs)*length(dpars)*length(kappas);
    grid = zeros(4, numCombs);
    ind = 1;
    for i=1:length(fs)
        for j=1:length(dpars)
		  for k=1:length(kappas)
                % Set dperp using the standard tortuosity model for
                % randomly placed cylinders unless fixed.
                dperp = dpars(j)*(1-fs(i));
                if(fix(3))
                    dperp = fixedvals(3);
                end
                pars = [fs(i) dpars(j) dperp kappas(k)];
                grid(:,ind) = pars;
                ind = ind + 1;
            end
        end
    end
elseif(strcmp(model, 'WatsonSHStickIsoV_B0'))
    if(fix(4))
        kappas = [fixedvals(4)];
    end
    if(fix(5))
        fisos = [fixedvals(5)];
    end
    if(fix(6))
        disos = [fixedvals(6)];
    end

    numCombs = length(fs)*length(dpars)*length(fisos)*length(disos)*length(kappas);
    grid = zeros(6, numCombs);
    ind = 1;
    for i=1:length(fs)
        for j=1:length(dpars)
            for k=1:length(fisos)
                for l=1:length(disos)
                    for m=1:length(kappas)
                      dperp = dpars(j)*(1-fs(i));
                      if(fix(3))
                          dperp = fixedvals(3);
                      end
                      pars = [fs(i) dpars(j) dperp kappas(m) fisos(k) disos(l)];
                      grid(:,ind) = pars;
                      ind = ind + 1;
                    end
                end
            end
        end
    end
elseif(strcmp(model, 'WatsonSHStickIsoVIsoDot_B0'))
    if(fix(5))
        fisos = [fixedvals(5)];
    end
    if(fix(6))
        disos = [fixedvals(6)];
    end
    if(fix(7))
        irfracs = [fixedvals(7)];
    end

    numCombs = length(fs)*length(dpars)*length(fisos)*length(disos)*length(kappas)*length(irfracs);
    grid = zeros(7, numCombs);
    ind = 1;
    for i=1:length(fs)
        for j=1:length(dpars)
            for k=1:length(fisos)
                for l=1:length(disos)
                    for m=1:length(kappas)
                        for n=1:length(irfracs)
                          dperp = dpars(j)*(1-fs(i));
                          if(fix(3))
                             dperp = fixedvals(3);
                          end
                          pars = [fs(i) dpars(j) dperp kappas(m) fisos(k) disos(l) irfracs(n)];
                          grid(:,ind) = pars;
                          ind = ind + 1;
                        end
                    end
                end
            end
        end
    end
elseif(strcmp(model, 'WatsonStickTort') || strcmp(model, 'WatsonSHStickTort'))
    numCombs = length(fs)*length(dpars)*length(kappas);
    grid = zeros(3, numCombs);
    ind = 1;
    for i=1:length(fs)
        for j=1:length(dpars)
		  for k=1:length(kappas)
                pars = [fs(i) dpars(j) kappas(k)];
                grid(:,ind) = pars;
                ind = ind + 1;
            end
        end
    end
elseif(strcmp(model, 'WatsonSHStickTortIsoV_B0'))
    if(fix(4))
        fisos = [fixedvals(4)];
    end
    if(fix(5))
        disos = [fixedvals(5)];
    end

    numCombs = length(fs)*length(dpars)*length(fisos)*length(disos)*length(kappas);
    grid = zeros(5, numCombs);
    ind = 1;
    for i=1:length(fs)
        for j=1:length(dpars)
            for k=1:length(fisos)
                for l=1:length(disos)
                    for m=1:length(kappas)
                      pars = [fs(i) dpars(j) kappas(m) fisos(k) disos(l)];
                      grid(:,ind) = pars;
                      ind = ind + 1;
                    end
                end
            end
        end
    end
elseif(strcmp(model, 'WatsonSHStickTortIsoVIsoDot_B0'))
    if(fix(4))
        fisos = [fixedvals(4)];
    end
    if(fix(5))
        disos = [fixedvals(5)];
    end
    if(fix(6))
        irfracs = [fixedvals(6)];
    end

    numCombs = length(fs)*length(dpars)*length(fisos)*length(disos)*length(kappas)*length(irfracs);
    grid = zeros(6, numCombs);
    ind = 1;
    for i=1:length(fs)
        for j=1:length(dpars)
            for k=1:length(fisos)
                for l=1:length(disos)
                    for m=1:length(kappas)
                        for n=1:length(irfracs)
                          pars = [fs(i) dpars(j) kappas(m) fisos(k) disos(l) irfracs(n)];
                          grid(:,ind) = pars;
                          ind = ind + 1;
                        end
                    end
                end
            end
        end
    end
elseif(strcmp(model, 'WatsonSHCylSingleRadTortIsoV_GPD') || strcmp(model, 'WatsonSHCylSingleRadTortIsoV_GPD_B0'))
    if(fix(3))
        Rs = [fixedvals(3)];
    end
    if(fix(5))
        fisos = [fixedvals(5)];
    end
    if(fix(6))
        disos = [fixedvals(6)];
    end

    numCombs = length(Rs)*length(fs)*length(dpars)*length(fisos)*length(disos)*length(kappas);
    grid = zeros(6, numCombs);
    ind = 1;
    for i=1:length(Rs)
        for j=1:length(fs)
            for k=1:length(dpars)
                for l=1:length(fisos)
                    for m=1:length(disos)
                      for n=1:length(kappas)
                        pars = [fs(j) dpars(k) Rs(i) kappas(n) fisos(l) disos(m)];
                        grid(:,ind) = pars;
                        ind = ind + 1;
                      end
                    end
                end
            end
        end
    end
elseif(strcmp(model, 'BinghamCylSingleRadTortIsoV_GPD_B0'))
    if(fix(3))
        Rs = [fixedvals(3)];
    end
    if(fix(7))
        fisos = [fixedvals(7)];
    end
    if(fix(8))
        disos = [fixedvals(8)];
    end

    numCombs = length(Rs)*length(fs)*length(dpars)*length(fisos)*length(disos)*length(kappas);
    grid = zeros(8, numCombs);
    ind = 1;
    for i=1:length(Rs)
        for j=1:length(fs)
            for k=1:length(dpars)
                for l=1:length(fisos)
                    for m=1:length(disos)
                        for n=1:length(kappas)
                            pars = [fs(j) dpars(k) Rs(i) kappas(n) 0 0 fisos(l) disos(m)];
                            grid(:,ind) = pars;
                            ind = ind + 1;
                        end
                    end
                end
            end
        end
    end
elseif(strcmp(model, 'BinghamStickTortIsoV_B0'))
    if(fix(6))
        fisos = [fixedvals(6)];
    end
    if(fix(7))
        disos = [fixedvals(7)];
    end

    numCombs = length(fs)*length(dpars)*length(fisos)*length(disos)*length(kappas);
    grid = zeros(7, numCombs);
    ind = 1;
    for i=1:length(fs)
        for j=1:length(dpars)
            for k=1:length(fisos)
                for l=1:length(disos)
                    for m=1:length(kappas)
                        pars = [fs(i) dpars(j) kappas(m) 0 0 fisos(k) disos(l)];
                        grid(:,ind) = pars;
                        ind = ind + 1;
                    end
                end
            end
        end
    end
elseif(strcmp(model, 'ExCrossingCylSingleRadGPD'))
    R1s = Rs;
    R2s = Rs;
    if(fix(4))
        R1s = [fixedvals(4)];
    end

    if(fix(5))
        R2s = [fixedvals(5)];
    end

    numCombs = length(R1s)*length(R2s)*length(fs)*length(dpars)*length(fic);
    grid = zeros(6, numCombs);
    ind = 1;
    for i=1:length(R1s)
        for j=1:length(R2s)
            for k=1:length(fs)
                for l=1:length(dpars)
                    for m=1:length(fic)
                        % Set dperp using the standard tortuosity model for
                        % randomly placed cylinders unless fixed.
                        dperp = dpars(l)*(1-fs(k));
                        if(fix(3))
                            dperp = fixedvals(3);
                        end
                        pars = [fs(k) dpars(l) dperp R1s(i) R2s(j) fic(m)];
                        grid(:,ind) = pars;
                        ind = ind + 1;
                    end
                end
            end
        end
    end
elseif(strcmp(model, 'ExCrossingCylSingleRadIsoDotTortIsoV_GPD_B0'))
    R1s = Rs;
    R2s = Rs;
    if(fix(3))
        R1s = [fixedvals(3)];
    end

    if(fix(4))
        R2s = [fixedvals(4)];
    end

    if(fix(6))
        irfracs = [fixedvals(6)];
    end

    if(fix(7))
        fisos = [fixedvals(7)];
    end

    if(fix(8))
        disos = [fixedvals(8)];
    end

    numCombs = length(R1s)*length(R2s)*length(fs)*length(dpars)*length(fic)*length(irfracs)*length(fisos)*length(disos);
    grid = zeros(8, numCombs);
    ind = 1;
    for i=1:length(R1s)
        for j=1:length(R2s)
            for k=1:length(fs)
                for l=1:length(dpars)
                    for m=1:length(fic)
								 for n=1:length(irfracs)
										for p=1:length(fisos)
											 for q=1:length(disos)
						  				  		  pars = [fs(k) dpars(l) R1s(i) R2s(j) fic(m) irfracs(n) fisos(p) disos(q)];
						  				  		  grid(:,ind) = pars;
						  				  		  ind = ind + 1;
									 		 end
										end
								 end
                    end
                end
            end
        end
    end
else
    error(['Starting combinations not implemented for model: ', model]);
end


function stamps(start_step,end_step,patches_flag,est_gamma_parm,patch_list_file,stamps_PART_limitation)
%STAMPS Stanford Method for Persistent Scatterers
%   STAMPS(START_STEP,END_STEP,PATCHES_FLAG,EST_GAMMA_FLAG) Default is to run all steps.
%   A subset of steps may be selected with START_STEP and/or END_STEP
%   STEP 1 = Initial load of data
%   STEP 2 = Estimate gamma
%   STEP 3 = Select PS pixels
%   STEP 4 = Weed out adjacent pixels
%   STEP 5 = Correct wrapped phase for spatially-uncorrelated look angle error and merge patches
%   STEP 6 = Unwrap phase
%   STEP 7 = Calculate spatially correlated look angle (DEM) error
%   STEP 8 = Filter spatially correlated noise
%   STEP 0 = Continue from the last known stage till the end-stage selected
%
%   PATCHES_FLAG Default 'y'. Set to 'n' to process all data as one patch
%
%   EST_GAMMA_PARM is an optional parameter passed to PS_EST_GAMMA_QUICK
%
%   PATCH_LIST_FILE is an optional argument specifying the file list of
%   patches to be processed. Note that from step 5 and above one should use
%   all patches to merge results.
%
%   If current directory is a single patch, stamps only operates in the
%   current directory, but if current directory contains many patches,
%   stamps operates on them all.
%
%   Andy Hooper, June 2006

%   =================================================================
%   07/2006 AH: END_STEP added
%   09/2006 AH: ps_load removed (obsolete)
%   09/2006 AH: small baselines added
%   11/2006 AH: patches added
%   01/2007 AH: calculate spatially correlated look angle error added
%   03/2009 AH: simultaneously estimate velocity when SCLA estimated
%   03/2009 AH: smooth SCLA for unwrapping iteration
%   03/2010 AH: move ps_cal_ifg_std to after merge step
%   12/2012 AH: add gamma option
%   12/2012 DB: add patch_list_file argument as option
%   09/2013 DB: update the stamps version number
%   09/2015 DB: Check if patches do have PS before proceeding with
%               processing.
%   09/2015 DB: Fix when running stamps in a patch folder mode when no PS are left
%   09/2015 AH: allow for non-differentiation of caps by dir
%   01/2016 DB: include stamps_save in step 1-4.
%   08/2016 AH: Fix bug of scn_kriging_flag not being set
%   06/2017 DB: Catching when no PS are left from step 1, allow for re-run
%               when parameters have changed.
%   06/2017 DB: Option to continue from last know processing step
%   08/2107 AH: Removed catch as proceeds also when error in Step 1
%   =================================================================

nfill=40;
fillstr=[repmat('#',1,nfill),'\n'];
skipstr='\n';
msgstr=fillstr;

fprintf(skipstr);
logit(fillstr);
msgstr(round(nfill)/2-12:round(nfill/2)+13)=' StaMPS/MTI Version 4.0b6 ';
logit(msgstr);
msgstr(round(nfill)/2-12:round(nfill/2)+13)='  Beta version, Jun 2018  ';
logit(msgstr);
logit(fillstr);
fprintf(skipstr);

quick_est_gamma_flag=getparm('quick_est_gamma_flag');
reest_gamma_flag=getparm('select_reest_gamma_flag');
unwrap_method=getparm('unwrap_method');
unwrap_prefilter_flag=getparm('unwrap_prefilter_flag');
small_baseline_flag=getparm('small_baseline_flag');
insar_processor=getparm('insar_processor');
scn_kriging_flag=getparm('scn_kriging_flag');

if nargin<1 || isempty(start_step)==1
    start_step=1;
end

if nargin<2 || isempty(end_step)==1
    end_step=8;
end

if nargin<3 || isempty(patches_flag)==1
    if start_step<6
        patches_flag='y';
    else
        patches_flag='n';
    end
end

if nargin<4 || isempty(est_gamma_parm)==1
    est_gamma_parm=0;
end

if nargin<5 || isempty(patch_list_file)     % [DB] allow for own specified patch list file
    patch_list_file = 'patch.list';
    new_patch_file = 0;
else
    % use own file
    new_patch_file = 1;
end

% In support of the multi-core option limit processing to steps 1-5a,
% 5b-upwards, or the old method where all is processed together.
if nargin<6 || isempty(stamps_PART_limitation)
    stamps_PART_limitation=0;
end
stamps_PART1_flag='y';
stamps_PART2_flag='y';
if stamps_PART_limitation==1
    stamps_PART2_flag='n';
end
if stamps_PART_limitation==2
    stamps_PART1_flag='n';
end

if strcmpi(patches_flag,'y')
    if exist(patch_list_file,'file')
        fid=fopen(patch_list_file);
        i=0;
        while 1
            nextline=fgetl(fid);
            if ischar(nextline)
                i=i+1;
                patchdir(i).name=nextline;
            else
                break
            end
        end
	fclose(fid)
    else
        patchdir=dir('PATCH_*');
        patchdir = patchdir(find(~cellfun(@(x) strcmpi(x,'patch_noover.in'),{patchdir(:).name})));
    end
    if isempty(patchdir)
        patches_flag='n';
    else
        ps_parms_default
        patches_flag='y';
    end
end

if ~strcmpi(patches_flag,'y')
    patchdir(1).name='.';
    logit('Will process current directory only')
else
    logit('Will process patch subdirectories')
end

currdir=pwd;

nfill=40;
fillstr=[repmat('#',1,nfill),'\n'];
msgstr=fillstr;

% limit the processing to step 1-5a
start_step_or = start_step;
if strcmpi(stamps_PART1_flag,'y')
  for i=1:length(patchdir)
    if ~isempty(patchdir(i).name)
      cd(patchdir(i).name)
      patchsplit=strsplit(pwd,'/');
      %fprintf(skipstr);
      %logit(sprintf('Processing %s',patchsplit{end}))

      % store if patch dir is empty
      if exist('no_ps_info.mat','file')~=2
         stamps_step_no_ps = zeros([5 1 ]);       % keep for the first 5 steps only
         save('no_ps_info.mat','stamps_step_no_ps')
      end

        % if start_step is 0, then start from the latest stage it was
        if start_step_or==0
            % check the processing stage of stamps for all the patches
            % step 4 find a ps_weed file
            % step 3 find a ps_select file
            % step 2 find a pm file
            % step 1 find a ps file
            % or no PS in case stamps_step_no_ps is found with a 1 in there

            if exist('weed1.mat','file')==2
                   start_step=5;
                   setpsver(2);
            elseif exist('select1.mat','file')==2
                   start_step=4;
            elseif exist('pm1.mat','file')==2
                   start_step=3;
            elseif exist('ps1.mat','file')==2
                   start_step=2;
            else
                start_step=1;
            end

            if start_step>end_step
                fprintf(['\n' patchsplit{end} ': already up to end stage ' num2str(end_step) ' \n'])
            else
                fprintf(['\n' patchsplit{end} ': complete up to stage ' num2str(start_step-1) ' \n'])
            end
        end

      if start_step==1
        msgstr(round(nfill)/2-3:round(nfill/2)+4)=' Step 1 ';
        fprintf(skipstr);
        logit(fillstr);
        logit(msgstr);
        logit(fillstr)
        logit(['Directory is ',patchsplit{end}])
        fprintf(skipstr);
        if strcmpi(small_baseline_flag,'y')
%             try
                if strcmpi(insar_processor,'gamma') | strcmpi(insar_processor,'snap')
                    sb_load_initial_gamma;
                elseif strcmpi(insar_processor,'gsar')
                    sb_load_initial_gsar;
                elseif  strcmpi(insar_processor,'isce')
                    if exist('data_inc','var')==0
                        % already in patch dir, file contained in the InSAR dir
                        inc_angle = ['..' filesep 'inc_angle.raw'];
                        if exist(inc_angle,'file')~=2
                             inc_angle = ['..' filesep inc_angle];
                        end
                        if exist(inc_angle,'file')==2
                            fprintf('Found inc angle file, will load the data \n')
                            data_inc = (load_isce(inc_angle));
                        else
                            data_inc=[];
                        end
                    end
                    sb_load_initial_isce(data_inc)
                else
                    sb_load_initial;
                end
                load('no_ps_info.mat');
                % reset as we are currently re-processing
                stamps_step_no_ps(1:end)=0;

%             catch
%
%                load('no_ps_info.mat');
%                % reset as we are currently re-processing
%                stamps_step_no_ps(1:end)=0;
%                fprintf('***No PS points left. Updating the stamps log for this****\n')
%                % update the flag indicating no PS left in step 1
%                stamps_step_no_ps(1)=1;
%                psver =1;
%                save('psver.mat','psver')
%
%             end
            save('no_ps_info.mat','stamps_step_no_ps')

        else
%             try
                if strcmpi(insar_processor,'gamma') | strcmpi(insar_processor,'snap')
                    ps_load_initial_gamma;
                elseif strcmpi(insar_processor,'gsar')
                    ps_load_initial_gsar;
                elseif  strcmpi(insar_processor,'isce')
                     if exist('data_inc','var')==0
                        % already in patch dir, file contained in the InSAR dir
                        inc_angle = ['..' filesep 'inc_angle.raw'];
                        if exist(inc_angle,'file')~=2
                             inc_angle = ['..' filesep inc_angle];
                        end
                        if exist(inc_angle,'file')==2
                            fprintf('Found inc angle file, will load the data \n')
                            data_inc = (load_isce(inc_angle));
                        else
                            data_inc=[];
                        end
                    end
                    ps_load_initial_isce(data_inc)

                else
                    ps_load_initial;
                end
                load('no_ps_info.mat');
                % reset as we are currently re-processing
                stamps_step_no_ps(1:end)=0;
%             catch
%                 load('no_ps_info.mat');
%                 % reset as we are currently re-processing
%                 stamps_step_no_ps(1:end)=0;
%                 fprintf('***No PS points left. Updating the stamps log for this****\n')
%                 % update the flag indicating no PS left in step 1
%                 stamps_step_no_ps(1)=1;
%                 save('no_ps_info.mat','stamps_step_no_ps')
%                 psver =1;
%                 save('psver.mat','psver')
%
%             end
            save('no_ps_info.mat','stamps_step_no_ps')
        end
        elseif start_step <=4
            setpsver(1)
      end

        if start_step<=2 & end_step >=2
            msgstr(round(nfill)/2-3:round(nfill/2)+4)=' Step 2 ';
            fprintf(skipstr);
            logit(fillstr);
            logit(msgstr);
            logit(fillstr)
            logit(['Directory is ',patchsplit{end}])
            fprintf(skipstr);

            % check if step 1 had more than 0 PS points
            load('no_ps_info.mat');
            % reset as we are currently re-processing
            stamps_step_no_ps(2:end)=0;

            % run step 2 when there are PS left in step 1
            if stamps_step_no_ps(1)==0
                if strcmpi(quick_est_gamma_flag,'y')
                    ps_est_gamma_quick(est_gamma_parm);
                else
                    ps_est_gamma(est_gamma_parm);
                end
            else
                stamps_step_no_ps(2)=1;
                fprintf('No PS left in step 1, so will skip step 2 \n')
            end
            save('no_ps_info.mat','stamps_step_no_ps')
        end

        if start_step<=3 & end_step >=3
            msgstr(round(nfill)/2-3:round(nfill/2)+4)=' Step 3 ';
            fprintf(skipstr);
            logit(fillstr);
            logit(msgstr);
            logit(fillstr)
            logit(['Directory is ',patchsplit{end}])
            fprintf(skipstr);

            % check if step 2 had more than 0 PS points
            load('no_ps_info.mat');
            % reset as we are currently re-processing
            stamps_step_no_ps(3:end)=0;

            % run step 3 when there are PS left in step 2
            if stamps_step_no_ps(2)==0
                if strcmpi(quick_est_gamma_flag,'y') & strcmpi(reest_gamma_flag,'y')
                    ps_select;
                else
                    ps_select(1);
                end
            else
                fprintf('No PS left in step 2, so will skip step 3 \n')
                stamps_step_no_ps(3)=1;
            end
            save('no_ps_info.mat','stamps_step_no_ps')
        end

        if start_step<=4 & end_step >=4
            msgstr(round(nfill)/2-3:round(nfill/2)+4)=' Step 4 ';
            fprintf(skipstr);
            logit(fillstr);
            logit(msgstr);
            logit(fillstr)
            logit(['Directory is ',patchsplit{end}])
            fprintf(skipstr);

            % check if step 3 had more than 0 PS points
            load('no_ps_info.mat');
            % reset as we are currently re-processing
            stamps_step_no_ps(4:end) =0;       % keep for the first 5 steps only

            % run step 4 when there are PS left in step 3
            if stamps_step_no_ps(3)==0
                if strcmpi(small_baseline_flag,'y')
                    ps_weed(0,1);
                else
                    ps_weed;
                end
            else
                fprintf('No PS left in step 3, so will skip step 4 \n')
                stamps_step_no_ps(4)=1;

            end
            save('no_ps_info.mat','stamps_step_no_ps')
        end

        if start_step<=5 & end_step >=5
            msgstr(round(nfill)/2-3:round(nfill/2)+4)=' Step 5 ';
            fprintf(skipstr);
            logit(fillstr);
            logit(msgstr);
            logit(fillstr)
            logit(['Directory is ',patchsplit{end}])
            fprintf(skipstr);

            % check if step 4 had more than 0 PS points
            load('no_ps_info.mat');
            % reset as we are currently re-processing
            stamps_step_no_ps(5:end) = 0;       % keep for the first 5 steps only

            % run step 5 when there are PS left in step 3
            if stamps_step_no_ps(4)==0
                ps_correct_phase;
            else
                fprintf('No PS left in step 4, so will skip step 5 \n')
                stamps_step_no_ps(5)=1;
            end
            save('no_ps_info.mat','stamps_step_no_ps')
        end

        cd(currdir)
      end
    end
end

patchsplit=strsplit(pwd,'/');

% check if one can process second part of step 5b and above
if strcmpi(stamps_PART2_flag,'y')
    %%% Loop throught the patches and update the patch.list and keep only those
    %%% that have PS left.
    if patches_flag=='y'
        % go in reverse order such patches can be dropped when needed

        fid = fopen('patch.list_new','w');
        for i=1:length(patchdir)
            % check the file with the PS information
            filename_PS_check = [patchdir(i).name filesep 'no_ps_info.mat'];

            % assume by default to keep patch for backward compatibility
            keep_patch = 1;
            if exist(filename_PS_check,'file')==2
                load(filename_PS_check)
                if sum(stamps_step_no_ps)>=1
                   keep_patch=0;
                end
            end

            % update the patch list.
            if keep_patch==1
                fprintf(fid,[patchdir(i).name '\n']);
            end
            if i==length(patchdir)
               fclose(fid) ;
            end
        end

        % update the files such in futhre the new patch list will be used.
        movefile('patch.list','patch.list_old');
        movefile('patch.list_new','patch.list');
    end

    if start_step<=5 & end_step >=5
        abord_flag=0;
        if patches_flag=='y'
            fprintf(skipstr);
            logit(['Directory is ',patchsplit{end}])
            fprintf(skipstr);
            ps_merge_patches
        else
            % this is processing of an individual patch
            % see if there are any PS left
            if exist('no_ps_info.mat','file')==2
                load('no_ps_info.mat')
                if sum(stamps_step_no_ps)>=1
                   abord_flag=1;
                end
            end
        end

        % see if step 5 can be ran
        if abord_flag==0
            ps_calc_ifg_std;
        else
            fprintf('No PS left in step 4, so will skip step 5 \n')
        end
    end

    if start_step<=6 & end_step >=6
        msgstr(round(nfill)/2-3:round(nfill/2)+4)=' Step 6 ';
        fprintf(skipstr);
        logit(fillstr);
        logit(msgstr);
        logit(fillstr)
        logit(['Directory is ',patchsplit{end}])
        fprintf(skipstr);

        ps_unwrap
        if strcmpi(small_baseline_flag,'y')
            sb_invert_uw
        end
    end

    if start_step<=7 & end_step >=7
        msgstr(round(nfill)/2-3:round(nfill/2)+4)=' Step 7 ';
        fprintf(skipstr);
        logit(fillstr);
        logit(msgstr);
        logit(fillstr)
        logit(['Directory is ',patchsplit{end}])
        fprintf(skipstr);

        if strcmpi(small_baseline_flag,'y')
            ps_calc_scla(1,1)   % small baselines
            ps_smooth_scla(1)
            ps_calc_scla(0,1) % single master
        else
            ps_calc_scla(0,1)
            ps_smooth_scla
        end
    end

    if start_step<=8 & end_step >=8
        msgstr(round(nfill)/2-3:round(nfill/2)+4)=' Step 8 ';
        fprintf(skipstr);
        logit(fillstr);
        logit(msgstr);
        logit(fillstr)
        logit(['Directory is ',patchsplit{end}])
        fprintf(skipstr);

        if strcmpi(scn_kriging_flag,'y')
            ps_scn_filt_krig
        else
            ps_scn_filt
        end
    end
end

logit(1);


function wt = wfbtput(d,k,w,wt,forceStr)
%WFBTPUT  Put node to the filterbank tree
%   Usage:  wt = wfbtput(d,k,w,wt);
%           wt = wfbtput(d,k,w,wt,'force');
%
%   Input parameters:
%           d   : Level in the tree (0 - root).
%           k   : Index (array of indexes) of the node at level *d* (starting at 0).
%           w   : Node, basic wavelet filterbank.
%           wt  : Wavelet filterbank tree structure (as returned from
%                 |wfbtinit|).
%
%   Output parameters:
%           wt : Modified filterbank structure.
%
%   `wfbtput(d,k,w,wt)` puts the basic filterbank *w* to the filter
%   tree structure *wt* at level *d* and index(es) *k*. The output is a
%   modified tree structure. *d* and *k* have to specify unconnected output
%   of the leaf node. Error is issued if *d* and *k* points to already
%   existing node. For possible formats of parameter *w* see help of |fwt|.
%   Parameter *wt* has to be a structure returned by |wfbtinit|.
%
%   `wfbtput(d,k,w,wt,'force')` does the same but replaces node at *d* and *k*
%   if it already exists. If the node to be replaced has any children,
%   the number of outputs of the replacing node have to be equal to number of
%   outputs of the node beeing replaced.
%
%   Examples:
%   ---------
%
%   This example shows magnitude frequency responses of a tree build from
%   the root:::
%
%      % Initialize empty struct
%      wt = wfbtinit();
%      % Put root node to the empty struct
%      wt1 = wfbtput(0,0,'db8',wt);
%      % Connect a different nodes to both outputs of the root
%      wt2 = wfbtput(1,[0,1],'db10',wt1);
%      % Connect another nodes just to high-pass outputs of nodes just added
%      wt3 = wfbtput(2,[1,3],'db10',wt2);
%      % Add another node at level 3
%      wt4 = wfbtput(3,1,'db16',wt3);
%
%      % Create identical filterbanks
%      [g1,a1] = wfbt2filterbank(wt1,'freq');
%      [g2,a2] = wfbt2filterbank(wt2,'freq');
%      [g3,a3] = wfbt2filterbank(wt3,'freq');
%      [g4,a4] = wfbt2filterbank(wt4,'freq');
%
%      % Plot frequency responses of the growing tree. Linear scale
%      % (both axis) is used and positive frequencies only are shown.
%      subplot(4,1,1);
%      filterbankfreqz(g1,a1,1024,'plot','linabs','posfreq');
%      subplot(4,1,2);
%      filterbankfreqz(g2,a2,1024,'plot','linabs','posfreq');
%      subplot(4,1,3);
%      filterbankfreqz(g3,a3,1024,'plot','linabs','posfreq');
%      subplot(4,1,4);
%      filterbankfreqz(g4,a4,1024,'plot','linabs','posfreq');
%

% AUTHOR: Zdenek Prusa

if nargin<4
   error('%s: Too few input parameters.',upper(mfilename));
end

%if isfield(wt,'dualnodes')
%    error('%s: Cannot modify the dual-tree struct.',upper(mfilename));
%end

do_force = 0;
if nargin==5
    if ~ischar(forceStr)
        error('%s: Fifth parameter should be a string.',upper(mfilename));
    end
    if strcmpi(forceStr,'force')
        do_force = 1;
    end
end

% This was replaced. Calling ltfatargheler was too slow.
%definput.flags.force = {'noforce','force'};
%[flags,kv]=ltfatarghelper({},definput,varargin);

node = fwtinit(w);

oldnodecount = numel(wt.nodes);
nodeschanged = [];

[nodeNoArray,nodeChildIdxArray] = depthIndex2NodeNo(d,k,wt);

for ii=1:numel(nodeNoArray)
 nodeNo = nodeNoArray(ii);
 nodeChildIdx = nodeChildIdxArray(ii);
if(nodeNo==0)
    % adding root
    if(~isempty(find(wt.parents==0,1)))
        if(do_force)
           rootId = find(wt.parents==0,1);
           % if root has children, check if the new root has the same
           % number of them
           if(~isempty(find(wt.children{rootId}~=0,1)))
              if(length(w.g)~=length(wt.nodes{rootId}.g))
                 error('%s: The replacing root have to have %d filters.',mfilename,length(wt.nodes{rootId}.g));
              end
           end
        else
            error('%s: Root already defined. Use FORCE option to replace.',mfilename);
        end
        wt.nodes{rootId} = node;
        nodeschanged(end+1) = rootId;

        if isfield(wt,'dualnodes')
            wt.dualnodes{rootId} = node;
        end
        continue;
    end
    wt.nodes{end+1} = node;
    wt.parents(end+1) = nodeNo;
    wt.children{end+1} = [];

    if isfield(wt,'dualnodes')
        wt.dualnodes{end+1} = node;
    end
    continue;
end

childrenIdx = find(wt.children{nodeNo}~=0);
found = find(childrenIdx==nodeChildIdx,1);
if(~isempty(found))
   if(do_force)
     %check if childrenIdx has any children
     tmpnode = wt.children{nodeNo}(found);
     if(~isempty(find(wt.children{tmpnode}~=0, 1)))
         if length(node.g)~=length(wt.nodes{tmpnode}.g)
            error('%s: The replacing node must have %d filters.',mfilename,length(wt.nodes{tmpnode}.g));
         end
     end
     wt.nodes{tmpnode} = node;
     nodeschanged(end+1) = tmpnode;
     if isfield(wt,'dualnodes')
         wt.dualnodes{tmpnode} = node;
     end
     % Since we are replacing a node, all links are already correct
     continue;
   else
       error('%s: Such node (depth=%d, idx=%d) already exists. Use FORCE option to replace.',mfilename,d,k);
   end
end

wt.nodes{end+1} = node;
wt.parents(end+1) = nodeNo;
wt.children{end+1} = [];
wt.children{nodeNo}(nodeChildIdx) = numel(wt.parents);

if isfield(wt,'dualnodes')
    wt.dualnodes{end+1} = node;
end

end

% We have to correctly shuffle filters in the just added (or modified) filters
% if the tree was already defined as frequency ordered.
if wt.freqOrder
      wt = nat2freqOrder(wt,[nodeschanged,oldnodecount+1:numel(wt.nodes)]);
end


function F = plus( F, G )
% + PLUS of two CHEBFUN2V objects.
%   F + G if F and G are CHEBFUN2V objects does componentwise addition.
%
%   F + G if F is a double and G is a CHEBFUN2V does componentwise addition.
%
%   F + G if F is a CHEBFUN2V and G is a double does componentwise addition.
%
%   PLUS(F,G) is called for the syntax F + G.

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

% Empty check:
if ( isempty( F ) || isempty( G ) )
    F = chebfun2v;
    return
end

if ( ~isa( F , 'chebfun2v' ) )
    F = plus(G, F);
    return
end

nF = F.nComponents;

if ( isa(G, 'double') )              % CHEBFUN2V + DOUBLE
    if ( numel(G) == 1 )             % CHEBFUN2V + SCALAR
        for jj = 1 : nF
            F.components{jj} = plus(F.components{jj}, G);
        end
    elseif ( numel(G) == nF )        % CHEBFUN2V + MATRIX
        for jj = 1 : nF
             F.components{jj} = plus(F.components{jj}, G(jj));
        end
    else
        error('CHEBFUN:CHEBFUN2V:plus:doubleSize', 'Dimension mismatch.')
    end
elseif ( isa(G, 'chebfun2') )        % CHEBFUN2V + CHEBFUN
    for jj = 1 : nF
        F.components{jj} = plus(F.components{jj}, G);
    end
elseif ( isa(G, 'chebfun2v') )       % CHEBFUN2V + CHEBFUN2V
    nG = G.nComponents;
    if ( nG ~= nF )
        error('CHEBFUN:CHEBFUN2V:plus:components', ...
            'The chebfun2v objects do not have the same components.')
    end
    if ( G.isTransposed ~= F.isTransposed )
        error('CHEBFUN:CHEBFUN2V:plus:transposed', 'Dimension mismatch.')
    end
    for jj = 1 : nF                  % Add each component together
        F.components{jj} = plus(F.components{jj}, G.components{jj});
    end
else
    error('CHEBFUN:CHEBFUN2V:plus:type', 'Unrecongized input arguments')
end

end


function [cdf] = gp_kfcv_cdf(gp,x,y,varargin)
%GP_KFCV_CDF  K-fold cross validation to predict CDF for GP model
%
%  Description
%    [cdf] = GP_KFCV_CDF(GP, X, Y, OPTIONS)
%    Performs K-fold cross-validation for a GP model given input matrix X
%    and target vector Y.
%
%    OPTIONS is optional parameter-value pair
%      z          - optional observed quantity in triplet (x_i,y_i,z_i)
%                   Some likelihoods may use this. For example, in
%                   case of Poisson likelihood we have z_i=E_i,
%                   that is, expected value for ith case.
%      yt         - optional observed yt in test points.
%                   Default option for yt is yt=y.
%      inf_method - inference method. Possible methods are
%                    'MAP'      parameters optimized to MAP (default)
%                    'MCMC'     MCMC sampling using GP_MC
%                    'IA'       integration approximation using GP_IA
%                    'fixed'    parameters are fixed, it either use MAP
%                               or integration approximation, depending if
%                               GP is a single GP structure or a GP array
%                               (for example from GP_IA)
%      optimf     - function handle for an optimization function, which is
%                   assumed to have similar input and output arguments
%                   as usual fmin*-functions. Default is @fminscg.
%      opt        - options for the inference method. If 'MAP' is used
%                   use optimset to set options for optimization.
%                   Default options for optimization are 'GradObj'
%                   is 'on', 'LargeScale' is 'off', 'Display' is 'off'
%      k          - number of folds in CV, default k=10
%      rstream    - number of a random stream to be used for
%                   permuting the data befor division. This way
%                   same permutation can be obtained for different
%                   models. Default is 1. See doc RandStream for
%                   more information.
%      trindex    - k-fold CV training indices. A cell array with k
%                   fields each containing index vector for respective
%                   training set.
%      tstindex   - k-fold CV test indices. A cell array with k
%                   fields each containing index vector for
%                   respective test set.
%      display    - defines if messages are displayed.
%                   - 'iter' displays output at each iteration
%
%    The output argument is
%
%           cdf- Predictive Cumulative Distribiton function evaluated in y
%           (default) or yt.
%
%
%  See also
%    DEMO_MODELCOMPARISON1, DEMO_MODELCOMPARISON2
%
% Copyright (c) 2009-2010 Jarno Vanhatalo
% Copyright (c) 2010-2011 Aki Vehtari
% Copyright (c) 2012 Ernesto Ulloa

% 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 = 'GP_KFCV';
ip.addRequired('gp',@(x) isstruct(x) || iscell(x));
ip.addRequired('x', @(x) ~isempty(x) && isreal(x) && all(isfinite(x(:))))
ip.addRequired('y', @(x) ~isempty(x) && isreal(x) && all(isfinite(x(:))))
ip.addParamValue('z', [], @(x) isreal(x) && all(isfinite(x(:))))
ip.addParamValue('yt', [], @(x) isreal(x) && all(isfinite(x(:))))
ip.addParamValue('inf_method', 'MAP', @(x) ...
  ismember(x,{'MAP' 'LOO' 'KFCV' 'WAIC' 'WAICV' 'WAICG' 'MCMC' 'IA' 'fixed'}))
ip.addParamValue('optimf', @fminscg, @(x) isa(x,'function_handle'))
ip.addParamValue('opt', struct(), @isstruct)
ip.addParamValue('k', 10, @(x) isreal(x) && isscalar(x) && isfinite(x) && x>0)
ip.addParamValue('rstream', 1, @(x) isreal(x) && isscalar(x) && isfinite(x) && x>0)
ip.addParamValue('trindex', [], @(x) isempty(x) || iscell(x))
ip.addParamValue('tstindex', [], @(x) isempty(x) || iscell(x))
ip.addParamValue('display', 'on', @(x) islogical(x) || ...
  ismember(x,{'iter' 'fold'}))
ip.parse(gp, x, y, varargin{:});
z=ip.Results.z;
yt=ip.Results.yt;
inf_method=ip.Results.inf_method;
optimf=ip.Results.optimf;
opt=ip.Results.opt;
k=ip.Results.k;
rstream=ip.Results.rstream;
trindex=ip.Results.trindex;
tstindex=ip.Results.tstindex;
display = ip.Results.display;
if isequal(display,'fold');display='iter';end

[n,nin] = size(x);

gp_orig = gp;

if ismember(inf_method,{'MAP' 'LOO' 'KFCV' 'WAIC' 'WAICV' 'WAICG'})
  optdefault=struct('Display','off');
  opt=optimset(optdefault,opt);
end

if (isempty(trindex) && ~isempty(tstindex)) || (~isempty(trindex) && isempty(tstindex))
  error('gp_kfcv: If you give cross-validation indexes, you need to provide both trindex and tstindex.')
end

if isempty(trindex) || isempty(tstindex)
  [trindex, tstindex] = cvit(n, k, rstream);
end

% *** note: yt must can be: a scalar or a vector of size(y)x1 or even a matrix of
% size(y)xN

if ~isempty(yt)
  if size(yt,1)==size(y,1)
    yt=yt;
  elseif size(yt,1)==1
    yt=bsxfun(@times,ones(size(y)),yt);
  else
    error('size of yt does not match y nor is it a scalar');
  end
  ytflag=1;
else
  ytflag=0;
  yt=y;
end

cvws=[];
trw=[];
% loop over the crossvalidation sets

for i=1:length(trindex)
  if isempty(tstindex{i})
    continue
  end

  if isequal(display,'iter')
    fprintf(' The CV-fold number: %d/%d \n', i, k)
  end

  % Set the training and test sets for i'th cross-validation set
  xtr = x(trindex{i},:);
  ytr = y(trindex{i},:);
  yttr= yt(trindex{i},:);

  xtst = x(tstindex{i},:);
  ytst = y(tstindex{i},:);
  yttst = yt(tstindex{i},:);

  if ~isempty(z)
    ztr = z(trindex{i},:);
    zt = z;
    flagz=1;
    %* yt = y;
    %       opt_tst.zt = z;
    %       opt_tst.yt = y;
  else
    flagz=0;
    ztr = [];
    %*yt = y;
    zt = [];
    %       opt_tr = struct();
    %       opt_tst.yt = y;
  end

  gp = gp_orig;

  if iscell(gp)
    gptype=gp{1}.type;
  else
    gptype=gp.type;
  end
  tstind2 = [];

  switch gptype
    case {'FIC' 'CS+FIC'}
      tstind2 = trindex{i};
    case 'PIC'
      % Set the block indices for the cv set of data points. Variable
      % naming(e.g tstind2) because parfor loop.
      ntr = size(xtr,1);
      ntst = size(xtst,1);
      trind2 = [];
      for i1=1:length(gp.tr_index)
        tstind2{i1} = [];
        trind2{i1} = [];
        for j1 = 1:length(gp.tr_index{i1})
          indtmp = find( sum((xtr - repmat(x(gp.tr_index{i1}(j1),:),ntr,1)).^2,2) == 0 );
          if isempty( indtmp )
            indtmp = find( sum((xtst - repmat(x(gp.tr_index{i1}(j1),:),ntst,1)).^2,2) == 0 );
            tstind2{i1} = [tstind2{i1} indtmp];
          else
            trind2{i1} = [trind2{i1} indtmp];
          end
        end
      end
      if iscell(gp)
        for j=1:numel(gp)
          gp{j}.tr_index=trind2;
        end
      else
        gp.tr_index = trind2;
      end
  end

  % Conduct inference
  switch inf_method
    case 'MAP'
      if flagz
        gp=gp_optim(gp,xtr,ytr,'z',ztr(:,size(z,2)),'opt',opt, 'optimf', optimf);
        w=gp_pak(gp);
        cvws(i,:)=w;
      else
        gp=gp_optim(gp,xtr,ytr,'z',ztr,'opt',opt, 'optimf', optimf);
        w=gp_pak(gp);
        cvws(i,:)=w;
      end
    case {'LOO' 'KFCV' 'WAIC' 'WAICV' 'WAICG'}
      if ismember('optimf',ip.UsingDefaults)

        if flagz
          gp=gp_optim(gp,xtr,ytr,'z',ztr(:,size(z,2)),'opt',opt,'loss',inf_method);
        else
          gp=gp_optim(gp,xtr,ytr,'z',ztr,'opt',opt,'loss',inf_method);
        end

      else
        if flagz
          gp=gp_optim(gp,xtr,ytr,'z',ztr(:,size(z,2)),'opt',opt,'loss',inf_method, 'optimf', optimf);
        else
          gp=gp_optim(gp,xtr,ytr,'z',ztr,'opt',opt,'loss',inf_method, 'optimf', optimf);
        end
      end
      w=gp_pak(gp);
      cvws(i,:)=w;
    case 'MCMC'
      if numel(gp.jitterSigma2)>1
        gp=thin(gp,numel(gp.jitterSigma2)-1);
      end
      % Scaled mixture noise model is a special case
      % where we need to modify the noiseSigmas2 vector
      % to a right length
      if isequal(gp.lik.type, 'lik_smt')
        gp.lik.noiseSigmas2 = gp_orig.lik.noiseSigmas2(trindex{i});
        gp.lik.r = gp_orig.lik.r(trindex{i});
        gp.lik.U = gp_orig.lik.U(trindex{i});
        gp.lik.ndata = length(trindex{i});
      end
      % Pick latent values for the training set in this fold
      if isfield(gp,'latentValues')
        if (~isfield(gp.lik, 'nondiagW') || ismember(gp.lik.type, {'Softmax', 'Multinom', ...
            'LGP', 'LGPC'}))
          latentValues=reshape(gp_orig.latentValues, size(y,1), size(y,2));
          gp.latentValues=reshape(latentValues(trindex{i},:), size(y,2)*length(trindex{i}), 1);
          % gp.latentValues=gp_orig.latentValues(trindex{i});
        else
          if ~isfield(gp.lik, 'xtime')
            nl=length(gp.comp_cf);
            gp.latentValues=gp_orig.latentValues(trindex{i}+(0:nl-1)*n);
          else
            ntime=size(gp.lik.xtime,1);
            gp.latentValues=gp_orig.latentValues([1:ntime, (ntime+trindex{i})]);
          end
        end
      end
      if flagz
        gp = gp_mc(gp, xtr, ytr, 'z', ztr(:,size(z,2)), opt);
      else
        gp = gp_mc(gp, xtr, ytr, 'z', ztr, opt);
      end
      nburnin = floor(length(gp.etr)/3);
      gp = thin(gp,nburnin);
    case 'IA'
      if flagz
        [gp,P_TH] = gp_ia(gp, xtr, ytr, 'z', ztr(:,size(z,2)), opt);
      else
        [gp,P_TH] = gp_ia(gp, xtr, ytr, 'z', ztr, opt);
      end
    case 'fixed'
      % nothing to do here
  end

  if iscell(gp)
    gplik=gp{1}.lik;
  else
    gplik=gp.lik;
  end

  switch inf_method
    case {'LOO' 'KFCV' 'WAIC' 'WAICV' 'WAICG' 'MAP'}
      if ~isfield(gplik.fh,'trcov') && isfield(gp.lik.fh,'predcdf')
        for it=1:size(yt,2)
          [Eft, Varft] = gp_pred(gp, xtr, ytr, x, 'tstind', tstind2, 'z', ztr(:,it), 'yt', yt(:,it), 'zt', zt(:,it));
          cdftemp{it}= gp.lik.fh.predcdf(gplik, Eft, Varft,yt(:,it));
        end
      elseif isfield(gplik.fh,'trcov') && isfield(gp.lik.fh,'predcdf')
        for it=1:size(yt,2)
          [Eft, Varft] = gp_pred(gp, xtr, ytr, x, 'tstind', tstind2,'yt', yt(:,it));
          cdftemp{it}= gp.lik.fh.predcdf(gplik, Eft, Varft,yt(:,it));
        end
      else
        error('This likelihood has not been implemented for this function')
      end
    case 'MCMC'
      nsamples=size(gp.etr,1);
      for i2=1:nsamples
        Gp=take_nth(gp,i2);
        gplik=Gp.lik;
        if ~isfield(gplik.fh,'trcov') && isfield(gp.lik.fh,'predcdf')
          for it=1:size(yt,2)
            [Eft, Varft] = gp_pred(Gp, xtr, ytr, x, 'tstind', tstind2, 'z', ztr(:,it), 'yt', yt(:,it), 'zt', zt(:,it));
            cdftemp{it,i2}= gp.lik.fh.predcdf(gplik, Eft, Varft,yt(:,it));
          end
        elseif isfield(gplik.fh,'trcov') && isfield(gp.lik.fh,'predcdf')
          for it=1:size(yt,2)
            [Eft, Varft] = gp_pred(gp, xtr, ytr, x, 'tstind', tstind2,'yt', yt(:,it));
            cdftemp{it,i2}= gp.lik.fh.predcdf(gplik, Eft, Varft,yt(:,it));
          end
        else
          error('This likelihood has not been implemented for this function')
        end
      end
      cdftemp = mat2cell(mean(cell2mat(cdftemp),2),repmat(1043,1,10),1);
    case 'IA'
      nsamples=length(gp);
      for i2=1:nsamples
        Gp=gp{i2};
        gplik=Gp.lik;
        if ~isfield(gplik.fh,'trcov') && isfield(gplik.fh,'predcdf')
          for it=1:size(yt,2)
            [Eft, Varft] = gp_pred(Gp, xtr, ytr, x, 'tstind', tstind2, 'z', ztr(:,it), 'yt', yt(:,it), 'zt', zt(:,it));
            cdftemp{it,i2}= gplik.fh.predcdf(gplik, Eft, Varft,yt(:,it));
          end
        elseif isfield(gplik.fh,'trcov') && isfield(gplik.fh,'predcdf')
          for it=1:size(yt,2)
            [Eft, Varft] = gp_pred(gp, xtr, ytr, x, 'tstind', tstind2,'yt', yt(:,it));
            cdftemp{it,i2}= gplik.fh.predcdf(gplik, Eft, Varft,yt(:,it));
          end
        else
          error('This likelihood has not been implemented for this function')
        end
      end
      cdftemp = mat2cell(sum(bsxfun(@times,cell2mat(cdftemp),P_TH'),2),repmat(1043,1,10),1);
  end

  for it=1:size(yt,2)
    cdf_cv(tstindex{i},it)=cdftemp{it}(tstindex{i},:);
  end

end

cdf=cdf_cv;
end


function g = lmcKernGradient(kern, X, X2, covGrad)

% LMCKERNGRADIENT Gradient of LMC kernel's parameters.
% FORMAT
% DESC computes the gradient of parameters associated to the LMC kernel.
% As well as the kernel structure and the input positions, the user
% provides a matrix COVGRAD which gives the partial derivatives of the
% function with respect to the relevant elements of the kernel matrix.
% RETURN g:  gradients of the function of interest with respect to the
% kernel parameters. The ordering of the vector should match that
% provided by the function kernExtractParam.
% ARG kern : the kernel structure for which the gradients are being
% computed.
% ARG X : the input locations for which the gradients are being computed.
% ARG covGrad : matrix of partial derivatives of the function of
% interest with respect to the kernel matrix. The argument takes the
% form of a square matrix of dimension  numData, where numData is the
% number of rows in X.
%
% FORMAT
% DESC  computes the derivatives as above, but input locations are now
% provided in two matrices associated with rows and columns of the kernel
% matrix.
% RETURN g : gradients of the function of interest with respect to the
% kernel parameters.
% ARG kern : the kernel structure for which the gradients are being
% computed.
% ARG X : the input locations associated with the rows of the kernel matrix.
% ARG X2 : the input locations associated with the columns of the kernel
% matrix.
% ARG covGrad : matrix of partial derivatives of the function of interest
% with respect to the kernel matrix. The matrix should have the same number
% of rows as X1 and the same number of columns as X2 has rows.
%
% COPYRIGHT : Mauricio A. Alvarez, 2010.

% KERN

fhandle = str2func([kern.basicKernelType 'KernCompute']);
fhandleGrad = str2func([kern.basicKernelType 'KernGradient']);
gBK = zeros(1, kern.nParamsBK);
gPartialB = zeros(kern.nout);

if iscell(X)
    if nargin>3 && ~iscell(X2)
        error('Time course information is not matched in Cell format!');
    end
    % Collate arguments.
    dim1 = zeros(1, kern.nout);
    dim2 = zeros(1, kern.nout);
    for i=1:kern.nout
        dim1(i) = size(X{i}, 1);
        if nargin > 3
            dim2(i) = size(X2{i}, 1);
        else
            dim2(i) = dim1(i);
            covGrad = X2;
        end
    end
    for i = 1:kern.nout
        startOne = sum(dim1(1:(i-1)))+1;
        endOne = sum(dim1(1:i));
        startThree = sum(dim2(1:(i-1))) + 1;
        endThree = sum(dim2(1:i));
        if nargin > 3
            gBK = gBK + kern.B(i,i)*fhandleGrad(kern, X{i}, X2{i}, covGrad(startOne:endOne, ...
                startThree:endThree));
            basicK = fhandle(kern, X{i}, X2{i});
        else
            gBK = gBK + kern.B(i,i)*fhandleGrad(kern, X{i}, covGrad(startOne:endOne,...
                startThree:endThree));
            basicK = fhandle(kern, X{i});
        end
        gPartialB(i,i) = sum(sum(covGrad(startOne:endOne, startThree:endThree).*basicK));
        for j = 1:i-1
            startTwo = sum(dim2(1:(j-1))) + 1;
            endTwo =  sum(dim2(1:j));
            if nargin > 3
                g2  = kern.B(i,j)*fhandleGrad(kern, X{i}, X2{j}, covGrad(startOne:endOne, ...
                    startTwo:endTwo));
                basicK = fhandle(kern, X{i}, X2{j});
            else
                g2  = kern.B(i,j)*fhandleGrad(kern, X{i}, X{j}, covGrad(startOne:endOne, ...
                    startTwo:endTwo));
                basicK = fhandle(kern, X{i}, X{j});
            end
            gBK = gBK + 2*g2;
            gPartialB(i,j) = sum(sum(covGrad(startOne:endOne, startTwo:endTwo).*basicK));
            gPartialB(j,i) = gPartialB(i,j);
        end
    end
else
    if nargin < 4
        covGrad = X2;
        X2 = X;
    end
    basicK = fhandle(kern, X, X2);
    startOne = 1;
    endOne = 0;
    startThree = 1;
    endThree = 0;
    for i=1:kern.nout
        endOne = endOne + size(X,1);
        endThree = endThree + size(X2,1);
        gBK = gBK + kern.B(i,i)*fhandleGrad(kern, X, X2, ...
            covGrad(startOne:endOne, startThree:endThree));
        gPartialB(i,i) = sum(sum(covGrad(startOne:endOne, startThree:endThree).*basicK));
        startTwo = 1;
        endTwo = 0;
        startFour = 1;
        endFour = 0;
        for j=1:i-1
            endTwo = endTwo + size(X2,1);
            g2  = kern.B(i,j)*fhandleGrad(kern, X, X2, covGrad(startOne:endOne, ...
                startTwo:endTwo));
            gBK = gBK + g2;
            gPartialB(i,j) = sum(sum(covGrad(startOne:endOne, startTwo:endTwo).*basicK));
            if nargin < 3
                gBK = gBK + g2;
                gPartialB(j,i) = gPartialB(i,j);
            else
                endFour = endFour + size(X,1);
                g3 = kern.B(j,i)*fhandleGrad(kern, X, X2, covGrad(startFour:endFour, ...
                    startThree:endThree));
                gBK = gBK + g3;
                gPartialB(j,i) = sum(sum(covGrad(startFour:endFour, startThree:endThree).*basicK));
                startFour = endFour + 1;
            end
            startTwo = endTwo + 1;
        end
        startOne = endOne + 1;
        startThree = endThree + 1;
    end
end

Jij = zeros(kern.nout, kern.rankCorregMatrix);
Jji = zeros(kern.rankCorregMatrix, kern.nout);
gB = zeros(kern.nout, kern.rankCorregMatrix);
for i=1:kern.nout
    for j=1:kern.rankCorregMatrix
        Jij(i,j) = 1; Jji(j,i) = 1;
        gB(i,j) = sum(sum((kern.A*Jij' + Jji'*kern.A').*gPartialB));
        Jij(i,j) = 0; Jji(j,i) = 0;
    end
end

g = [gBK gB(:)'];


function [ResultsMaleFemale] = compareMaleFemale(male,female)
% This function compares basic features of the male and female whole-body
% metabolic models
%
% [ResultsMaleFemale] = compareMaleFemale(male,female)
%
% INPUT
% male                  model structure (male whole-body metabolic model)
% female                model structure (female whole-body metabolic model)
%
% OUTPUT
% ResultsMaleFemale     structure containing the basic differences and
%                       commenalities between male and female model
%
% Ines Thiele 2017

% reactions unique to male
ResultsMaleFemale.MaleOnly = setdiff(male.rxns,female.rxns);
ResultsMaleFemale.FemaleOnly = setdiff(female.rxns,male.rxns);
ResultsMaleFemale.BothGender = intersect(female.rxns,male.rxns);

[maleOrgans]=unique(strtok(male.rxns,'_'));
[femaleOrgans]=unique(strtok(female.rxns,'_'));

for i = 1 : length(maleOrgans)
    ResultsMaleFemale.OrgansNumRxnMale(i,1) = length(strmatch(maleOrgans(i),male.rxns));
    ResultsMaleFemale.OrgansNumRxnMale(i,2) = length(strmatch(maleOrgans(i),ResultsMaleFemale.MaleOnly));
    % fraction
    ResultsMaleFemale.OrgansNumRxnMale(i,3) = ResultsMaleFemale.OrgansNumRxnMale(i,2)/ResultsMaleFemale.OrgansNumRxnMale(i,1);
end

for i = 1 : length(femaleOrgans)
    ResultsMaleFemale.OrgansNumRxnFemale(i,1) = length(strmatch(femaleOrgans(i),female.rxns));
    ResultsMaleFemale.OrgansNumRxnFemale(i,2) = length(strmatch(femaleOrgans(i),ResultsMaleFemale.FemaleOnly));
    % fraction
    ResultsMaleFemale.OrgansNumRxnFemale(i,3) = ResultsMaleFemale.OrgansNumRxnFemale(i,2)/ResultsMaleFemale.OrgansNumRxnFemale(i,1);
end

ResultsMaleFemale.maleOrgans = maleOrgans;
ResultsMaleFemale.femaleOrgans = femaleOrgans;

%get subsystems for gall rxns
FemaleSS = female.subSystems(find(ismember(female.rxns,ResultsMaleFemale.FemaleOnly(strmatch('Gall_',ResultsMaleFemale.FemaleOnly)))));
ResultsMaleFemale.FemaleGallSSEnrich = unique(FemaleSS);
for i = 1 : length(ResultsMaleFemale.FemaleGallSSEnrich)
    ResultsMaleFemale.FemaleGallSSEnrich{i,2} = num2str(length(strmatch(ResultsMaleFemale.FemaleGallSSEnrich{i},FemaleSS,'exact')));
end
MaleSS = male.subSystems(find(ismember(male.rxns,ResultsMaleFemale.MaleOnly(strmatch('Gall_',ResultsMaleFemale.MaleOnly)))));
ResultsMaleFemale.MaleGallSSEnrich = unique(MaleSS);
for i = 1 : length(ResultsMaleFemale.MaleGallSSEnrich)
    ResultsMaleFemale.MaleGallSSEnrich{i,2} = num2str(length(strmatch(ResultsMaleFemale.MaleGallSSEnrich{i},MaleSS,'exact')));
end

% unique biofluid exchange reactions

ResultsMaleFemale.MaleOnlyBiofluid = ResultsMaleFemale.MaleOnly(find(~cellfun(@isempty,strfind(ResultsMaleFemale.MaleOnly,'_EX_'))));
ResultsMaleFemale.FemaleOnlyBiofluid = ResultsMaleFemale.FemaleOnly(find(~cellfun(@isempty,strfind(ResultsMaleFemale.FemaleOnly,'_EX_'))));