function radDepthV = getRadiologicalDepth(xV, yV, zV, gantryAngle, isocenter, isodistance, structNum, scanSet, planC)
%"getRadiologicalDepth"
%   Returns the radiological depth at the points in xV, yV, zV, as
%   determined from scanSet with a point source at sourceV.  structNum
%   tells the interpolation routine to only include the densities of
%   points within a certain structure when calculating the radiological
%   depth.
%
%JRA 06/16/05
%
%Usage:
%  function radDepthV = getRadiologicalDepth(xV, yV, zV, gA, iC, iD, structNum, scanSet, planC)
%
% Copyright 2010, Joseph O. Deasy, on behalf of the CERR development team.
%
% This file is part of The Computational Environment for Radiotherapy Research (CERR).
%
% CERR development has been led by:  Aditya Apte, Divya Khullar, James Alaly, and Joseph O. Deasy.
%
% CERR has been financially supported by the US National Institutes of Health under multiple grants.
%
% CERR is distributed under the terms of the Lesser GNU Public License.
%
%     This version of CERR 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.
%
% CERR 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 CERR.  If not, see <http://www.gnu.org/licenses/>.

if ~exist('planC')
    global planC
end
indexS = planC{end};

siz = size(xV);

%Get mask of requested structure.
rS = getRasterSegments(structNum, planC);
[structMask, slicesV] = rasterToMask(rS, scanSet, planC);
% rS = getRasterSegments(structNum, planC);
% [structMask, slicesV] = rasterToMask(rS, scanSet, planC);
% [surfPoints] = getSurfacePoints(structMask);

%Get coordinates of scan voxels inside the structure.
[xS, yS, zS] = getScanXYZVals(planC{indexS.scan}(scanSet));
[xM, yM, zM] = meshgrid(xS, yS, zS(slicesV));
xM = xM(structMask);
yM = yM(structMask);
zM = zM(structMask);
% [xS, yS, zS] = getScanXYZVals(planC{indexS.scan}(scanSet));
% zS = zS(slicesV);
% xM = xS(surfPoints(:,1));
% yM = yS(surfPoints(:,2));
% zM = zS(surfPoints(:,3));
% clear surfPoints

%Convert points to collimator coordinates.
coll3V = scan2Collimator([xM(:) yM(:) zM(:)], gantryAngle, 0, 0, isocenter, isodistance);
clear xM yM zM;

%Get distance from source to all points.
distsquared = sepsq(coll3V', [0 0 0]');

%Project vector to points to the perpendicular plane at isocenter.
coll3V = coll3V./repmat(coll3V(:,3), [1 3]) * -isodistance;

