%DATE:21/04/2005
%COFDM SIMULATOR
%BY:Apurva Gupta
%B.ENG,ELECTRONICs & COMMUNICATION ENGINEERING,
%UNIVERSITY OF LEEDS,UK
%SUPERVISOR:Prof.G.Markarian

function varargout = final_simulator(varargin)
% FINAL_SIMULATOR M-file for final_simulator.fig
%      FINAL_SIMULATOR, by itself, creates a new FINAL_SIMULATOR or raises the existing
%      singleton*.
%
%      H = FINAL_SIMULATOR returns the handle to a new FINAL_SIMULATOR or the handle to
%      the existing singleton*.
%
%      FINAL_SIMULATOR('CALLBACK',hObject,eventData,handles,...) calls the local
%      function named CALLBACK in FINAL_SIMULATOR.M with the given input arguments.
%
%      FINAL_SIMULATOR('Property','Value',...) creates a new FINAL_SIMULATOR or raises the
%      existing singleton*.  Starting from the left, property value pairs are
%      applied to the GUI before final_simulator_OpeningFunction gets called.  An
%      unrecognized property name or invalid value makes property application
%      stop.  All inputs are passed to final_simulator_OpeningFcn via varargin.
%
%      *See GUI Options on GUIDE's Tools menu.  Choose "GUI allows only one
%      instance to run (singleton)".
%
% See also: GUIDE, GUIDATA, GUIHANDLES

% Copyright 2002-2003 The MathWorks, Inc.

% Edit the above text to modify the response to help final_simulator

% Last Modified by GUIDE v2.5 24-Apr-2005 12:30:13

% Begin initialization code - DO NOT EDIT
gui_Singleton = 1;
gui_State = struct('gui_Name',       mfilename, ...
                   'gui_Singleton',  gui_Singleton, ...
                   'gui_OpeningFcn', @final_simulator_OpeningFcn, ...
                   'gui_OutputFcn',  @final_simulator_OutputFcn, ...
                   'gui_LayoutFcn',  [] , ...
                   'gui_Callback',   []);
if nargin && ischar(varargin{1})
    gui_State.gui_Callback = str2func(varargin{1});
end

if nargout
    [varargout{1:nargout}] = gui_mainfcn(gui_State, varargin{:});
else
    gui_mainfcn(gui_State, varargin{:});
end
% End initialization code - DO NOT EDIT

% --- Executes just before final_simulator is made visible.
function final_simulator_OpeningFcn(hObject, eventdata, handles, varargin)
% This function has no output args, see OutputFcn.
% hObject    handle to figure
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
% varargin   command line arguments to final_simulator (see VARARGIN)

% Choose default command line output for final_simulator
handles.output = hObject;

% Update handles structure
guidata(hObject, handles);

% UIWAIT makes final_simulator wait for user response (see UIRESUME)
% uiwait(handles.figure1);

% --- Outputs from this function are returned to the command line.
function varargout = final_simulator_OutputFcn(hObject, eventdata, handles)
% varargout  cell array for returning output args (see VARARGOUT);
% hObject    handle to figure
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Get default command line output from handles structure
varargout{1} = handles.output;

function [varargout] = code1(varargin)
M=2;
EbNomin = 1; EbNomax = 5; % EbNo range, in dB
numerrmin = 10; % Compute BER only after 5 errors occur.
EbNovec = EbNomin:1:EbNomax; % Vector of EbNo values
numEbNos = length(EbNovec); % Number of EbNo values
% Preallocate space for certain data.
ber = zeros(1,numEbNos); % BER values
intv = cell(1,numEbNos); % Cell array of confidence intervals
% Loop over the vector of EbNo values.
for jj = 1:numEbNos
   EbNo = EbNovec(jj);
   snr = EbNo; % Because of binary modulation
   ntrials = 0; % Number of passes through the while loop below
   numerr = 0; % Number of errors for this EbNo value
   % Simulate until numerrmin errors occur.
   while (numerr < numerrmin)
      msg= zeros(1,1e6);%message is a string of a thousand zeros.
      %t = poly2trellis([4 3],[4 5 17;7 4 2]); % Define trellis.
      %code = convenc(msg,t); % Encode a string of ones.
      txsig = pskmod(msg,M); % Modulate.
      rxsig = awgn(txsig, snr, 'measured'); % Add noise.
      decodmsg = pskdemod(rxsig,M); % Demodulate.
      %qcode = quantiz(decodmsg,[0.001,.1,.3,.5,.7,.9,.999]);% Quantize to prepare for soft-decision decoding.
      %tblen = 48; delay = tblen; % Traceback length
      %decoded = vitdec(qcode,t,tblen,'cont','soft',3); % Decode.
      newerrs = biterr(msg,decodmsg); % Errors in this trial
      numerr = numerr + newerrs; % Total errors for this EbNo value
      ntrials = ntrials + 1; % Update trial index.
   end
   % Error rate and 98% confidence interval for this EbNo value
   [ber(jj), intv1] = berconfint(numerr,(ntrials * 1e6),.98);
   intv{jj} = intv1; % Store in cell array for later use.
   disp(['EbNo = ' num2str(EbNo) ' dB, ' num2str(numerr) ...
         ' errors, BER = ' num2str(ber(jj))])
end
% Use BERFIT to plot the best fitted curve,
% interpolating to get a smooth plot.
fitEbNo = EbNomin:0.25:EbNomax; % Interpolation values
berfit(EbNovec,ber,fitEbNo);

% Also plot confidence intervals.
hold on;
grid on;
%axes(handles.axes2);
for jj=1:numEbNos
   semilogy([EbNovec(jj) EbNovec(jj)],intv{jj},'g-+');
end
%hold off;
if nargout == 0
  builtin('code1', varargin{:});
else
  [varargout{1:nargout}] = builtin('code1', varargin{:});
end

