function [eigVec, eigVal, mmPerVox] = dtiLoadTensor(vectorsBasename, tensorBasename, b0Basename)
% [eigVec, eigVal, mmPerVox] = dtiLoadTensor([vectorsBasename], [tensorBasename], [b0Basename])
%
% 'eigVec' is a 5d array- a 3d array of 3x3 eigenVectors.
% To get the eigenVector for a particular voxel, use: v = eigVec(x,y,z,:,:);
%
% If you also provide the 'Tensor.float' file, then the eigen values are
% also returned (eigVal).
%
% If you provide the B0 filename, then we can get some useful info from the
% header, like the voxel dimensions.
%
% Loads the 3x3 matrix from the tensorcalc-format 'Vectors.float' file.
% The Vectors.float files are 32-bit floating point arrays (raw data- no header)
% with 9 images per file:
%   X1, Y1, Z1 = (signed) components of the eigenvector corresponding to the
%                largest eigenvalue.
%   X2, Y2, Z2 = (signed) components of the eigenvector corresponding to the
%                middle eigenvalue.
%   X3, Y3, Z3 = (signed) components of the eigenvector corresponding to the
%                smallest eigenvalue.
% All vectors are unit vectors.
%
%
% HISTORY:
% 2002.08.16 RFD (bobd@stanford.edu) Wrote it.
% 2003.11.13 RFD changed to return eigen vectors and values separately.
% 2003.11.26 RFD- we now get some useful info from the B0 header, if it's available.
%
% TODO:
%
%
% EXAMPLE:
% [eigVec,eigVal] = dtiLoadTensor('Vectors.float','Tensor.float');
% meanDiffusivity = sum(eigVal,4)/3;
% stdevDiffusivity = sqrt(sum((eigVal-repmat(meanDiffusivity,[1,1,1,3])).^2,4));
% normDiffusivity = sqrt(sum(eigVal.^2,4));
% fa = sqrt(3/2).*(stdevDiffusivity./normDiffusivity);
%

if(~exist('vectorsBasename', 'var') | isempty(vectorsBasename))
    [f,p] = uigetfile({'*.001';'*.*'},'Select a slice from the Vectors file...');
    vectorsBasename = fullfile(p,f(1:end-3));
    tensorBasename = fullfile(p,'Tensor.float.');
    b0Basename = fullfile(p,'B0.');
end
if(~exist('tensorBasename', 'var') | isempty(tensorBasename))
    tensorBasename = fullfile(fileparts(vectorsBasename),'Tensor.float.');
else
    if(tensorBasename(end) ~= '.')
        tensorBasename = [tensorBasename '.'];
    end
end
if(~exist('b0Basename', 'var') | isempty(b0Basename))
    b0Basename = fullfile(fileparts(vectorsBasename),'B0.');
else
    if(b0Basename(end) ~= '.')
        b0Basename = [b0Basename '.'];
    end
end

if(vectorsBasename(end) ~= '.')
    vectorsBasename = [vectorsBasename '.'];
end

nSlices = length(dir([vectorsBasename,'*']));
if(~isempty(b0Basename))
    [su,ex,se,im] = readIfileHeader([b0Basename,'001']);
    imdim = [im.imatrix_X, im.imatrix_Y, nSlices];
    mmPerVox = [im.pixsize_X, im.pixsize_Y, im.slthick];
else
    warning('B0 file not provided- guessing parameters!');
    mmPerVox = [260/128 260/128 3];
    % Now try to guess the image size
    d = dir([vectorsBasename,'001']);
    % OK- there are 9*4=36 bytes per voxel -> d.bytes/36 voxels. log2 tells us
    % how many bits this requires. We assume that the bits are divided equally
    % between X and Y (thus the /2), and then convert from bits to decimal (2^)
    xySize = 2^(log2(d.bytes./36)./2);
    imdim = [xySize,xySize,nSlices];
    disp(['I''m guessing that the image dimension is [',num2str(imdim),'].']);
end

npix = imdim(1)*imdim(2);
eigVec = zeros([imdim,3,3]);
if(~isempty(tensorBasename))
    eigVal = zeros([imdim,3]);
else
    eigVal = [];
end
disp(['Reading ',num2str(nSlices),' slices...']);
for(ii=1:imdim(3))
    fname = sprintf('%s%03d', vectorsBasename, ii);
    fid = fopen(fname, 'rb', 'ieee-be');
    eigVec(:,:,ii,:,:) = reshape(fread(fid, inf, 'float32'),[imdim(1),imdim(2),1,3,3]);
    fclose(fid);
    % Unfortunately, tensorcalc stores only the unit vectors in the Vectors
    % file. So, we need to open up the corresponding Tensor file and get
    % the eigenvalues from there.
    if(~isempty(tensorBasename))
        fname = sprintf('%s%03d', tensorBasename, ii);
        % 2004.03.26 RFD: Data now in ieee-le format?
        fid = fopen(fname, 'rb', 'ieee-be');
        % The eigen values are in the 2:4 sections of the Tensor.float
        % file.  Here, we just read the first four sections.
        tmp = fread(fid, npix*4, 'float32');
        % Drop the first section, since it's the mean diffusivity.
        tmp = tmp(npix+1:end);
        fclose(fid);
        eigVal(:,:,ii,1) = reshape(tmp(1:npix), imdim(1), imdim(2));
        eigVal(:,:,ii,2) = reshape(tmp(npix+1:npix*2), imdim(1), imdim(2));
        eigVal(:,:,ii,3) = reshape(tmp(npix*2+1:npix*3), imdim(1), imdim(2));
    end
end
return;