%Find the extents of X,Y,D for the region covering the structure (ie, all
%nonzero scan numbers since we will set scan densities outside of structure
%to be 0.
minExt = min(coll3V);
maxExt = max(coll3V);
clear coll3V;

minProjX = minExt(1);
maxProjX = maxExt(1);

minProjY = minExt(2);
maxProjY = maxExt(2);

maxDsq = max(distsquared);
minDsq = min(distsquared);
clear distsquared;

%Set points in scanArray outside of structure to 0.
sA = planC{indexS.scan}(scanSet).scanArray;
for i=1:size(sA, 3);
    [ismem, loc] = ismember(i, slicesV);
    if ismem
        slc = sA(:,:,i);
        slc(~structMask(:,:,loc)) = 0;
        sA(:,:,i) = slc;
    else
        sA(:,:,i) = zeros(size(sA(:,:,i)));
    end
end
clear structMask;

%Convert requested points to collimator coordinates.
coll3V = scan2Collimator([xV(:) yV(:) zV(:)], gantryAngle, 0, 0, isocenter, isodistance);
clear xM yM zM;

%Get distance from source to all requested points.
distsquared = sepsq(coll3V', [0 0 0]');

%Project vector to points to the perpendicular plane at isocenter.
coll3V = coll3V./repmat(coll3V(:,3), [1 3]) * -isodistance;

clear xV yV zV

%Find out of bounds points, for which calculation is not necessary,
%radDepth is zero.
OOBPoints = coll3V(:,1) < minProjX | coll3V(:,1) > maxProjX | coll3V(:,2) < minProjY | coll3V(:,2) > maxProjY;

%Clip the r values so they take the density of the nearest calculated
%point (points less than minDsq would take zero, and greater than maxDsq
%would take the last radDepthV value along that ray.
r = clip(distsquared, minDsq, maxDsq, 'limits');

%Set the resolution of the radiological depth calculation (at isocenter).
dX = .5;
dY = .5;
dR = .25;

%Mesh the radiological depth calculation grid in collimator coordinates.
projXV = minProjX:dX:maxProjX;
projYV = minProjY:dY:maxProjY;
distV = sqrt(minDsq):dR:sqrt(maxDsq);
[projXVM, projYVM, distVM] = meshgrid(projXV, projYV, distV);

projDistV = sqrt(sepsq([projXVM(:) projYVM(:) -isodistance*ones(size(projXVM(:)))]', [0 0 0]'));
projXVM = projXVM(:) .* (distVM(:) ./ projDistV);
projYVM = projYVM(:) .* (distVM(:) ./ projDistV);
projZVM = -isodistance*ones(size(distVM(:))) .* (distVM(:) ./ projDistV);

%Convert the depth calculation grid to rectangular coordinates.
newXYZ = collimator2Scan([projXVM(:) projYVM(:) projZVM(:)], gantryAngle, 0, 0, isocenter, isodistance);
% clear projXVM projYVM distVM

%Get the scan values at each point in the radiological depth grid.
[xVS, yVS, zVS] = getScanXYZVals(planC{indexS.scan}(scanSet));

%Interpolate to values in xV/yV/zV from the scanarray, 0 if out of bounds.
scanVals = finterp3(newXYZ(:,1), newXYZ(:,2), newXYZ(:,3), sA, [xVS(1) xVS(2)-xVS(1) xVS(end)], [yVS(1) yVS(2)-yVS(1) yVS(end)], zVS, 0);

scanVals = reshape(scanVals, length(projXV), length(projYV), length(distV));

%Scale values by the resolution of R to get the linear radiological depth.
%Divide by 1024 to get the radiological depth in units of water.
scanVals = scanVals .* (dR / 1024);

%Take the cumsum to get the actual radiological depth at each point in the
%depth calculation grid.
cumDens = cumsum(scanVals, 3);
clear scanVals

cumDens = reshape(cumDens, size(distVM));

%Interpolate to values in xV/yV/zV from the scanarray, 0 if out of bounds.
radDepthV = finterp3(coll3V(:,1), coll3V(:,2), sqrt(distsquared), cumDens, [projXV(1) projXV(2)-projXV(1) projXV(end)], [projYV(1) projYV(2)-projYV(1) projYV(end)], distV, 0);

radDepthV(OOBPoints) = 0;
radDepthV = reshape(radDepthV, siz);


report_this_filefun(mfilename('fullpath'));

figure
rect = [0.15,  0.60, 0.75, 0.35];
axes('position',rect)

pl =plot(bg(:,10),-bg(:,7),'gx');
set(pl,'Linewidth',1.5,'MarkerSize',7)
hold on

pl =plot(aft(:,12),-aft(:,7),'bx');
set(pl,'LineWidth',1.5,'MarkerSize',7)

pl =plot(maex,-maey,'hm');
set(pl,'LineWidth',1.5,'MarkerSize',15,...
    'MarkerFaceColor','w','MarkerEdgeColor','k')

set(gca,'Ylim',[-14 0])
set(gca,'Xlim',[ 0 48 ])

set(gca,'Color',[color_bg([1,2]) 0.7])
set(gca,'box','on',...
    'SortMethod','childorder','TickDir','out','FontWeight',...
    'bold','FontSize',16,'Linewidth',1.2)
ylabel('Depth in [km]')

set(gca,'XTickLabel',[]);

ax = axis;

rect = [0.15,  0.15, 0.75, 0.35];
axes('position',rect)

hold on
pco1 = pcolor(gx,gy,re4);
hold on

pl =plot(maex,-maey,'hm');
set(pl,'LineWidth',1.5,'MarkerSize',15,...
    'MarkerFaceColor','w','MarkerEdgeColor','k')

colormap(hsv)

shading interp

if fre == 1
    caxis([fix1 fix2])
end

axis([ax]);
set(gca,'box','on',...
    'SortMethod','childorder','TickDir','out','FontWeight',...
    'bold','FontSize',16,'Linewidth',1.8)

ylabel('Depth in  [km]','FontWeight','bold','FontSize',ZmapGlobal.Data.fontsz.m)
xlabel('Distance along projection in [km]','FontWeight','bold','FontSize',ZmapGlobal.Data.fontsz.m)

vx =  (min(min(re3)):0.1:max(max(re3)));
if fre == 1; vx =  (fix1:0.1:fix2); end
v = [vx ; vx]; v = v';
rect = [0.94 0.15 0.01 0.35];
axes('position',rect)
pcolor((1:2),vx,v)
shading interp
set(gca,'XTickLabels',[])
set(gca,'FontSize',12,'FontWeight','bold',...
    'LineWidth',1.0,'YAxisLocation','right',...
    'Box','on','SortMethod','childorder','TickDir','out')
ax3 = gca;

whitebg(gcf,[0 0 0])
set(gcf,'Color','k','InvertHardcopy','off')

matdraw


function [names, mf_settings, data, data_endo, data_endo_a, data_endo_c, data_endo_c_lags, data_exo, data_exo_a, data_exo_p, data_exo_c, data_exo_c_lags, Fperiods, Fcomp, Fcperiods, Fcenddate]= ...
    gensample_mf(startdate,enddate,VARtype,Fstartdate,Fenddate,Fendsmpl,endo,exo,frequency,lags,F,CF,pref, numendo)

% Phase 1: data loading and error checking

% first read the data from Excel
[data_m, names_m]=xlsread(pref.excelFile,'mfvar_monthly');
[data_q, names_q]=xlsread(pref.excelFile,'mfvar_quarterly');
[select, names_s] = xlsread(pref.excelFile,'mf_var_trans');

n_m = size(data_m,2);           mf_settings.Nm = n_m;
n_q = size(data_q,2);           mf_settings.Nq = n_q;
T_m = size(data_m,1);           mf_settings.T_m = T_m;
T_q = size(data_q,1);
T_b = T_q*3;                    mf_settings.T_b = T_b;    % The end of the balanced dataset (when all quarterly and monthly data exist)
ym_q = kron(data_q,ones(3,1));
% ym_q = reshape(repmat(data_q,1,3)',T_b,1);

data = [data_m [ym_q; nan(T_m - T_b,n_q)]];
data(:,select == 0) = log(data(:,select==0));
data(:,select == 1) = data(:,select==1)./100;
names =  [names_m repmat(names_m(:,end),1,n_q)];
names(1,n_m+2:end) = names_q(1,2:end);

% Check if the monthly variables
% if n_q ~= floor(n_m/3)

% now, as a preliminary step: check if there is any Nan in the data; if yes, return an error since the model won't be able to run with missing data
% a simple way to test for NaN is to check for "smaller or equal to infinity": Nan is the only number for which matlab will return 'false' when asked so
% [r,c]=size(data);
% for ii=1:r
%    for jj=1:c
%    temp=data(ii,jj);
%       if (temp<=inf)==0
%       % identify the variable and the date
%       NaNvariable=names{1,jj+1};
%       NaNdate=names{ii+1,1};
%       message=['Error: variable ' NaNvariable ' at date ' NaNdate ' (and possibly other sample entries) is identified as NaN. Please check your Excel spreadsheet: entry may be blank or non-numerical.'];
%       msgbox(message);
%       error('programme termination: data error');
%       end
%    end
% end

% identify the date strings
datestrings=names_m(2:end,1);
% identify the position of the string corresponding to the start period
startlocation=find(strcmp(datestrings,startdate));
% identify the position of the string corresponding to the end period
endlocation=find(strcmp(datestrings,enddate));

% if either the start date or the date date is not recognised, return an error message
if isempty(startlocation)
    error('bear:gensample_mf', 'Unknown start date for the sample. Please check your sample start date (remember that names are case-sensitive).')
elseif isempty(endlocation)
    error('bear:gensample_mf', 'Unknown end date for the sample. Please check your sample end date (remember that names are case-sensitive).');
end
% also, if the start date is posterior to the end date, obviously return an error
if startlocation>=endlocation==1
    error('bear:gensample_mf','Inconsistency between the start and end dates. The start date must be anterior to the end date.');
end

% identify the variable strings, endogenous and exogenous
variablestrings=[names_m(1,2:end) names_q(1,2:end)];

% identify the position of the strings corresponding to the endogenous variables
% for each variable, find the corresponding string
for ii=1:numendo
    % check first that the variable ii in endo appears in the list of variable strings
    % if not, the variable is unknown: return an error
    var=endo{ii,1};
    check=find(strcmp(variablestrings,var));
    if isempty(check)==1
        error('bear:gensample_mf', ['Endogenous variable ' var ' cannot be found on the excel data spreadsheet.']);
    end
    % if the variable is known, go on
    endolocation(ii,1)=find(strcmp(variablestrings,endo(ii,1)));
end

% identify the position of the strings corresponding to the exogenous variables
% proceed similarly to the endogenous variables, but account for the fact that exogenous may be empty
% so check first whether there are exogenous variables altogether
if isempty(exo)
    numexo=0;
else
    % if not empty, repeat what has been done with the exogenous
    numexo=size(exo,1);
    % for each variable, find the corresponding string
    for ii=1:numexo
        % check first that the variable ii in endo appears in the list of variable strings
        % if not, the variable is unknown: return an error
        var=exo{ii,1};
        check=find(strcmp(variablestrings,var));
        if isempty(check)==1
            error('bear:gensample_mf', ['Error: exogenous variable ' var ' cannot be found on the excel data spreadsheet.']);
        end
        % if the variable is known, go on
        exolocation(ii,1)=find(strcmp(variablestrings,exo(ii,1)));
    end
end

% Phase 2: creation of the data matrices data_endo and data_exo

% now create the matrix of endogenous variables for the estimation sample
% it is simply the concatenation of the vectors of each endogenous variables, over the selected sample dates
data_endo=[];
% loop over endogenous variables
for ii=1:numendo
    data_endo=[data_endo data(startlocation:endlocation,endolocation(ii,1))];
end

% Similarly, create the matrix of exogenous variables for the estimation sample
data_exo=[];
for ii=1:numexo
    data_exo=[data_exo data(startlocation:endlocation,exolocation(ii,1))];
end

% these are the transformations for the relevant variables; 0 for taking logs and 1 for dividing by 100
% They are read from the sheet mf_var_trans on line 23 in this file
mf_settings.select = select(:,endolocation(:,1)');

% Phase 3: determination of the position of the forecast start and end periods

% if both unconditional and conditional forecasts were not selected, there is no need for all the forecast-specific matrices: simply return empty matrices
if (VARtype==1 && F==0) || ((VARtype==2 || VARtype==3 || VARtype==5 || VARtype==6|| VARtype==7 ) && (F==0 && CF==0))
    data_endo_a=[];
    data_exo_a=[];
    data_exo_p=[];
    Fperiods=[];
    Fcomp=[];
    Fcperiods=[];
    data_endo_c=[];
    data_endo_c_lags=[];
    data_exo_c=[];
    data_exo_c_lags=[];
    Fcenddate=[];

    % if forecast were selected, create all the required elements
else

    % preliminary tasks
    % first, identify the date strings, and the variable strings
    datestrings=names(2:end,1);
    variablestrings=names(1,2:end);

    % identify the location of the last period in the dataset
    dataendlocation=size(datestrings,1);

    % identify the position of the start period (the easy part)
    % if the start period has been selected as the first period after the sample end, identifies it directly
    if Fendsmpl==1
        Fstartlocation=find(strcmp(datestrings,enddate))+1;
        % if the start period has not been selected as the first period after the sample end, it must be within the sample: look for it
    elseif Fendsmpl==0
        Fstartlocation=find(strcmp(datestrings,Fstartdate));
        % if the start date is not recognised, return an error message
        if isempty(Fstartlocation)==1
            error('bear:gensample_mf', 'Unknown start date for the forecasts. Select a date within a sample, or select "Start forecasts after last sample period"');
        end
    end

    % identify the position of the final forecast period (the hard part)
    % this period can be in or outside the sample, depending on the user's choice

    % if the data is yearly
    if frequency==1
        Fendlocation=str2num(Fenddate(1,1:end-1))-str2num(datestrings{1,1}(1,1:end-1))+1;

        % if the data is quarterly
    elseif frequency==2
        % first identify the year and quarter of the initial date in the whole data set (not just the sample)
        datastartyear=str2num(datestrings{1,1}(1,1:4));
        datastartquarter=str2num(datestrings{1,1}(1,6));
        % convert this date into quarters only
        datastart=datastartyear*4+datastartquarter;
        % then identify the year and quarter of the final forecast date
        forecastendyear=str2num(Fenddate(1,1:4));
        forecastendquarter=str2num(Fenddate(1,6));
        % convert this date into quarters only
        forecastend=forecastendyear*4+forecastendquarter;
        % finally, compute the number of periods that separate the two dates
        Fendlocation=forecastend-datastart+1;

        % if the data is monthly
    elseif frequency==3
        % first identify the year and month of the initial date in the whole data set (not just the sample)
        temp=datestrings{1,1};
        datastartyear=str2num(temp(1,1:4));
        temp(1,5)=' ';
        [~,datastartmonth]=strtok(temp);
        % convert this date into months only
        datastart=datastartyear*12+str2num(datastartmonth);
        % then identify the year and month of the final forecast date
        temp=Fenddate;
        forecastendyear=str2num(temp(1,1:4));
        temp(1,5)=' ';
        [~,forecastendmonth]=strtok(temp);
        % convert this date into months only
        forecastend=forecastendyear*12+str2num(forecastendmonth);
        Fendlocation=forecastend-datastart+1;

        % if the data is weekly
    elseif frequency==4
        % then identify the year and week corresponding to this final period
        temp=datestrings{end,1};
        dataendyear=str2num(temp(1,1:4));
        temp(1,5)=' ';
        [~,dataendweek]=strtok(temp);
        dataendweek=str2num(dataendweek);
        % identify the year and week corresponding to the end of the forecast period
        temp=Fenddate;
        Fendyear=str2num(temp(1,1:4));
        temp(1,5)=' ';
        [~,Fendweek]=strtok(temp);
        Fendweek=str2num(Fendweek);
        % determine whether the forecast period ends within the data set, or after the end of the data set
        if Fendyear<dataendyear
            insmpl=1;
        elseif Fendyear==dataendyear && Fendweek<=dataendweek
            insmpl=1;
        else
            insmpl=0;
        end
        % if the forecast end period lies within the dataset, simply detect its location
        if insmpl==1
            Fendlocation=find(strcmp(Fenddate,datestrings));
            % if it is outside the data set, complete until the forecast end date is reached (assuming 52 weeks per year)
        elseif insmpl==0
            % Consider two cases separately
            % first case: if the end year of the forecast is the same as the end year of the dataset
            % in this case, simply complete the missing periods
            if dataendyear==Fendyear
                complement=Fendweek-dataendweek;
                Fendlocation=dataendlocation+complement;
                % if the end year of the forecasts is posterior to the last year of the data
            elseif dataendyear<Fendyear
                % complete the first year (the one shared that ends the data set)
                complement=52-dataendweek;
                % complete the following years before the last one (if any)
                for ii=dataendyear+1:Fendyear-1
                    complement=complement+52;
                end
                % complete the final forecast year
                complement=complement+Fendweek;
                Fendlocation=dataendlocation+complement;
            end
        end

        % if the data is daily
    elseif frequency==5
        % then identify the year and day corresponding to this final period
        temp=datestrings{end,1};
        dataendyear=str2num(temp(1,1:4));
        temp(1,5)=' ';
        [~,dataendday]=strtok(temp);
        dataendday=str2num(dataendday);
        % identify the year and day corresponding to the end of the forecast period
        temp=Fenddate;
        Fendyear=str2num(temp(1,1:4));
        temp(1,5)=' ';
        [~,Fendday]=strtok(temp);
        Fendday=str2num(Fendday);
        % determine whether the forecast period ends within the data set, or after the end of the data set
        if Fendyear<dataendyear
            insmpl=1;
        elseif Fendyear==dataendyear && Fendday<=dataendday
            insmpl=1;
        else
            insmpl=0;
        end
        % if the forecast end period lies within the dataset, simply detect its location
        if insmpl==1
            Fendlocation=find(strcmp(Fenddate,datestrings));
            % if it is outside the data set, complete until the forecast end date is reached (assuming 261 opening days per year)
        elseif insmpl==0
            % Consider two cases separately
            % first case: if the end year of the forecast is the same as the end year of the dataset
            % in this case, simply complete the missing periods
            if dataendyear==Fendyear
                complement=Fendday-dataendday;
                Fendlocation=dataendlocation+complement;
                % if the end year of the forecasts is posterior to the last year of the data
            elseif dataendyear<Fendyear
                % complete the first year (the one shared that ends the data set)
                complement=261-dataendday;
                % complete the following years before the last one (if any)
                for ii=dataendyear+1:Fendyear-1
                    complement=complement+261;
                end
                % complete the final forecast year
                complement=complement+Fendday;
                Fendlocation=dataendlocation+complement;
            end
        end

        % finally, if the data is undated
    elseif frequency==6
        Fendlocation=str2num(Fenddate(1,1:end-1))-str2num(datestrings{1,1}(1,1:end-1))+1;
    end

    % from this, conclude the total number of forecast periods
    Fperiods=Fendlocation-Fstartlocation+1;

    if Fperiods<0
        error('bear:gensample_mf','The forecast start date needs to be prior to the forecast end date');
    end

    % Phase 4: generation of the forecast-specific matrices

    % now create the matrix of endogenous variables for the pre-forecast period
    % it is simply the concatenation of the vectors of each endogenous variables, over the selected sample dates
    data_endo_a=[];
    % loop over endogenous variables
    for ii=1:numendo
        data_endo_a=[data_endo_a data(1:Fstartlocation-1,endolocation(ii,1))];
    end
    % also, create the matrix of exogenous variables for the pre-forecast period
    data_exo_a=[];
    for ii=1:numexo
        data_exo_a=[data_exo_a data(1:Fstartlocation-1,exolocation(ii,1))];
    end

    % create the matrix of endogenous variables for the period common to actual data and forecasts (for forecast evaluation)
    % first, check that there are such common periods: it is the case if the beginning of the forecast period is anterior to the end of the dataset
    if Fstartlocation<=dataendlocation
        % return a scalar value to indicate that forecast evaluation is possible
        Fcomp=1;
        % compute the number of common periods
        % if the forecast period ends before the end of the data set, the common periods end with the end of the forecasts
        if Fendlocation<=dataendlocation
            Fcperiods=Fperiods;
            % record the end date of the common periods
            Fcenddate=Fenddate;
            % if the forecast period ends later than the data set, the common periods end at the end of the data set
        elseif Fendlocation>dataendlocation
            Fcperiods=dataendlocation-Fstartlocation+1;
            % record the end date of the common periods
            Fcenddate=datestrings{end,1};
        end

        % create a matrix of endogenous data for the common periods
        data_endo_c=[];
        for ii=1:numendo
            data_endo_c=[data_endo_c data(Fstartlocation:min(dataendlocation,Fendlocation),endolocation(ii,1))];
        end

        % create a lagged matrix of endogenous data prior to the common periods
        % the number of values is equal to "lags"; this will be used for computation of the log predictive score
        data_endo_c_lags=[];
        for ii=1:numendo
            data_endo_c_lags=[data_endo_c_lags data(Fstartlocation-lags:Fstartlocation-1,endolocation(ii,1))];
        end

        % create a matrix of exogenous data for the common periods
        data_exo_c=[];
        for ii=1:numexo
            data_exo_c=[data_exo_c data(Fstartlocation:min(dataendlocation,Fendlocation),exolocation(ii,1))];
        end

        % create a lagged matrix of exogenous data prior to the common periods
        % the number of values is equal to "lags"; this will be used for computation of the log predictive score
        data_exo_c_lags=[];
        for ii=1:numexo
            data_exo_c_lags=[data_exo_c_lags data(Fstartlocation-lags:Fstartlocation-1,exolocation(ii,1))];
        end
        % if there are no common periods, return a scalar value to indicate that forecast evaluation is not possible
    else
        Fcomp=0;
        Fcperiods=0;
        data_exo_c=[];
        data_endo_c=[];
        Fcenddate=[];
        data_endo_c_lags=[];
        data_exo_c_lags=[];
    end

    % now create the matrix data_exo_p
    % two possible cases

    % if there are no exogenous variables, simply create an empty matrix
    if isempty(exo)
        data_exo_p=[];

        % if there are exogenous variables, load from excel
    else
        % load the data from Excel
        [num txt strngs]=xlsread(pref.excelFile,'pred exo');

        % obtain the row location of the forecast start date
        [Fstartlocation,~]=find(strcmp(strngs,Fstartdate));
        % check that the start date for the forecast appears in the sheet; if not, return an error
        if isempty(Fstartlocation)
            message=['Error: a forecast application is selected for a model that uses exogenous variables. Hence, predicted exogenous values should be supplied over the forecast periods. Yet the start date for forecasts (' Fstartdate ') cannot be found on the ''pred exo'' sheet of the Excel data file. Please verify that this sheet is properly filled, and remember that dates are case-sensitive.'];
            msgbox(message);
            error('programme termination: data error');
        end
        % obtain the row location of the forecast end date
        [Fendlocation,~]=find(strcmp(strngs,Fenddate));
        % check that the end date for the forecast appears in the sheet; if not, return an error
        if isempty(Fendlocation)
            message=['Error: a forecast application is selected for a model that uses exogenous variables. Hence, predicted exogenous values should be supplied over the forecast periods. Yet the end date for forecasts (' Fenddate ') cannot be found on the ''pred exo'' sheet of the Excel data file. Please verify that this sheet is properly filled, and remember that dates are case-sensitive.'];
            msgbox(message);
            error('programme termination: data error');
        end

        % identify the strings for the exogenous variables
        % loop over exogenous
        for ii=1:numexo
            % try to find a column match for exogenous variable ii
            [~,location]=find(strcmp(strngs,exo{ii,1}));
            % if no match is found, return an error
            if isempty(location)
                message=['Error: a forecast application is selected for a model that uses exogenous variables. Hence, predicted exogenous values should be supplied over the forecast periods. Yet the exogenous variable ''' exo{ii,1} ''' cannot be found on the ''pred exo'' sheet of the Excel data file. Please verify that this sheet is properly filled, and remember that variable names are case-sensitive.'];
                msgbox(message);
                error('programme termination: data error');
                % else, record the value
            else
                pexolocation(ii,1)=location;
            end
        end

        % if everything was fine, reconstitute the matrix data_exo_p
        % initiate
        data_exo_p=[];
        % loop over exogenous variables
        for ii=1:numexo
            % initiate the predicted values for exogenous variable ii
            predexo=[];
            % loop over forecast periods
            for jj=1:Fperiods
                temp=strngs{Fstartlocation+jj-1,pexolocation(ii,1)};
                % if this entry is empty or NaN, return an error
                if (isempty(temp) || (temp<=inf)==0)
                    message=['Error: the predicted value for exogenous variable ' exo{ii,1} ' at forecast period ' strngs{Fstartlocation+jj,1} ' (and possibly other entries) is either empty or NaN. Please verify that the ''pred exo'' sheet of the Excel data file is properly filled.'];
                    msgbox(message);
                    error('programme termination: data error');
                    % if this entry is a number, record it
                else
                    predexo=[predexo;temp];
                end
            end
            % concatenate
            data_exo_p=[data_exo_p predexo];
        end

        % also, record the exogenous values on Excel
        % replace NaN entries by blanks
        strngs(cellfun(@(x) any(isnan(x)),strngs))={[]};
        % then save on Excel
        if pref.results==1
            warning off MATLAB:xlswrite:AddSheet
            xlswrite(fullfile(pref.results_path, [pref.results_sub '.xlsx']),strngs,'pred exo','A1');
            warning on MATLAB:xlswrite:AddSheet
        end
    end

end


function varargout = process_aec1( varargin )
% PROCESS_PLV1: Compute amplitude envelope correlation between one signal and all the others, in one file.

% @=============================================================================
% This function is part of the Brainstorm software:
% https://neuroimage.usc.edu/brainstorm
%
% Copyright (c) University of Southern California & McGill University
% This software is distributed under the terms of the GNU General Public License
% as published by the Free Software Foundation. Further details on the GPLv3
% license can be found at http://www.gnu.org/copyleft/gpl.html.
%
% FOR RESEARCH PURPOSES ONLY. THE SOFTWARE IS PROVIDED "AS IS," AND THE
% UNIVERSITY OF SOUTHERN CALIFORNIA AND ITS COLLABORATORS DO NOT MAKE ANY
% WARRANTY, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO WARRANTIES OF
% MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, NOR DO THEY ASSUME ANY
% LIABILITY OR RESPONSIBILITY FOR THE USE OF THIS SOFTWARE.
%
% For more information type "brainstorm license" at command prompt.
% =============================================================================@
%
% Authors: Francois Tadel, 2012-2014
%          Peter Donhauser, 2017

eval(macro_method);
end

%% ===== GET DESCRIPTION =====
function sProcess = GetDescription() %#ok<DEFNU>
    % Description the process
    sProcess.Comment     = 'Amplitude Envelope Correlation 1xN';
    sProcess.Category    = 'Custom';
    sProcess.SubGroup    = 'Connectivity';
    sProcess.Index       = 680;
    sProcess.Description = 'https://neuroimage.usc.edu/brainstorm/Tutorials/Connectivity';
    % Definition of the input accepted by this process
    sProcess.InputTypes  = {'data',     'results',  'matrix'};
    sProcess.OutputTypes = {'timefreq', 'timefreq', 'timefreq'};
    sProcess.nInputs     = 1;
    sProcess.nMinFiles   = 1;

    % === CONNECT INPUT
    sProcess = process_corr1n('DefineConnectOptions', sProcess, 0);
    % === FREQ BANDS
    sProcess.options.freqbands.Comment = 'Frequency bands for the Hilbert transform:';
    sProcess.options.freqbands.Type    = 'groupbands';
    sProcess.options.freqbands.Value   = bst_get('DefaultFreqBands');
    % === Orthogonalize pairs of signals
    sProcess.options.isorth.Comment = 'Orthogonalize signal pairs before envelope computation';
    sProcess.options.isorth.Type    = 'checkbox';
    sProcess.options.isorth.Value   = 0;
    % === OUTPUT MODE
    sProcess.options.outputmode.Comment = {'Save individual results (one file per input file)', 'Concatenate input files before processing (one file)', 'Save average connectivity matrix (one file)'};
    sProcess.options.outputmode.Type    = 'radio';
    sProcess.options.outputmode.Value   = 1;
    sProcess.options.outputmode.Group   = 'output';
end

%% ===== FORMAT COMMENT =====
function Comment = FormatComment(sProcess) %#ok<DEFNU>
    Comment = sProcess.Comment;
end

%% ===== RUN =====
function OutputFiles = Run(sProcess, sInputA) %#ok<DEFNU>
    % Input options
    OPTIONS = process_corr1n('GetConnectOptions', sProcess, sInputA);
    if isempty(OPTIONS)
        OutputFiles = {};
        return
    end

    OPTIONS.Method = 'aec';
    % Hilbert and frequency bands options
    OPTIONS.Freqs = sProcess.options.freqbands.Value;
    OPTIONS.isOrth = sProcess.options.isorth.Value;

    % Compute metric
    OutputFiles = bst_connectivity({sInputA.FileName}, {sInputA.FileName}, OPTIONS);
end


function cooccurM = calcCooccur(quantizedM, offsetsM, nL, cooccurType)
% function cooccurM = calcCooccur(quantizedM, offsetsM, nL, cooccurType)
%
% This function calculates the cooccurrence matrix for the passed quantized
% image.
%
% INPUTS:
%       quantizedM: quantized 3d matrix obtained, for example, by
%       imquantize_cerr.m
%       offsetsM: Offsets for directionality/neighbors, obtained by
%       getOffsets.m
%       nL: Number of gray levels.
%       cooccurType: flag, 1 or 2.
%                   1: returns a single cooccurrence matrix by combining
%                   contributions from all offsets into one cooccurrence
%                   matrix.
%                   2: returns cooccurM with each column containing
%                   cooccurrence matrix for the row of offsetsM.
% OUTPUT:
%       cooccurM: cooccurrence matrix of size (nL*nL) x 1 for cooccurType =
%       1, or (nL*nL) x size(offsetsM,1) for cooccurType = 2.
%       cooccurM can be passed to cooccurToScalarFeatures.m to get texture
%       features.
%
%
% APA, 05/23/2016

% Default to building cooccurrence by combining all offsets
if ~exist('cooccurType','var')
    cooccurType = 1;
end

% Apply pading of 1 row/col/slc. This assumes offsets are 1. Need to
% parameterize this in case of offsets other than 2. Rarely used for
% medical images.
numColsPad = 1;
numRowsPad = 1;
numSlcsPad = 1;

% Get number of voxels per slice
[numRows, numCols, numSlices] = size(quantizedM);

% Pad quantizedM
if exist('padarray.m','file')
    q = padarray(quantizedM,[numRowsPad numColsPad numSlcsPad],NaN,'both');
else
    q = padarray_oct(quantizedM,[numRowsPad numColsPad numSlcsPad],NaN,'both');
end

% Add level for NaN
lq = nL + 1;
q(isnan(q)) = lq;

q = uint16(q); % q is the quantized image

% Number of offsets
numOffsets = size(offsetsM,1);

% Indices of last level to filter out
nanIndV = false([lq*lq,1]);
nanIndV([lq:lq:lq*lq-lq, lq*lq-lq:lq*lq]) = true;

% Build linear indices column/row-wise for Symmetry
indRowV = zeros(1,lq*lq);
for i=1:lq
    indRowV((i-1)*lq+1:(i-1)*lq+lq) = i:lq:lq*lq;
end

tic
% Initialize cooccurrence matrix (vectorized for speed)
if cooccurType == 1
    %cooccurM = zeros(lq*lq,1,'single');
    cooccurM = sparse(lq*lq,1);
else
    %cooccurM = zeros(lq*lq,numOffsets,'single');
    cooccurM = sparse(lq*lq,numOffsets);
end
for off = 1:numOffsets

    offset = offsetsM(off,:);
    slc1M = q(numRowsPad+(1:numRows),numColsPad+(1:numCols),...
        numSlcsPad+(1:numSlices));
    slc2M = circshift(q,offset);
    slc2M = slc2M(numRowsPad+(1:numRows),numColsPad+(1:numCols),numSlcsPad+(1:numSlices))...
        + (slc1M-1)*lq;
    if cooccurType == 1
        cooccurM = cooccurM + accumarray(slc2M(:),1, [lq*lq,1]);
    else
        cooccurM(:,off) = accumarray(slc2M(:),1, [lq*lq,1]);
    end

end

cooccurM = cooccurM + cooccurM(indRowV,:); % for symmetry
cooccurM(nanIndV,:) = [];
cooccurM = bsxfun(@rdivide,cooccurM,sum(cooccurM,1)+eps);

return;


function im = displaySegments(smap, segim, grayim, dodisp, varargin)
% im = displaySegments(smap, segim, grayim, dodisp, options)
% options: 'color': [r g b]
%          'width': line width

if ~exist('dodisp', 'var')
    dodisp = 1;
end

dispColor = [1 0 0];
scale = round(max(size(grayim))/200);
for k = 1:2:numel(varargin)
    if strcmp(varargin{k}, 'color')
        dispColor = varargin{k+1};
    elseif strcmp(varargin{k}, 'width')
        scale = varargin{k+1};
    end
end

 [h,s,v] = rgb2hsv(label2rgb(smap(segim)));
%
 imagesc(hsv2rgb(h,s,im2double(grayim)))

[gx, gy] = gradient(double(smap(segim)));
g = gx.^2 + gy.^2;

g = conv2(g, ones(scale), 'same');
edgepix = find(g>0);

npix = prod(size(segim));

im = repmat(grayim, [1 1 3]);
%im = 0.5*im2double(label2rgb(smap(segim))) + 0.5*im;
% size(im)
% size(segim)
% max(edgepix)
for b = 1:3
    im((b-1)*npix+edgepix) = dispColor(b);
end

if dodisp
    imagesc(im), axis image
end


function test_bug2685

% MEM 4gb
% WALLTIME 00:10:00
% DEPENDENCY ft_scalpcurrentdensity ft_fetch_sens

%% load data
load(dccnpath('/home/common/matlab/fieldtrip/data/test/bug2685/bug2685.mat'));

%% scalp current density
cfg                 = [];
cfg.method          = 'spline';
cfg.elec            = ERP_standard.elec;
%cfg.elec            = % Do we have electrode positions?

scd   = ft_scalpcurrentdensity(cfg, ERP_standard);
end


%% Optical flow evaluation demo
%
% Computes flow field between two images using various methods and display it
% (deepflow, simpleflow, sparsetodenseflow, Farneback, TV-L1).
%
% Sources:
%
% * <https://github.com/opencv/opencv_contrib/blob/3.1.0/modules/optflow/samples/optical_flow_evaluation.cpp>
%

%% Input images
% a pair of 8-bit color images
im1 = imread(fullfile(mexopencv.root(),'test','RubberWhale1.png'));
im2 = imread(fullfile(mexopencv.root(),'test','RubberWhale2.png'));
assert(isequal(size(im1), size(im2)), ...
    'Dimension mismatch between input images');
if ~mexopencv.isOctave() && mexopencv.require('images')
    %HACK: IMSHOWPAIR not implemented in Octave
    imshowpair(im1, im2);
end

%% Compare the different methods
algorithms = {'farneback', 'simpleflow', 'tvl1', 'deepflow', ...
    'sparsetodenseflow', 'pcaflow', ...
    'DISflow_ultrafast', 'DISflow_fast', 'DISflow_medium', 'variational'};
for i=1:numel(algorithms)
    % prepare images
    if any(strcmp(algorithms{i}, {'farneback', 'tvl1', 'deepflow', ...
            'DISflow_ultrafast', 'DISflow_fast', 'DISflow_medium', ...
            'variational'})) && size(im1,3)==3
        % 1-channel images are expected
        img1 = cv.cvtColor(im1, 'RGB2GRAY');
        img2 = cv.cvtColor(im2, 'RGB2GRAY');
    elseif strcmp(algorithms{i}, 'simpleflow') && size(im1,3)==1
        % 3-channel images expected
        img1 = cv.cvtColor(im1, 'GRAY2RGB');
        img2 = cv.cvtColor(im2, 'GRAY2RGB');
    else
        % sparsetodenseflow/pcaflow handle both 1- or 3-channels
        img1 = im1;
        img2 = im2;
    end

    % compute flow field between img1 and img2 using current method
    tic
    switch lower(algorithms{i})
        case 'farneback'
            %{
            obj = cv.FarnebackOpticalFlow();
            flow = obj.calc(img1, img2);
            %}
            flow = cv.calcOpticalFlowFarneback(img1, img2);
        case 'simpleflow'
            flow = cv.calcOpticalFlowSF(img1, img2);
        case 'deepflow'
            flow = cv.calcOpticalFlowDF(img1, img2);
        case 'sparsetodenseflow'
            flow = cv.calcOpticalFlowSparseToDense(img1, img2);
        case 'tvl1'
            obj = cv.DualTVL1OpticalFlow();
            flow = obj.calc(img1, img2);
        case 'pcaflow'
            %obj = cv.OpticalFlowPCAFlow(prior);  % path to a prior file for PCAFlow
            obj = cv.OpticalFlowPCAFlow();
            flow = obj.calc(img1, img2);
        case 'disflow_ultrafast'
            obj = cv.DISOpticalFlow('Preset','UltraFast');
            flow = obj.calc(img1, img2);
        case 'disflow_fast'
            obj = cv.DISOpticalFlow('Preset','Fast');
            flow = obj.calc(img1, img2);
        case 'disflow_medium'
            obj = cv.DISOpticalFlow('Preset','Medium');
            flow = obj.calc(img1, img2);
        case 'variational'
            obj = cv.VariationalRefinement();
            flow = obj.calc(img1, img2);
    end
    fprintf('%18s: ', algorithms{i});
    toc

    % display the flow
    if true
        [mag, ang] = cv.cartToPolar(flow(:,:,1), flow(:,:,2), 'Degrees',true);
    else
        [ang, mag] = cart2pol(flow(:,:,1), flow(:,:,2));
        if mexopencv.isOctave()
            %HACK: RAD2DEG not implemented in Octave
            ang = (ang + pi) * (180 / pi);
        else
            ang = rad2deg(ang + pi);
        end
    end
    mag = cv.normalize(mag, 'Alpha',0, 'Beta',1, 'NormType','MinMax');
    hsv = cat(3, ang, ones(size(ang),class(ang)), mag); % H=[0,360], S,V=[0,1]
    rgb = cv.cvtColor(hsv, 'HSV2RGB');                  % R,G,B=[0,1]
    figure, imshow(rgb)
    title(sprintf('Computed flow: %s',algorithms{i}), 'Interpreter','none')
end


function a = gt( x, y )

%Disciplined convex programming information for GT (>):
%   The right-hand side of a less-than constraint must be convex. The
%   left-hand side must be concave. Of course, real constant and affine
%   expressions are both convex and concave and can be used on either
%   side as well.
%
%Disciplined geometric programming information for GT (>):
%   The right-hand side of a less-than constraint must be log-convex---
%   including positive constants, monomials, posynomials, generalized
%   posynomials, and products thereof. The left-hand side must be log-
%   concave---including positive constants, monomials, reciprocals of
%   log-convex expressions, and products thereof.
%
%Note that CVX does not distinguish between strict greater-than (>) and
%greater-than-or-equal (>=) constraints; they are treated identically.
%Feasible interior-point solvers tend to return points which satisfy
%strict inequality, but not all solvers do.

cvx_warn_strict( '>' );
evalin( 'caller', 'cvx_verify' );
b = cvx_pushcnstr( x, y, '>' );
if nargout, a = b; end

% Copyright 2005-2014 CVX Research, Inc.
% See the file LICENSE.txt for full copyright information.
% The command 'cvx_where' will show where this file is located.


function [dataIdx,pilotIdx] = helperSubcarrierIndices(cfg,fieldType)
% helperSubcarrierIndices Return the subcarrier indices within the FFT size
%
%   [DATAIDX,PILOTIDX] = helperSubcarrierIndices(CFGFORMAT,FIELDTYPE)
%   returns the data and pilot indices within the FFT size for the
%   specified format configuration object, CFGFORMAT, and specified
%   FIELDTYPE.
%
%   DATAIDX is a column vector containing the indices of data subcarriers.
%
%   PILOTIDX is a column vector containing the indices of pilot
%   subcarriers.
%
%   CFGFORMAT is the format configuration object of type wlanVHTConfig,
%   wlanHTConfig, or wlanNonHTConfig, which specifies the parameters for
%   the VHT, HT-Mixed, and Non-HT formats, respectively.
%
%   FIELDTYPE is a string specifying the format of the field and must be
%   one of 'Legacy','HT' or 'VHT'.
%
%   [DATAIDX,PILOTIDX] = helperSubcarrierIndices(CHANBW) returns indices
%   for the specified channel bandwidth. CHANBW must be one of 'CBW5',
%   'CBW10', 'CBW20', 'CBW40', 'CBW80' or 'CBW160'.
%
%   Example: Return indices for a VHT format configuration.
%
%   cfgVHT = wlanVHTConfig;
%   [dataIdx,pilotIdx] = helperSubcarrierIndices(cfgVHT,'VHT')

% Copyright 2015 The MathWorks, Inc.

%#codegen

if ischar(cfg)
    coder.internal.errorIf( ~any(strcmp(cfg, {'CBW5', 'CBW10', ...
            'CBW20', 'CBW40', 'CBW80', 'CBW160'})), ...
        'wlan:helperSubcarrierIndices:InvalidChBandwidth');

    chanBW = cfg;
else
    validateattributes(cfg,{'wlanVHTConfig','wlanHTConfig','wlanNonHTConfig'}, ...
        {'scalar'},mfilename,'format configuration object');

    coder.internal.errorIf(isa(cfg,'wlanNonHTConfig') && ...
        ~strcmp(cfg.Modulation,'OFDM'), ...
            'wlan:helperSubcarrierIndices:InvalidNonHTModulation')

    chanBW = cfg.ChannelBandwidth;
end

FFTLen = helperFFTLength(chanBW);

fieldType = validatestring(fieldType,{'Legacy','HT','VHT'}, ...
    mfilename,'Field type');
if strcmp(fieldType,'Legacy')
    switch chanBW
      case 'CBW40'
        Num20MHzChan = 2;
      case {'CBW80', 'CBW80+80'}
        Num20MHzChan = 4;
      case 'CBW160'
        Num20MHzChan = 8;
      otherwise   % For 'CBW20', 'CBW10', 'CBW5'
        Num20MHzChan = 1;
    end

    numGuardBands = [6; 5];
    pilotIdx20MHz =  [12; 26; 40; 54];

    % Get non-data subcarrier indices per 20MHz channel bandwidth
    nonDataIdxPerGroup = [(1:numGuardBands(1))'; 33; ...
        (64-numGuardBands(2)+1:64)'; pilotIdx20MHz];
    % Get non-data subcarrier indices for the whole bandwidth
    nonDataIdxAll = bsxfun(@plus, nonDataIdxPerGroup, 64*(0:Num20MHzChan-1));
    dataIdx  = setdiff((1:FFTLen)', sort(nonDataIdxAll(:)));
    pilotIdx = reshape(bsxfun(@plus, pilotIdx20MHz, 64*(0:Num20MHzChan-1)), [], 1);
else % 'HT' & 'VHT'
    switch chanBW
      case 'CBW40'
        numGuardBands = [6; 5];
        customNullIdx = [-1; 1];
        pilotIdx = [-53; -25; -11; 11; 25; 53];
      case 'CBW80'
        numGuardBands = [6; 5];
        customNullIdx = [-1; 1];
        pilotIdx = [-103; -75; -39; -11; 11; 39; 75; 103];
      case 'CBW80+80' % Merge with 80MHz case, if same. Separate for now
        numGuardBands = [6; 5];
        customNullIdx = [-1; 1];
        pilotIdx = [-103; -75; -39; -11; 11; 39; 75; 103];
      case 'CBW160'
        numGuardBands = [6; 5];
        customNullIdx = [(-129:-127)'; (-5:-1)'; (1:5)'; (127:129)'];
        pilotIdx = [-231; -203; -167; -139; -117; -89; -53; -25; 25; 53; 89; 117; 139; 167; 203; 231];
      otherwise  % CBW20
        numGuardBands = [4; 3];
        customNullIdx = [];
        pilotIdx = [-21; -7; 7; 21];
    end

    pilotIdx = pilotIdx + FFTLen/2 + 1; % Convert to 1-based indexing
    customNullIdx = customNullIdx + FFTLen/2 + 1; % Convert to 1-based indexing
    % Get non-data subcarrier indices for the whole bandwidth
    nonDataIdx = [(1:numGuardBands(1))'; FFTLen/2+1; ...
        (FFTLen-numGuardBands(2)+1:FFTLen)'; pilotIdx; customNullIdx];
    dataIdx = setdiff((1:FFTLen)', sort(nonDataIdx));
end
end


function elem=delendelem(elem,mask)
%
% elem=delendelem(elem,mask)
%
% delete elements whose nodes are all edge nodes
%
% author: Qianqian Fang (fangq<at> nmr.mgh.harvard.edu)
% date: 2007/11/24
%
% input/output:
%      elem: input/output, surface/volumetric element list
%      mask: of length of node number, =0 for internal nodes, =1 for edge nodes
%
% -- this function is part of iso2mesh toolbox (http://iso2mesh.sf.net)
%

badidx=sum(mask(elem)');
elem(find(badidx==size(elem,2)),:)=[];


function [flowField, int_dF, errorData, errorReg, poincare, interval] = ...
  bst_opticalflow(F, FV, Time, tStart, tEnd, hornSchunck)
% BST_OPTICALFLOW: Computes the optical flow of MEG/EEG activities on the cortical surface.
%
% USAGE:  [flow, dEnergy, int_dI, errorData, errorReg, index] = ...
%   bst_opticalflow(dataFile, channelFile, tStart, tEnd, hornSchunck)
%
% @=============================================================================
% This function is part of the Brainstorm software:
% https://neuroimage.usc.edu/brainstorm
%
% Copyright (c) University of Southern California & McGill University
% This software is distributed under the terms of the GNU General Public License
% as published by the Free Software Foundation. Further details on the GPLv3
% license can be found at http://www.gnu.org/copyleft/gpl.html.
%
% FOR RESEARCH PURPOSES ONLY. THE SOFTWARE IS PROVIDED "AS IS," AND THE
% UNIVERSITY OF SOUTHERN CALIFORNIA AND ITS COLLABORATORS DO NOT MAKE ANY
% WARRANTY, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO WARRANTIES OF
% MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, NOR DO THEY ASSUME ANY
% LIABILITY OR RESPONSIBILITY FOR THE USE OF THIS SOFTWARE.
%
% For more information type "brainstorm license" at command prompt.
% =============================================================================@
%
% Authors: Julien Lef�vre, 2006-2010
%          Syed Ashrafulla, 2010

% INPUTS
%   F                 - Reconstructed sources
%   FV                - Tesselation for calculating spatial derivatives
%   Time              - Time points of sampling of recordings
%   tStart            - First time point for optical flow analysis
%   tEnd              - Last time point for optical flow analysis
%   hornSchunck       - Parameter to tune optical flow calculation
%   segment           - Flag to split data into stable and transient states
% OUTPUTS
%   flowField         - Optical flow field
%                       dimension (# of vertices) X length(tStart:tEnd)
%   int_dF            - Constant term in variational formulation
%   errorData         - Error in fit to data
%   errorReg          - Energy in regularization
%   poincare          - Poincar� index

%/---Script Authors---------------------\
%|                                      |
%|   *** J.Lef�vre, PhD                 |
%|   julien.lefevre@chups.jussieu.fr    |
%|                                      |
%\--------------------------------------/

dimension = 3; % 2 for projected maps
Faces = FV.Faces; Vertices = FV.Vertices; VertNormals = FV.VertNormals;
nVertices = size(Vertices,1); % VertNormals = FV.VertNormals';
nFaces = size(Faces,1);
tStartIndex = find(Time < tStart-eps, 1, 'last')+1; % Index of first time point for flow calculation
if isempty(tStartIndex)
    [tmp, tStartIndex] = min(Time);
    tStartIndex = tStartIndex + 1;
end
tEndIndex = find(Time < tEnd-eps, 1, 'last')+1; % Index of last time point for flow calculation
if isempty(tEndIndex)
    [tmp, tEndIndex] = max(Time);
    tEndIndex = tEndIndex + 1;
end
if tEndIndex > size(Time, 2)
    tEndIndex = size(Time, 2);
end
interval = Time(tStartIndex:tEndIndex); % Interval of flow calculations
intervalLength = tEndIndex-tStartIndex+1; % Length of time interval for calculations
M = max(max(abs(F(:,tStartIndex-1:tEndIndex)))); F = F/M; % Scale values to avoid precision error
flowField = zeros(nVertices, dimension, intervalLength);
dEnergy = zeros(1, intervalLength);
errorData = zeros(1, intervalLength);
errorReg = zeros(1, intervalLength);
int_dF = zeros(1, intervalLength);

[regularizerOld, gradientBasis, tangentPlaneBasisCell, tangentPlaneBasis, ...
  triangleAreas, FaceNormals, sparseIndex1, sparseIndex2]= ...
  regularizing_matrix(Faces, Vertices, VertNormals, dimension); % 2 for projected maps
regularizer = hornSchunck * regularizerOld;

% Projection of flow on triangle (for Poincar� index)
Pn = zeros(3,3,nFaces);
for facesIdx = 1:nFaces
    Pn(:,:,facesIdx) = eye(3) - ...
      (FaceNormals(facesIdx,:)'*FaceNormals(facesIdx,:));
end
poincare = zeros(nFaces, intervalLength);

% Optical flow calculations
bst_progress('start', 'Optical Flow', ...
  'Computing optical flow ...', tStartIndex-1, tEndIndex);
for timePoint = tStartIndex:tEndIndex
  timeIdx = timePoint-tStartIndex+1;

  % Solve for flow
  dF = F(:,timePoint)-F(:,timePoint-1);
  [dataFit, B] = data_fit_matrix(Faces, nVertices, ...
    gradientBasis, triangleAreas, tangentPlaneBasisCell, ...
    F(:,timePoint), dF, dimension, ...
    sparseIndex1, sparseIndex2);
  X = (dataFit + regularizer) \ B;

  % Save flows correctly (for 3D surface, have to project back to 3D)
  if dimension == 3 % X coordinates are in tangent space
      flowField(:,:,timeIdx) = ...
        repmat(X(1:end/2), [1,3]) .* tangentPlaneBasis(:,:,1) ...
        + repmat(X(end/2+1:end), [1,3]) .* tangentPlaneBasis(:,:,2);
  else % X coordinates are in R^2
      flowField(:,:,timeIdx) = [X zeros(nVertices,1)];
  end

  errorData(timeIdx)= X'*dataFit*X - 2*B'*X; % Data fit error
  errorReg(timeIdx)= X'*regularizer*X; % Regularization error

  % Variational formulation constant
  dF12=(dF(Faces(:,1),:)+dF(Faces(:,2))).^2;
  dF23=(dF(Faces(:,2),:)+dF(Faces(:,3))).^2;
  dF13=(dF(Faces(:,1),:)+dF(Faces(:,3))).^2;
  int_dF(timeIdx) = sum(triangleAreas.*(dF12+dF23+dF13)) / 24;

  % Precompute flowfield with faces to save time in the loop.
  FacesFlowField = reshape(flowField(Faces', :, timeIdx)', [3,3,nFaces]);

  % Poincare Index of each triangle
  for facesIdx=1:nFaces
      poincare(facesIdx,timeIdx) = ... % projection of flowField(f,:,t) on triangle f
          poincare_index(Pn(:,:,facesIdx) * FacesFlowField(:,:,facesIdx));
  end

  % Displacement energy
  v12 = sum((flowField(Faces(:,1),:,timeIdx)+flowField(Faces(:,2),:,timeIdx)).^2,2) / 4;
  v23 = sum((flowField(Faces(:,2),:,timeIdx)+flowField(Faces(:,3),:,timeIdx)).^2,2) / 4;
  v13 = sum((flowField(Faces(:,1),:,timeIdx)+flowField(Faces(:,3),:,timeIdx)).^2,2) / 4;
  dEnergy(timeIdx) = sum(triangleAreas.*(v12+v23+v13));

  bst_progress('inc', 1); % Update progress bar
end

bst_progress('stop');

end

% =========================================================================
% =====  EXTERNAL FUNCTIONS ===============================================
% =========================================================================

%% ===== TESSELATION NORMALS =====
function [gradientBasis, triangleAreas, FaceNormals] = ...
  geometry_tesselation(Faces, Vertices, dimension)
% GEOMETRY_TESSELATION    Computes some geometric quantities from a surface
%
% INPUTS:
%   Faces           - triangles of tesselation
%   Vertices        - coordinates of nodes
%   dimension       - 3 for scalp or cortical surface (default)
%                     2 for plane (channel cap, etc)
% OUTPUTS:
%   gradientBasis   - gradient of basis function (FEM) on each triangle
%   triangleAreas 	- area of each triangle
%   FaceNormals    - normal of each triangle

% Edges of each triangles
u = Vertices(Faces(:,2),:)-Vertices(Faces(:,1),:);
v = Vertices(Faces(:,3),:)-Vertices(Faces(:,2),:);
w = Vertices(Faces(:,1),:)-Vertices(Faces(:,3),:);

% Length of each edges and angles bewteen edges
uu = sum(u.^2,2);
vv = sum(v.^2,2);
ww = sum(w.^2,2);
uv = sum(u.*v,2);
vw = sum(v.*w,2);
wu = sum(w.*u,2);

% 3 heights of each triangle and their norm
h1 = w-((vw./vv)*ones(1,dimension)).*v;
h2 = u-((wu./ww)*ones(1,dimension)).*w;
h3 = v-((uv./uu)*ones(1,dimension)).*u;
hh1 = sum(h1.^2,2);
hh2 = sum(h2.^2,2);
hh3 = sum(h3.^2,2);

% Gradient of the 3 basis functions on a triangle
gradientBasis = cell(1,dimension);
gradientBasis{1} = h1./(hh1*ones(1,dimension));
gradientBasis{2} = h2./(hh2*ones(1,dimension));
gradientBasis{3} = h3./(hh3*ones(1,dimension));

% Remove pathological gradients
indices1 = find(sum(gradientBasis{1}.^2,2)==0|isnan(sum(gradientBasis{1}.^2,2)));
indices2 = find(sum(gradientBasis{2}.^2,2)==0|isnan(sum(gradientBasis{2}.^2,2)));
indices3 = find(sum(gradientBasis{3}.^2,2)==0|isnan(sum(gradientBasis{3}.^2,2)));

min_norm_grad = min([ ...
  sum(gradientBasis{1}(sum(gradientBasis{1}.^2,2) > 0,:).^2,2); ...
  sum(gradientBasis{2}(sum(gradientBasis{2}.^2,2) > 0,:).^2,2); ...
  sum(gradientBasis{3}(sum(gradientBasis{3}.^2,2) > 0,:).^2,2) ...
  ]);

gradientBasis{1}(indices1,:) = repmat([1 1 1]/min_norm_grad, length(indices1), 1);
gradientBasis{2}(indices2,:) = repmat([1 1 1]/min_norm_grad, length(indices2), 1);
gradientBasis{3}(indices3,:) = repmat([1 1 1]/min_norm_grad, length(indices3), 1);

% Area of each face
triangleAreas = sqrt(hh1.*vv)/2;
triangleAreas(isnan(triangleAreas)) = 0;

% Calculate normals to surface at each face
if dimension == 3
    FaceNormals = cross(w,u);
    FaceNormals = FaceNormals./repmat(sqrt(sum(FaceNormals.^2,2)),1,3);
else
    FaceNormals = [];
end

% % Calculate normals to surface at each vertex (from normals at each face)
% VertNormals = zeros(size(Vertices,1),3);
% bst_progress('start', 'Optical Flow', ...
%   'Computing normals to surface at every vertex ...', 1, size(Faces,1));
% for facesIdx=1:size(Faces,1);
%   VertNormals(Faces(facesIdx,:),:) = ...
%     VertNormals(Faces(facesIdx,:),:) + ...
%     repmat(FaceNormals(facesIdx,:), [3 1]);
%
%   if mod(facesIdx,20) == 0
%     bst_progress('inc', 20); % Update progress bar
%   end
% end
% bst_progress('stop');
%
% % Normalize perpendicular-to-surface vectors for each vertex
% VertNormals = VertNormals ./ ...
%   repmat(sqrt(sum(VertNormals.^2,2)),1,3);
% VertNormals(isnan(VertNormals)) = 0; % For pathological anatomy

end

%% ===== TESSELATION TANGENT BUNDLE =====
function tangentPlaneBasis = basis_vertices(VertNormals)
% BASIS_VERTICES  Gives an orthonormal basis orthogonal to several vectors
%
% INPUTS:
%   VertNormals       - list of 3D vectors
%   type              - 'uniform' for normal structure of orthonormal
%                       'polar' for R/theta structure of the orthonormal
% OUTPUTS:
%   tangentPlaneBasis - for each vertex, a pair of vectors defining
%                       the orthonormal basis to that vertex using
%                       the normal to the surface at that vertex
% The cheat: if [x y z] is the normal-to-surface, then the tangent plane
% includes the vector [z-y x-z y-x]

nVertices = size(VertNormals,1); VertNormals = -VertNormals;
tangentPlaneBasis = zeros(nVertices, 3, 2); % Initialize

% First vector in basis: [3-2 1-3 2-1]
tangentPlaneBasis(:,:,1) = diff(VertNormals(:, [2 3 1 2]).').';

% Correct for [1 1 1]-ish vertices: use [y -x 0]
bad = abs(dot(VertNormals, ones(nVertices,3)/sqrt(3), 2)) > 0.97;
tangentPlaneBasis(bad,:,1) = ...
    [VertNormals(bad,2) -VertNormals(bad,1) zeros(sum(bad),1)];

% Second vector in basis by cross product
tangentPlaneBasis(:,:,2) = cross(VertNormals, tangentPlaneBasis(:,:,1));

% Normalize to get orthonormal basis
tangentPlaneBasisNorm = sqrt(sum(tangentPlaneBasis.^2,2));
tangentPlaneBasis = tangentPlaneBasis ./ tangentPlaneBasisNorm(:,[1 1 1],:);

end

%% ===== HORN-SCHUNCK REGULARIZATION MATRIX (FOR MANIFOLD) =====
function [regularizer, gradientBasis, tangentPlaneBasisCell, ...
  tangentPlaneBasis, triangleAreas, FaceNormals, ...
  sparseIndex1,sparseIndex2] = ...
  regularizing_matrix(Faces, Vertices, VertNormals, dimension)
% REGULARIZING_MATRIX: Computation of the regularizing part in the
%                      variationnal approach (SS grad(v_k)grad(v_k')) and
%                      other geometrical quantities.
%
% USAGE: [regularizer, gradientBasis, tangentPlaneBasisCell, ...
%        tangentPlaneBasis, triangleAreas, FaceNormals, ...
%        VertNormals, sparseIndex1, sparseIndex2] = ...
%        regularizing_matrix(tri, coord, dim)
%
% INPUTS
%   Faces                   - triangles of the tesselation
%   Vertices                - vertices of the tesselation
%   VertNormals             - normals to surface at each vertex
%   dimension               - 3 for scalp or cortical surface
%                             2 for flat surface
% OUTPUTS :
%   regularizer             - regularizing matrix
%   gradientBasis           - gradient of the basis functions in FEM
%   tangentPlaneBasisCell   - basis of the tangent plane at a surface node
%                             --> nodes listed according to tesselation
%   tangentPlaneBasis       - basis of the tangent plane at a node
%   triangleAreas           - area of the triangles
%   FaceNormals             - normal of each triangle
%   sparseIndex1            - 1st index of sparse tangent basis magnitudes
%   sparseIndex2            - 2nd index of sparse tangent basis magnitudes
%
%/---Script Authors---------------------\
%|                                      |
%|   *** J.Lef�vre, PhD                 |
%|   julien.lefevre@chups.jussieu.fr    |
%|                                      |
%\--------------------------------------/

nVertices = size(Vertices,1); % Number of nodes
[gradientBasis, triangleAreas, FaceNormals] = ...
  geometry_tesselation(Faces, Vertices, dimension);

% Basis of the tangent plane at each vertex
tangentPlaneBasis = basis_vertices(VertNormals);
tangentPlaneBasisCell = cell(2,3); % similar structure to gradientBasis
% 2 = # of basis vectors, 3 = # of nodes in each element (triangle)

tangentPlaneBasisCell{1,1} = tangentPlaneBasis(Faces(:,1),:,1);
tangentPlaneBasisCell{1,2} = tangentPlaneBasis(Faces(:,2),:,1);
tangentPlaneBasisCell{1,3} = tangentPlaneBasis(Faces(:,3),:,1);

tangentPlaneBasisCell{2,1} = tangentPlaneBasis(Faces(:,1),:,2);
tangentPlaneBasisCell{2,2} = tangentPlaneBasis(Faces(:,2),:,2);
tangentPlaneBasisCell{2,3} = tangentPlaneBasis(Faces(:,3),:,2);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%% Regularizing matrix SS grad(v_k)grad(v_k') %%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

sparseIndex1 = [Faces(:,1) Faces(:,1) Faces(:,2)];
sparseIndex2 = [Faces(:,2) Faces(:,3) Faces(:,3)];
gradientBasisSum = [sum(gradientBasis{1}.*gradientBasis{2},2) ...
  sum(gradientBasis{1}.*gradientBasis{3},2) ...
  sum(gradientBasis{2}.*gradientBasis{3},2)];

tang_scal_11 = [ ...
  sum(tangentPlaneBasisCell{1,1}.*tangentPlaneBasisCell{1,2},2) ...
  sum(tangentPlaneBasisCell{1,1}.*tangentPlaneBasisCell{1,3},2) ...
  sum(tangentPlaneBasisCell{1,2}.*tangentPlaneBasisCell{1,3},2) ...
  ] .* gradientBasisSum .* repmat(triangleAreas, [1 3]);
tang_scal_12 = [ ...
  sum(tangentPlaneBasisCell{1,1}.*tangentPlaneBasisCell{2,2},2) ...
  sum(tangentPlaneBasisCell{1,1}.*tangentPlaneBasisCell{2,3},2) ...
  sum(tangentPlaneBasisCell{1,2}.*tangentPlaneBasisCell{2,3},2) ...
  ] .* gradientBasisSum .* repmat(triangleAreas, [1 3]);
tang_scal_21 = [ ...
  sum(tangentPlaneBasisCell{2,1}.*tangentPlaneBasisCell{1,2},2) ...
  sum(tangentPlaneBasisCell{2,1}.*tangentPlaneBasisCell{1,3},2) ...
  sum(tangentPlaneBasisCell{2,2}.*tangentPlaneBasisCell{1,3},2) ...
  ] .* gradientBasisSum .* repmat(triangleAreas, [1 3]);
tang_scal_22 = [ ...
  sum(tangentPlaneBasisCell{2,1}.*tangentPlaneBasisCell{2,2},2) ...
  sum(tangentPlaneBasisCell{2,1}.*tangentPlaneBasisCell{2,3},2) ...
  sum(tangentPlaneBasisCell{2,2}.*tangentPlaneBasisCell{2,3},2) ...
  ] .* gradientBasisSum .* repmat(triangleAreas, [1 3]);

termes_diag = repmat(triangleAreas, [1 3]) .* [ ...
  sum(gradientBasis{1}.^2,2) ...
  sum(gradientBasis{2}.^2,2) ...
  sum(gradientBasis{3}.^2,2) ];

D = sparse(Faces, Faces, termes_diag, nVertices, nVertices);
E11=sparse(sparseIndex1, sparseIndex2, tang_scal_11, nVertices, nVertices);
E11=E11+E11'+D;
E22=sparse(sparseIndex1,sparseIndex2,tang_scal_22,nVertices,nVertices);
E22=E22+E22'+D;
E12=sparse(sparseIndex1,sparseIndex2,tang_scal_12,nVertices,nVertices);
E21=sparse(sparseIndex1,sparseIndex2,tang_scal_21,nVertices,nVertices);

regularizer = [E11 E12+E21'; ...
               E12'+E21 E22];

end

%% ===== HORN-SCHUNCK DATA FIT MATRIX (FOR MANIFOLD) =====
function [dataFit, B] = data_fit_matrix(Faces, nVertices, ...
  gradientBasis, triangleAreas, tangentPlaneBasisCell, F, dF, ...
  dimension, sparseIndex1, sparseIndex2)
% DATA_FIT_MATRIX   Computation of data-fit matrices of the variational
%                   formulation
% INPUTS:
%   Faces                   - triangles
%   nVertices               - number of nodes
%   gradientBasis           - gradient of the basis functions
%   triangleAreas           - area of each triangle
%   tangentPlaneBasisCell 	- basis of each tangent plane
%   F                       - activity at time step t
%   dF                      - change in activity at time step t
%   dimension               - 3 for scalp or cortical surface
%                             2 for channel surface or cap
%   sparseIndex1            - 1st index of sparse tangent basis magnitudes
%   sparseIndex2            - 2nd index of sparse tangent basis magnitudes
% OUTPUTS:
%   dataFit                 - fit to data matrix:
%                             SS (grad(F).w_k)(grad(F).w_k')
%   B                       - fit to data vector:
%                             -2SS(dF/dt)(grad(F).v_k)

% Gradient of intensity obtained through interpolation
grad_F = repmat(F(Faces(:,1)), 1, dimension) .* gradientBasis{1} ...
  + repmat(F(Faces(:,2)), 1, dimension) .* gradientBasis{2} ...
  + repmat(F(Faces(:,3)), 1, dimension) .* gradientBasis{3};

% Projection of the gradient of F on the tangent space
P_grad_F=cell(1,3); % same structure as gradientBasis : size = nFaces,3 ;
for s=1:3
    for k=1:2
       P_grad_F{s}(:,k)=sum(grad_F.*tangentPlaneBasisCell{k,s},2);
    end
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%% Construction of B %%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%

% m�thode Guillaume Obosinski

B = zeros(2*nVertices, 1);
for k = 1:2
   for s = 1:3
     B(Faces(:,s)+(k-1)*nVertices) = ...
       B(Faces(:,s)+(k-1)*nVertices) + ...
       (-1/12 * triangleAreas .* (P_grad_F{s}(:,k)) .* (dF(Faces(:,s))+sum(dF(Faces),2)));
   end
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%   Construction of dataFit   %%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% scal_F_11=[];
% scal_F_22=[];
% scal_F_12=[];
% scal_F_21=[];
% scal_F_diag_11=[];
% scal_F_diag_22=[];
% scal_F_diag_12=[];

scal_F_11 = [ ...
  P_grad_F{1}(:,1).*P_grad_F{2}(:,1) ...
  P_grad_F{1}(:,1).*P_grad_F{3}(:,1) ...
  P_grad_F{2}(:,1).*P_grad_F{3}(:,1) ...
  ] .* repmat(triangleAreas, [1 3])/12;
scal_F_12 = [ ...
  P_grad_F{1}(:,1).*P_grad_F{2}(:,2) ...
  P_grad_F{1}(:,1).*P_grad_F{3}(:,2) ...
  P_grad_F{2}(:,1).*P_grad_F{3}(:,2) ...
  ] .* repmat(triangleAreas, [1 3])/12;
scal_F_21 = [ ...
  P_grad_F{1}(:,2).*P_grad_F{2}(:,1) ...
  P_grad_F{1}(:,2).*P_grad_F{3}(:,1) ...
  P_grad_F{2}(:,2).*P_grad_F{3}(:,1) ...
  ] .* repmat(triangleAreas, [1 3])/12;
scal_F_22 = [ ...
  P_grad_F{1}(:,2).*P_grad_F{2}(:,2) ...
  P_grad_F{1}(:,2).*P_grad_F{3}(:,2) ...
  P_grad_F{2}(:,2).*P_grad_F{3}(:,2) ...
  ] .* repmat(triangleAreas, [1 3])/12;

scal_F_diag_11 = [ ...
  P_grad_F{1}(:,1).*P_grad_F{1}(:,1) ...
  P_grad_F{2}(:,1).*P_grad_F{2}(:,1) ...
  P_grad_F{3}(:,1).*P_grad_F{3}(:,1) ...
  ] .* repmat(triangleAreas, [1 3])/6;
scal_F_diag_22 = [ ...
  P_grad_F{1}(:,2).*P_grad_F{1}(:,2) ...
  P_grad_F{2}(:,2).*P_grad_F{2}(:,2) ...
  P_grad_F{3}(:,2).*P_grad_F{3}(:,2) ...
  ] .* repmat(triangleAreas, [1 3])/6;
scal_F_diag_12 = [ ...
  P_grad_F{1}(:,1).*P_grad_F{1}(:,2) ...
  P_grad_F{2}(:,1).*P_grad_F{2}(:,2) ...
  P_grad_F{3}(:,1).*P_grad_F{3}(:,2) ...
  ] .* repmat(triangleAreas, [1 3])/6;

S11=sparse(sparseIndex1, sparseIndex2, scal_F_11, nVertices, nVertices);
S22=sparse(sparseIndex1, sparseIndex2, scal_F_22, nVertices, nVertices);
S12=sparse(sparseIndex1, sparseIndex2, scal_F_12, nVertices, nVertices);
S21=sparse(sparseIndex1, sparseIndex2, scal_F_21, nVertices, nVertices);

D11=sparse(Faces, Faces, scal_F_diag_11, nVertices, nVertices);
D22=sparse(Faces, Faces, scal_F_diag_22, nVertices, nVertices);
D12=sparse(Faces, Faces, scal_F_diag_12, nVertices, nVertices);

dataFit = [S11+S11'+D11 S12+S21'+D12; ...
  S12'+S21+D12 S22+S22'+D22];

end

%% ===== POINCARE INDEX OF FLOW FIELD =====
function index = poincare_index(flowField)
% Compute the index of a vector field VF along a curve (whose it is not
% necessary to give the coordinates !!)
% VF has dimension 2*nbr vectors
  theta = myangle(flowField);
  difftheta = diffangle([theta(2),theta(3),theta(1)], theta);
  index = sum(difftheta)/(2*pi);
end

% gives the good angle of a vector (between 0 and 2pi)
function theta = myangle(flowField)
  normv=sqrt(sum(flowField.^2,1));
  c=flowField(1,:)./normv;
  s=flowField(2,:)./normv;
  theta = acos(c);

  for ii=1:size(flowField,2)
    if s(ii) < 0
      theta(ii)= -theta(ii) + 2*pi;
    end
  end
end

% Difference of two angles (result between -pi and pi)
function theta = diffangle(theta2, theta1)
  theta = theta2 - theta1;
  for ii=1:length(theta)
    if theta(ii) < -pi
      theta(ii) = theta(ii) + 2*pi;
    elseif theta(ii) > pi
        theta(ii) = theta(ii) - 2*pi;
    end
  end
end


function [engine, loglik] = initialize_engine(engine)
%initialize
bnet = bnet_from_engine(engine);
ns = bnet.node_sizes(:);
N = length(bnet.dag);

pot_type = 'scg'
check_for_cd_arcs([], bnet.cnodes, bnet.dag);

% Evaluate CPDs with evidence, and convert to potentials
pot = cell(1, N);
C = length(engine.cliques);
inited = zeros(1, C);
clpot = cell(1, C);
evidence = cell(1, N);
for n=1:N
  fam = family(bnet.dag, n);
  e = bnet.equiv_class(n);
  pot{n} = CPD_to_scgpot(bnet.CPD{e}, fam, ns, bnet.cnodes, evidence);
  cindex = engine.clq_ass_to_node(n);
  if inited(cindex)
      %clpot{cindex} = direct_combine_pots(clpot{cindex}, pot{n});
      clpot{cindex} = direct_combine_pots(pot{n}, clpot{cindex});
  else
      clpot{cindex} = pot{n};
      inited(cindex) = 1;
  end
end

for i=1:C
    if inited(i) == 0
        clpot{i} = scgpot([], [], [], []);
    end
end

seppot = cell(C, C);
% separators are is not need to initialize

% collect to root (node to parents)
for n=engine.postorder(1:end-1)
  for p=parents(engine.jtree, n)
      [margpot, comppot] = complement_pot(clpot{n}, engine.separator{p,n});
      margpot = marginalize_pot(clpot{n}, engine.separator{p,n});
      clpot{n} = comppot;
      %seppot{p, n} = margpot;
      clpot{p} = combine_pots(clpot{p}, margpot);
      %clpot{p} = combine_pots(margpot, clpot{p});
  end
end

temppot = clpot;
%temppot = clpot{engine.root};
for n=engine.preorder
  for c=children(engine.jtree, n)
    seppot{n,c} = marginalize_pot(temppot{n}, engine.separator{n,c});
    %seppot{n,c} = marginalize_pot(clpot{n}, engine.separator{n,c});
    %clpot{c} = direct_combine_pots(clpot{c}, seppot{n,c});
    temppot{c} = direct_combine_pots(temppot{c}, seppot{n,c});
  end
end

engine.clpot = clpot;
engine.seppot = seppot;


function imdb=getImdb(Name_batch,theConf,meta,IsTrain)
trainRate=0.9;
posRate=0.75;

objset=readAnnotation(Name_batch,theConf);
objset_neg=getNegObjSet(theConf,Name_batch);
num_pos=length(objset);
num_neg=length(objset_neg);
tarN=round(posRate/(1-posRate)*num_neg);
repN=ceil(tarN/(num_pos*2));
list_train_pos=round(linspace(1,num_pos,round(num_pos*trainRate)));
list_train_pos=[list_train_pos.*2-1,list_train_pos.*2];
if(repN>1)
    list_train_pos=repmat(list_train_pos,[repN,1])+repmat((0:repN-1)'.*(num_pos*2),[1,numel(list_train_pos)]);
    list_train_pos=reshape(sort(list_train_pos(:)),[1,numel(list_train_pos)]);
end
list_train_pos=list_train_pos(list_train_pos<=tarN);
list_train_neg=round(linspace(1,num_neg,round(num_neg*trainRate)));
list_train=sort([list_train_pos,list_train_neg+tarN]);

image_size=meta.normalization.imageSize(1:2);
data=zeros(image_size(1),image_size(2),3,num_pos*2,'single');
data_neg=zeros(image_size(1),image_size(2),3,num_neg,'single');
for i=1:num_pos
    tar=i*2-1;
    IsFlip=false;
    [I_patch,~]=getI(objset(i),theConf,image_size,IsFlip);
    data(:,:,:,tar)=I_patch;
    tar=i*2;
    IsFlip=true;
    [I_patch,~]=getI(objset(i),theConf,image_size,IsFlip);
    data(:,:,:,tar)=I_patch;
end
data=repmat(data,[1,1,1,repN]);
data=data(:,:,:,1:tarN);
theConf_neg=theConf;
if(isfield(theConf_neg.data,'imgdir_neg'))
    theConf_neg.data.imgdir=theConf_neg.data.imgdir_neg;
end
for i=1:num_neg
    tar=i;
    IsFlip=false;
    [I_patch,~]=getI(objset_neg(i),theConf_neg,image_size,IsFlip);
    data_neg(:,:,:,tar)=I_patch;
end
total_images=tarN+num_neg;
imdb.images.data=zeros(image_size(1),image_size(2),3,total_images,'single');
list=1:tarN;
imdb.images.data(:,:,:,list)=data;
imdb.images.data(:,:,:,tarN+(1:num_neg))=data_neg;
clear data data_neg
imdb.images.labels=ones(1,total_images,'single').*(-1);
imdb.images.labels(1,list)=1;
imdb.images.set=zeros(1,total_images,'uint8');
imdb.images.set(1:end)=2;
imdb.images.set(list_train)=1;
dataMean=mean(imdb.images.data(:,:,:,imdb.images.set==1),4);
imdb.images.data=bsxfun(@minus,imdb.images.data,dataMean);
if(IsTrain)
    rng(0);
    list=randperm(total_images);
    imdb.images.data=imdb.images.data(:,:,:,list);
    imdb.images.labels=imdb.images.labels(:,list);
    imdb.images.set=imdb.images.set(:,list);
    [~,imdb.images.order]=sort(list);
end
imdb.meta.classes={Name_batch};
imdb.meta.sets={'train','val','test'} ;
imdb.meta.dataMean=dataMean;
end


%DEMO_MEMORYSAVE  Demonstration of memory save option in GPstuff
%
%  Description
%    GP = GP_SET(..., 'savememory', 'on');
%
%    This demo consists of various combinations of covariance
%    functions, likelihoods and full/sparse approximations. This demo
%    is intended for showing how to use memory save option in
%    GPstuff. Unfortunately MATLAB doesn't have memory usage
%    monitoring, so the purpose of the demo is only to test that
%    results are the same whether you use memory save or not and to
%    show that the running times with memory saving are little bit
%    longer (more overhead). Memory saving can be enabled with the
%    following command
%
%    Usually GPstuff computes covariance matrix derivatives with
%    respect to all covariance function parameters at once which
%    avoids repeating computations common to each derivatibe, but
%    requires storage of Q NxN matrices, where Q is the number of
%    covariance function parameters. Q can be large, for example, if
%    there are many covariates each having own lengthsacle
%    parameter. With memory save option on, each matrix derivative is
%    computed separately, increasing the computation time little, but
%    reducing the memory usage. In an application, which motivated the
%    implementation of the option, the memory requirement of GPstuff
%    was reduced from over 60GB to less than 6GB.
%
%  See also
%    GP_SET
%
% Copyright (c) 2012 Ville Tolvanen

% 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.

% Classification data with full GP's

S = which('demo_classific');
L = strrep(S,'demo_classific.m','demodata/synth.tr');
x=load(L);
y=x(:,end);
y = 2.*y-1;
x(:,end)=[];

lik = lik_logit();

pl = prior_t();
pm = prior_sqrtunif();

gpcf = gpcf_neuralnetwork('weightSigma2', [0.9 0.9], 'biasSigma2', 10);
gpcf = gpcf_neuralnetwork(gpcf, 'weightSigma2_prior', pl,'biasSigma2_prior', pm); %

gpcf2 = gpcf_sexp('lengthScale', [0.9 0.9], 'magnSigma2', 10);
gpcf2 = gpcf_sexp(gpcf2, 'lengthScale_prior', pl,'magnSigma2_prior', pm); %

% GP without memory saving
gp = gp_set('lik', lik, 'cf', gpcf, 'jitterSigma2', 1e-4, 'latent_method', 'EP');
% GP with memory saving option enabled
gp2 = gp_set('lik', lik, 'cf', gpcf, 'jitterSigma2', 1e-4, 'latent_method', 'EP', 'savememory', 'on');

fprintf('Classification (EP), Neuralnetwork covariance function without and with memory saving (optimization and prediction)\n');
opt=optimset('TolX',1e-3,'TolFun',1e-3, 'Display', 'off');

% Optimization and prediction without memory saving
tic,gp=gp_optim(gp,x,y,'opt',opt);
[Eft,Varft, lpyt]=gp_pred(gp,x,y,x, 'yt', y);toc

% Optimization and prediction with memory saving
tic,gp2=gp_optim(gp2,x,y,'opt',opt);
[Eft2,Varft2, lpyt2]=gp_pred(gp2,x,y,x, 'yt', y);toc

% Check that predictions (and optimization) with and without memory saving are same
assert(all(Eft==Eft2)); assert(all(Varft==Varft2)); assert(all(lpyt==lpyt2));

gp = gp_set('lik', lik, 'cf', gpcf2, 'jitterSigma2', 1e-6, 'latent_method', 'Laplace');
gp2 = gp_set('lik', lik, 'cf', gpcf2, 'jitterSigma2', 1e-6, 'latent_method', 'Laplace', 'savememory', 'on');

fprintf('Classification (Laplace), Squared-Exponential covariance function without and with memory saving (optimization and prediction)\n');

% Optimization and prediction without memory saving
tic,gp=gp_optim(gp,x,y,'opt',opt);
[Eft,Varft, lpyt]=gp_pred(gp,x,y,x, 'yt', y);toc

% Optimization and prediction with memory saving
tic,gp2=gp_optim(gp2,x,y,'opt',opt);
[Eft2,Varft2, lpyt2]=gp_pred(gp2,x,y,x, 'yt', y);toc

% Check that predictions (and optimization) with and without memory saving are same
assert(all(Eft==Eft2)); assert(all(Varft==Varft2)); assert(all(lpyt==lpyt2));

% Regression data with sparse approximations

prevstream=setrandstream();
xx=linspace(1,10,901);
x1=logspace(0,1,100);
x1=round(x1*100)-99;
x=xx(x1)';
y=2*sin(4*x)+0.2*randn(size(x));
xt=[1:0.01:14]';
[n,nin] = size(x);

pl = prior_t('s2', 1);
pm = prior_logunif();
pn = prior_logunif();
gpcfse = gpcf_matern32('lengthScale',0.5,'magnSigma2',1, 'lengthScale_prior', pl, 'magnSigma2_prior', pm);
lik = lik_gaussian('sigma2', 0.1, 'sigma2_prior', pn);
gp = gp_set('lik', lik, 'cf', gpcfse, 'jitterSigma2', 1e-6);
opt=optimset('TolFun',1e-4,'TolX',1e-4);

fprintf('Regression, FIC GP, Matern-3/2 covariance function, without and with memory saving (optimization and prediction)\n')
Xu=round(10+90*rand(18,1))/10; % Random placement

gp_fic = gp_set(gp, 'type','FIC','X_u',Xu,'infer_params','covariance+likelihood+inducing');
gp_fic2 = gp_set(gp, 'type','FIC','X_u',Xu,'infer_params','covariance+likelihood+inducing', 'savememory', 'on');

opt=optimset('TolFun',1e-4,'TolX',1e-4, 'Display', 'off');

% Optimization and prediction without memory saving
tic,gp_fic=gp_optim(gp_fic,x,y,'opt',opt);
[Eft_fic, Varft_fic] = gp_pred(gp_fic, x, y, xt);
Varft_fic = Varft_fic + gp_fic.lik.sigma2;toc

% Optimization and prediction with memory saving
tic,gp_fic2=gp_optim(gp_fic2,x,y,'opt',opt);
[Eft_fic2, Varft_fic2] = gp_pred(gp_fic2, x, y, xt);
Varft_fic2 = Varft_fic2 + gp_fic.lik.sigma2;toc
setrandstream([],prevstream);

% Check that predictions (and optimization) with and without memory saving are same
assert(all(Eft_fic==Eft_fic2)); assert(all(Varft_fic==Varft_fic2));

gpcfse = gpcf_sexp('lengthScale',0.5,'magnSigma2',1, 'lengthScale_prior', pl, 'magnSigma2_prior', pm);
gp = gp_set('lik', lik, 'cf', gpcfse, 'jitterSigma2', 1e-6);

gp_fic = gp_set(gp, 'type','VAR','X_u',Xu,'infer_params','covariance+likelihood+inducing');
gp_fic2 = gp_set(gp, 'type','VAR','X_u',Xu,'infer_params','covariance+likelihood+inducing', 'savememory', 'on');

fprintf('Regression, VAR GP, Squared-Exponential covariance function, without and with memory saving (optimization and prediction)\n')

% Optimization and prediction without memory saving
tic,gp_fic=gp_optim(gp_fic,x,y,'opt',opt);
[Eft_fic, Varft_fic] = gp_pred(gp_fic, x, y, xt);
Varft_fic = Varft_fic + gp_fic.lik.sigma2;toc

% Optimization and prediction with memory saving
tic,gp_fic2=gp_optim(gp_fic2,x,y,'opt',opt);
[Eft_fic2, Varft_fic2] = gp_pred(gp_fic2, x, y, xt);
Varft_fic2 = Varft_fic2 + gp_fic.lik.sigma2;toc
setrandstream([],prevstream);

% Check that predictions (and optimization) with and without memory saving are same
assert(all(Eft_fic==Eft_fic2)); assert(all(Varft_fic==Varft_fic2));

% Spatial data with sparse approximations

S = which('demo_spatial1');
data = load(strrep(S,'demo_spatial1.m','demodata/spatial1.txt'));
x = data(1:200,1:2);
ye = data(1:200,3);
y = data(1:200,4);
dims = [1    60     1    35];
[trindex, Xu] = set_PIC(x, dims, 5, 'corners', 0);
[n,nin] = size(x);

pl = prior_t('s2',10);
pm = prior_sqrtunif();

gpcf1 = gpcf_ppcs3('nin',nin,'lengthScale', 1, 'magnSigma2', 0.1, 'lengthScale_prior', pl, 'magnSigma2_prior', pm);
gpcf2 = gpcf_sexp('lengthScale', 5, 'magnSigma2', 0.05, 'lengthScale_prior', pl, 'magnSigma2_prior', pm);
gpcf3 = gpcf_neuralnetwork('weightSigma2', [1 1], 'biasSigma2', 0.05, 'weightSigma2_prior', pl, 'biasSigma2_prior', pm);

lik = lik_negbin();
% GP without memory save
gp = gp_set('type', 'FIC', 'lik', lik, 'cf', gpcf1, 'X_u', Xu, ...
            'jitterSigma2', 1e-4, 'infer_params', 'covariance+inducing');
% GP with memory saving option enabled
gp2 = gp_set('type', 'FIC', 'lik', lik, 'cf', gpcf1, 'X_u', Xu, ...
            'jitterSigma2', 1e-4, 'infer_params', 'covariance+inducing', 'savememory', 'on');
opt=optimset('TolFun',1e-2,'TolX',1e-2, 'Display', 'off');

fprintf('Spatial process (Laplace), FIC GP, PPCS3 covariance function, without and with memory saving (optimization and prediction)\n');

% Optimization and prediction without memory saving
tic,gp=gp_optim(gp,x,y,'z',ye,'opt',opt);
[Ef, Varf] = gp_pred(gp, x, y, x, 'z', ye, 'tstind', [1:n]); toc

% Optimization and prediction with memory saving
tic,gp2=gp_optim(gp2,x,y,'z',ye,'opt',opt);
[Ef2, Varf2] = gp_pred(gp2, x, y, x, 'z', ye, 'tstind', [1:n]); toc

% Check that predictions (and optimization) with and without memory saving are same
assert(all(Ef==Ef2)); assert(all(Varf==Varf2));

gp = gp_set('lik', lik, 'cf', gpcf2, 'jitterSigma2', 1e-4, 'latent_method', 'EP');
gp2 = gp_set('lik', lik, 'cf', gpcf2, 'jitterSigma2', 1e-4, 'latent_method', 'EP', 'savememory', 'on');

fprintf('Spatial process (EP), FIC GP, Squared-Exponential covariance function, without and with memory saving (optimization and prediction)\n');

% Optimization and prediction without memory saving
tic,gp=gp_optim(gp,x,y,'z',ye,'opt',opt);
[Ef, Varf] = gp_pred(gp, x, y, x, 'z', ye, 'tstind', [1:n]); toc

% Optimization and prediction with memory saving
tic,gp2=gp_optim(gp2,x,y,'z',ye,'opt',opt);
[Ef2, Varf2] = gp_pred(gp2, x, y, x, 'z', ye, 'tstind', [1:n]); toc

% Check that predictions (and optimization) with and without memory saving are same
assert(all(Ef==Ef2)); assert(all(Varf==Varf2));

gp = gp_set('lik', lik, 'cf', gpcf3, 'jitterSigma2', 1e-4);
gp2 = gp_set('lik', lik, 'cf', gpcf3, 'jitterSigma2', 1e-4, 'savememory', 'on');

fprintf('Spatial process (Laplace), FIC GP, Neuralnetwork covariance function, without and with memory saving (optimization and prediction)\n');
tic,gp=gp_optim(gp,x,y,'z',ye,'opt',opt);
[Ef, Varf] = gp_pred(gp, x, y, x, 'z', ye, 'tstind', [1:n]); toc

tic,gp2=gp_optim(gp2,x,y,'z',ye,'opt',opt);
[Ef2, Varf2] = gp_pred(gp2, x, y, x, 'z', ye, 'tstind', [1:n]); toc

% Check that predictions (and optimization) with and without memory saving are same
assert(all(Ef==Ef2)); assert(all(Varf==Varf2));


%--------------------------------------------------------------------------------------------------------
% The system is created based on the principles described in the following paper
% Jimmy SJ. Ren and Li Xu, "On Vectorization of Deep Convolutional Neural Networks for Vision Tasks",
% The 29th AAAI Conference on Artificial Intelligence (AAAI-15). Austin, Texas, USA, January 25-30, 2015
% email: jimmy.sj.ren@gmail.com
%--------------------------------------------------------------------------------------------------------
addpath applications/image_denoise/
addpath applications/image_denoise/utility/
addpath utils/
addpath cuda/
addpath mem/
addpath layers/
addpath layers_adapters/
addpath pipeline/

clearvars -global config;
clearvars -global mem;
clear;
I = im2double(imread('sample1.png'));
global config;
prepare_net(size(I, 1), size(I, 2), 'w.mat');
final_output = apply_net(config.NEW_MEM(I));
final_output = gather(final_output);
figure, imshow([I final_output]);
drawnow();


% eeglab
try
clear all
close all
clc
catch ME
    disp(ME.message)
clear all
close all
end

%%
% eeglabPath = fileparts(which('eeglab'));
mobilabPath = 'C:\DevelCore\mobilab'; %'/Users/timmullen/Documents/WORK/Toolboxes/mobilab'; %[eeglabPath filesep 'plugins' filesep 'mobilab'];
% addpath(genpath(mobilabPath));

templateFile = [mobilabPath filesep 'data' filesep 'headModelColin27_11997.mat'];
standardMontage = [mobilabPath filesep 'data' filesep 'standard_1020.elc'];
template = load([mobilabPath filesep 'data' filesep 'headModelColin27_11997.mat']);
individualMontage = [mobilabPath filesep 'data' filesep 'personA_chanlocs.sfp'];
surfFile = 'demo_Colin27_11997.mat';
surfData = template.surfData;
save(surfFile,'surfData');

%% display template head model: Colin27 + aal atlas + 10/20 montage
[elec,label] = readMontage(standardMontage);
hmObj = headModel('surfaces',surfFile,'atlas',template.atlas,'fiducials',template.fiducials,'channelSpace',elec,'label',label);
plotHeadModel(hmObj);

%% warping individual channel space to template
individualSurfaces = 'demo_Colin27_11997.mat';

hmObj = headModel(individualMontage);
plotMontage(hmObj);

aff = hmObj.warpChannelSpace2Template(templateFile,individualSurfaces,'affine');
plotHeadModel(hmObj);

%% warping template to individual channel space
individualSurfaces = 'warped_demo_Colin27_11997.mat';  % name of the output file

hmObj = headModel(individualMontage);
plotMontage(hmObj);

hmObj.warpTemplate2channelSpace(templateFile,individualSurfaces);
plotHeadModel(hmObj);

%% solving the forward problem with OpenMEEG
conductivity = [0.33 0.022 0.33]; % brain and scalp = 0.33 S/m, skull = 0.022 S/m; these conductivies were taken from
                                  % Valdes-Hernandez et al., 2009, Oostendrop TF, 2000; Wendel and Malmivuo, 2006
normal2surface = true;
hmObj.computeLeadFieldBEM(conductivity,normal2surface);

%% solving the forward problem with NFT
% conductivity = [0.33 0.022 0.33]; % brain and scalp = 0.33 S/m, skull = 0.022 S/m; these conductivies were taken from
%                                   % Valdes-Hernandez et al., 2009, Oostendrop TF, 2000; Wendel and Malmivuo, 2006
% normal2surface = true;
% hmObj.computeLeadFieldBEM_NFT(conductivity,normal2surface);

%% solving the inverse problem with sLORETA
% K: lead field matrix
% L: Laplaciian operator
% rmIndices: indices to be removed (the Thalamus)
% surfData(3).vertices: source space

brainStructsToKeep = {'Occipital_Sup_L','Occipital_Mid_L','Occipital_Inf_L'};
brainStructsToRemove = setdiff_bc(hmObj.atlas.label,brainStructsToKeep); %{'Thalamus_L','Thalamus_R'};
[sourceSpace,K,L,rmIndices] = getSourceSpace4PEB(hmObj,brainStructsToRemove);
load(hmObj.surfaces)
n = size(surfData(3).vertices,1);
J = zeros(n,1);
Jtrue = J;
Jest = J;
ind = setdiff_bc(1:n,rmIndices);

% simulating some Gaussian sources
x0 = [-81.6328 17.9887 93.8088];  % occipital l
x1 = [30.5384 -12.5882 117.1195]; % frontal superior r
x2 = [0.8625 -58.2585 78.9345];   % supra marginal r
x3 = [25.9583 48.3573 35.8547];   % insula l

d = sqrt(sum((surfData(3).vertices(ind,:) - ones(length(ind),1)*x0).^2,2));
Jtrue(ind) = normpdf(d,0,10);
Vtrue = K*Jtrue(ind);

nlambdas = 100;
plotGCV  = true;
threshold = [25 85]; % threshold = [1 99]; threshold = [];
Jt = inverseSolutionLoreta(Vtrue,K,L,nlambdas,plotGCV,threshold);  % Jt contains the solution for the source potentials within the chosen space, the remaining elements are zero
Jest(ind) = Jt;
Vest = K*Jest(ind);
hmObj.plotOnModel(Jtrue,Vtrue,'True source');
hmObj.plotOnModel(Jest,Vest,'Estimated source');

disp('adding noise');
% adding noise
snr = 5;
vn = std(Vtrue)/snr*randn(length(Vtrue),1);
Vtrue_noise = Vtrue + vn;

Jt = inverseSolutionLoreta(Vtrue_noise,K,L,nlambdas,plotGCV,threshold);
Jest(ind) = Jt;
Vest = K*Jest(ind);
disp('plotting');
hmObj.plotOnModel(Jtrue,Vtrue,'True source (noisy)');
hmObj.plotOnModel(Jest,Vest,'Estimated source (noisy)');


% CELLTOMAT - convert cell array to matrix
%
% Usage: >> M = celltomat( C );
%
% Author: Arnaud Delorme, CNL / Salk Institute, Jan 25 2002
%
% Note: This function overloads the neuralnet toolbox function CELLTOMAT,
% but does not have all its capacities. Delete this version if you have
% the toolbox.

% Copyright (C) Jan 25 2002 Arnaud Delorme, CNL / Salk Institute
%
% This file is part of EEGLAB, see http://www.eeglab.org
% for the documentation and details.
%
% Redistribution and use in source and binary forms, with or without
% modification, are permitted provided that the following conditions are met:
%
% 1. Redistributions of source code must retain the above copyright notice,
% this list of conditions and the following disclaimer.
%
% 2. Redistributions in binary form must reproduce the above copyright notice,
% this list of conditions and the following disclaimer in the documentation
% and/or other materials provided with the distribution.
%
% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
% THE POSSIBILITY OF SUCH DAMAGE.

function M = celltomat( C, varargin );

if nargin < 1
	help celltomat;
	return;
end

M = zeros(size(C));
for i=1:size(C,1)
    for j=1:size(C,2)
        M(i,j) = C{i,j};
    end
end;


% PRTBRVVB - PRT BRV Variational Bayes Object
%   Contains properties and methods for prtBrv objects that impliment
%   iterative variational Bayesian algorithms.
%
% Properties:
%   vbConvergenceThreshold = 1e-6; % This is a percentage of the total change in NFE
%   vbMaxIterations = 1e3; % Maximum number of iterations
%   vbVerboseText = false; % display text
%   vbVerbosePlot = false; % plot after each iteration
%   vbVerboseMovie = false; % make a movie with each frame of plotting
%   vbVerboseMovieFrames = []; % Where we store the frames
% Methods:
%   vbCheckConvergence - Used to check convergence at the end of each VB
%       iteration. Checks the percentage change in negative free energy.

classdef prtBrvVb

    methods (Abstract)
        [self, training] = vbBatch(self, x);
    end

    properties
        vbCheckConvergence = true; % Actually bother to calculate NFE
        vbConvergenceThreshold = 1e-6; % This is total change in NFE
        vbConvergenceDecreaseThreshold = 1e-3; % This is total change in NFE that will cause an exit
        vbMaxIterations = 1e3; % Maximum number of iterations
        vbVerboseText = false; % display text
        vbVerbosePlot = false; % plot after each iteration
        vbVerboseMovie = false; % make a movie with each frame of plotting
        vbVerboseMovieFrames = []; % Where we store the frames
    end

    methods

        function [self, training] = vb(self, x)
            [self, training] = vbBatch(self, x);
        end

        function [converged, err] = vbIsConverged(self, priorSelf, x, training) %#ok<INUSL>
            F = training.negativeFreeEnergy;
            pF = training.previousNegativeFreeEnergy;

            signedPercentage = (F-pF);
            converged = signedPercentage < self.vbConvergenceThreshold;

            if self.vbVerboseText
                fprintf('\t\t\t Negative Free Energy: %0.2f, %+.2e Change',F,signedPercentage)
            end

            if signedPercentage < 0
                if self.vbVerboseText
                    fprintf('\tDecrease in negative free energy occured!!!')
                end
                converged = false;
                if abs(signedPercentage)>self.vbConvergenceDecreaseThreshold
                    if self.vbVerboseText
                        fprintf('\n\t\tDecrease in negative free energy is beyond numerical error expectations. Exiting.')
                    end
                    err = true;
                else
                    err = false;
                end
            else
                err = false;
            end

            if self.vbVerboseText
                fprintf('\n');
            end

        end
        function [converged, err] = vbIsConvergedAbs(self, priorSelf, x, training) %#ok<INUSL>
            F = training.negativeFreeEnergy;
            pF = training.previousNegativeFreeEnergy;

            signedPercentage = (F-pF);
            converged = abs(signedPercentage) < self.vbConvergenceThreshold;

            if self.vbVerboseText
                fprintf('\t\t\t Negative Free Energy: %0.2f, %+.2e Change\n',F,signedPercentage)
            end

            if signedPercentage < 0
                %if self.vbVerboseText
                %    fprintf('\t\tDecrease in negative free energy occured!!! Exiting.\n')
                %end
                %converged = false;
                err = true;
            else
                err = false;
            end
        end
    end
end


function [ projections ] = Ax(img, geo, angles, varargin )
%AX computes projections for images and geometry information

%--------------------------------------------------------------------------
% This file is part of the TIGRE Toolbox
%
% Copyright (c) 2015, University of Bath and
%                     CERN-European Organization for Nuclear Research
%                     All rights reserved.
%
% License:            Open Source under BSD.
%                     See the full license at
%                     https://github.com/CERN/TIGRE/blob/master/LICENSE
%
% Contact:            tigre.toolbox@gmail.com
% Codes:              https://github.com/CERN/TIGRE/blob/master/LICENSE
% Coded by:           Ander Biguri
%--------------------------------------------------------------------------
% Lets make 100% sure that data is correct
%% Optionals

ptype='Siddon';
% expected projection types. 'ray-voxel' is obsolete. Use 'Siddon'
expectedProjectionTypes = {'Siddon','ray-voxel','interpolated'};
acceptableOptionName = {'gpuids'};

if nargin > 3
    if any(strcmp(varargin{1}, expectedProjectionTypes))
        ptype = varargin{1};
        [gpuids] = parse_inputs(varargin{2:length(varargin)});
        %[ptype, gpuids] = parse_inputs1(varargin{1}, expectedProjectionTypes, varargin{2:length(varargin)});
    elseif any(strcmp(varargin{1}, acceptableOptionName))
        [gpuids] = parse_inputs(varargin{:});
        %[ptype, gpuids] = parse_inputs1(ptype, expectedProjectionTypes, varargin{:});
    else
        assert(false,'TIGRE:Ax:InvalidInput','Projection type should be either ''interpolated'' or ''Siddon''.');
    end
else
    gpuids = GpuIds();
end

%% image
assert(isa(img,'single'),'TIGRE:Ax:InvalidInput','Image should be single type');
assert(isreal(img),'TIGRE:Ax:InvalidInput','Image should be real (non-complex)');
% assert(size(img,3)>1,'TIGRE:Ax:InvalidInput', 'image should be 3D'); %TODO: needed?
%% Angles
assert(isreal(angles),'TIGRE:Ax:InvalidInput','Angles should be real (non-complex)');
assert(size(angles,1)==1 | size(angles,1)==3 ,'TIGRE:Ax:InvalidInput','Angles should be of size 1xN or 3xN');
angles=double(angles); %in case they were single.
if size(angles,1)==1
   angles=repmat(angles,[3 1]);
   angles(2,:)=0;
   angles(3,:)=0;
end
%% geometry
geo=checkGeo(geo,angles);
assert(isequal([size(img,1) size(img,2) size(img,3)],squeeze(geo.nVoxel.')),'TIGRE:Ax:BadGeometry','nVoxel does not match with provided image size');

%% Temporary (?)

s= sum(abs(angles),2);
if (s(2)~=0 || s(3)~=0) && (strcmp(ptype,'Siddon')||strcmp(ptype,'ray-voxel')) && strcmp(geo.mode,'parallel')
    warning(['''Siddon'' Not supported for parallel beam arbitrary axis rotation, changed to ''interpolated''.',char(10),'Ignore this message if you are not purposedly triying enforce its use.']);
    ptype='interpolated';
end

%% Thats it, lets call the mex fucntion
%% TODO: When Ax_mex accepts class-objects, gpuids itself will be passed.
projections=Ax_mex(img,geo,angles,ptype, gpuids.devices);

end

function [gpuids]=parse_inputs(varargin)
    %fprintf('parse_inputs0(varargin (%d))\n', length(varargin));
    if isempty(varargin)
        gpuids = GpuIds();
    else
        % create input parser
        p=inputParser;
        % add optional parameters
        addParameter(p,'gpuids', GpuIds());
        %execute
        parse(p,varargin{:});
        %extract
        gpuids=p.Results.gpuids;
    end
end