%--------------------------------------------------------------
function [varargout] = code2(varargin)
M=2;
EbNomin = 1; EbNomax = 5; % EbNo range, in dB
numerrmin = 10; % Compute BER only after 5 errors occur.
EbNovec = EbNomin:1:EbNomax; % Vector of EbNo values
numEbNos = length(EbNovec); % Number of EbNo values
% Preallocate space for certain data.
ber = zeros(1,numEbNos); % BER values
intv = cell(1,numEbNos); % Cell array of confidence intervals
% Loop over the vector of EbNo values.
for jj = 1:numEbNos
   EbNo = EbNovec(jj);
   snr = EbNo; % Because of binary modulation
   ntrials = 0; % Number of passes through the while loop below
   numerr = 0; % Number of errors for this EbNo value
   % Simulate until numerrmin errors occur.
   while (numerr < numerrmin)
      msg= zeros(1,1e6);%message is a string of a thousand zeros.
      %t = poly2trellis([4 3],[4 5 17;7 4 2]); % Define trellis.
      %code = convenc(msg,t); % Encode a string of ones.
      txsig = pskmod(msg,M); % Modulate.
      rxsig = awgn(txsig, snr, 'measured'); % Add noise.
      decodmsg = pskdemod(rxsig,M); % Demodulate.
      %qcode = quantiz(decodmsg,[0.001,.1,.3,.5,.7,.9,.999]);% Quantize to prepare for soft-decision decoding.
      %tblen = 48; delay = tblen; % Traceback length
      %decoded = vitdec(qcode,t,tblen,'cont','soft',3); % Decode.
      newerrs = biterr(msg,decodmsg); % Errors in this trial
      numerr = numerr + newerrs; % Total errors for this EbNo value
      ntrials = ntrials + 1; % Update trial index.
   end
   % Error rate and 98% confidence interval for this EbNo value
   [ber(jj), intv1] = berconfint(numerr,(ntrials * 1e6),.98);
   intv{jj} = intv1; % Store in cell array for later use.
   disp(['EbNo = ' num2str(EbNo) ' dB, ' num2str(numerr) ...
         ' errors, BER = ' num2str(ber(jj))])
end
% Use BERFIT to plot the best fitted curve,
% interpolating to get a smooth plot.
fitEbNo = EbNomin:0.25:EbNomax; % Interpolation values
berfit(EbNovec,ber,fitEbNo);

% Also plot confidence intervals.
hold on;
grid on;
%axes(handles.axes2);
for jj=1:numEbNos
   semilogy([EbNovec(jj) EbNovec(jj)],intv{jj},'g-+');
end
%hold off;
if nargout == 0
  builtin('code2', varargin{:});
else
  [varargout{1:nargout}] = builtin('code2', varargin{:});
end

%-------16QAM-------------------------------------------
function [varargout] = code3(varargin)
bits=1e5;
M=16;
EbNomin = 1; EbNomax = 5; % EbNo range, in dB
numerrmin = 10; % Compute BER only after 5 errors occur.
EbNovec = EbNomin:1:EbNomax; % Vector of EbNo values
numEbNos = length(EbNovec); % Number of EbNo values
% Preallocate space for certain data.
ber = zeros(1,numEbNos); % BER values
intv = cell(1,numEbNos); % Cell array of confidence intervals
% Loop over the vector of EbNo values.
for jj = 1:numEbNos
   EbNo = EbNovec(jj);
   snr = EbNo;%*0.5*log2(M);% Because of binary modulation
   ntrials = 0; % Number of passes through the while loop below
   numerr = 0; % Number of errors for this EbNo value
   % Simulate until numerrmin errors occur.
   while (numerr < numerrmin)
      msg= zeros(bits,1);%message is a string of a thousand zeros.
      txsig = qammod(msg,M); % Modulate.
      noise = awgn(txsig, snr,'measured'); % Add noise.
      decodmsg = qamdemod(noise,M); % Demodulate.
      newerrs = biterr(msg,decodmsg); % Errors in this trial
      numerr = numerr + newerrs; % Total errors for this EbNo value
      ntrials = ntrials + 1; % Update trial index.
   end
   % Error rate and 98% confidence interval for this EbNo value
   [ber(jj), intv1] = berconfint(numerr,(ntrials * bits),.98);
   intv{jj} = intv1; % Store in cell array for later use.
   disp(['EbNo = ' num2str(EbNo) ' dB, ' num2str(numerr) ...
         ' errors, BER = ' num2str(ber(jj))])
end
% Use BERFIT to plot the best fitted curve,
% interpolating to get a smooth plot.
fitEbNo = EbNomin:0.25:EbNomax; % Interpolation values
berfit(EbNovec,ber,fitEbNo);

% Also plot confidence intervals.
hold on;
grid on;
%set(gcf,'DefaultAxesColorOrder',[1 0 0;0 1 0;0 0 1])
%axes(handles.axes2);
for jj=1:numEbNos
   semilogy([EbNovec(jj) EbNovec(jj)],intv{jj},'g-+');
end
if nargout == 0
  builtin('code3', varargin{:});
else
  [varargout{1:nargout}] = builtin('code3', varargin{:});
end

%------64QAM------------------------------------------------
function [varargout] = code4(varargin)
bits=1e5;
M=64;
EbNomin = 5; EbNomax = 15; % EbNo range, in dB
numerrmin = 5; % Compute BER only after 5 errors occur.
EbNovec = EbNomin:1:EbNomax; % Vector of EbNo values
numEbNos = length(EbNovec); % Number of EbNo values
% Preallocate space for certain data.
ber = zeros(1,numEbNos); % BER values
intv = cell(1,numEbNos); % Cell array of confidence intervals
% Loop over the vector of EbNo values.
for jj = 1:numEbNos
   EbNo = EbNovec(jj);
   snr = EbNo;%*0.5*log2(M);% Because of binary modulation
   ntrials = 0; % Number of passes through the while loop below
   numerr = 0; % Number of errors for this EbNo value
   % Simulate until numerrmin errors occur.
   while (numerr < numerrmin)
      msg= zeros(bits,1);%message is a string of a thousand zeros.
      txsig = qammod(msg,M); % Modulate.
      noise = awgn(txsig, snr,'measured'); % Add noise.
      decodmsg = qamdemod(noise,M); % Demodulate.
      newerrs = biterr(msg,decodmsg); % Errors in this trial
      numerr = numerr + newerrs; % Total errors for this EbNo value
      ntrials = ntrials + 1; % Update trial index.
   end
   % Error rate and 98% confidence interval for this EbNo value
   [ber(jj), intv1] = berconfint(numerr,(ntrials * bits),.98);
   intv{jj} = intv1; % Store in cell array for later use.
   disp(['EbNo = ' num2str(EbNo) ' dB, ' num2str(numerr) ...
         ' errors, BER = ' num2str(ber(jj))])
end
% Use BERFIT to plot the best fitted curve,
% interpolating to get a smooth plot.
fitEbNo = EbNomin:0.25:EbNomax; % Interpolation values
berfit(EbNovec,ber,fitEbNo);