% Genreal notes about tensorcalc data:
% Each slice is in a separate file. Included are two different data file types,
% Tensor.float and Vectors.float. Tensor.float contains most of the info that
% you would need to do fiber tracing, except that it does not contain the
% eigenvectors corresponding to the two smaller eigenvalues. Vectors.float
% contains all 3 eigenvectors. (For more info about these files, see
% http://sirl.stanford.edu/dwi/maj/)
%
% Tensor.float files are 32-bit floating point arrays (raw data- no header)
% containing the following maps:
%
% 1. Mean diffusivity (or Trace/3, units x106 mm2/s)
% 2. Maximum eigenvalue
% 3. Medium eigenvalue
% 4. Minimum eigenvalue
% 5. X component of the maximum eigenvector (values between -1 and 1)
% 6. Y component of the maximum eigenvector
% 7. Z component of the maximum eigenvector
% 8. Fractional Anisotropy, FA (multiplied by 1000)
% 9. Lattice index, Add (if asked for via -Add option)
% 10. Coherence index, CI (if asked for via -CI option)
% 11. b=0 image (arbitrary units)
%
% Sample matlab code to read these files:
%
% baseName = 'Tensor.float.';
% imDim = [128,128,38];
% meanDiffusion = zeros(imDim);
% maxEigVal = zeros(imDim);
% medEigVal = zeros(imDim);
% minEigVal = zeros(imDim);
% maxEigValX = zeros(imDim);
% maxEigValY = zeros(imDim);
% maxEigValZ = zeros(imDim);
% fa = zeros(imDim);
% for(ii=1:imDim(3))
%     fname = sprintf('%s%03d', baseName, ii);
%     fid = fopen(fname, 'rb', 'ieee-be');
%     tmp = reshape(fread(fid, inf, 'float32'),[imDim(1),imDim(2),11]);
%     fclose(fid);
%     meanDiffusion(:,:,ii) = tmp(:,:,1);
%     maxEigVal(:,:,ii) = tmp(:,:,2);
%     medEigVal(:,:,ii) = tmp(:,:,3);
%     minEigVal(:,:,ii) = tmp(:,:,4);
%     maxEigValX(:,:,ii) = tmp(:,:,5);
%     maxEigValY(:,:,ii) = tmp(:,:,6);
%     maxEigValZ(:,:,ii) = tmp(:,:,7);
%     fa(:,:,ii) = tmp(:,:,8);
%     b0(:,:,ii) = tmp(:,:,11);
% end


function [stepsize newx storedb lsmem lsstats] = ...
                linesearch(problem, x, d, f0, df0, options, storedb, lsmem)
% Standard line search algorithm (step size selection) for descent methods.
%
% function [stepsize newx storedb lsmem lsstats] =
%               linesearch(problem, x, d, f0, df0, options, storedb, lsmem)
%
% Base linesearch algorithm for descent methods, based on a simple
% backtracking method. The search direction provided has to be a descent
% direction, as indicated by a negative df0 = directional derivative of f
% at x along d.
%
% The algorithm is invariant under positive scaling of the cost function
% and under offsetting, that is: if the cost function f is replaced by
% 8*f+3 for example, the returned step size will be the same. Furthermore,
% the returned step size is independent of the norm of the search direction
% vector d: only the direction of d is important.
%
% Below, the step will be constructed as alpha*d, and the step size is the
% norm of that vector, thus: stepsize = alpha*norm_d. The step is executed
% by retracting the vector alpha*d from the current point x, giving newx.
%
% Inputs
%
%  problem : structure holding the description of the optimization problem
%  x : current point on the manifold problem.M
%  d : tangent vector at x (descent direction) -- its norm is irrelevant
%  f0 : cost value at x
%  df0 : directional derivative at x along d
%  options : options structure (see in code for usage)
%  storedb : store database structure for caching purposes
%  lsmem : a special memory holder to give the linesearch the opportunity
%          to "remember" what happened in the previous calls. See below.
%
% Outputs
%
%  stepsize : norm of the vector retracted to reach newx from x.
%  newx : next iterate suggested by the line search algorithm, such that
%         the retraction at x of the vector alpha*d reaches newx.
%  storedb : the (possibly updated) store database structure.
%  lsmem : the (possibly updated) lsmem memory holder.
%  lsstats : statistics about the line search procedure (stepsize, number
%            of trials etc).
%
% About lsmem : It can be anything, and will typically be a matrix or a
%               structure. When first calling the line search, it should be
%               passed as the empty matrix []. For subsequent calls
%               (pertaining to the same solver call), the previously
%               returned lsmem should be passed as input. This memory
%               holder gives the line search a chance to exploit knowledge
%               of previous decisions to make new decisions.
%
% See also: steepestdescent conjugategradients linesearch_adaptive

% This file is part of Manopt: www.manopt.org.
% Original author: Nicolas Boumal, Dec. 30, 2012.
% Contributors:
% Change log:
%     Sept. 13, 2013 (NB) : User control over the parameters of the
%                           linesearch via the options
%                           ls_contraction_factor, ls_optimism,
%                           ls_suff_decr and ls_max_steps. See in code for
%                           the effect of those.
%     Sept. 13, 2013 (NB) : The automatic direction reversal feature was
%                           removed (it triggered when df0 > 0). Direction
%                           reversal is a decision that needs to be made by
%                           the solver, so it can know about it.
%     Sept. 13, 2013 (NB) : The linesearch is now invariant under rescaling
%                           of the cost function f. That is, if f is
%                           replaced by 8*f (and hence the directional
%                           derivatives of f are scaled accordingly), the
%                           stepsizes computed will not change.
%     Nov. 7, 2013 (NB) :   The linesearch is now invariant under rescaling
%                           of the search direction d. The meaning of
%                           stepsize is also more clear in the comments.
%                           Added a parameter ls_initial_stepsize to give
%                           users control over the first step size trial.

    % Backtracking default parameters. These can be overwritten in the
    % options structure which is passed to the solver.
    default_options.ls_contraction_factor = .5;
    default_options.ls_optimism = 1/.5;
    default_options.ls_suff_decr = 1e-4;
    default_options.ls_max_steps = 25;
    default_options.ls_initial_stepsize = 1;

    options = mergeOptions(default_options, options);

    contraction_factor = options.ls_contraction_factor;
    optimism = options.ls_optimism;
    suff_decr = options.ls_suff_decr;
    max_ls_steps = options.ls_max_steps;
    initial_stepsize = options.ls_initial_stepsize;

    % Compute the norm of the search direction.
    % This is useful to make the linesearch algorithm invariant under the
    % scaling of d. The rationale is that the important information is the
    % search direction, not the size of that vector. The question of how
    % far we should go is precisely what the linesearch algorithm is
    % supposed to answer: the calling algorithm should not need to care.
    norm_d = problem.M.norm(x, d);

    % At first, we have no idea of what the step size should be.
    alpha = NaN;

    % If we know about what happened at the previous step, we can leverage
    % that to compute an initial guess for the step size, as inspired from
    % Nocedal&Wright, p59.
    if isstruct(lsmem) && isfield(lsmem, 'f0')
        % Pick initial step size based on where we were last time,
        alpha = 2*(f0-lsmem.f0)/df0;
        % and go look a little further (or less far), just in case.
        alpha = optimism*alpha;
    end

    % If we have no information about the previous iteration (maybe this is
    % the first one?) or if the above formula gave a too small step size
    % (perhaps it is even negative), then fall back to a user supplied
    % suggestion for the first step size (the "a priori").
    % At any rate, the choice should be invariant under rescaling of the
    % cost function f and of the search direction d, and it should be
    % bounded away from zero for convergence guarantees. We must allow it
    % to be close to zero though, for fine convergence.
    if isnan(alpha) || alpha*norm_d <= eps
        alpha = initial_stepsize/norm_d;
    end

    % Make the chosen step and compute the cost there.
    newx = problem.M.retr(x, d, alpha);
    [newf storedb] = getCost(problem, newx, storedb);
    cost_evaluations = 1;

    % Backtrack while the Armijo criterion is not satisfied
    while newf > f0 + suff_decr*alpha*df0

        % Reduce the step size,
        alpha = contraction_factor * alpha;

        % and look closer down the line
        newx = problem.M.retr(x, d, alpha);
        [newf storedb] = getCost(problem, newx, storedb);
        cost_evaluations = cost_evaluations + 1;

        % Make sure we don't run out of budget
        if cost_evaluations >= max_ls_steps
            break;
        end

    end

    % If we got here without obtaining a decrease, we reject the step.
    if newf > f0
        alpha = 0;
        newx = x;
        newf = f0; %#ok<NASGU>
    end

    % As seen outside this function, stepsize is the size of the vector we
    % retract to make the step from x to newx. Since the step is alpha*d:
    stepsize = alpha * norm_d;

    % Save the situtation faced now so that at the next iteration, we will
    % know something about the previous decision.
    lsmem.f0 = f0;
    lsmem.df0 = df0;
    lsmem.stepsize = stepsize;

    % Save some statistics also, for possible analysis.
    lsstats.costevals = cost_evaluations;
    lsstats.stepsize = stepsize;
    lsstats.alpha = alpha;

end


%% Copyright (C) 2014-2016, 2018-2019, 2022 Colin B. Macdonald
%% Copyright (C) 2014-2015 Andrés Prieto
%% Copyright (C) 2020 Rafael Laboissière
%% Copyright (C) 2020 Jing-Chen Peng
%%
%% This file is part of OctSymPy.
%%
%% OctSymPy 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 software 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.
%%
%% You should have received a copy of the GNU General Public
%% License along with this software; see the file COPYING.
%% If not, see <http://www.gnu.org/licenses/>.

%% -*- texinfo -*-
%% @documentencoding UTF-8
%% @deftypemethod  @@sym {@var{sol} =} dsolve (@var{ode})
%% @deftypemethodx @@sym {@var{sol} =} dsolve (@var{ode}, @var{IC})
%% @deftypemethodx @@sym {@var{sol} =} dsolve (@var{ODEs}, @var{IC1}, @var{IC2}, @dots{})
%% @deftypemethodx @@sym {@var{sol} =} dsolve (@var{ODEs}, @var{ICs})
%% @deftypemethodx @@sym {[@var{sol}, @var{classify}] =} dsolve (@dots{})
%% Solve ordinary differential equations (ODEs) symbolically.
%%
%% Basic example:
%% @example
%% @group
%% syms y(x)
%% DE = diff(y, x) - 4*y == 0
%%   @result{} DE = (sym)
%%                 d
%%       -4⋅y(x) + ──(y(x)) = 0
%%                 dx
%% @end group
%%
%% @group
%% sol = dsolve (DE)
%%   @result{} sol = (sym)
%%           4⋅x
%%       C₁⋅ℯ
%% @end group
%% @end example
%%
%% You can specify initial conditions:
%% @example
%% @group
%% sol = dsolve (DE, y(0) == 1)
%%   @result{} sol = (sym)
%%        4⋅x
%%       ℯ
%% @end group
%% @end example
%%
%% In some cases, SymPy can return a classification of the
%% differential equation:
%% @example
%% @group
%% DE = diff(y) == y^2
%%   @result{} DE = (sym)
%%       d           2
%%       ──(y(x)) = y (x)
%%       dx
%%
%% [sol, classify] = dsolve (DE, y(0) == 1)
%%   @result{} sol = (sym)
%%        -1
%%       ─────
%%       x - 1
%%   @result{} classify = ... separable ...
%% @end group
%% @end example
%%
%% Many types of ODEs can be solved, including initial-value
%% problems and boundary-value problem:
%% @example
%% @group
%% DE = diff(y, 2) == -9*y
%%   @result{} DE = (sym)
%%          2
%%         d
%%        ───(y(x)) = -9⋅y(x)
%%          2
%%        dx
%%
%% dsolve (DE, y(0) == 1, diff(y)(0) == 12)
%%   @result{} (sym) 4⋅sin(3⋅x) + cos(3⋅x)
%%
%% dsolve (DE, y(0) == 1, y(sym(pi)/2) == 2)
%%   @result{} (sym) -2⋅sin(3⋅x) + cos(3⋅x)
%% @end group
%% @end example
%%
%% Some systems can be solved, including initial-value problems
%% involving linear systems of first order ODEs with constant
%% coefficients:
%% @example
%% @group
%% syms x(t) y(t)
%% ode_sys = [diff(x(t),t) == 2*y(t);  diff(y(t),t) == 2*x(t)]
%%   @result{} ode_sys = (sym 2×1 matrix)
%%       ⎡d                ⎤
%%       ⎢──(x(t)) = 2⋅y(t)⎥
%%       ⎢dt               ⎥
%%       ⎢                 ⎥
%%       ⎢d                ⎥
%%       ⎢──(y(t)) = 2⋅x(t)⎥
%%       ⎣dt               ⎦
%% @end group
%%
%% @group
%% soln = dsolve (ode_sys)
%%   @result{} soln = scalar structure containing ...
%%        x = ...
%%        y = ...
%%
%% @c doctest: +SKIP_IF(pycall_sympy__ ('return Version(spver) <= Version("1.5.1")'))
%% soln.x
%%   @result{} ans =
%%       (sym)
%%               -2⋅t       2⋅t
%%         - C₁⋅ℯ     + C₂⋅ℯ
%%
%% @c doctest: +SKIP_IF(pycall_sympy__ ('return Version(spver) <= Version("1.5.1")'))
%% soln.y
%%   @result{} ans =
%%       (sym)
%%             -2⋅t       2⋅t
%%         C₁⋅ℯ     + C₂⋅ℯ
%% @end group
%% @end example
%%
%% Note: The Symbolic Math Toolbox used to support strings like 'Dy + y = 0'; we
%% are unlikely to support this so you will need to assemble a symbolic
%% equation instead.
%%
%% @seealso{@@sym/diff, @@sym/int, @@sym/solve}
%% @end deftypemethod

function [soln,classify] = dsolve(ode,varargin)

  % Usually we cast to sym in the _cmd call, but want to be
  % careful here b/c of symfuns
  if (~ iscell (ode) && ~ all (isa (ode, 'sym')))
    error('Inputs must be sym or symfun')
  end

  % FIXME: might be nice to expose SymPy's "sp.ode.classify_sysode" and
  %        "sp.ode.classify_ode" with their own commands
  if (isscalar(ode) && nargout==2)
    cmd = { 'from sympy.solvers import classify_ode'
            'return classify_ode(_ins[0]),' };
    classify = pycall_sympy__ (cmd, ode);
  elseif(~isscalar(ode) && nargout==2)
    warning('Classification of systems of ODEs is currently not supported')
    classify='';
  end

  cmd = { 'ode=_ins[0]; ics=_ins[1:]'
          'if len(ics) == 1:'
          '    ics = ics[0]'
          'try:'
          '    ics = {ic.lhs: ic.rhs for ic in ics}'
          'except TypeError:'  % not iterable
          '    ics = {ics.lhs: ics.rhs}'
          'sol = sp.dsolve(ode, ics=ics)'
          'def convert_helper(sympy_obj):'
          '    if isinstance(sympy_obj, Eq) and sympy_obj.lhs.is_Function:'
                   % y(t) = rhs to str "y", rhs expression
          '        return str(sympy_obj.lhs.func), sympy_obj.rhs'
          '    return None, None'
          % if just single equality with simple lhs, we return the rhs
          'if isinstance(sol, Eq):'
          '    if sol.lhs.is_Function:'
          '        return sol.rhs'
          % If the solution set is iterable (system or multiple solutions),
          % we will try to convert to structure of {"x": expr, "y": expr2, ...}
          'try:'
          '    return_data = dict()'
          '    for solution_part in sol:'
          '        key, rhs = convert_helper(solution_part)'
          '        if key is None:'
          '            raise ValueError("not of right form for extraction")'
          '        if key in return_data.keys():'
          '            raise KeyError(f"repeated key {key}")'
          '        return_data[key] = rhs'
          '    return return_data'
          'except (TypeError, ValueError, KeyError):'
          '    pass'
          % if nothing else worked, give back whatever form we have
          'return sol' };
  soln = pycall_sympy__ (cmd, ode, varargin{:});
end

%!error <sym> dsolve (1, sym('x'))

%!test
%! syms y(x)
%! de = diff(y, 2) - 4*y == 0;
%! f = dsolve(de);
%! syms C1 C2
%! g1 = C1*exp(-2*x) + C2*exp(2*x);
%! g2 = C2*exp(-2*x) + C1*exp(2*x);
%! assert (isequal (f, g1) || isequal (f, g2))

%!test
%! % Not enough initial conditions
%! syms y(x) C1
%! de = diff(y, 2) + 4*y == 0;
%! g = 3*cos(2*x) + C1*sin(2*x);
%! try
%!   f = dsolve(de, y(0) == 3);
%!   waserr = false;
%! catch
%!   waserr = true;
%!   expectederr = regexp (lasterr (), 'Perhaps.*under-specified');
%!   f = 42;
%! end
%! assert ((waserr && expectederr) || isequal (f, g))

%!test
%! % Solution in implicit form
%! syms y(x) C1
%! de = (2*x*y(x) - exp(-2*y(x)))*diff(y(x), x) + y(x) == 0;
%! sol = dsolve (de);
%! eqn = x*exp(2*y(x)) - log(y(x)) == C1;
%! % could differ by signs
%! sol = lhs (sol) - rhs (sol);
%! eqn = lhs (eqn) - rhs (eqn);
%! sol2 = subs (sol, C1, -C1);
%! assert (isequal (sol, eqn) || isequal (sol2, eqn))

%%!xtest
%%! % system with solution in implicit form
%%! % TODO: not implemented upstream?
%%! syms y(x) z(x) C1
%%! de1 = (2*x*y(x) - exp(-2*y(x)))*diff(y(x), x) + y(x) == 0;
%%! de2 = diff(z, x) == 0;
%%! sol = dsolve ([de1; de2]);

%!test
%! % Compute solution and classification
%! syms y(x) C1
%! de = (2*x*y(x) - exp(-2*y(x)))*diff(y(x), x) + y(x) == 0;
%! [sol, classy] = dsolve (de);
%! assert (any (strcmp (classy, '1st_exact')))

%!test
%! % initial conditions (first order ode)
%! syms y(x)
%! de = diff(y, x) + 4*y == 0;
%! f = dsolve(de, y(0) == 3);
%! g = 3*exp(-4*x);
%! assert (isequal (f, g))

%!test
%! % initial conditions (second order ode)
%! syms y(x)
%! de = diff(y, 2) + 4*y == 0;
%! f = dsolve(de, y(0) == 3, subs(diff(y,x),x,0)==0);
%! g = 3*cos(2*x);
%! assert (isequal (f, g))

%!test
%! % Dirichlet boundary conditions (second order ode)
%! syms y(x)
%! de = diff(y, 2) + 4*y == 0;
%! f = dsolve(de, y(0) == 2, y(1) == 0);
%! g = -2*sin(2*x)/tan(sym('2'))+2*cos(2*x);
%! assert (isequal (simplify (f - g), 0))

%!test
%! % Neumann boundary conditions (second order ode)
%! syms y(x)
%! de = diff(y, 2) + 4*y == 0;
%! f = dsolve(de, subs(diff(y,x),x,0)==1, subs(diff(y,x),x,1)==0);
%! g = sin(2*x)/2+cos(2*x)/(2*tan(sym('2')));
%! assert (isequal (simplify (f - g), 0))

%!test
%! % Dirichlet-Neumann boundary conditions (second order ode)
%! syms y(x)
%! de = diff(y, 2) + 4*y == 0;
%! f = dsolve(de, y(0) == 3, subs(diff(y,x),x,1)==0);
%! g = 3*sin(2*x)*tan(sym('2'))+3*cos(2*x);
%! assert (isequal (simplify (f - g), 0))

%!test
%! % System of ODEs gives struct, Issue #1003.
%! syms x(t) y(t)
%! ode1 = diff(x(t),t) == 2*y(t);
%! ode2 = diff(y(t),t) == 2*x(t);
%! soln = dsolve([ode1, ode2]);
%! assert (isstruct (soln))
%! assert (numfields (soln) == 2)
%! assert (isequal (sort (fieldnames (soln)), {'x'; 'y'}))

%!test
%! % System of ODEs
%! syms x(t) y(t) C1 C2
%! ode1 = diff(x(t),t) == 2*y(t);
%! ode2 = diff(y(t),t) == 2*x(t);
%! soln = dsolve([ode1, ode2]);
%! soln = [soln.x, soln.y];
%! g1 = [C1*exp(-2*t) + C2*exp(2*t), -C1*exp(-2*t) + C2*exp(2*t)];
%! g2 = [C1*exp(2*t) + C2*exp(-2*t), C1*exp(2*t) - C2*exp(-2*t)];
%! g3 = [-C1*exp(-2*t) + C2*exp(2*t), C1*exp(-2*t) + C2*exp(2*t)];
%! g4 = [C1*exp(2*t) - C2*exp(-2*t), C1*exp(2*t) + C2*exp(-2*t)];
%! % old SymPy <= 1.5.1 had some extra twos
%! g5 = [2*C1*exp(-2*t) + 2*C2*exp(2*t), -2*C1*exp(-2*t) + 2*C2*exp(2*t)];
%! g6 = [2*C1*exp(2*t) + 2*C2*exp(-2*t), 2*C1*exp(2*t) - 2*C2*exp(-2*t)];
%! assert (isequal (soln, g1) || isequal (soln, g2) || ...
%!         isequal (soln, g3) || isequal (soln, g4) || ...
%!         isequal (soln, g5) || isequal (soln, g6))

%!test
%! % System of ODEs (initial-value problem)
%! syms x(t) y(t)
%! ode_1=diff(x(t),t) == 2*y(t);
%! ode_2=diff(y(t),t) == 2*x(t);
%! sol_ivp=dsolve([ode_1,ode_2],x(0)==1,y(0)==0);
%! g_ivp=[exp(-2*t)/2+exp(2*t)/2,-exp(-2*t)/2+exp(2*t)/2];
%! assert (isequal ([sol_ivp.x, sol_ivp.y], g_ivp))

%!test
%! syms y(x)
%! de = diff(y, 2) + 4*y == 0;
%! f = dsolve(de, y(0) == 0, y(sym(pi)/4) == 1);
%! g = sin(2*x);
%! assert (isequal (f, g))

%!test
%! % Nonlinear example
%! syms y(x) C1
%! e = diff(y, x) == y^2;
%! g = -1 / (C1 + x);
%! soln = dsolve(e);
%! assert (isequal (soln, g))

%!test
%! % Nonlinear example with initial condition
%! syms y(x)
%! e = diff(y, x) == y^2;
%! g = -1 / (x - 1);
%! soln = dsolve(e, y(0) == 1);
%! assert (isequal (soln, g))

%!test
%! % forcing, Issue #183, broken in older sympy
%! if (pycall_sympy__ ('return Version(spver) >= Version("1.7.1")'))
%! syms x(t) y(t)
%! ode1 = diff(x) == x + sin(t) + 2;
%! ode2 = diff(y) == y - t - 3;
%! soln = dsolve([ode1 ode2], x(0) == 1, y(0) == 2);
%! X = soln.x;
%! Y = soln.y;
%! assert (isequal (diff(X) - (X + sin(t) + 2), 0))
%! assert (isequal (diff(Y) - (Y - t - 3), 0))
%! end

%!test
%! syms f(x) a b
%! de = diff(f, x) == 4*f;
%! s = dsolve(de, f(a) == b);
%! assert (isequal (subs(s, x, a), b))

%!test
%! % array of ICs
%! syms x(t) y(t)
%! ode_1 = diff (x(t), t) == 2*y(t);
%! ode_2 = diff (y(t), t) == 2*x(t);
%! sol = dsolve([ode_1, ode_2], [x(0)==1 y(0)==0]);
%! g = [exp(-2*t)/2+exp(2*t)/2, -exp(-2*t)/2+exp(2*t)/2];
%! assert (isequal ([sol.x, sol.y], g))

%!test
%! % cell-array of ICs or ODEs, but not both
%! % Note: to support both we'd need a wrapper outside of @sym
%! syms x(t) y(t)
%! ode_1 = diff (x(t), t) == 2*y(t);
%! ode_2 = diff (y(t), t) == 2*x(t);
%! sol = dsolve([ode_1, ode_2], {x(0)==1 y(0)==0});
%! g = [exp(-2*t)/2+exp(2*t)/2, -exp(-2*t)/2+exp(2*t)/2];
%! assert (isequal ([sol.x, sol.y], g))
%! sol = dsolve({ode_1, ode_2}, [x(0)==1 y(0)==0]);
%! g = [exp(-2*t)/2+exp(2*t)/2, -exp(-2*t)/2+exp(2*t)/2];
%! assert (isequal ([sol.x, sol.y], g))

%!test
%! % array of ICs, Issue #1040.
%! if (pycall_sympy__ ('return Version(spver) >= Version("1.7.1")'))
%! syms x(t) y(t) z(t)
%! syms x_0 y_0 z_0
%! diffEqns = [diff(x, t) == -x + 1, diff(y, t) == -y, diff(z, t) == -z];
%! initCond = [x(0) == x_0, y(0) == y_0, z(0) == z_0];
%! soln = dsolve (diffEqns, initCond);
%! soln = [soln.x, soln.y, soln.z];
%! exact_soln = [(x_0 - 1)*exp(-t) + 1  y_0*exp(-t)  z_0*exp(-t)];
%! assert (isequal (soln, exact_soln))
%! end


% MP_INTERFACE_HCURL: create a global numbering of basis functions in multipatch domains, assigning the correct orientation for tangential traces.
%
%   [glob_num, glob_ndof, dofs_ornt] = mp_interface_hcurl (interfaces, sp);
%
% INPUT:
%
%  interfaces: structure with the information of the interfaces between patches (see mp_geo_read_file)
%  sp:         object representing the space of discrete functions, with the curl-preserving transform (see sp_vector)
%
% OUTPUT:
%
%  glob_num:  global numbering of the discrete basis functions
%  glob_ndof: total number of degrees of freedom
%  dofs_ornt: a cell-array with the orientation of the basis functions
%
% Copyright (C) 2010, 2011, 2015 Rafael Vazquez
%
%    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.
%
%    You should have received a copy of the GNU General Public License
%    along with this program.  If not, see <http://www.gnu.org/licenses/>.

function [glob_num, glob_ndof, dofs_ornt] = mp_interface_hcurl (interfaces, sp)

  npatch = numel (sp);

  ndim = size (sp{1}.ndof_dir, 2);

  if (~isempty (interfaces))
    glob_num   = cell (npatch, 1);
    dofs_ornt  = cell (npatch, 1);
% patch_intrfc is only used for ndim == 3
    patch_intrfc = cell (npatch, 1);

    ttform = cell (ndim-1, npatch, numel (interfaces));
    ppnum  = cell (ndim-1, numel (interfaces));

    for iptc = 1:npatch
      glob_num{iptc} = zeros (1, sp{iptc}.ndof);
      dofs_ornt{iptc} = ones (1, sp{iptc}.ndof);
      patch_intrfc{iptc} = union (find([interfaces.patch1] == iptc), ...
                                  find([interfaces.patch2] == iptc));
    end

    for intrfc = 1:numel(interfaces)
      iptc1  = interfaces(intrfc).patch1;
      iptc2  = interfaces(intrfc).patch2;
      iside1 = interfaces(intrfc).side1;
      iside2 = interfaces(intrfc).side2;
      for idim = 1:ndim-1
        ttform{idim,iptc1, intrfc} = sp{iptc1}.boundary(iside1).dofs(sp{iptc1}.boundary(iside1).comp_dofs{idim});
        nghbr_dofs{idim} = reshape (sp{iptc2}.boundary(iside2).dofs(sp{iptc2}.boundary(iside2).comp_dofs{idim}), ...
				    [sp{iptc2}.boundary(iside2).ndof_dir(idim,:), 1]);
      end
      if (ndim == 2)
        if (interfaces(intrfc).ornt == -1)
          nghbr_dofs{1} = flipud (nghbr_dofs{1});
        end
      elseif (ndim == 3)
        if (interfaces(intrfc).flag == -1)
          aux = nghbr_dofs{1};
          nghbr_dofs{1} = nghbr_dofs{2}';
          nghbr_dofs{2} = aux';
        end
        if (interfaces(intrfc).ornt1 == -1)
          nghbr_dofs{1} = flipud (nghbr_dofs{1});
          nghbr_dofs{2} = flipud (nghbr_dofs{2});
        end
        if (interfaces(intrfc).ornt2 == -1)
          nghbr_dofs{1} = fliplr (nghbr_dofs{1});
          nghbr_dofs{2} = fliplr (nghbr_dofs{2});
        end
      end
      for idim = 1:ndim-1
        ttform{idim,iptc2, intrfc} = nghbr_dofs{idim}(:)';
        ppnum{idim,intrfc} = zeros (1, numel(sp{iptc1}.boundary(iside1).comp_dofs{idim}));
      end
    end

    glob_ndof = 0;
% We start with the dofs that do not belong to any interface
    for iptc = 1:npatch
      non_intrfc_dofs = setdiff(1:sp{iptc}.ndof, [ttform{:,iptc,:}]);
      glob_num{iptc}(non_intrfc_dofs) = glob_ndof + (1:numel(non_intrfc_dofs));
      glob_ndof = glob_ndof + numel (non_intrfc_dofs);
    end

% Then we set the interfaces
    for intrfc = 1:numel(interfaces)
      iptc     = interfaces(intrfc).patch1;
      iptc2    = interfaces(intrfc).patch2;

      for idim = 1:ndim-1
        new_dofs{idim} = find (glob_num{iptc}(ttform{idim, iptc, intrfc}) == 0);
        new_dofs_number = glob_ndof + (1:numel (new_dofs{idim}));
        glob_num{iptc}(ttform{idim, iptc, intrfc}(new_dofs{idim})) = new_dofs_number;
        dofs_ornt{iptc}(ttform{idim, iptc, intrfc}(new_dofs{idim})) = 1;
        ppnum{idim,intrfc}(new_dofs{idim}) = new_dofs_number;
        glob_ndof = glob_ndof + numel (new_dofs{idim});
      end

      if (ndim == 2)
        glob_num{iptc2}(ttform{1, iptc2, intrfc}) = ...
             glob_num{iptc}(ttform{1, iptc, intrfc});
        dofs_ornt{iptc2}(ttform{1, iptc2, intrfc}) = ...
             interfaces(intrfc).ornt * dofs_ornt{iptc}(ttform{1, iptc, intrfc});
      elseif (ndim == 3)
        [glob_num, dofs_ornt, ppnum] = set_same_interface (iptc, intrfc, ...
            ttform, new_dofs, interfaces, glob_num, dofs_ornt, ppnum, patch_intrfc);
        [glob_num, dofs_ornt, ppnum] = set_same_patch (iptc, intrfc, ...
            ttform, new_dofs, interfaces, glob_num, dofs_ornt, ppnum, patch_intrfc);

      end
    end

  else
    glob_ndof = 0;
    glob_num = cell (npatch, 1);
    dofs_ornt  = cell (npatch, 1);
    for iptc = 1:npatch
      glob_num{iptc} = glob_ndof + (1:sp{iptc}.ndof);
      glob_ndof = glob_ndof + sp{iptc}.ndof;
      dofs_ornt{iptc} = ones (1, sp{iptc}.ndof);
    end
  end

end

function [glob_num, dofs_ornt, ppnum] = set_same_patch (iptc, intrfc, ...
            ttform, new_dofs, interfaces, glob_num, ...
            dofs_ornt, ppnum, patch_intrfc);

  for idim = 1:size(ttform, 1)
    intrfc_dofs{idim} = ttform{idim, iptc, intrfc}(new_dofs{idim});
  end

  for ii = setdiff (patch_intrfc{iptc}, intrfc)
    for idim = 1:size(ttform, 1)
      jdim = mod (idim, 2) + 1;
      [~, pos1{idim}, pos2{idim}] = intersect (ttform{idim, iptc, ii}, intrfc_dofs{idim});
      not_set_p1{idim} = find (ppnum{idim,ii}(pos1{idim}) == 0);

      [~, pos1b{idim}, pos2b{jdim}] = ...
          intersect (ttform{idim, iptc, ii}, intrfc_dofs{jdim});
      not_set_p1b{idim} = find (ppnum{idim, ii}(pos1b{idim}) == 0);
    end

    if (~(all (cellfun (@isempty, not_set_p1))))
      for idim = 1:size(ttform, 1)
        ppnum{idim,ii}(pos1{idim}(not_set_p1{idim})) = ...
          ppnum{idim,intrfc}(new_dofs{idim}(pos2{idim}(not_set_p1{idim})));
        renew_dofs{idim} = pos1{idim}(not_set_p1{idim});
      end
      [glob_num, dofs_ornt, ppnum] = set_same_interface (iptc, ii, ...
          ttform, renew_dofs, interfaces, glob_num, dofs_ornt, ppnum, patch_intrfc);
    elseif (~(all (cellfun (@isempty, not_set_p1b))))
      for idim = 1:size(ttform, 1)
        jdim = mod (idim, 2) + 1;
        ppnum{idim,ii}(pos1b{idim}(not_set_p1b{idim})) = ...
          ppnum{jdim,intrfc}(new_dofs{jdim}(pos2b{jdim}(not_set_p1b{idim})));
        renew_dofs{idim} = pos1b{idim}(not_set_p1b{idim});
      end
      [glob_num, dofs_ornt, ppnum] = set_same_interface (iptc, ii, ...
          ttform, renew_dofs, interfaces, glob_num, dofs_ornt, ppnum, patch_intrfc);
    end
  end
end

function [glob_num, dofs_ornt, ppnum] = set_same_interface (iptc, intrfc, ...
            ttform, new_dofs, interfaces, glob_num, ...
            dofs_ornt, ppnum, patch_intrfc)

  ornt(1) = interfaces(intrfc).ornt1;
  ornt(2) = interfaces(intrfc).ornt2;

  iptc2 = setdiff ([interfaces(intrfc).patch1 interfaces(intrfc).patch2], iptc);
  for idim = 1:size(ttform, 1)
    intrfc_dofs{idim} = ttform{idim, iptc, intrfc}(new_dofs{idim});
    glob_num{iptc2}(ttform{idim, iptc2, intrfc}(new_dofs{idim})) = ...
                              glob_num{iptc}(intrfc_dofs{idim});
    dofs_ornt{iptc2}(ttform{idim, iptc2, intrfc}(new_dofs{idim})) = ...
             ornt(idim) * dofs_ornt{iptc}(intrfc_dofs{idim});
%             interfaces(intrfc).ornt(idim) * dofs_ornt{iptc}(intrfc_dofs{idim});
  end

  [glob_num, dofs_ornt, ppnum] = set_same_patch (iptc2, intrfc, ...
           ttform, new_dofs, interfaces, glob_num, ...
           dofs_ornt, ppnum, patch_intrfc);

end


function [ node_coord, node_att, node_marker ] = triangle_node_data_read ( ...
  node_filename, node_num, node_dim, node_att_num, node_marker_num )

%*****************************************************************************80
%
%% TRIANGLE_NODE_DATA_READ reads the data from a node file.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    11 October 2014
%
%  Author:
%
%    John Burkardt
%
% Parameters:
%
%   Input, string NODE_FILENAME, the name of the node file.
%
%   Input, integer NODE_NUM, the number of nodes.
%
%   Input, integer NODE_DIM, the spatial dimension.
%
%   Input, integer NODE_ATT_NUM, number of node attributes
%   listed on each node record.
%
%   Input, integer NODE_MARKER_NUM, 1 if every node record
%   includes a final boundary marker value.
%
%   Output, real NODE_COORD(NODE_DIM,NODE_NUM), the nodal
%   coordinates.
%
%   Output, real NODE_ATT(NODE_ATT_NUM,NODE_NUM), the nodal
%   attributes.
%
%   Output, integer NODE_MARKER(NODE_MARKER_NUM,NODE_NUM), the
%   node markers.
%
  node = 0;

  input = fopen ( node_filename, 'rt' );

  if ( input < 0 )
    fprintf ( 1, '\n' );
    fprintf ( 1, 'TRIANGLE_NODE_DATA_READ - Fatal error!\n' );
    fprintf ( 1, '  Unable to open file.\n' );
    error ( 'TRIANGLE_NODE_DATA_READ - Fatal error!' );
  end
%
%  Create format.
%
  format = '%d';
  for i = 1 : node_dim
    format = strcat ( format, '%g' );
  end
  for i = 1 : node_att_num
    format = strcat ( format, '%g' );
  end
  for i = 1 : node_marker_num
    format = strcat ( format, '%d' );
  end
%
%  Create arrays.
%
  node_coord = zeros ( node_dim, node_num );
  node_att = zeros ( node_att_num, node_num );
  node_marker = zeros ( node_marker_num, node_num );
%
%  Read values and place in the arrays.
%
  while ( 1 )

    text = fgets ( input );

    if ( text == -1 )
      fprintf ( 1, '\n' );
      fprintf ( 1, 'TRIANGLE_NODE_DATA_READ - Fatal error!\n' );
      fprintf ( 1, '  Unexpected end of file while reading.\n' );
      error ( 'TRIANGLE_NODE_DATA_READ - Fatal error!' );
    end

    if ( s_len_trim ( text ) == 0 )
      continue
    end

    if ( text(1) == '#' )
      continue
    end
%
%  Ignore the dimension line.
%
    if ( node == 0 )

    else

      [ value, count ] = sscanf ( text, format );

      if ( count < 1 + node_dim + node_att_num + node_marker_num )
        fprintf ( 1, '\n' );
        fprintf ( 1, 'TRIANGLE_NODE_DATA_READ - Fatal error!\n' );
        fprintf ( 1, '  Line did not have enough data.\n' );
        error ( 'TRIANGLE_NODE_DATA_READ - Fatal error!' );
      end

      j = 1;

      for i = 1 : node_dim
        j = j + 1;
        node_coord(i,node) = value(j);
      end

      for i = 1 : node_att_num
        j = j + 1;
        node_att(i,node) = value(j);
      end

      for i = 1 : node_marker_num
        j = j + 1;
        node_marker(i,node) = value(j);
      end

    end

    node = node + 1;

    if ( node_num < node )
      break
    end

  end

  fclose ( input );

  return
end


function [ varargout ] = Beam_Propagation_Method_GUI ( varargin )

%% GUI for Beam Propagation Method (BPM)
%
%%
%    Author:   Ido Schwartz (IS)
%    April 2011 (ver. 1.0.0)
%
%% build GUI
fig = build_gui;
h   = guidata ( fig );

switch nargout      %----------------------- check nargout
    case 0
        % do nothing
    case 1                                  % return a handle to GUI
        varargout(1) = {fig};
    otherwise
        error ('Invalid number of output arguments');
end

e = exist ('data_bpm_tmp.mat','file');
if e==2
    delete('data_bpm_tmp.mat');
end

end

%% uifunctions
function fig = build_gui

setappdata(0,'UseNativeSystemDialogs',false);

fig = figure;

set (fig, 'Name', ' Beam Propagation Method GUI','NumberTitle', 'off', 'Visible','off');
set (fig, 'deleteFcn', @close_figure_callback ); %%%%%%%%%%%%%%%
set (fig, 'Color', get(0,'defaultUicontrolBackgroundColor') );

back_color = get (fig,'Color');
edit_box_back_color = [1 1 1];      % white

%% ------------------------------------ draw tools panel
tools_panel = uipanel('units', 'normalized', 'position' , [.005 .005 .99 .11] ,...  %% draw panel
    'parent' , fig,    'tag' , ' tools_panel'  , 'TitlePosition', 'righttop', ...
    'title' , 'tools');

uicontrol ('style', 'pushbutton', 'units', 'normalized', 'position', [0.003 0.1 0.12 0.8],...
    'HorizontalAlignment','center', 'FontWeight', 'bold','fontsize' , 12 ,...
    'string', 'simulation panel'  ,'tag', 'graphs_pushbutton', 'callback', @graphs_pushbutton_callback, 'parent' , tools_panel );

uicontrol ('style', 'pushbutton', 'units', 'normalized', 'position', [0.126 0.1 0.12 0.8],...
    'HorizontalAlignment','center', 'FontWeight', 'bold','fontsize' , 12 ,...
    'string', 'set parameters'  ,'tag', 'setparameters_pushbutton', 'callback', @setparameters_pushbutton_callback, 'parent' , tools_panel );

uicontrol ('style', 'pushbutton', 'units', 'normalized', 'position', [0.877 0.1 0.12 0.8],...
    'HorizontalAlignment','center', 'FontWeight', 'bold','fontsize' , 12 ,...
    'string', 'Exit'  ,'tag', 'exit_pushbutton', 'callback', @exit_pushbutton_callback, 'parent' , tools_panel );

%% ------------------------------------ draw graphs panel
graph_panel = uipanel('units', 'normalized', 'position' , [.005 .11 .99 .885] ,...  %% draw panel
    'parent' , fig,    'tag' , 'g_panel'  , 'TitlePosition', 'righttop', ...
    'title' , 'Graphs');

uicontrol ('style', 'pushbutton', 'units', 'normalized', 'position', [0.003 0.003 0.12 0.07],...
    'HorizontalAlignment','center', 'FontWeight', 'bold','fontsize' , 12 ,...
    'string', 'start'  ,'tag', 'start_pushbutton', 'callback', @start_pushbutton_callback, 'parent' , graph_panel );

uicontrol ('style', 'pushbutton', 'units', 'normalized', 'position', [0.126 0.003 0.12 0.07],...
    'HorizontalAlignment','center', 'FontWeight', 'bold','fontsize' , 12 ,'enable','off',...
    'string', 'stop'  ,'tag', 'stop_pushbutton', 'callback', @stop_pushbutton_callback, 'parent' , graph_panel );

uicontrol ('style', 'pushbutton', 'units', 'normalized', 'position', [0.875 0.003 0.12 0.07],...
    'HorizontalAlignment','center', 'FontWeight', 'bold','fontsize' , 12 ,'enable','on',...
    'string', 'save'  ,'tag', 'save_pushbutton', 'callback', @save_pushbutton_callback, 'parent' , graph_panel );

axes('parent',graph_panel,'tag','ax1','Position', [.04 .125 .58 .83]'); %define the axes space
axes('parent',graph_panel,'tag','ax2','Position',[.665 .4 .3 .5]); %define the axes space

%% ------------------------------------ draw Set Parameters panel
parameters_panel = uipanel('units', 'normalized', 'position' , [.005 .11 .99 .885] ,...  %% draw panel
    'parent' , fig,    'tag' , 'p_panel'  , 'TitlePosition', 'righttop', ...
    'title' , 'Set Parameters');
% --------------------------------------------------------- Field parameters
field_panel = uipanel('units', 'normalized', 'position' , [.005 .005 .3 .99] ,...  %% draw panel
    'parent' , parameters_panel,    'tag' , 'field_panel'  , 'TitlePosition', 'righttop', ...
    'title' , 'Field Initial Parameters');

uicontrol ('style', 'text', 'units', 'normalized', 'position', [0.05 0.9 0.9 0.1],...
    'HorizontalAlignment','left', 'FontWeight', 'bold','fontsize' , 12 ,...
    'string', 'F(r,z=0)=A*exp(-0.5*(r/w)^2)'  ,'tag', 'field_text', 'parent' , field_panel );

uicontrol ('style', 'text', 'units', 'normalized', 'position', [0.05 0.83 0.11 0.07],...
    'HorizontalAlignment','left', 'FontWeight', 'normal','fontsize' , 12 ,...
    'string', 'A = '  ,'tag', 'A_text', 'parent' , field_panel );

uicontrol ('style', 'edit', 'units', 'normalized', 'position', [0.165 0.83 0.15 0.07],...
    'HorizontalAlignment','center', 'FontWeight', 'normal','fontsize' , 12 ,...
    'string', '1' , 'backgroundcolor',[1 1 1],'tag', 'A_edit','enable','on', 'parent' , field_panel );

uicontrol ('style', 'text', 'units', 'normalized', 'position', [0.05 0.75 0.11 0.07],...
    'HorizontalAlignment','left', 'FontWeight', 'normal','fontsize' , 12 ,...
    'string', 'w = '  ,'tag', 'w_text', 'parent' , field_panel );

uicontrol ('style', 'edit', 'units', 'normalized', 'position', [0.165 0.75 0.15 0.07],...
    'HorizontalAlignment','center', 'FontWeight', 'normal','fontsize' , 12 ,...
    'string', '10' ,'backgroundcolor',[1 1 1],'tag', 'w_edit','enable','on', 'parent' , field_panel );

uicontrol ('style', 'text', 'units', 'normalized', 'position', [0.33 0.75 0.22 0.07],...
    'HorizontalAlignment','left', 'FontWeight', 'normal','fontsize' , 12 ,...
    'string', 'micrometer '  ,'tag', 'w_units_text', 'parent' , field_panel );

uicontrol ('style', 'text', 'units', 'normalized', 'position', [0.05 0.67 0.11 0.07],...
    'HorizontalAlignment','left', 'FontWeight', 'normal','fontsize' , 12 ,...
    'string', 'wave length'  ,'tag', 'lambda_text', 'parent' , field_panel );

uicontrol ('style', 'edit', 'units', 'normalized', 'position', [0.165 0.67 0.15 0.07],...
    'HorizontalAlignment','center', 'FontWeight', 'normal','fontsize' , 12 ,...
    'string', '0.5' ,'backgroundcolor',[1 1 1],'tag', 'lambda_edit','enable','on', 'parent' , field_panel );

uicontrol ('style', 'popupmenu', 'units', 'normalized', 'position', [0.33 0.67 0.3 0.07],...
    'HorizontalAlignment','left', 'FontWeight', 'normal','fontsize' , 12,'backgroundcolor',[1 1 1] ,...
    'string', {'micrometer','nanometer'} ,'tag', 'lambda_popup', 'callback', @lambda_popupmenu_callback, 'parent' , field_panel );

% --------------------------------------------------------- Medium Parameters ---
medium_panel = uipanel('units', 'normalized', 'position' , [.305 .005 .3 .99] ,...  %% draw panel
    'parent' , parameters_panel,    'tag' , 'medium_panel'  , 'TitlePosition', 'righttop', ...
    'title' , 'Medium Parameters');

tex=axes('parent',medium_panel,'tag','tex','Position', [.005 .98 .0001 .0001],'XTick',[],'YTick',[]); %define the axes space
text( 1,1, '\bfn=n_0+n_1exp(-\alphar^m/w^m)', 'parent' , tex ,'FontSize',12);

uicontrol ('style', 'popupmenu', 'units', 'normalized', 'position', [0.005 0.87 0.5 0.07],...
    'HorizontalAlignment','left', 'FontWeight', 'normal','fontsize' , 12 ,'backgroundcolor',[1 1 1],...
    'string', {'uniform','linear','quadratic','customize'} ,'tag', 'medium_popup', 'callback', @medium_popupmenu_callback, 'parent' , medium_panel );

tex=axes('parent',medium_panel,'tag','tex','Position', [.005 .8 .0001 .0001],'XTick',[],'YTick',[]); %define the axes space
text( 1,1, 'n_0 =', 'parent' , tex ,'FontSize',12);

uicontrol ('style', 'edit', 'units', 'normalized', 'position', [0.15 0.775 0.15 0.06],...
    'HorizontalAlignment','center', 'FontWeight', 'normal','fontsize' , 12 ,...
    'string', '1' ,'backgroundcolor',[1 1 1],'tag', 'n0_edit','enable','on', 'parent' , medium_panel );

tex=axes('parent',medium_panel,'tag','tex','Position', [.005 .73 .0001 .0001],'XTick',[],'YTick',[]); %define the axes space
text( 1,1, 'n_1 =', 'parent' , tex ,'FontSize',12);

uicontrol ('style', 'edit', 'units', 'normalized', 'position', [0.15 0.705 0.15 0.06],...
    'HorizontalAlignment','center', 'FontWeight', 'normal','fontsize' , 12 ,...
    'string', '0' ,'backgroundcolor',[1 1 1],'tag', 'n1_edit','enable','off', 'parent' , medium_panel );

tex=axes('parent',medium_panel,'tag','tex','Position', [.35 .74 .0001 .0001],'XTick',[],'YTick',[]); %define the axes space
text( 1,1, '*10^-^4', 'parent' , tex ,'FontSize',12);

tex=axes('parent',medium_panel,'tag','tex','Position', [.005 .66 .0001 .0001],'XTick',[],'YTick',[]); %define the axes space
text( 1,1, '\alpha =', 'parent' , tex ,'FontSize',12);

uicontrol ('style', 'edit', 'units', 'normalized', 'position', [0.15 0.635 0.15 0.06],...
    'HorizontalAlignment','center', 'FontWeight', 'normal','fontsize' , 12 ,...
    'string', '0.5' ,'backgroundcolor',[1 1 1],'tag', 'alpha_edit','enable','off', 'parent' , medium_panel );

tex=axes('parent',medium_panel,'tag','tex','Position', [.005 .59 .0001 .0001],'XTick',[],'YTick',[]); %define the axes space
text( 1,1, 'm =', 'parent' , tex ,'FontSize',12);

uicontrol ('style', 'edit', 'units', 'normalized', 'position', [0.15 0.565 0.15 0.06],...
    'HorizontalAlignment','center', 'FontWeight', 'normal','fontsize' , 12 ,...
    'string', '0' ,'backgroundcolor',[1 1 1],'tag', 'm_edit','enable','off', 'parent' , medium_panel );

uicontrol ('style', 'text', 'units', 'normalized', 'position', [0.001 0.47 0.8 0.07],...
    'HorizontalAlignment','left', 'FontWeight', 'bold','fontsize' , 12 ,...
    'string', 'max value of r = 1000*(wave length)'  ,'tag', 'r_max_text', 'parent' , medium_panel );

uicontrol ('style', 'text', 'units', 'normalized', 'position', [0.001 0.415 0.8 0.07],...
    'HorizontalAlignment','left', 'FontWeight', 'bold','fontsize' , 12 ,...
    'string', 'size of propagation step:'  ,'tag', 'dz_size_text', 'parent' , medium_panel );

uicontrol ('style', 'text', 'units', 'normalized', 'position', [0.05 0.365 0.15 0.07],...
    'HorizontalAlignment','left', 'FontWeight', 'bold','fontsize' , 12 ,...
    'string', 'dz ='  ,'tag', 'dz_text', 'parent' , medium_panel );

 uicontrol ('style', 'edit', 'units', 'normalized', 'position', [0.16 0.38 0.15 0.06],...
     'HorizontalAlignment','center', 'FontWeight', 'normal','fontsize' , 12 ,...
     'string', '5' ,'backgroundcolor',[1 1 1],'tag', 'dz_edit','enable','on', 'parent' , medium_panel );

 uicontrol ('style', 'text', 'units', 'normalized', 'position', [0.35 0.365 0.3 0.07],...
     'HorizontalAlignment','left', 'FontWeight', 'bold','fontsize' , 12 ,...
     'string', 'micrometer'  ,'tag', 'dz_unit_text', 'parent' , medium_panel );

 uicontrol ('style', 'text', 'units', 'normalized', 'position', [0.001 0.285 0.8 0.07],...
     'HorizontalAlignment','left', 'FontWeight', 'bold','fontsize' , 12 ,...
     'string', 'propagation distance:'  ,'tag', 'z_distance_text', 'parent' , medium_panel );

 uicontrol ('style', 'text', 'units', 'normalized', 'position', [0.05 0.235 0.15 0.07],...
     'HorizontalAlignment','left', 'FontWeight', 'bold','fontsize' , 12 ,...
     'string', 'z ='  ,'tag', 'z_text', 'parent' , medium_panel );

uicontrol ('style', 'edit', 'units', 'normalized', 'position', [0.16 0.25 0.15 0.06],...
    'HorizontalAlignment','center', 'FontWeight', 'normal','fontsize' , 12 ,...
    'string', '1000' ,'backgroundcolor',[1 1 1],'tag', 'steps_edit','enable','on', 'parent' , medium_panel );

uicontrol ('style', 'popupmenu', 'units', 'normalized', 'position', [0.35 0.237 0.28 0.07],...
    'HorizontalAlignment','left', 'FontWeight', 'normal','fontsize' , 12 ,'backgroundcolor',[1 1 1],...
    'string', {'dz steps','micrometer'} ,'tag', 'steps_popup', 'callback', @steps_popupmenu_callback, 'parent' , medium_panel );

%
%% ------------------------------------ prepare gui_handles
h = guihandles;                                                                            % get handles of uicontrols
h.fig = fig;                                                                                % figures handle
h.object = [];                                                                                % instrument object
h.back_color = back_color;                                                 % figure's background color
h.edit_box_back_color = edit_box_back_color;   % edit box background color
h.start_itr = 1;
h.Output = [];

h.dir_path = fileparts(mfilename('fullpath')); % path of the gui mfile
%
%% ------------------------------------ show figure
% update guidata
guidata ( fig, h );

% show figure
hide_object(parameters_panel);
show_object(graph_panel);
movegui(fig,'center');
set(gcf,'Visible','on');

% focus on uicontrol
focus_on(h.start_pushbutton);
end
%
%% ============================================== focus_on uicontrol ===
function focus_on ( hObject )
% focus on uicontrol only if figure is visible
h = guidata (hObject);
visible_fig = findobj(h.fig,'flat','Visible','on');
if ~isempty(visible_fig)
    uicontrol(hObject);
    drawnow expose;
end
end
%
%% ============================================== hide_object ===
function hide_object (hObject)
set ( hObject, 'Visible', 'off' );
end
%
%% ============================================== show_object ===
function show_object (hObject)
set ( hObject, 'Visible', 'on' );
drawnow expose;
end
%
%% ============================================== callbacks ===
%
%% ============================================== exit_pushbutton_callback ===
function exit_pushbutton_callback (hObject,eventdata)
e = exist ('data_bpm_tmp.mat','file');
if e==2
    delete('data_bpm_tmp.mat');
end
close;                                      % close GUI and all open ports
                                            % (see close_figure_callback)
end
%
%% ============================================== close_figure_callback ===
function close_figure_callback (hObject,eventdata)
stop_pushbutton_callback (hObject,eventdata);
end
%
%% ============================================== stop_pushbutton_callback ===
function stop_pushbutton_callback (hObject,eventdata)
h = guidata(hObject);
set(findobj('tag','stop_pushbutton'),'enable','off');
set(findobj('tag','start_pushbutton'),'enable','on','BackGroundColor',h.back_color,'string','start');
set(findobj('tag','setparameters_pushbutton'),'enable','on');
set(findobj('tag','graphs_pushbutton'),'enable','on');
end
%
%% ============================================== start_pushbutton_callback ===
function flag = start_pushbutton_callback (hObject,flag)
h = guidata(hObject);
get(findobj('tag','start_pushbutton'),'string');

set(findobj('tag','stop_pushbutton'),'enable','on');
set(findobj('tag','setparameters_pushbutton'),'enable','off');
set(findobj('tag','graphs_pushbutton'),'enable','off');
set(findobj('tag','start_pushbutton'),'enable','off','string','running','BackGroundColor',[0 1 0]);
% --------------------------------------------------------------------------

[A w Lam n0 deltaN alpha m dz z]=get_data_from_gui(h);
% Propagation of gaussian using BPM

NR          =     2^13;                % Numerical Resolution

%************Propagating Parameters ********%
steps=round(z/dz);                                          % max value of z-axis
%************Medium Parameters **********%
x_max=1000*Lam;                                        % maximum value of x-axis
%........................................................................%
dx = 2*x_max/(NR-1);
x = -x_max : dx : x_max;
dkx = pi/(x_max)*(NR-1)/NR;
kx = ( (-NR/2):(NR/2-1) )*dkx;
k0  = 2*pi/Lam;
%........................................................................%

%************Inpout Field  Parameters****************%

fz=A*exp(-0.5*(abs(x)/w).^2);        % initial field distribution

N=deltaN*exp(-alpha*(abs(x)/w).^m);

    start_itr = 1;
    Output = zeros(NR,steps);
    Output(:,1) = abs(fz).^2;

%%%%%%%%%%%%%%%%%%%%%%%%
%************PROPAGATION *****************%
%%%%%%%%%%%%%%%%%%%%%%%%
    Fz=ifftshift(ifft(ifftshift(fz)));
    Fz_d=Fz.*exp(1i*(kx.^2)./(2*k0*(n0+N))*dz/2);
    fz=fftshift(fft(fftshift(Fz_d)));

for itr=start_itr:steps-1

    fz_N=fz.*exp(-1i*N*k0*dz);
    Fz=ifftshift(ifft(ifftshift(fz_N)));
    D=exp(1i*(kx.^2)./(2*k0*(n0+N))*dz);
    Fz_D=Fz.*D;
    fz=fftshift(fft(fftshift(Fz_D)));

    plot(h.ax1,x,abs(fz).^2);
    axis(h.ax1,[-0.5*10^-4 0.5*10^-4 0 4*A]);
    set(get(h.ax1,'XLabel'),'String','\bfr [m]');
    set(get(h.ax1,'YLabel'),'String','\bfI [r]');
    set(get(h.ax1,'Title'),'String','\bfIntensity');

    Output(:,itr)=abs(fz).^2;

    imagesc((0:itr)*dz,x,Output(:,1:itr),'parent',h.ax2);
    colormap('hot');
    ylim(h.ax2,[-0.5*10^-4 0.5*10^-4]);
    set(get(h.ax2,'XLabel'),'String','\bfz [m]');
    set(get(h.ax2,'YLabel'),'String','\bfr [m]');
    set(get(h.ax2,'Title'),'String','\bfIntensity Map');
    drawnow;

    val1 = get(findobj('tag','stop_pushbutton'),'enable');
    if strcmp(val1,'off')
        break
    end

end

Fz=ifftshift(ifft(ifftshift(fz)));
Fz_d=Fz.*exp(1i*(kx.^2)./(2*k0*(n0+N))*dz/2);
fz=fftshift(fft(fftshift(Fz_d)));

Output(:,itr+1)=abs(fz).^2;
save('data_bpm_tmp.mat','Output');

plot(h.ax1,x,abs(fz).^2);
axis(h.ax1,[-0.5*10^-4 0.5*10^-4 0 4*A]);
set(get(h.ax1,'XLabel'),'String','\bfr [m]');
set(get(h.ax1,'YLabel'),'String','\bfI [r]');
set(get(h.ax1,'Title'),'String','\bfIntensity');

imagesc((0:itr)*dz,x,Output(:,1:itr+1),'parent',h.ax2);
colormap('hot');
ylim(h.ax2,[-0.5*10^-4 0.5*10^-4]);
set(get(h.ax2,'XLabel'),'String','\bfz [m]');
set(get(h.ax2,'YLabel'),'String','\bfr [m]');
set(get(h.ax2,'Title'),'String','\bfIntensity Map');

% ---------------------------------------------
    set(findobj('tag','start_pushbutton'),'enable','on','string','running','BackGroundColor',h.back_color,'string','start');
    set(findobj('tag','stop_pushbutton'),'enable','off');
    set(findobj('tag','setparameters_pushbutton'),'enable','on');
    set(findobj('tag','graphs_pushbutton'),'enable','on');
end
%
%% ============================================== save_pushbutton_callback ===
function save_pushbutton_callback (hObject,eventdata)
h = guidata(hObject);
e = exist ('data_bpm_tmp.mat','file');
if e==2
    load('data_bpm_tmp.mat');
else
    Output = [];
end
uisave('Output','BPM Simulation.mat');
end
%
%% ============================================== graphs_pushbutton_callback ===
function graphs_pushbutton_callback (hObject,eventdata)
h=guidata(hObject);
hide_object(h.p_panel);
show_object(h.g_panel);
end
%
%% ============================================== setparameters_pushbutton_callback ===
function setparameters_pushbutton_callback (hObject,eventdata)
h=guidata(hObject);
hide_object(h.g_panel);
show_object(h.p_panel);

% focus on uicontrol
focus_on(h.start_pushbutton);
end
%
%% ============================================== lambda_popupmenu_callback ===
function lambda_popupmenu_callback (hObject,eventdata)
h=guidata(hObject);
val=get(h.lambda_popup,'value');
lambda_edit=findobj('tag','lambda_edit');
lambda=get(lambda_edit,'string');

switch val
    case 1
        new_lambda=num2str(str2double(lambda)/1000);
        set(lambda_edit,'string',new_lambda);
    case 2
        new_lambda=num2str(str2double(lambda)*1000);
        set(lambda_edit,'string',new_lambda);
end
end
%
%% ============================================== medium_popupmenu_callback ===
function medium_popupmenu_callback (hObject,eventdata)
h=guidata(hObject);
val=get(h.medium_popup,'value');
n0=findobj('tag','n0_edit');
n1=findobj('tag','n1_edit');
alpha=findobj('tag','alpha_edit');
m=findobj('tag','m_edit');

switch val
    case 1
        set(n0,'enable','on');
        set(n1,'enable','off','string','0');
        set(alpha,'enable','off','string','0.5');
        set(m,'enable','off','string','0');
    case 2
        set(n0,'enable','on');
        set(n1,'enable','on','string','10');
        set(alpha,'enable','on');
        set(m,'enable','off','string','1');
    case 3
        set(n0,'enable','on');
        set(n1,'enable','on','string','10');
        set(alpha,'enable','on');
        set(m,'enable','off','string','2');
    case 4
        set(n0,'enable','on');
        set(n1,'enable','on');
        set(alpha,'enable','on');
        set(m,'enable','on');
end

end
%
%% ============================================== steps_popupmenu_callback ===
function steps_popupmenu_callback (hObject,eventdata)
h=guidata(hObject);
val=get(h.steps_popup,'value');
steps_edit=findobj('tag','steps_edit');
steps=get(steps_edit,'string');
dz=str2double(get(findobj('tag','dz_edit'),'string'));

switch val
    case 1
        new_steps=num2str(round(str2double(steps)/dz));
        set(steps_edit,'string',new_steps);
    case 2
        new_steps=num2str(round(str2double(steps)*dz));
        set(steps_edit,'string',new_steps);
end

end
%
%% ============================================== get_data_from_gui ===
function [A w lambda n0 n1 alpha m dz z]=get_data_from_gui(h)
A_text=get(h.A_edit,'string');
A=str2double(A_text);

w_text=get(h.w_edit,'string');
w=str2double(w_text);
w=w*10^(-6);

lambda_text=get(h.lambda_edit,'string');
lambda=str2double(lambda_text);
w_val=get(h.lambda_popup,'value');
switch w_val
    case 1
        lambda=lambda*10^(-6);
    case 2
        lambda=lambda*10^(-9);
end

n0_text=get(h.n0_edit,'string');
n0=str2double(n0_text);

n1_text=get(h.n1_edit,'string');
n1=str2double(n1_text);
n1=n1*10^(-4);

alpha_text=get(h.alpha_edit,'string');
alpha=str2double(alpha_text);

m_text=get(h.m_edit,'string');
m=str2double(m_text);

dz_text=get(h.dz_edit,'string');
dz=str2double(dz_text);
dz=dz*10^(-6);

z_text=get(h.steps_edit,'string');
z=str2double(z_text);
z_val=get(h.steps_popup,'value');
switch z_val
    case 1
        z=z*dz;
    case 2
        z=z*10^(-6);
end
end


function gpcf = gpcf_noise(varargin)
%GPCF_NOISE  Create a independent noise covariance function
%
%  Description
%    GPCF = GPCF_NOISE('PARAM1',VALUE1,'PARAM2,VALUE2,...) creates
%    independent noise covariance function structure in which the
%    named parameters have the specified values. Any unspecified
%    parameters are set to default values.
%
%    GPCF = GPCF_NOISE(GPCF,'PARAM1',VALUE1,'PARAM2,VALUE2,...)
%    modify a covariance function structure with the named
%    parameters altered with the specified values.
%
%    Parameters for independent noise covariance function [default]
%      noiseSigma2       - variance of the independent noise [0.1]
%      noiseSigma2_prior - prior for noiseSigma2 [prior_logunif]
%
%    Note! If the prior is 'prior_fixed' then the parameter in
%    question is considered fixed and it is not handled in
%    optimization, grid integration, MCMC etc.
%
%  See also
%    GP_SET, GPCF_*, PRIOR_*

% Copyright (c) 2007-2010 Jarno Vanhatalo
% Copyright (c) 2010 Aki Vehtari

% 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_NOISE';
  ip.addOptional('gpcf', [], @isstruct);
  ip.addParamValue('noiseSigma2',0.1, @(x) isscalar(x) && x>0);
  ip.addParamValue('noiseSigma2_prior',prior_logunif, @(x) isstruct(x) || isempty(x));
  ip.parse(varargin{:});
  gpcf=ip.Results.gpcf;

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

  % Initialize parameter
  if init || ~ismember('noiseSigma2',ip.UsingDefaults)
    gpcf.noiseSigma2=ip.Results.noiseSigma2;
  end

  % Initialize prior structure
  if init
    gpcf.p=[];
  end
  if init || ~ismember('noiseSigma2_prior',ip.UsingDefaults)
    gpcf.p.noiseSigma2=ip.Results.noiseSigma2_prior;
  end

  if init
    % Set the function handles to the subfunctions
    gpcf.fh.pak = @gpcf_noise_pak;
    gpcf.fh.unpak = @gpcf_noise_unpak;
    gpcf.fh.lp = @gpcf_noise_lp;
    gpcf.fh.lpg = @gpcf_noise_lpg;
    gpcf.fh.cfg = @gpcf_noise_cfg;
    gpcf.fh.ginput = @gpcf_noise_ginput;
    gpcf.fh.cov = @gpcf_noise_cov;
    gpcf.fh.trcov  = @gpcf_noise_trcov;
    gpcf.fh.trvar  = @gpcf_noise_trvar;
    gpcf.fh.recappend = @gpcf_noise_recappend;
  end

end

function [w, s] = gpcf_noise_pak(gpcf)
%GPCF_NOISE_PAK  Combine GP covariance function parameters into
%                one vector.
%
%  Description
%    W = GPCF_NOISE_PAK(GPCF) takes a covariance function data
%    structure GPCF and combines the covariance function
%    parameters and their hyperparameters into a single row
%    vector W. This is a mandatory subfunction used for example
%    in energy and gradient computations.
%
%       w = [ log(gpcf.noiseSigma2)
%             (hyperparameters of gpcf.magnSigma2)]'
%
%
%  See also
%    GPCF_NOISE_UNPAK

  w = []; s = {};
  if ~isempty(gpcf.p.noiseSigma2)
    w(1) = log(gpcf.noiseSigma2);
    s = [s 'log(noise.noiseSigma2)'];
    % Hyperparameters of noiseSigma2
    [wh sh] = gpcf.p.noiseSigma2.fh.pak(gpcf.p.noiseSigma2);
    w = [w wh];
    s = [s sh];
  end

end

function [gpcf, w] = gpcf_noise_unpak(gpcf, w)
%GPCF_NOISE_UNPAK  Sets the covariance function parameters
%                     into the structure
%
%  Description
%    [GPCF, W] = GPCF_NOISE_UNPAK(GPCF, W) takes a covariance
%    function data structure GPCF and a hyper-parameter vector W,
%    and returns a covariance function data structure identical
%    to the input, except that the covariance hyper-parameters
%    have been set to the values in W. Deletes the values set to
%    GPCF from W and returns the modified W. This is a mandatory
%    subfunction used for example in energy and gradient computations.
%
%    Assignment is inverse of
%       w = [ log(gpcf.noiseSigma2)
%             (hyperparameters of gpcf.magnSigma2)]'
%
%  See also
%    GPCF_NOISE_PAK

  if ~isempty(gpcf.p.noiseSigma2)
    gpcf.noiseSigma2 = exp(w(1));
    w = w(2:end);

    % Hyperparameters of lengthScale
    [p, w] = gpcf.p.noiseSigma2.fh.unpak(gpcf.p.noiseSigma2, w);
    gpcf.p.noiseSigma2 = p;
  end
end

function lp = gpcf_noise_lp(gpcf)
%GPCF_NOISE_LP  Evaluate the log prior of covariance function parameters
%
%  Description
%    LP = GPCF_NOISE_LP(GPCF) 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_NOISE_PAK, GPCF_NOISE_UNPAK, GPCF_NOISE_G, GP_E

% Evaluate the prior contribution to the error. The parameters that
% are sampled are from space W = log(w) where w is all the
% "real" samples. On the other hand errors are evaluated in the
% W-space so we need take into account also the Jacobian of
% transformation W -> w = exp(W). See Gelman et.al., 2004,
% Bayesian data Analysis, second edition, p24.

  lp = 0;
  gpp=gpcf.p;

  if ~isempty(gpcf.p.noiseSigma2)
    % Evaluate the prior contribution to the error.
    lp = gpp.noiseSigma2.fh.lp(gpcf.noiseSigma2, gpp.noiseSigma2) +log(gpcf.noiseSigma2);
  end
end

function lpg = gpcf_noise_lpg(gpcf)
%GPCF_NOISE_LPG  Evaluate gradient of the log prior with respect
%                to the parameters.
%
%  Description
%    LPG = GPCF_NOISE_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_NOISE_PAK, GPCF_NOISE_UNPAK, GPCF_NOISE_LP, GP_G

  lpg = [];
  gpp=gpcf.p;

  if ~isempty(gpcf.p.noiseSigma2)
    lpgs = gpp.noiseSigma2.fh.lpg(gpcf.noiseSigma2, gpp.noiseSigma2);
    lpg = [lpg lpgs(1).*gpcf.noiseSigma2+1 lpgs(2:end)];
  end
end

function DKff = gpcf_noise_cfg(gpcf, x, x2, mask, i1)
%GPCF_NOISE_CFG  Evaluate gradient of covariance function
%                with respect to the parameters
%
%  Description
%    DKff = GPCF_NOISE_CFG(GPCF, X) takes a covariance function
%    data 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_NOISE_CFG(GPCF, X, X2) takes a covariance
%    function data 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_NOISE_CFG(GPCF, X, [], MASK) takes a covariance
%    function data 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).
%
%  See also
%    GPCF_NOISE_PAK, GPCF_NOISE_UNPAK, GPCF_NOISE_E, GP_G

  DKff = {};

  if ~isempty(gpcf.p.noiseSigma2)
    gpp=gpcf.p;
    DKff{1}=gpcf.noiseSigma2;
  end
  if nargin==4
    % Use memory save option
    if i1==0
      % Return number of hyperparameters
      DKff=1;
      return
    end
    DKff=DKff{1};
  end

end

function DKff = gpcf_noise_ginput(gpcf, x, t, i1)
%GPCF_NOISE_GINPUT  Evaluate gradient of covariance function with
%                   respect to x
%
%  Description
%    DKff = GPCF_NOISE_GINPUT(GPCF, X) takes a covariance
%    function data 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_NOISE_GINPUT(GPCF, X, X2) takes a covariance
%    function data 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.
%
%  See also
%    GPCF_NOISE_PAK, GPCF_NOISE_UNPAK, GPCF_NOISE_E, GP_G

end

function C = gpcf_noise_cov(gpcf, x1, x2)
% GP_NOISE_COV     Evaluate covariance matrix between two input vectors
%
%  Description
%    C = GP_NOISE_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_NOISE_TRCOV, GPCF_NOISE_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

  C = sparse([],[],[],n1,n2,0);
end

function C = gpcf_noise_trcov(gpcf, x)
%GP_NOISE_TRCOV  Evaluate training covariance matrix of inputs
%
%  Description
%    C = GP_NOISE_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_NOISE_COV, GPCF_NOISE_TRVAR, GP_COV, GP_TRCOV

  [n, m] =size(x);
  n1=n+1;

  C = sparse([],[],[],n,n,0);
  C(1:n1:end)=C(1:n1:end)+gpcf.noiseSigma2;

end

function C = gpcf_noise_trvar(gpcf, x)
% GP_NOISE_TRVAR     Evaluate training variance vector
%
%    Description
%    C = GP_NOISE_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_NOISE_COV, GP_COV, GP_TRCOV

  [n, m] =size(x);
  C=ones(n,1)*gpcf.noiseSigma2;

end

function reccf = gpcf_noise_recappend(reccf, ri, gpcf)
%RECAPPEND Record append
%
%  Description
%    RECCF = GPCF_NOISE_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 hyperparameters. 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 and GP_MC -> RECAPPEND

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

    % Initialize parameters
    reccf.noiseSigma2 = [];

    % Set the function handles
    reccf.fh.pak = @gpcf_noise_pak;
    reccf.fh.unpak = @gpcf_noise_unpak;
    reccf.fh.e = @gpcf_noise_lp;
    reccf.fh.lpg = @gpcf_noise_lpg;
    reccf.fh.cfg = @gpcf_noise_cfg;
    reccf.fh.cov = @gpcf_noise_cov;
    reccf.fh.trcov  = @gpcf_noise_trcov;
    reccf.fh.trvar  = @gpcf_noise_trvar;
    %  gpcf.fh.sampling = @hmc2;
    reccf.sampling_opt = hmc2_opt;
    reccf.fh.recappend = @gpcf_noise_recappend;
    reccf.p=[];
    reccf.p.noiseSigma2=[];
    if ~isempty(ri.p.noiseSigma2)
      reccf.p.noiseSigma2 = ri.p.noiseSigma2;
    end
  else
    % Append to the record
    gpp = gpcf.p;

    % record noiseSigma2
    reccf.noiseSigma2(ri,:)=gpcf.noiseSigma2;
    if isfield(gpp,'noiseSigma2') && ~isempty(gpp.noiseSigma2)
      reccf.p.noiseSigma2 = gpp.noiseSigma2.fh.recappend(reccf.p.noiseSigma2, ri, gpcf.p.noiseSigma2);
    end
  end
end