% Also plot confidence intervals.
hold on;
grid on;
%set(gcf,'DefaultAxesColorOrder',[1 0 0;0 1 0;0 0 1])
%axes(handles.axes2);
for jj=1:numEbNos
    %legend('Emperical BER','64QAM','Poly Ratio fit');
    semilogy([EbNovec(jj) EbNovec(jj)],intv{jj},'g-+');
end
if nargout == 0
  builtin('code4', varargin{:});
else
  [varargout{1:nargout}] = builtin('code4', varargin{:});
end

function [varargout] = conv_code1(varargin)
M=2;
EbNomin = 1; EbNomax = 5; % EbNo range, in dB
numerrmin = 5; % Compute BER only after 5 errors occur.
EbNovec = EbNomin:1:EbNomax; % Vector of EbNo values
numEbNos = length(EbNovec); % Number of EbNo values
% Preallocate space for certain data.
ber = zeros(1,numEbNos); % BER values
intv = cell(1,numEbNos); % Cell array of confidence intervals
% Loop over the vector of EbNo values.
for jj = 1:numEbNos
   EbNo = EbNovec(jj);
   snr = EbNo; % Because of binary modulation
   ntrials = 0; % Number of passes through the while loop below
   numerr = 0; % Number of errors for this EbNo value
   % Simulate until numerrmin errors occur.
   while (numerr < numerrmin)
      msg= zeros(1,1e6);%message is a string of a thousand zeros.
      t = poly2trellis([4 3],[4 5 17;7 4 2]); % Define trellis.
      code = convenc(msg,t); % Encode a string of ones.
      txsig = pskmod(code,M); % Modulate.
      rxsig = awgn(txsig, snr, 'measured'); % Add noise.
      decodmsg = pskdemod(rxsig,M); % Demodulate.
      qcode = quantiz(decodmsg,[0.001,.1,.3,.5,.7,.9,.999]);% Quantize to prepare for soft-decision decoding.
      tblen = 48; delay = tblen; % Traceback length
      decoded = vitdec(qcode,t,tblen,'cont','soft',3); % Decode.
      newerrs = biterr(msg,decoded); % Errors in this trial
      numerr = numerr + newerrs; % Total errors for this EbNo value
      ntrials = ntrials + 1; % Update trial index.
   end
   % Error rate and 98% confidence interval for this EbNo value
   [ber(jj), intv1] = berconfint(numerr,(ntrials * 1e6),.98);
   intv{jj} = intv1; % Store in cell array for later use.
   disp(['EbNo = ' num2str(EbNo) ' dB, ' num2str(numerr) ...
         ' errors, BER = ' num2str(ber(jj))])
end
% Use BERFIT to plot the best fitted curve,
% interpolating to get a smooth plot.
fitEbNo = EbNomin:0.25:EbNomax; % Interpolation values
berfit(EbNovec,ber,fitEbNo);

% Also plot confidence intervals.
hold on;
grid on;
%axes(handles.axes2);
for jj=1:numEbNos
   semilogy([EbNovec(jj) EbNovec(jj)],intv{jj},'g-+');
end
%hold off;
if nargout == 0
  builtin('conv_code1', varargin{:});
else
  [varargout{1:nargout}] = builtin('conv_code1', varargin{:});
end
%------------------------------------------------------------
function [varargout] = conv_code2(varargin)
M=4;
EbNomin = 1; EbNomax = 10; % EbNo range, in dB
numerrmin = 5; % Compute BER only after 5 errors occur.
EbNovec = EbNomin:1:EbNomax; % Vector of EbNo values
numEbNos = length(EbNovec); % Number of EbNo values
% Preallocate space for certain data.
ber = zeros(1,numEbNos); % BER values
intv = cell(1,numEbNos); % Cell array of confidence intervals
% Loop over the vector of EbNo values.
for jj = 1:numEbNos
   EbNo = EbNovec(jj);
   snr = EbNo; % Because of binary modulation
   ntrials = 0; % Number of passes through the while loop below
   numerr = 0; % Number of errors for this EbNo value
   % Simulate until numerrmin errors occur.
   while (numerr < numerrmin)
      msg= zeros(1,1e6);%message is a string of a thousand zeros.
      t = poly2trellis([4 3],[4 5 17;7 4 2]); % Define trellis.
      code = convenc(msg,t); % Encode a string of ones.
      txsig = pskmod(code,M); % Modulate.
      rxsig = awgn(txsig, snr, 'measured'); % Add noise.
      decodmsg = pskdemod(rxsig,M); % Demodulate.
      qcode = quantiz(decodmsg,[0.001,.1,.3,.5,.7,.9,.999]);% Quantize to prepare for soft-decision decoding.
      tblen = 48; delay = tblen; % Traceback length
      decoded = vitdec(qcode,t,tblen,'cont','soft',3); % Decode.
      newerrs = biterr(msg,decoded); % Errors in this trial
      numerr = numerr + newerrs; % Total errors for this EbNo value
      ntrials = ntrials + 1; % Update trial index.
   end
   % Error rate and 98% confidence interval for this EbNo value
   [ber(jj), intv1] = berconfint(numerr,(ntrials * 1e6),.98);
   intv{jj} = intv1; % Store in cell array for later use.
   disp(['EbNo = ' num2str(EbNo) ' dB, ' num2str(numerr) ...
         ' errors, BER = ' num2str(ber(jj))])
end
% Use BERFIT to plot the best fitted curve,
% interpolating to get a smooth plot.
fitEbNo = EbNomin:0.25:EbNomax; % Interpolation values
berfit(EbNovec,ber,fitEbNo);

% Also plot confidence intervals.
hold on;
grid on;
%axes(handles.axes2);
for jj=1:numEbNos
   semilogy([EbNovec(jj) EbNovec(jj)],intv{jj},'g-+');
end
%hold off;
if nargout == 0
  builtin('conv_code2', varargin{:});
else
  [varargout{1:nargout}] = builtin('conv_code2', varargin{:});
end
%-------------------------------------------------------------
function [varargout] = conv_code3(varargin)
M=2;
EbNomin = 1; EbNomax = 5; % EbNo range, in dB
numerrmin = 10; % Compute BER only after 10 errors occur.
EbNovec = EbNomin:1:EbNomax; % Vector of EbNo values
numEbNos = length(EbNovec); % Number of EbNo values
% Preallocate space for certain data.
ber = zeros(1,numEbNos); % BER values
intv = cell(1,numEbNos); % Cell array of confidence intervals
% Loop over the vector of EbNo values.
for jj = 1:numEbNos
   EbNo = EbNovec(jj);
   snr = EbNo;%*0.5*log2(M); % Because of binary modulation
   ntrials = 0; % Number of passes through the while loop below
   numerr = 0; % Number of errors for this EbNo value
   % Simulate until numerrmin errors occur.
   while (numerr < numerrmin)

      msg = zeros(1e6,1);%message is a string of a thousand zeros.
      t = poly2trellis(7,[171 133]); % Define trellis.
      code = convenc(msg,t); % Encode a string of ones.
      txsig = pskmod(code,M); % Modulate.
      rxsig = awgn(txsig, snr, 'measured'); % Add noise.
      decodmsg = pskdemod(rxsig,M); % Demodulate.
      qcode = quantiz(decodmsg,[0.001,.1,.3,.5,.7,.9,.999]);% Quantize to prepare for soft-decision decoding.
      tblen = 48; delay = tblen; % Traceback length
      decoded = vitdec(qcode,t,tblen,'cont','soft',3); % Decode.
      newerrs = biterr(msg,decoded); % Errors in this trial
      numerr = numerr + newerrs; % Total errors for this EbNo value
      ntrials = ntrials + 1; % Update trial index.
   end
   % Error rate and 98% confidence interval for this EbNo value
   [ber(jj), intv1] = berconfint(numerr,(ntrials * 1e6),.98);
   intv{jj} = intv1; % Store in cell array for later use.
   disp(['EbNo = ' num2str(EbNo) ' dB, ' num2str(numerr) ...
         ' errors, BER = ' num2str(ber(jj))])
end
% Use BERFIT to plot the best fitted curve,
% interpolating to get a smooth plot.
fitEbNo = EbNomin:0.25:EbNomax; % Interpolation values
berfit(EbNovec,ber,fitEbNo);

% Also plot confidence intervals.
hold on;
grid on;
%axes(handles.axes2);
for jj=1:numEbNos
   semilogy([EbNovec(jj) EbNovec(jj)],intv{jj},'g-+');
end
%hold off;
if nargout == 0
  builtin('conv_code3', varargin{:});
else
  [varargout{1:nargout}] = builtin('conv_code3', varargin{:});
end

%-------------------------------------------------------
function [varargout] = conv_code4(varargin)
M=4;
EbNomin = 1; EbNomax = 5; % EbNo range, in dB
numerrmin = 10; % Compute BER only after 10 errors occur.
EbNovec = EbNomin:1:EbNomax; % Vector of EbNo values
numEbNos = length(EbNovec); % Number of EbNo values
% Preallocate space for certain data.
ber = zeros(1,numEbNos); % BER values
intv = cell(1,numEbNos); % Cell array of confidence intervals
% Loop over the vector of EbNo values.
for jj = 1:numEbNos
   EbNo = EbNovec(jj);
   snr = EbNo*0.5*log2(M); % Because of binary modulation
   ntrials = 0; % Number of passes through the while loop below
   numerr = 0; % Number of errors for this EbNo value
   % Simulate until numerrmin errors occur.
   while (numerr < numerrmin)

      msg = zeros(1e6,1);%message is a string of a thousand zeros.
      t = poly2trellis(7,[171 133]); % Define trellis.
      code = convenc(msg,t); % Encode a string of ones.
      txsig = pskmod(code,M); % Modulate.
      rxsig = awgn(txsig, snr, 'measured'); % Add noise.
      decodmsg = pskdemod(rxsig,M); % Demodulate.
      qcode = quantiz(decodmsg,[0.001,.1,.3,.5,.7,.9,.999]);% Quantize to prepare for soft-decision decoding.
      tblen = 48; delay = tblen; % Traceback length
      decoded = vitdec(qcode,t,tblen,'cont','soft',3); % Decode.
      newerrs = biterr(msg,decoded); % Errors in this trial
      numerr = numerr + newerrs; % Total errors for this EbNo value
      ntrials = ntrials + 1; % Update trial index.
   end
   % Error rate and 98% confidence interval for this EbNo value
   [ber(jj), intv1] = berconfint(numerr,(ntrials * 1e6),.98);
   intv{jj} = intv1; % Store in cell array for later use.
   disp(['EbNo = ' num2str(EbNo) ' dB, ' num2str(numerr) ...
         ' errors, BER = ' num2str(ber(jj))])
end
% Use BERFIT to plot the best fitted curve,
% interpolating to get a smooth plot.
fitEbNo = EbNomin:0.25:EbNomax; % Interpolation values
berfit(EbNovec,ber,fitEbNo);

% Also plot confidence intervals.
hold on;
grid on;
%axes(handles.axes2);
for jj=1:numEbNos
   semilogy([EbNovec(jj) EbNovec(jj)],intv{jj},'g-+');
end
%hold off;
if nargout == 0
  builtin('conv_code4', varargin{:});
else
  [varargout{1:nargout}] = builtin('conv_code4', varargin{:});
end

% --- Executes on selection change in modulation.
function modulation_Callback(hObject, eventdata, handles)
% hObject    handle to modulation (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: contents = get(hObject,'String') returns modulation contents as cell array
%        contents{get(hObject,'Value')} returns selected item from modulation

% --- Executes during object creation, after setting all properties.
function modulation_CreateFcn(hObject, eventdata, handles)
% hObject    handle to modulation (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: popupmenu controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc
    set(hObject,'BackgroundColor','white');
else
    set(hObject,'BackgroundColor',get(0,'defaultUicontrolBackgroundColor'));
end

% --- Executes on selection change in popupmenu2.
function popupmenu2_Callback(hObject, eventdata, handles)
% hObject    handle to popupmenu2 (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: contents = get(hObject,'String') returns popupmenu2 contents as cell array
%        contents{get(hObject,'Value')} returns selected item from popupmenu2

% --- Executes during object creation, after setting all properties.
function popupmenu2_CreateFcn(hObject, eventdata, handles)
% hObject    handle to popupmenu2 (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: popupmenu controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc
    set(hObject,'BackgroundColor','white');
else
    set(hObject,'BackgroundColor',get(0,'defaultUicontrolBackgroundColor'));
end

% --- Executes on selection change in coding.
function coding_Callback(hObject, eventdata, handles)
% hObject    handle to coding (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: contents = get(hObject,'String') returns coding contents as cell array
%        contents{get(hObject,'Value')} returns selected item from coding

% --- Executes during object creation, after setting all properties.
function coding_CreateFcn(hObject, eventdata, handles)
% hObject    handle to coding (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: popupmenu controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc
    set(hObject,'BackgroundColor','white');
else
    set(hObject,'BackgroundColor',get(0,'defaultUicontrolBackgroundColor'));
end

% --- Executes on selection change in interleaving.
function interleaving_Callback(hObject, eventdata, handles)
% hObject    handle to interleaving (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: contents = get(hObject,'String') returns interleaving contents as cell array
%        contents{get(hObject,'Value')} returns selected item from interleaving

% --- Executes during object creation, after setting all properties.
function interleaving_CreateFcn(hObject, eventdata, handles)
% hObject    handle to interleaving (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: popupmenu controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc
    set(hObject,'BackgroundColor','white');
else
    set(hObject,'BackgroundColor',get(0,'defaultUicontrolBackgroundColor'));
end

% --- Executes on button press in int.
function int_Callback(hObject, eventdata, handles)
% hObject    handle to int (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
popup_sel_index = get(handles.interleaving, 'Value');
switch popup_sel_index
    case 1

bits =1e6
M=2
EbNomin = 1; EbNomax = 5; % EbNo range, in dB
numerrmin = 10; % Compute BER only after 5 errors occur.
EbNovec = EbNomin:1:EbNomax; % Vector of EbNo values
numEbNos = length(EbNovec); % Number of EbNo values
% Preallocate space for certain data.
ber = zeros(1,numEbNos); % BER values
intv = cell(1,numEbNos); % Cell array of confidence intervals
% Loop over the vector of EbNo values.
for jj = 1:numEbNos
   EbNo = EbNovec(jj);
   snr = EbNo; % Because of binary modulation
   ntrials = 0; % Number of passes through the while loop below
   numerr = 0; % Number of errors for this EbNo value
   % Simulate until numerrmin errors occur.
   while (numerr < numerrmin)
      % st1 = 27221; st2 = 4831; % States for random number generator
      msg= zeros(bits,1);%message is a string of a thousand zeros.
      t = poly2trellis(7,[171 133]); % Define trellis.
      %t = poly2trellis([4 3],[4 5 17;7 4 2]); % Define trellis.
      code = convenc(msg,t); % Encode a string of ones.
      nrows = 5; % Use 5 shift registers
      slope = 3; % Delays are 0, 3, 6, 9, and 12.
      inter = convintrlv(code,nrows,slope); % Interleave.
      txsig = pskmod(inter,M); % Modulate.
      rxsig = awgn(txsig, snr, 'measured'); % Add noise.
      decodmsg = pskdemod(rxsig,M); % Demodulate.
      deinter = convdeintrlv(decodmsg,nrows,slope); % Deinterleave.
      qcode = quantiz(deinter,[0.001,.1,.3,.5,.7,.9,.999]);% Quantize to prepare for soft-decision decoding.
      tblen = 48; delay = tblen; % Traceback length
      decoded = vitdec(qcode,t,tblen,'cont','soft',3); % Decode.
      newerrs = biterr(msg,decoded); % Errors in this trial
      numerr = numerr + newerrs; % Total errors for this EbNo value
      ntrials = ntrials + 1; % Update trial index.
   end
   % Error rate and 98% confidence interval for this EbNo value
   [ber(jj), intv1] = berconfint(numerr,(ntrials *bits ),.98);
   intv{jj} = intv1; % Store in cell array for later use.
   disp(['EbNo = ' num2str(EbNo) ' dB, ' num2str(numerr) ...
         ' errors, BER = ' num2str(ber(jj))])
end
% Use BERFIT to plot the best fitted curve,
% interpolating to get a smooth plot.
fitEbNo = EbNomin:0.25:EbNomax; % Interpolation values
berfit(EbNovec,ber,fitEbNo);

% Also plot confidence intervals.
hold on;
grid on;
%set(gcf,'DefaultAxesColorOrder',[1 0 0;0 1 0;0 0 1])
%axes(handles.axes2);
for jj=1:numEbNos
   semilogy([EbNovec(jj) EbNovec(jj)],intv{jj},'g-+');
end

    case 2

bits =1e6
M=4
EbNomin = 1; EbNomax = 5; % EbNo range, in dB
numerrmin = 10; % Compute BER only after 5 errors occur.
EbNovec = EbNomin:1:EbNomax; % Vector of EbNo values
numEbNos = length(EbNovec); % Number of EbNo values
% Preallocate space for certain data.
ber = zeros(1,numEbNos); % BER values
intv = cell(1,numEbNos); % Cell array of confidence intervals
% Loop over the vector of EbNo values.
for jj = 1:numEbNos
   EbNo = EbNovec(jj);
   snr = EbNo*0.5*log2(M); % Because of binary modulation
   ntrials = 0; % Number of passes through the while loop below
   numerr = 0; % Number of errors for this EbNo value
   % Simulate until numerrmin errors occur.
   while (numerr < numerrmin)
      % st1 = 27221; st2 = 4831; % States for random number generator
      msg= zeros(bits,1);%message is a string of a thousand zeros.
      t = poly2trellis(7,[171 133]); % Define trellis.
      %t = poly2trellis([4 3],[4 5 17;7 4 2]); % Define trellis.
      code = convenc(msg,t); % Encode a string of ones.
      nrows = 5; % Use 5 shift registers
      slope = 3; % Delays are 0, 3, 6, 9, and 12.
      inter = convintrlv(code,nrows,slope); % Interleave.
      txsig = pskmod(inter,M); % Modulate.
      rxsig = awgn(txsig, snr, 'measured'); % Add noise.
      decodmsg = pskdemod(rxsig,M); % Demodulate.
      deinter = convdeintrlv(decodmsg,nrows,slope); % Deinterleave.
      qcode = quantiz(deinter,[0.001,.1,.3,.5,.7,.9,.999]);% Quantize to prepare for soft-decision decoding.
      tblen = 48; delay = tblen; % Traceback length
      decoded = vitdec(qcode,t,tblen,'cont','soft',3); % Decode.
      newerrs = biterr(msg,decoded); % Errors in this trial
      numerr = numerr + newerrs; % Total errors for this EbNo value
      ntrials = ntrials + 1; % Update trial index.
   end
   % Error rate and 98% confidence interval for this EbNo value
   [ber(jj), intv1] = berconfint(numerr,(ntrials *bits ),.98);
   intv{jj} = intv1; % Store in cell array for later use.
   disp(['EbNo = ' num2str(EbNo) ' dB, ' num2str(numerr) ...
         ' errors, BER = ' num2str(ber(jj))])
end
% Use BERFIT to plot the best fitted curve,
% interpolating to get a smooth plot.
fitEbNo = EbNomin:0.25:EbNomax; % Interpolation values
berfit(EbNovec,ber,fitEbNo);

% Also plot confidence intervals.
hold on;
grid on;
%set(gcf,'DefaultAxesColorOrder',[1 0 0;0 1 0;0 0 1])
%axes(handles.axes2);
for jj=1:numEbNos
   semilogy([EbNovec(jj) EbNovec(jj)],intv{jj},'g-+');
end
end

% --- Executes on button press in pushbutton2.
function pushbutton2_Callback(hObject, eventdata, handles)
% hObject    handle to pushbutton2 (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
popup_sel_index = get(handles.modulation, 'Value');
switch popup_sel_index
    case 1
popup_sel_index = get(handles.coding, 'Value');
switch popup_sel_index
    case 1
        code1;
    case 2
        conv_code3;
    case 3
        conv_code1;

end
    case 2
popup_sel_index = get(handles.coding, 'Value');
switch popup_sel_index
    case 1
        code2;
    case 2
        conv_code4;
    case 3
        conv_code2;
end
    case 3
        code3;
    case 4
        code4;
end

% --- Executes on button press in clr.
function clr_Callback(hObject, eventdata, handles)
% hObject    handle to clr (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
cla

% --- Executes on button press in hlp.
function hlp_Callback(hObject, eventdata, handles)
% hObject    handle to hlp (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
helpPath = which('help.txt');
web(helpPath);

% --------------------------------------------------------------------
function close_Callback(hObject, eventdata, handles)
% hObject    handle to close (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
selection = questdlg(['Close ' get(handles.figure1,'Name') '?'],...
                     ['Close ' get(handles.figure1,'Name') '...'],...
                     'Yes','No','Yes');
if strcmp(selection,'No')
    return;
end

delete(handles.figure1)

% --------------------------------------------------------------------
function print_Callback(hObject, eventdata, handles)
% hObject    handle to print (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
printdlg(handles.figure1)

% --------------------------------------------------------------------
function file_Callback(hObject, eventdata, handles)
% hObject    handle to file (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)


function z = cvmgt(x, y, p)
%CVMGT  Conditional merge of two vectors
%
%   Z = CVMGT(X, Y, P) return a vector Z whose elements are X if P is true
%   and Y otherwise.  P, X, and Y should be the same shape except that X
%   and Y may be scalars.  CVMGT stands for conditional vector merge true
%   (an intrinsic function for the Cray fortran compiler).  It implements
%   the C++ statement
%
%     Z = P ? X : Y;

  z = zeros(size(p));
  if isscalar(x)
    z(p) = x;
  else
    z(p) = x(p);
  end
  if isscalar(y)
    z(~p) = y;
  else
    z(~p) = y(~p);
  end
end


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

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

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

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

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

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

end

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

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

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

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

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

end

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

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

end

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

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

end

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

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

  DKff = {};

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

ncf = length(gpcf.cf);

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

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

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

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

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

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

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

end

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

ncf = length(gpcf.cf);

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

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

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

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

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

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

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

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

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

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

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

end

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

  [n, m] =size(x);

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

    ncf = length(gpcf.cf);

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

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

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

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

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

    ncf = length(gpcf.cf);

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

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

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

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

end

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

ncf = length(gpcf.cf);

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

end

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

ncf = length(gpcf.cf);

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

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

end

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

ncf = length(gpcf.cf);

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

end

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

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

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

  ncf = length(gpcf.cf);

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

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

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

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

  ncf = length(gpcf.cf);

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

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

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

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

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

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

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

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

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

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

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

  end

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

end

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

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


function exact = p00_exact ( prob )

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

  return
end


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

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

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

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

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

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

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

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

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

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

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

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

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

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


function value = p20_r8 ( action, name, value )

%*****************************************************************************80
%
%% P20_R8 sets or gets R8 parameters for problem 20.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    19 March 2007
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, string ACTION,
%    'D' sets the parameter to its default value;
%    'G' gets a parameter.
%    'R' sets the parameter to a random value.
%    'S' sets a parameter,
%
%    Input, string NAME, the name of the parameter.
%    'A' is the value of all lower integral bounds.
%    'B' is the value of all upper integral bounds.
%    'P' is the value of the exponent in the integrand.
%
%    Input/output, real VALUE.
%    * If ACTION = 'S', then VALUE is an input quantity, and is the
%      new value to be assigned to NAME.
%    * If ACTION = 'G' or 'R', then VALUE is an output quantity,
%      and is the current value of NAME.
%
  persistent a;
  persistent b;
  persistent p;

  if ( action == 'D' | action == 'd' )

    if ( name == 'A' | name == 'a' | name == '*' )
      a = 0.0;
    end

    if ( name == 'B' | name == 'b' | name == '*' )
      b = 1.0;
    end

    if ( name == 'P' | name == 'p' | name == '*' )
      p = 2.0;
    end

  elseif ( action == 'G' | action == 'g' )

    if ( name == 'A' | name == 'a' )
      value = a;
    elseif ( name == 'B' | name == 'b' )
      value = b;
    elseif ( name == 'P' | name == 'p' )
      value = p;
    else
      fprintf ( 1, '\n' );
      fprintf ( 1, 'P20_R8 - Fatal error!\n' );
      fprintf ( 1, '  Unrecognized name = "%s".\n', name );
      error ( 'P20_R8 - Fatal error!' );
    end

  elseif ( action == 'R' | action == 'r' )

    if ( name == 'A' | name == 'a' )
      a = rand ( 1, 1 );
      value = a;
    elseif ( name == 'B' | name == 'b' )
      b = rand ( 1, 1 );
      value = b;
    elseif ( name == 'P' | name == 'p' )
      p = rand ( 1, 1 );
      value = p;
    else
      fprintf ( 1, '\n' );
      fprintf ( 1, 'P20_R8 - Fatal error!\n' );
      fprintf ( 1, '  Unrecognized name = "%s".\n', name );
      error ( 'P20_R8 - Fatal error!' );
    end

  elseif ( action == 'S' | action == 's' )

    if ( name == 'A' | name == 'a' )
      a = value;
    elseif ( name == 'B' | name == 'b' )
      b = value;
    elseif ( name == 'P' | name == 'p' )
      p = value;
    else
      fprintf ( 1, '\n' );
      fprintf ( 1, 'P20_R8 - Fatal error!\n' );
      fprintf ( 1, '  Unrecognized name = "%s".\n', name );
      error ( 'P20_R8 - Fatal error!' );
    end

  else

    fprintf ( 1, '\n' );
    fprintf ( 1, 'P20_R8 - Fatal error!\n' );
    fprintf ( 1, '  Unrecognized action = "%s".\n', action );
    error ( 'P20_R8 - Fatal error!' );

  end

  return
end


function H=tricontour(tri,x,y,z,v,levels,color)
%TRICONTOUR Contour plot for triangulated data.
%   TRICONTOUR(TRI,X,Y,Z,Levels) displays the contour plot based on
%   triangles defined in the M-by-3 face matrix TRI as a surface. A
%   row of TRI contains indexes into the X,Y, and Z vertex vectors
%   to define a single triangular face.
%
%   TRICONTOUR(TRI,X,Y,Z,V,Levels) displays the contour plot based on
%   triangles defined in the M-by-3 face matrix TRI as a surface. A
%   row of TRI contains indexes into the X,Y, and Z vertex vectors
%   to define a single triangular face. The contours are determined
%   by the values V and the Levels.
%
%   H = TRICONTOUR(...) a vector H of handles to LINE or PATCH objects,
%   one handle per line. TRICONTOUR is not compatible with CLABEL.
%
%   The contours are normally colored based on the current colormap
%   and are drawn as PATCH objects. You can override this behavior
%   with the syntax CONTOUR(...,'LINESPEC') to draw the contours as
%   LINE objects with the color and linetype specified.
%
%   Example:
%      x=rand(20); y=rand(20); z=rand(20); tri=delaunay(x,y);
%      tricontour(tri,x,y,z,.3:.1:1); colorbar
%
%   See also CONTOUR, CONTOURF, TRICONTOURF

%----- LGPL --------------------------------------------------------------------
%
%   Copyright (C) 2011-2013 Stichting Deltares.
%
%   This library is free software; you can redistribute it and/or
%   modify it under the terms of the GNU Lesser General Public
%   License as published by the Free Software Foundation version 2.1.
%
%   This library 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
%   Lesser General Public License for more details.
%
%   You should have received a copy of the GNU Lesser General Public
%   License along with this library; if not, see <http://www.gnu.org/licenses/>.
%
%   contact: delft3d.support@deltares.nl
%   Stichting Deltares
%   P.O. Box 177
%   2600 MH Delft, The Netherlands
%
%   All indications and logos of, and references to, "Delft3D" and "Deltares"
%   are registered trademarks of Stichting Deltares, and remain the property of
%   Stichting Deltares. All rights reserved.
%
%-------------------------------------------------------------------------------
%   http://www.deltaressystems.com
%   $HeadURL: https://svn.oss.deltares.nl/repos/delft3d/trunk/src/tools_lgpl/matlab/quickplot/progsrc/tricontour.m $
%   $Id: tricontour.m 2498 2013-05-16 12:38:27Z jagers $

error(nargchk(5,7,nargin));
getdata=0;
if nargin==7
    if strcmp(color,'getdata')
        getdata=1;
        lin = '*';
        col = '*';
    else
        [lin,col,mark,msg] = colstyle(color);
        if ~isempty(msg)
            error(msg)
        end
    end
elseif nargin==6
    if ischar(levels)
        color=levels;
        levels=v;
        v=z;
        z=[];
        if strcmp(color,'getdata')
            getdata=1;
            lin = '*';
            col = '*';
        else
            [lin,col,mark,msg] = colstyle(color);
            if ~isempty(msg)
                error(msg)
            end
        end
    else
        lin = '';
        col = '';
    end
else
    levels=v;
    v=z;
    z=[];
    lin = '';
    col = '';
end

if ~getdata
    ax = newplot;
    if isempty(col) % no color spec was given
        colortab = get(ax,'colororder');
        ncol = size(colortab,1);
    end
end

x=transpose(x(:));
y=transpose(y(:));
zdef=~isempty(z);
if zdef
    z=transpose(z(:));
end
v=transpose(v(:));

Patches=1:size(tri,1);
nlevels = length(levels);
if getdata
    H=cell(1,nlevels);
else
    H=zeros(1,nlevels);
end
NonEmptyLevel=zeros(1,nlevels);
for LevelNr=1:nlevels
    level=levels(LevelNr);
    Nlarger=sum(v(tri)>level,2);
    Nsmaller=sum(v(tri)<level,2);
    NaN_tri=any(isnan(v(tri)),2);
    Nsmaller(NaN_tri)=3;
    Nlarger(NaN_tri)=0;
    CLIndex=1+4*Nlarger+Nsmaller;
    Cutslevel=[0 3 2 0 3 3 3 0 2 3 0 0 0 0 0];
    NLevel=sum(Cutslevel(CLIndex));
    XLevel=repmat(NaN,[NLevel 1]);
    YLevel=XLevel;
    if zdef
        ZLevel=XLevel;
    end
    LvlOffset=0;

    % patches with three equal
    % Patch=Patches(CLIndex==1);

    % patches with two equal
    Patch=Patches((CLIndex==2) | (CLIndex==5));
    if ~isempty(Patch)
        LvlIndex=LvlOffset+(1:3:(3*length(Patch)));
        LvlIndex=[LvlIndex;LvlIndex+1];
        Index=tri(Patch,:);
        [Dummy,Permutation]=sort(v(Index)==level,2);
        Index=Index((Permutation-1)*size(Index,1)+transpose(1:size(Index,1))*[1 1 1]);
        % first element has elevation different from level
        Index=Index(:,[2 3]);
        XPoint=transpose(x(Index));
        XLevel(LvlIndex(:))=XPoint(:);
        YPoint=transpose(y(Index));
        YLevel(LvlIndex(:))=YPoint(:);
        if zdef
            ZPoint=transpose(z(Index));
            ZLevel(LvlIndex(:))=ZPoint(:);
        end
        LvlOffset=LvlOffset+3*length(Patch);
    end

    % patches with one equal, one larger and one smaller
    Patch=Patches(CLIndex==6);
    if ~isempty(Patch)
        LvlIndex=LvlOffset+(1:3:(3*length(Patch)));
        LvlIndex=[LvlIndex;LvlIndex+1];
        Index=tri(Patch,:);
        [Dummy,Permutation]=sort(v(Index),2);
        Index=Index((Permutation-1)*size(Index,1)+transpose(1:size(Index,1))*[1 1 1]);
        % second element has elevation equal to level
        VTemp=v(Index);
        Lambda=min(1,(level-VTemp(:,1))./(VTemp(:,3)-VTemp(:,1)));
        XPoint=transpose([transpose(x(Index(:,1)))+Lambda.*transpose(x(Index(:,3))-x(Index(:,1))) transpose(x(Index(:,2)))]);
        XLevel(LvlIndex(:))=XPoint(:);
        YPoint=transpose([transpose(y(Index(:,1)))+Lambda.*transpose(y(Index(:,3))-y(Index(:,1))) transpose(y(Index(:,2)))]);
        YLevel(LvlIndex(:))=YPoint(:);
        if zdef
            ZPoint=transpose([transpose(z(Index(:,1)))+Lambda.*transpose(z(Index(:,3))-z(Index(:,1))) transpose(z(Index(:,2)))]);
            ZLevel(LvlIndex(:))=ZPoint(:);
        end
        LvlOffset=LvlOffset+3*length(Patch);
    end

    % patches with two larger and one smaller
    Patch=Patches(CLIndex==10);
    if ~isempty(Patch)
        LvlIndex=LvlOffset+(1:3:(3*length(Patch)));
        LvlIndex=[LvlIndex;LvlIndex+1];
        Index=tri(Patch,:);
        [Dummy,Permutation]=sort(v(Index)<level,2);
        Index=Index((Permutation-1)*size(Index,1)+transpose(1:size(Index,1))*[1 1 1]);
        % last element has elevation less than level
        VTemp=v(Index);
        Lambda=min(1,(level-VTemp(:,3)*ones(1,2))./(VTemp(:,[1 2])-VTemp(:,3)*ones(1,2)));
        XPoint=transpose(transpose(x(Index(:,3)))*ones(1,2)+Lambda.*(x(Index(:,[1 2]))-transpose(x(Index(:,3)))*ones(1,2)));
        XLevel(LvlIndex(:))=XPoint(:);
        YPoint=transpose(transpose(y(Index(:,3)))*ones(1,2)+Lambda.*(y(Index(:,[1 2]))-transpose(y(Index(:,3)))*ones(1,2)));
        YLevel(LvlIndex(:))=YPoint(:);
        if zdef
            ZPoint=transpose(transpose(z(Index(:,3)))*ones(1,2)+Lambda.*(z(Index(:,[1 2]))-transpose(z(Index(:,3)))*ones(1,2)));
            ZLevel(LvlIndex(:))=ZPoint(:);
        end
        LvlOffset=LvlOffset+3*length(Patch);
    end

    % patches with two smaller and one larger
    Patch=Patches(CLIndex==7);
    if ~isempty(Patch)
        LvlIndex=LvlOffset+(1:3:(3*length(Patch)));
        LvlIndex=[LvlIndex;LvlIndex+1];
        Index=tri(Patch,:);
        [Dummy,Permutation]=sort(v(Index)>level,2);
        Index=Index((Permutation-1)*size(Index,1)+transpose(1:size(Index,1))*[1 1 1]);
        % last element has elevation larger than level
        VTemp=v(Index);
        Lambda=min(1,(level-VTemp(:,[1 2]))./(VTemp(:,3)*ones(1,2)-VTemp(:,[1 2])));
        XPoint=transpose(x(Index(:,[1 2]))+Lambda.*(transpose(x(Index(:,3)))*ones(1,2)-x(Index(:,[1 2]))));
        XLevel(LvlIndex(:))=XPoint(:);
        YPoint=transpose(y(Index(:,[1 2]))+Lambda.*(transpose(y(Index(:,3)))*ones(1,2)-y(Index(:,[1 2]))));
        YLevel(LvlIndex(:))=YPoint(:);
        if zdef
            ZPoint=transpose(z(Index(:,[1 2]))+Lambda.*(transpose(z(Index(:,3)))*ones(1,2)-z(Index(:,[1 2]))));
            ZLevel(LvlIndex(:))=ZPoint(:);
        end
        LvlOffset=LvlOffset+3*length(Patch);
    end

    % patches with one equal and two larger or two smaller
    Patch=Patches((CLIndex==3) | (CLIndex==9));
    if ~isempty(Patch)
        LvlIndex=LvlOffset+(1:2:(2*length(Patch)));
        Index=tri(Patch,:);
        [Dummy,Permutation]=sort(v(Index)==level,2);
        Index=Index((Permutation-1)*size(Index,1)+transpose(1:size(Index,1))*[1 1 1]);
        % third element has elevation equal to level
        Index=Index(:,3);
        XPoint=x(Index);
        XLevel(LvlIndex(:))=XPoint(:);
        YPoint=y(Index);
        YLevel(LvlIndex(:))=YPoint(:);
        if zdef
            ZPoint=z(Index);
            ZLevel(LvlIndex(:))=ZPoint(:);
        end
        LvlOffset=LvlOffset+2*length(Patch);
    end

    VLevel=level*ones(size(XLevel));

    NonEmptyLevel(LevelNr)=1;
    if getdata
        if zdef
            H{LevelNr}={XLevel YLevel ZLevel LevelNr};
        else
            H{LevelNr}={XLevel YLevel LevelNr};
        end
    elseif isempty(col) && isempty(lin)
        if ~zdef
            ZLevel=VLevel;
        end
        NewH = patch('XData',transpose([XLevel;XLevel]), ...
            'YData',transpose([YLevel;YLevel]), ...
            'ZData',transpose([ZLevel;ZLevel]), ...
            'CData',transpose([VLevel;VLevel]), ...
            'facecolor','none', ...
            'edgecolor','flat',...
            'userdata',level, ...
            'parent',ax);
        H(LevelNr)=NewH;
    else
        if ~zdef
            ZLevel=VLevel;
        end
        NewH = line('XData',XLevel, ...
            'YData',YLevel, ...
            'ZData',ZLevel, ...
            'userdata',level, ...
            'parent',ax);
        H(LevelNr)=NewH;
    end
end

levels=levels(logical(NonEmptyLevel));

if getdata
    % do nothing
elseif isempty(col) && ~isempty(lin)
    nlvl = length(levels);
    colortab = colortab(mod(1:nlvl,ncol)+1,:);
    for i = 1:length(H)
        set(H(i),'linestyle',lin,'color',colortab(i,:));
    end
else
    if ~isempty(lin)
        set(H,'linestyle',lin);
    end
    if ~isempty(col)
        set(H,'color',col);
    end
end