function [decimaldates1,decimaldates2,stringdates1,stringdates2,stringdates3,Fstartlocation,Fendlocation]=gendates(names,lags,frequency,startdate,enddate,Fstartdate,Fenddate,Fcenddate,Fendsmpl,F,CF,favar)

% function [decimaldates1,decimaldates2,stringdates1,stringdates2,stringdates3,Fstartlocation,Fendlocation]=gendates(names,lags,frequency,startdate,enddate,Fstartdate,Fenddate,Fcenddate,Fendsmpl,F)
% generate cells of date strings and vector of dates converted into decimal numbers; used for plots
% inputs:  - vector 'decimaldates1': dates converted into decimal values, for the sample period
%          - vector 'decimaldates2': dates converted into decimal values, for the sample+forecasts period
%          - cell 'stringdates1': date strings for the sample period
%          - cell 'stringdates2': date strings for the sample+forecasts period
%          - cell 'stringdates3': date strings for the forecast evaluation period (i.e. period for which forecast is estimated and actual data exists)
%          - integer 'Fstartlocation': position of the forecast start date in stringdates2
%          - integer 'Fendlocation': position of the forecast end date in stringdates2
% outputs: - cell 'names': cell containing the excel spreadsheet labels (names and dates)
%          - integer 'lags': number of lags included in the model
%          - integer 'frequency': frequency of the data set
%          - string 'startdate': start date of the sample
%          - string 'enddate': end date of the sample
%          - string 'Fstartdate': start date of the forecasts
%          - string 'Fenddate': end date of the forecasts
%          - string 'Fcenddate': end date of the forecat evaluation (i.e. period for which forecast is estimated and actual data exists)
%          - integer 'Fendsmpl': 0-1 value to determine if forecasts must start after the final sample period
%          - integer 'F': 0-1 value to determine if forecasts must be estimated

% preliminary tasks
% define date strings
datestrings=names(2:end,1);

if favar.FAVAR==1 % in case we transform the data to first or second differences, we have a different startlocation
    if favar.transformation==1
        datestrings=names(1+favar.informationstartlocation:favar.informationendlocation_sub,:); %first row are labels
    end
end

% PHASE 1: CREATION OF DECIMAL DATES AND DATE STRINGS FOR THE ESTIMATION SAMPLE (DECIMALDATES1, STRINGDATES1)

% deal first with the data if it is yearly
if frequency==1
    % identify the number of years covered by the sample
    startyear=str2double(startdate(1,1:end-1));
    endyear=str2double(enddate(1,1:end-1));
    % create the decimal value vector
    decimaldates1=(startyear:endyear)';
    % convert into strings
    stringdates1=cellfun(@num2str,num2cell(decimaldates1),'UniformOutput',0);
    % add a 'y' character at the end
    for ii=1:size(stringdates1,1)
        stringdates1{ii,1}=[stringdates1{ii,1} 'y'];
    end
    % trim a number of initial periods equal to the number of lags, as these periods will be used to create initial conditions
    decimaldates1=decimaldates1(lags+1:end,:);
    stringdates1=stringdates1(lags+1:end,:);

    % deal now with the data if it is quarterly
elseif frequency==2
    % first identify the year and quarter of the initial date
    startyear=str2double(startdate(1,1:4));
    startquarter=str2double(startdate(1,6));
    % proceed similarly for the year and quarter of the final date
    endyear=str2double(enddate(1,1:4));
    endquarter=str2double(enddate(1,6));
    % initiate the decimal value vector and the string cell
    decimaldates1=[];
    stringdates1={};
    % create the decimal vector from start date up to the penultimate year
    year=startyear;
    quarter=startquarter;
    while year<=endyear-1
        while quarter<=4
            decimaldates1=[decimaldates1;year+(quarter-1)/4];
            temp=[num2str(year) 'q' num2str(quarter)];
            stringdates1{end+1,1}=temp;
            quarter=quarter+1;
        end
        quarter=1;
        year=year+1;
    end
    % complete with final year
    while quarter<=endquarter
        decimaldates1=[decimaldates1;year+(quarter-1)/4];
        temp=[num2str(year) 'q' num2str(quarter)];
        stringdates1{end+1,1}=temp;
        quarter=quarter+1;
    end
    % finally, trim a number of initial periods equal to the number of lags, as these periods will be used to create initial conditions
    decimaldates1=decimaldates1(lags+1:end,:);
    stringdates1=stringdates1(lags+1:end,:);

    % deal now with the data if it is monthly
elseif frequency==3
    % first identify the year and month of the initial date
    temp=startdate;
    temp(1,5)=' ';
    [startyear,startmonth]=strtok(temp);
    startyear=str2double(startyear);
    startmonth=str2double(startmonth);
    % proceed similarly for the year and month of the final date
    temp=enddate;
    temp(1,5)=' ';
    [endyear,endmonth]=strtok(temp);
    endyear=str2double(endyear);
    endmonth=str2double(endmonth);
    % initiate the decimal value vector and the string cell
    decimaldates1=[];
    stringdates1={};
    % create the decimal vector from start date up to the penultimate year
    year=startyear;
    month=startmonth;
    while year<=endyear-1
        while month<=12
            decimaldates1=[decimaldates1;year+(month-1)/12];
            temp=[num2str(year) 'm' num2str(month)];
            stringdates1{end+1,1}=temp;
            month=month+1;
        end
        month=1;
        year=year+1;
    end
    % complete with final year
    while month<=endmonth
        decimaldates1=[decimaldates1;year+(month-1)/12];
        temp=[num2str(year) 'm' num2str(month)];
        stringdates1{end+1,1}=temp;
        month=month+1;
    end
    % finally, trim a number of initial periods equal to the number of lags, as these periods will be used to create initial conditions
    decimaldates1=decimaldates1(lags+1:end,:);
    stringdates1=stringdates1(lags+1:end,:);

    % deal now with the data if it is weekly
elseif frequency==4
    % first identify the first and last years of the sample
    startyear=str2double(startdate(1,1:4));
    endyear=str2double(enddate(1,1:4));
    % calculate the total number of years over the period
    numyear=endyear-startyear+1;
    % create a vector of date strings for the sample period
    start=find(strcmp(datestrings,startdate));
    finish=find(strcmp(datestrings,enddate));
    stringdates1=datestrings(start:finish,1);
    % for each year in the sample, identify the earliest and latest week
    for ii=startyear:endyear
        % first identify which periods in the sample correspond to this year
        periods=strfind(stringdates1,num2str(ii));
        for jj=1:size(periods,1)
            if isempty(periods{jj,1})
                periods{jj,1}=0;
            end
        end
        periods=cell2mat(periods);
        % identify the position of the first week and the last week of this year
        first=min(find(periods==1));
        last=max(find(periods==1));
        % identify the week number to which these positions correspond
        temp=char(stringdates1(first,1));
        temp(1,5)=' ';
        [~,weeknum]=strtok(temp);
        week(ii-startyear+1,1)=str2double(weeknum);
        temp=char(stringdates1(last,1));
        temp(1,5)=' ';
        [~,weeknum]=strtok(temp);
        week(ii-startyear+1,2)=str2double(weeknum);
    end
    % now create the vector of decimal data
    decimaldates1=[];
    % compute until penultimate year
    for ii=1:numyear-1
        for jj=week(ii,1):week(ii,2)
            decimaldates1=[decimaldates1;(startyear+ii-1)+((jj-1)/week(ii,2))];
        end
    end
    % compute for last year (assuming a year of 52 weeks if the number of weeks is shorter)
    if week(numyear,2)<52
        total=52;
    else
        total=week(numyear,2);
    end
    for jj=week(numyear,1):week(numyear,2)
        decimaldates1=[decimaldates1;endyear+((jj-1)/total)];
    end
    % finally, trim a number of initial periods equal to the number of lags, as these periods will be used to create initial conditions
    decimaldates1=decimaldates1(lags+1:end,:);
    stringdates1=stringdates1(lags+1:end,:);

    % deal now with the data if it is daily
elseif frequency==5
    % first identify the first and last years of the sample
    startyear=str2double(startdate(1,1:4));
    endyear=str2double(enddate(1,1:4));
    % calculate the total number of years over the period
    numyear=endyear-startyear+1;
    % create a vector of date strings for the sample period
    start=find(strcmp(datestrings,startdate));
    finish=find(strcmp(datestrings,enddate));
    stringdates1=datestrings(start:finish,1);
    % for each year in the sample, identify the earliest and latest day
    for ii=startyear:endyear
        % first identify which periods in the sample correspond to this year
        periods=strfind(stringdates1,num2str(ii));
        for jj=1:size(periods,1)
            if isempty(periods{jj,1})
                periods{jj,1}=0;
            end
        end
        periods=cell2mat(periods);
        % identify the position of the first day and the last day of this year
        first=min(find(periods==1));
        last=max(find(periods==1));
        % indentify the day number to which these positions correspond
        temp=char(stringdates1(first,1));
        temp(1,5)=' ';
        [~,daynum]=strtok(temp);
        day(ii-startyear+1,1)=str2double(daynum);
        temp=char(stringdates1(last,1));
        temp(1,5)=' ';
        [~,daynum]=strtok(temp);
        day(ii-startyear+1,2)=str2double(daynum);
    end
    % now create the vector of decimal data
    decimaldates1=[];
    % compute until penultimate year
    for ii=1:numyear-1
        for jj=day(ii,1):day(ii,2)
            decimaldates1=[decimaldates1;(startyear+ii-1)+((jj-1)/day(ii,2))];
        end
    end
    % compute for last year (assuming a working year of 5 days a week, i.e. 261 opening days a year, if the total number of day is shorter)
    if day(numyear,2)<261
        total=261;
    else
        total=day(numyear,2);
    end
    for jj=day(numyear,1):day(numyear,2)
        decimaldates1=[decimaldates1;endyear+((jj-1)/total)];
    end
    % finally, trim a number of initial periods equal to the number of lags, as these periods will be used to create initial conditions
    decimaldates1=decimaldates1(lags+1:end,:);
    stringdates1=stringdates1(lags+1:end,:);

    % finally, if the data is undated
elseif frequency==6
    % identify the number of periods covered by the sample
    startperiod=str2double(startdate(1,1:end-1));
    endperiod=str2double(enddate(1,1:end-1));
    % create the decimal value vector
    decimaldates1=(startperiod:endperiod)';
    % convert into strings
    stringdates1=cellfun(@num2str,num2cell(decimaldates1),'UniformOutput',0);
    % add a 'u' character at the end
    for ii=1:size(stringdates1,1)
        stringdates1{ii,1}=[stringdates1{ii,1} 'u'];
    end
    % trim a number of initial periods equal to the number of lags, as these periods will be used to create initial conditions
    decimaldates1=decimaldates1(lags+1:end,:);
    stringdates1=stringdates1(lags+1:end,:);
end

% from now on, the code applies only if forecasts have been selected
% if not, simply return empty matrices

if F==0 && CF==0
    decimaldates2=[];
    stringdates2=[];
    Fstartlocation=[];
    Fendlocation=[];
    stringdates3=[];
else

    % PHASE 2: CREATION OF DECIMAL DATES FOR THE FORECAST PERIOD (DECIMALDATES2)

    % determine first whether the final forecast period is included or not in the sample
    % first deal with data if it is yearly
    if frequency==1
        % simply check wether the sample end year is anterior to the forecast end year
        if str2double(enddate(1,1:end-1))<str2double(Fenddate(1,1:end-1))
            included=0;
        else
            included=1;
        end
        % if data is quarterly
    elseif frequency==2
        % first identify the year and quarter of the final sample date
        smplendyear=str2double(enddate(1,1:4));
        smplendquarter=str2double(enddate(1,6));
        % similarly, identify the year and quarter of the final forecast date
        Fendyear=str2double(Fenddate(1,1:4));
        Fendquarter=str2double(Fenddate(1,6));
        if smplendyear<Fendyear
            included=0;
        elseif smplendyear==Fendyear && smplendquarter<Fendquarter
            included=0;
        else
            included=1;
        end
        % if data is monthly
    elseif frequency==3
        % first identify the year and month of the final sample date
        temp=enddate;
        temp(1,5)=' ';
        [smplendyear,smplendmonth]=strtok(temp);
        smplendyear=str2double(smplendyear);
        smplendmonth=str2double(smplendmonth);
        % identify the year and month of the final forecast date
        temp=Fenddate;
        temp(1,5)=' ';
        [Fendyear,Fendmonth]=strtok(temp);
        Fendyear=str2double(Fendyear);
        Fendmonth=str2double(Fendmonth);
        if smplendyear<Fendyear
            included=0;
        elseif smplendyear==Fendyear && smplendmonth<Fendmonth
            included=0;
        else
            included=1;
        end
        % if data is weekly
    elseif frequency==4
        % first identify the year and week of the final sample date
        temp=enddate;
        temp(1,5)=' ';
        [smplendyear,smplendweek]=strtok(temp);
        smplendyear=str2double(smplendyear);
        smplendweek=str2double(smplendweek);
        % identify the year and week of the final forecast date
        temp=Fenddate;
        temp(1,5)=' ';
        [Fendyear,Fendweek]=strtok(temp);
        Fendyear=str2double(Fendyear);
        Fendweek=str2double(Fendweek);
        if smplendyear<Fendyear
            included=0;
        elseif smplendyear==Fendyear && smplendweek<Fendweek
            included=0;
        else
            included=1;
        end
        % if data is daily
    elseif frequency==5
        % first identify the year and day of the final sample date
        temp=enddate;
        temp(1,5)=' ';
        [smplendyear,smplendday]=strtok(temp);
        smplendyear=str2double(smplendyear);
        smplendday=str2double(smplendday);
        % identify the year and day of the final forecast date
        temp=Fenddate;
        temp(1,5)=' ';
        [Fendyear,Fendday]=strtok(temp);
        Fendyear=str2double(Fendyear);
        Fendday=str2double(Fendday);
        if smplendyear<Fendyear
            included=0;
        elseif smplendyear==Fendyear && smplendday<Fendday
            included=0;
        else
            included=1;
        end
        % finally, if data is undated
    elseif frequency==6
        % simply check wether the sample end period is anterior to the forecast end period
        if str2double(enddate(1,1:end-1))<str2double(Fenddate(1,1:end-1))
            included=0;
        else
            included=1;
        end
    end

    % if the final forecast period is included in the sample, simply define decimaldates2 as decimaldates1
    if included==1
        decimaldates2=decimaldates1;
        stringdates2=stringdates1;

        % if the final forecast period lies beyond the end of the sample, define a new vector of decimal dates running from the sample start until the last forecast period
    elseif included==0
        % deal first with the data if it is yearly
        if frequency==1
            % simply complete decimaldates1 up to the final forecast period
            decimaldates2=[decimaldates1;(decimaldates1(end,1)+1:str2double(Fenddate(1,1:end-1)))'];
            stringdates2=cellfun(@num2str,num2cell(decimaldates2),'UniformOutput',0);
            % add a 'y' character at the end
            for ii=1:size(stringdates2,1)
                stringdates2{ii,1}=[stringdates2{ii,1} 'y'];
            end

            % if the data is quarterly
        elseif frequency==2
            % initiate the series
            decimaldates2=decimaldates1;
            stringdates2=stringdates1;
            % identify the year and quarter of the last sample period
            endyear=str2double(enddate(1,1:4));
            endquarter=str2double(enddate(1,6));
            % identify the year and quarter of the last forecast period
            Fendyear=str2double(Fenddate(1,1:4));
            Fendquarter=str2double(Fenddate(1,6));
            % advance sample end by one period
            year=endyear;
            quarter=endquarter;
            if quarter<4
                quarter=quarter+1;
            elseif quarter==4
                year=year+1;
                quarter=1;
            end
            while year<=Fendyear-1
                while quarter<=4
                    decimaldates2=[decimaldates2;year+(quarter-1)/4];
                    temp=[num2str(year) 'q' num2str(quarter)];
                    stringdates2{end+1,1}=temp;
                    quarter=quarter+1;
                end
                quarter=1;
                year=year+1;
            end
            % complete with final year
            while quarter<=Fendquarter
                decimaldates2=[decimaldates2;year+(quarter-1)/4];
                temp=[num2str(year) 'q' num2str(quarter)];
                stringdates2{end+1,1}=temp;
                quarter=quarter+1;
            end

            % if the data is monthly
        elseif frequency==3
            % initiate the series
            decimaldates2=decimaldates1;
            stringdates2=stringdates1;
            % identify the year and month of the last sample period
            temp=enddate;
            temp(1,5)=' ';
            [endyear,endmonth]=strtok(temp);
            endyear=str2double(endyear);
            endmonth=str2double(endmonth);
            % identify the year and month of the last forecast period
            temp=Fenddate;
            temp(1,5)=' ';
            [Fendyear,Fendmonth]=strtok(temp);
            Fendyear=str2double(Fendyear);
            Fendmonth=str2double(Fendmonth);
            % advance sample end by one period
            year=endyear;
            month=endmonth;
            if month<12
                month=month+1;
            elseif month==12
                year=year+1;
                month=1;
            end
            while year<=Fendyear-1
                while month<=12
                    decimaldates2=[decimaldates2;year+(month-1)/12];
                    temp=[num2str(year) 'm' num2str(month)];
                    stringdates2{end+1,1}=temp;
                    month=month+1;
                end
                month=1;
                year=year+1;
            end
            % complete with final year
            while month<=Fendmonth
                decimaldates2=[decimaldates2;year+(month-1)/12];
                temp=[num2str(year) 'm' num2str(month)];
                stringdates2{end+1,1}=temp;
                month=month+1;
            end

            % if the data is weekly
        elseif frequency==4
            % first identify the year and week of the final forecast date
            temp=Fenddate;
            temp(1,5)=' ';
            [Fendyear,Fendweek]=strtok(temp);
            Fendyear=str2double(Fendyear);
            Fendweek=str2double(Fendweek);
            % identify the year and week of the final data set date
            temp=datestrings{end,1};
            temp(1,5)=' ';
            [dataendyear,dataendweek]=strtok(temp);
            dataendyear=str2double(dataendyear);
            dataendweek=str2double(dataendweek);
            % now, there are two possibilities: either the forecast period is entirely included in the dataset, or it goes beyond the dataset
            % if entirely included in the dataset
            if Fendyear<dataendyear || (Fendyear==dataendyear && Fendweek<dataendweek)
                % then just copy the date strings from sample start to forecast end
                % find position for sample start
                start=find(cellfun(@isempty,strfind(datestrings,startdate))==0);
                % find position for forecast end
                finish=find(cellfun(@isempty,strfind(datestrings,Fenddate))==0);
                % create the strings
                stringdates2=datestrings(start:finish,1);
                % if the forecast periods goes beyond the data set
            else
                % find position for sample start
                start=find(cellfun(@isempty,strfind(datestrings,startdate))==0);
                % copy the dataset from sample start to its end
                stringdates2=datestrings(start:end,1);
                % advance the end of the dataset by one period
                if dataendweek<52
                    year=dataendyear;
                    week=dataendweek+1;
                else
                    year=dataendyear+1;
                    week=1;
                end
                % now complete stringdates2
                for ii=dataendyear:Fendyear-1
                    while week<=52
                        stringdates2{end+1,1}=[num2str(year) 'w' num2str(week)];
                        week=week+1;
                    end
                    week=1;
                    year=year+1;
                end
                % complete for the final year
                while week<=Fendweek
                    stringdates2{end+1,1}=[num2str(year) 'w' num2str(week)];
                    week=week+1;
                end
            end
            % finally, trim a number of initial conditions equal to the number of lags
            stringdates2=stringdates2(lags+1:end,1);
            % now that the strings are obtained, use them to generate the decimal dates
            % for each year in stringdates2, identify the number of weeks
            maxweek=zeros(size(stringdates2,1),1);
            for ii=str2double(startdate(1,1:4)):Fendyear-1
                periods=strfind(stringdates2,num2str(ii));
                for jj=1:size(periods,1)
                    if isempty(periods{jj,1})
                        periods{jj,1}=0;
                    end
                end
                periods=cell2mat(periods);
                location=max(find(periods==1));
                temp=char(stringdates2(location,1));
                temp(1,5)=' ';
                [~,weeknum]=strtok(temp);
                maxweek=maxweek+periods*str2double(weeknum);
            end
            % complete for the final year
            periods=strfind(stringdates2,num2str(Fendyear));
            for jj=1:size(periods,1)
                if isempty(periods{jj,1})
                    periods{jj,1}=0;
                end
            end
            periods=cell2mat(periods);
            maxweek=maxweek+periods*max(52,Fendweek);
            % finally, compute the decimal dates
            for ii=1:size(stringdates2,1)
                temp=stringdates2{ii,1};
                temp(1,5)=' ';
                [~,weeknum]=strtok(temp);
                decimaldates2(ii,1)=str2double(temp(1,1:4))+str2double(weeknum)/maxweek(ii,1);
            end

            % if the data is daily
        elseif frequency==5
            % first identify the year and day of the final forecast date
            temp=Fenddate;
            temp(1,5)=' ';
            [Fendyear,Fendday]=strtok(temp);
            Fendyear=str2double(Fendyear);
            Fendday=str2double(Fendday);
            % identify the year and day of the final data set date
            temp=datestrings{end,1};
            temp(1,5)=' ';
            [dataendyear,dataendday]=strtok(temp);
            dataendyear=str2double(dataendyear);
            dataendday=str2double(dataendday);
            % now, there are two possibilities: either the forecast period is entirely included in the dataset, or it goes beyond the dataset
            % if entirely included in the dataset
            if Fendyear<dataendyear || (Fendyear==dataendyear && Fendday<dataendday)
                % then just copy the date strings from sample start to forecast end
                % find position for sample start
                start=find(cellfun(@isempty,strfind(datestrings,startdate))==0);
                % find position for forecast end
                finish=find(cellfun(@isempty,strfind(datestrings,Fenddate))==0);
                % create the strings
                stringdates2=datestrings(start:finish,1);
                % if the forecast periods goes beyond the data set
            else
                % find position for sample start
                start=find(cellfun(@isempty,strfind(datestrings,startdate))==0);
                % copy the dataset from sample start to its end
                stringdates2=datestrings(start:end,1);
                % advance the day of the dataset by one period
                if dataendday<261
                    year=dataendyear;
                    day=dataendday+1;
                else
                    year=dataendyear+1;
                    day=1;
                end
                % now complete stringdates2
                for ii=dataendyear:Fendyear-1
                    while day<=261
                        stringdates2{end+1,1}=[num2str(year) 'd' num2str(day)];
                        day=day+1;
                    end
                    day=1;
                    year=year+1;
                end
                % complete for the final year
                while day<=Fendday
                    stringdates2{end+1,1}=[num2str(year) 'd' num2str(day)];
                    day=day+1;
                end
            end
            % finally, trim a number of initial conditions equal to the number of lags
            stringdates2=stringdates2(lags+1:end,1);
            % now that the strings are obtained, use them to generate the decimal dates
            % for each year in stringdates2, identify the number of day
            maxday=zeros(size(stringdates2,1),1);
            for ii=str2double(startdate(1,1:4)):Fendyear-1
                periods=strfind(stringdates2,num2str(ii));
                for jj=1:size(periods,1)
                    if isempty(periods{jj,1})
                        periods{jj,1}=0;
                    end
                end
                periods=cell2mat(periods);
                location=max(find(periods==1));
                temp=char(stringdates2(location,1));
                temp(1,5)=' ';
                [~,daynum]=strtok(temp);
                maxday=maxday+periods*str2double(daynum);
            end
            % complete for the final year
            periods=strfind(stringdates2,num2str(Fendyear));
            for jj=1:size(periods,1)
                if isempty(periods{jj,1})
                    periods{jj,1}=0;
                end
            end
            periods=cell2mat(periods);
            maxday=maxday+periods*max(261,Fendday);
            % finally, compute the decimal dates
            for ii=1:size(stringdates2,1)
                temp=stringdates2{ii,1};
                temp(1,5)=' ';
                [~,daynum]=strtok(temp);
                decimaldates2(ii,1)=str2double(temp(1,1:4))+str2double(daynum)/maxday(ii,1);
            end

            % finally, if the data is undated
        elseif frequency==6
            % simply complete decimaldates1 up to the final forecast period
            decimaldates2=[decimaldates1;(decimaldates1(end,1)+1:str2double(Fenddate(1,1:end-1)))'];
            stringdates2=cellfun(@num2str,num2cell(decimaldates2),'UniformOutput',0);
            % add a 'u' character at the end
            for ii=1:size(stringdates2,1)
                stringdates2{ii,1}=[stringdates2{ii,1} 'u'];
            end
        end
    end

    % PHASE 3: IDENTIFICATION OF THE POSITION OF THE INITIAL FORECAST PERIOD (IN TERMS OF THE VECTOR DECIMALDATES2)

    % identify the position of the start period for the forecasts
    % if the start period has been selected as the first period after the sample end, identifies it directly
    if Fendsmpl==1
        Fstartlocation=size(stringdates1,1)+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(stringdates1,Fstartdate));
    end

    % identify the position of the end period for the forecasts
    % if the end period is included in the sample, it can be identified directly
    if included==1
        Fendlocation=find(strcmp(stringdates1,Fenddate));
        % if the end period is beyond the sample, then its position is simply the last period of datestrings2
    elseif included==0
        Fendlocation=size(stringdates2,1);
    end

    % PHASE 3: CREATION OF DATE STRINGS FOR THE COMMON PERIOD

    % these date strings will be used only at one point: for the display of forecast evaluation
    % to identify the strings, simply copy from datestrings2 the dates over the common forecast period
    startfcperiod=find(strcmp(stringdates2,Fstartdate));
    endfcperiod=find(strcmp(stringdates2,Fcenddate));
    stringdates3=stringdates2(startfcperiod:endfcperiod,1);

end


function d2img = draw_2Dskeleton_ICVL(tline, coord_pixel)

    db_path = '/home/gyeongsikmoon/workspace/Data/Hand_pose_estimation/ICVL/Testing/Depth/';
    jointNum =  16;
     cubicSz = 200;
    imgWidth = 320;
    imgHeight = 240;

    coord_pixel = squeeze(coord_pixel);
    rgb_img = zeros(imgHeight,imgWidth,3);
    refDepths = zeros(1,jointNum);
    line_width = 4;

    splitted = strsplit(tline);
    img_name = splitted{1};
    bin_name = strcat(db_path,img_name(1:size(img_name,2)-3),'bin');

    for jid = 1:jointNum
        refDepths(1,jid) = str2num(splitted{(jid-1)*3+4});
    end
    refDepth = (min(refDepths(:)) + max(refDepths(:)))/2;

    fp_bin = fopen(bin_name,'r');
    img = fread(fp_bin,[imgWidth imgHeight],'float');
    img = permute(img,[2,1]);
    img(img==0) = refDepth+cubicSz/2;
    fclose(fp_bin);

    img(img>refDepth+cubicSz/2) = refDepth + cubicSz/2;
    img(img<refDepth-cubicSz/2) = refDepth - cubicSz/2;
    img = img - refDepth;
    img = img/(cubicSz/2);

    img = img + 1;
    img = img/2;

    rgb_img(:,:,1) = img*255/255;
    rgb_img(:,:,2) = img*240/255;
    rgb_img(:,:,3) = img*204/255;

    f = figure;
    set(f, 'visible', 'off');
    imshow(rgb_img);
    hold on;

    plot([coord_pixel(1,1),coord_pixel(1,2)],[coord_pixel(2,1),coord_pixel(2,2)],'Color',[255/255,153/255,153/255],'LineWidth',line_width); %wrist to thumb root
    plot([coord_pixel(1,2),coord_pixel(1,3)],[coord_pixel(2,2),coord_pixel(2,3)],'Color',[255/255,102/255,102/255],'LineWidth',line_width) %thumb root to thumb mid
    plot([coord_pixel(1,3),coord_pixel(1,4)],[coord_pixel(2,3),coord_pixel(2,4)],'Color',[255/255,51/255,51/255],'LineWidth',line_width) %thumb mid to thumb tip

    plot([coord_pixel(1,1),coord_pixel(1,5)],[coord_pixel(2,1),coord_pixel(2,5)],'Color',[153/255,255/255,153/255],'LineWidth',line_width) %wrist to index root
    plot([coord_pixel(1,5),coord_pixel(1,6)],[coord_pixel(2,5),coord_pixel(2,6)],'Color',[102/255,255/255,102/255],'LineWidth',line_width) %index root to index mid
    plot([coord_pixel(1,6),coord_pixel(1,7)],[coord_pixel(2,6),coord_pixel(2,7)],'Color',[51/255,255/255,51/255],'LineWidth',line_width) %index mid to index tip

    plot([coord_pixel(1,1),coord_pixel(1,8)],[coord_pixel(2,1),coord_pixel(2,8)],'Color',[255/255,204/255,153/255],'LineWidth',line_width) %wrist to middle root
    plot([coord_pixel(1,8),coord_pixel(1,9)],[coord_pixel(2,8),coord_pixel(2,9)],'Color',[255/255,178/255,102/255],'LineWidth',line_width) %middle root to middle mid
    plot([coord_pixel(1,9),coord_pixel(1,10)],[coord_pixel(2,9),coord_pixel(2,10)],'Color',[255/255,153/255,51/255],'LineWidth',line_width) %middle mid to middle tip

    plot([coord_pixel(1,1),coord_pixel(1,11)],[coord_pixel(2,1),coord_pixel(2,11)],'Color',[153/255,204/255,255/255],'LineWidth',line_width) %wrist to ring root
    plot([coord_pixel(1,11),coord_pixel(1,12)],[coord_pixel(2,11),coord_pixel(2,12)],'Color',[102/255,178/255,255/255],'LineWidth',line_width) %ring root to ring mid
    plot([coord_pixel(1,12),coord_pixel(1,13)],[coord_pixel(2,12),coord_pixel(2,13)],'Color',[51/255,153/255,255/255],'LineWidth',line_width) %ring mid to ring tip

    plot([coord_pixel(1,1),coord_pixel(1,14)],[coord_pixel(2,1),coord_pixel(2,14)],'Color',[255/255,153/255,255/255],'LineWidth',line_width) %wrist to pinky root
    plot([coord_pixel(1,14),coord_pixel(1,15)],[coord_pixel(2,14),coord_pixel(2,15)],'Color',[255/255,102/255,255/255],'LineWidth',line_width) %pinky root to pinky mid
    plot([coord_pixel(1,15),coord_pixel(1,16)],[coord_pixel(2,15),coord_pixel(2,16)],'Color',[255/255,51/255,255/255],'LineWidth',line_width) %pinky mid to pinky tip

    colorList = [
    230/255 230/255 0/255;

    255/255,153/255,153/255;
    255/255 102/255 102/255;
    255/255 51/255 51/255;

    153/255,255/255,153/255;
    102/255,255/255,102/255;
    51/255,255/255,51/255;

    255/255,204/255,153/255;
    255/255 178/255 102/255;
    255/255 153/255 51/255;

    153/255,204/255,255/255;
    102/255 178/255 255/255;
    51/255 153/255 255/255;

    255/255,153/255,255/255;
    255/255,102/255,255/255;
    255/255,51/255,255/255];
    scatter(coord_pixel(1,:),coord_pixel(2,:),100,colorList,'filled');

    set(gca,'Units','normalized','Position',[0 0 1 1]);  %# Modify axes size
    set(gcf,'Units','pixels','Position',[200 200 2*imgWidth 2*imgHeight]);  %# Modify figure size

    frame = getframe(gcf);
    framedata = frame.cdata;

    xmin = min(coord_pixel(1,:));
    xmax = max(coord_pixel(1,:));
    ymin = min(coord_pixel(2,:));
    ymax = max(coord_pixel(2,:));

    len = max(xmax-xmin+1,ymax-ymin+1) + 70;
    xcenter = (xmin + xmax)/2;
    ycenter = (ymin + ymax)/2;

    xmin = max(round(xcenter - len/2),1);
    xmax = min(round(xmin + len),imgWidth);
    ymin = max(round(ycenter - len/2),1);
    ymax = min(round(ymin + len),imgHeight);
    framedata = framedata(2*ymin:2*ymax,2*xmin:2*xmax,:);

    hold off;
    close(f);

    d2img = framedata;
end


function [ output_meta ] = meta2sicd_rs_xml( xml_domnode, beta_domnode, noise_domnode )
%META2SICD_RS_XML Converts Radarsat product.xml description into a SICD-style metadata structure
%
% Takes as input a Document Object Model (DOM) node from the RS2
% product.xml descriptor file.
%
% This function does NOT handle ScanSAR datasets.
%
% There is an outstanding question with regard to the meaning of the
% pulseRepetitionFrequency as provided.  See comments in Timeline section
% below.
%
% Written by: Wade Schwartzkopf, NGA/Research
%
% //////////////////////////////////////////
% /// CLASSIFICATION: UNCLASSIFIED       ///
% //////////////////////////////////////////

%% Setup
SECONDS_IN_A_DAY = 24*60*60;
xp=javax.xml.xpath.XPathFactory.newInstance.newXPath();

%% CollectionInfo
output_meta.CollectionInfo.CollectorName=char(xp.evaluate(...
    xpath_str({'product','sourceAttributes','satellite'}),xml_domnode));
if strcmpi(output_meta.CollectionInfo.CollectorName, 'RADARSAT-2')
    gen = 'RS2';
elseif strncmpi(output_meta.CollectionInfo.CollectorName, 'RCM', 3)
    gen = 'RCM';
end
[rawDataStartTime, rawDataStartTimeFrac] = datenum_w_frac(char(xp.evaluate(...
    xpath_str({'product','sourceAttributes','rawDataStartTime'}),...
    xml_domnode)));
if strcmp(gen,'RS2')
    output_meta.CollectionInfo.CoreName = [... % Start with NGA-like prefix
        upper(datestr(rawDataStartTime,'ddmmmyy')) 'RS02' ...
        char(xp.evaluate(xpath_str({'product','sourceAttributes','imageId'}),xml_domnode))];
elseif strcmp(gen, 'RCM')
    output_meta.CollectionInfo.CoreName = [... % Start with NGA-like prefix
        upper(datestr(rawDataStartTime,'ddmmmyy')) ...
        'RCM' output_meta.CollectionInfo.CollectorName(end) ...
        datestr(rawDataStartTime,'HHMMSS')]; % Make time of day unique identier within day
    % ScanSAR might need multiple CoreNames, one for each burst?
end
output_meta.CollectionInfo.CollectType='MONOSTATIC';
output_meta.CollectionInfo.RadarMode.ModeID=char(xp.evaluate(...
    xpath_str({'product','sourceAttributes','beamModeMnemonic'}),xml_domnode));
beamMode = char(xp.evaluate(xpath_str({...
    'product','sourceAttributes','beamMode'}),xml_domnode));
acqType = char(xp.evaluate(xpath_str({...
    'product','sourceAttributes','radarParameters','acquisitionType'}),xml_domnode));
if ((~isempty(beamMode) && strncmpi(beamMode, 'SPOTLIGHT', 9)) || ...
   (~isempty(acqType) && strncmpi(acqType, 'SPOTLIGHT', 9)) || ...
   ~isempty(strfind(output_meta.CollectionInfo.RadarMode.ModeID,'SL')))
    output_meta.CollectionInfo.RadarMode.ModeType = 'SPOTLIGHT';
elseif strcmpi(output_meta.CollectionInfo.RadarMode.ModeID(1:2), 'SC')
    error('META2SICD_RS_XML:RS_SCANSAR', 'ScanSAR mode data is not currently handled.');
else % Finally assume it's stripmap
    output_meta.CollectionInfo.RadarMode.ModeType = 'STRIPMAP';
end
if strcmp(gen, 'RS2') % All RS2 data is unclassified
    output_meta.CollectionInfo.Classification='UNCLASSIFIED';
elseif strcmp(gen, 'RCM') % RCM has this as a specific field
    output_meta.CollectionInfo.Classification=upper(char(xp.evaluate(...
        xpath_str({'product', 'securityAttributes', 'securityClassification'}), xml_domnode)));
end

%% ImageCreation
output_meta.ImageCreation.Application=char(xp.evaluate(...
    xpath_str({'product','imageGenerationParameters','generalProcessingInformation','softwareVersion'}),...
    xml_domnode));
output_meta.ImageCreation.DateTime=datenum(char(xp.evaluate(...
    xpath_str({'product','imageGenerationParameters','generalProcessingInformation','processingTime'}),...
    xml_domnode)),'yyyy-mm-ddTHH:MM:SS.FFF');
output_meta.ImageCreation.Site=char(xp.evaluate(...
    xpath_str({'product','imageGenerationParameters','generalProcessingInformation','processingFacility'}),...
    xml_domnode));
output_meta.ImageCreation.Profile='Prototype';

%% ImageData
if strcmp(gen, 'RS2')
    output_meta.ImageData.NumRows=uint32(str2double(xp.evaluate(...
       xpath_str({'product','imageAttributes','rasterAttributes','numberOfSamplesPerLine'}),...
       xml_domnode)));
    output_meta.ImageData.NumCols=uint32(str2double(xp.evaluate(...
       xpath_str({'product','imageAttributes','rasterAttributes','numberOfLines'}),...
        xml_domnode)));
elseif strcmp(gen, 'RCM')
    output_meta.ImageData.NumRows=uint32(str2double(xp.evaluate(...
       xpath_str({'product','sceneAttributes','imageAttributes','samplesPerLine'}),...
       xml_domnode)));
    output_meta.ImageData.NumCols=uint32(str2double(xp.evaluate(...
       xpath_str({'product','sceneAttributes','imageAttributes','numLines'}),...
        xml_domnode)));
end
output_meta.ImageData.FullImage=output_meta.ImageData;
output_meta.ImageData.FirstRow=uint32(0); output_meta.ImageData.FirstCol=uint32(0);
output_meta.ImageData.PixelType='RE16I_IM16I';  % RS2 always 16-bit
if strcmp(gen, 'RCM') && str2double(xp.evaluate(...  % RCM can be 16 or 32
       xpath_str({'product','imageReferenceAttributes','rasterAttributes','bitsPerSample'}),...
        xml_domnode)) == 32
    output_meta.ImageData.PixelType='RE32F_IM32F';
end
% Seems that all pixels are always valid
output_meta.ImageData.ValidData.Vertex(1).Row = uint32(0);
output_meta.ImageData.ValidData.Vertex(1).Col = uint32(0);
output_meta.ImageData.ValidData.Vertex(2).Row = uint32(0);
output_meta.ImageData.ValidData.Vertex(2).Col = output_meta.ImageData.NumCols-1;
output_meta.ImageData.ValidData.Vertex(3).Row = output_meta.ImageData.NumRows-1;
output_meta.ImageData.ValidData.Vertex(3).Col = output_meta.ImageData.NumCols-1;
output_meta.ImageData.ValidData.Vertex(4).Row = output_meta.ImageData.NumRows-1;
output_meta.ImageData.ValidData.Vertex(4).Col = uint32(0);

%% SCP
if strcmp(gen, 'RS2')
    im_at_str = 'imageAttributes';
elseif strcmp(gen, 'RCM')
    im_at_str = 'imageReferenceAttributes';
end
% There are many different equally valid options for picking the SCP point.
% One way is to chose the tie point that is closest to the image center.
num_tie_points=str2double(xp.evaluate(...
    ['count(' xpath_str({'product',im_at_str,'geographicInformation','geolocationGrid','imageTiePoint'}) ')'],...
    xml_domnode));
tiePointPixels = zeros(2,num_tie_points);
tiePointGeo    = zeros(3,num_tie_points);
for i=1:num_tie_points
    tiePointPixels(1,i) = str2double(xp.evaluate(...
            [xpath_str({'product',im_at_str,'geographicInformation','geolocationGrid','imageTiePoint'})...
            '[' num2str(i) ']' xpath_str({'imageCoordinate','pixel'})],...
            xml_domnode));
    tiePointPixels(2,i) = str2double(xp.evaluate(...
            [xpath_str({'product',im_at_str,'geographicInformation','geolocationGrid','imageTiePoint'})...
            '[' num2str(i) ']' xpath_str({'imageCoordinate','line'})],...
            xml_domnode));
    tiePointGeo(1,i) = str2double(xp.evaluate(...
            [xpath_str({'product',im_at_str,'geographicInformation','geolocationGrid','imageTiePoint'})...
            '[' num2str(i) ']' xpath_str({'geodeticCoordinate','latitude'})],...
            xml_domnode));
    tiePointGeo(2,i) = str2double(xp.evaluate(...
            [xpath_str({'product',im_at_str,'geographicInformation','geolocationGrid','imageTiePoint'})...
            '[' num2str(i) ']' xpath_str({'geodeticCoordinate','longitude'})],...
            xml_domnode));
    tiePointGeo(3,i) = str2double(xp.evaluate(...
            [xpath_str({'product',im_at_str,'geographicInformation','geolocationGrid','imageTiePoint'})...
            '[' num2str(i) ']' xpath_str({'geodeticCoordinate','height'})],...
            xml_domnode));
end
% Pick tie point closest to center for SCP
centerPoint = [double(output_meta.ImageData.NumRows-1)/2.0; ...
               double(output_meta.ImageData.NumCols-1)/2.0];
D = (tiePointPixels - repmat(centerPoint,1,num_tie_points));
[C,scp_index] = min( sqrt( sum(D.^2) ) );
output_meta.ImageData.SCPPixel.Row = uint32(tiePointPixels(1,scp_index));
output_meta.ImageData.SCPPixel.Col = uint32(tiePointPixels(2,scp_index));

% Sometimes lines up with SCP in Lockheed SICDs (and sometimes not):
% output_meta.ImageData.SCPPixel.Col = ceil(output_meta.ImageData.NumCols/2)-1;
% output_meta.ImageData.SCPPixel.Row = floor(output_meta.ImageData.NumRows/2);

%% GeoData
% All RS2 and RCM data we know use the WGS84 model, although it is stated
% in slightly different XML fields in the RS2 and RCM product.xml
output_meta.GeoData.EarthModel='WGS_84';
% Initially, we just seed this with a rough value.  Later we will put in
% something more precise.
output_meta.GeoData.SCP.LLH.Lat = tiePointGeo(1,scp_index);
output_meta.GeoData.SCP.LLH.Lon = tiePointGeo(2,scp_index);
output_meta.GeoData.SCP.LLH.HAE = tiePointGeo(3,scp_index);
pos_ecf = geodetic_to_ecf(tiePointGeo(:,scp_index));
output_meta.GeoData.SCP.ECF.X = pos_ecf(1);
output_meta.GeoData.SCP.ECF.Y = pos_ecf(2);
output_meta.GeoData.SCP.ECF.Z = pos_ecf(3);
% Corner coordinates will be computed later by derived_sicd_fields.
% Corners
% min_row=min(tiePointPixels(1,:));
% min_col=min(tiePointPixels(2,:));
% max_row=max(tiePointPixels(1,:));
% max_col=max(tiePointPixels(2,:));
% ll_index=find((tiePointPixels(1,:)==min_row)&(tiePointPixels(2,:)==min_col), 1);
% ul_index=find((tiePointPixels(1,:)==max_row)&(tiePointPixels(2,:)==min_col), 1);
% ur_index=find((tiePointPixels(1,:)==max_row)&(tiePointPixels(2,:)==max_col), 1);
% lr_index=find((tiePointPixels(1,:)==min_row)&(tiePointPixels(2,:)==max_col), 1);
% output_meta.GeoData.ImageCorners.ICP.FRFC.Lat=tiePointGeo(1,ul_index);
% output_meta.GeoData.ImageCorners.ICP.FRFC.Lon=tiePointGeo(2,ul_index);
% output_meta.GeoData.ImageCorners.ICP.FRLC.Lat=tiePointGeo(1,ur_index);
% output_meta.GeoData.ImageCorners.ICP.FRLC.Lon=tiePointGeo(2,ur_index);
% output_meta.GeoData.ImageCorners.ICP.LRLC.Lat=tiePointGeo(1,lr_index);
% output_meta.GeoData.ImageCorners.ICP.LRLC.Lon=tiePointGeo(2,lr_index);
% output_meta.GeoData.ImageCorners.ICP.LRFC.Lat=tiePointGeo(1,ll_index);
% output_meta.GeoData.ImageCorners.ICP.LRFC.Lon=tiePointGeo(2,ll_index);

%% State vectors
num_state_vectors=str2double(xp.evaluate(...
    ['count(' xpath_str({'product','sourceAttributes','orbitAndAttitude','orbitInformation','stateVector'}) ')'],...
    xml_domnode));
state_vector_T  = zeros(1,num_state_vectors);
state_vector_T_frac  = zeros(1,num_state_vectors);
state_vector_X  = zeros(1,num_state_vectors);
state_vector_Y  = zeros(1,num_state_vectors);
state_vector_Z  = zeros(1,num_state_vectors);
% state_vector_VX = zeros(1,num_state_vectors);
% state_vector_VY = zeros(1,num_state_vectors);
% state_vector_VZ = zeros(1,num_state_vectors);
for i=1:num_state_vectors
    timeStamp = char(xp.evaluate(...
        [xpath_str({'product','sourceAttributes','orbitAndAttitude','orbitInformation','stateVector'})...
        '[' num2str(i) ']' xpath_str({'timeStamp'})],...
        xml_domnode));
    [state_vector_T(i), state_vector_T_frac(i)] = datenum_w_frac(timeStamp);

    state_vector_X(i) = str2double(xp.evaluate(...
        [xpath_str({'product','sourceAttributes','orbitAndAttitude','orbitInformation','stateVector'})...
        '[' num2str(i) ']' xpath_str({'xPosition'})],...
        xml_domnode));
    state_vector_Y(i) = str2double(xp.evaluate(...
        [xpath_str({'product','sourceAttributes','orbitAndAttitude','orbitInformation','stateVector'})...
        '[' num2str(i) ']' xpath_str({'yPosition'})],...
        xml_domnode));
    state_vector_Z(i) = str2double(xp.evaluate(...
        [xpath_str({'product','sourceAttributes','orbitAndAttitude','orbitInformation','stateVector'})...
        '[' num2str(i) ']' xpath_str({'zPosition'})],...
        xml_domnode));
%     state_vector_VX(i) = str2double(xp.evaluate(...
%         [xpath_str({'product','sourceAttributes','orbitAndAttitude','orbitInformation','stateVector'})...
%         '[' num2str(i) ']' xpath_str({'xVelocity'})],...
%         xml_domnode));
%     state_vector_VY(i) = str2double(xp.evaluate(...
%         [xpath_str({'product','sourceAttributes','orbitAndAttitude','orbitInformation','stateVector'})...
%         '[' num2str(i) ']' xpath_str({'yVelocity'})],...
%         xml_domnode));
%     state_vector_VZ(i) = str2double(xp.evaluate(...
%         [xpath_str({'product','sourceAttributes','orbitAndAttitude','orbitInformation','stateVector'})...
%         '[' num2str(i) ']' xpath_str({'zVelocity'})],...
%         xml_domnode));
end
state_vector_T = round((state_vector_T-rawDataStartTime)*SECONDS_IN_A_DAY) + ... % Convert from days to secs
        (state_vector_T_frac-rawDataStartTimeFrac); % Handle fractional seconds
% sv2poly.m shows ways to determine best polynomial order, but 5th is almost always best
polyorder = min(5, numel(state_vector_T) - 1);
P_x = polyfit(state_vector_T, state_vector_X, polyorder);
P_y = polyfit(state_vector_T, state_vector_Y, polyorder);
P_z = polyfit(state_vector_T, state_vector_Z, polyorder);
output_meta.Position.ARPPoly.X = P_x(end:-1:1).';
output_meta.Position.ARPPoly.Y = P_y(end:-1:1).';
output_meta.Position.ARPPoly.Z = P_z(end:-1:1).';

%% Grid
if strcmp(char(xp.evaluate(...
    xpath_str({'product','imageGenerationParameters','generalProcessingInformation','productType'}),...
    xml_domnode)),'SLC')
    output_meta.Grid.ImagePlane = 'SLANT';
    output_meta.Grid.Type = 'RGZERO';
else
    output_meta.Grid.ImagePlane = 'GROUND';
end
output_meta.Grid.Row.SS = str2double(xp.evaluate(...
    xpath_str({'product',im_at_str,'rasterAttributes','sampledPixelSpacing'}),...
    xml_domnode));
% Col.SS is derived after DRateSFPoly below, rather than used from this
% given field, so that SICD metadata can be internally consistent.
% output_meta.Grid.Col.SS = str2double(xp.evaluate(...
%     xpath_str({'product',im_at_str,'rasterAttributes','sampledLineSpacing'}),...
%     xml_domnode));
output_meta.Grid.Row.Sgn = -1; % Always true for RS2
output_meta.Grid.Col.Sgn = -1; % Always true for RS2
fc = str2double(xp.evaluate(...
    xpath_str({'product','sourceAttributes','radarParameters','radarCenterFrequency'}),...
    xml_domnode)); % Center frequency
output_meta.Grid.Row.ImpRespBW = 2*str2double(xp.evaluate(...
    xpath_str({'product','imageGenerationParameters','sarProcessingInformation','totalProcessedRangeBandwidth'}),...
    xml_domnode))/SPEED_OF_LIGHT;
dop_bw = str2double(xp.evaluate(...
    xpath_str({'product','imageGenerationParameters','sarProcessingInformation','totalProcessedAzimuthBandwidth'}),...
    xml_domnode)); % Doppler bandwidth
[zd_last, zd_last_frac] = datenum_w_frac(char(xp.evaluate(...
    xpath_str({'product','imageGenerationParameters','sarProcessingInformation','zeroDopplerTimeLastLine'}),...
    xml_domnode)));
[zd_first, zd_first_frac] = datenum_w_frac(char(xp.evaluate(...
    xpath_str({'product','imageGenerationParameters','sarProcessingInformation','zeroDopplerTimeFirstLine'}),...
    xml_domnode)));
ss_zd_s = abs(round((zd_last-zd_first)*SECONDS_IN_A_DAY) + ... % Convert from days to secs
        (zd_last_frac-zd_first_frac))/... % Handle fractional seconds
        double(output_meta.ImageData.NumCols-1); % Image column spacing in zero doppler time (seconds)
output_meta.Grid.Row.KCtr = 2*fc/SPEED_OF_LIGHT;
output_meta.Grid.Col.KCtr = 0;
output_meta.Grid.Row.DeltaK1 = -output_meta.Grid.Row.ImpRespBW/2;
output_meta.Grid.Row.DeltaK2 = -output_meta.Grid.Row.DeltaK1;
output_meta.Grid.Row.DeltaKCOAPoly = 0;
% Constants used to compute weighting parameters
NUM_SAMPLES = 512;
OVERSAMPLE = 1024;
output_meta.Grid.Row.WgtType.WindowName = upper(char(xp.evaluate(...
    xpath_str({'product','imageGenerationParameters','sarProcessingInformation','rangeWindow','windowName'}),...
    xml_domnode)));
if strcmpi(output_meta.Grid.Row.WgtType.WindowName,'KAISER') % The usual RS2 weigting
    output_meta.Grid.Row.WgtType.Parameter.name = 'BETA';
    output_meta.Grid.Row.WgtType.Parameter.value = char(xp.evaluate(...
        xpath_str({'product','imageGenerationParameters','sarProcessingInformation','rangeWindow','windowCoefficient'}),...
        xml_domnode));
    beta_row = str2double(output_meta.Grid.Row.WgtType.Parameter.value);
    % We don't use the Mathworks Kaiser function, so we won't be dependent on the Signal Processing Toolbox
    output_meta.Grid.Row.WgtFunct = kaiser_nosptb(NUM_SAMPLES,beta_row);
    imp_resp = abs(fft(output_meta.Grid.Row.WgtFunct, round(NUM_SAMPLES*OVERSAMPLE))); % Oversampled response function
    imp_resp = imp_resp/sum(output_meta.Grid.Row.WgtFunct); % Normalize to unit peak
    ind = find(imp_resp<1/sqrt(2),1,'first')+[-1 -0]; % Samples surrounding half-power point
    ind = interp1(imp_resp(ind), ind, 1/sqrt(2)); % Linear interpolation to solve for half-power point
    row_broadening_factor = 2*(ind - 1)/OVERSAMPLE;
    output_meta.Grid.Row.ImpRespWid = row_broadening_factor/output_meta.Grid.Row.ImpRespBW;
end
output_meta.Grid.Col.WgtType.WindowName = upper(char(xp.evaluate(...
    xpath_str({'product','imageGenerationParameters','sarProcessingInformation','azimuthWindow','windowName'}),...
    xml_domnode)));

%% Radar Collection
% Ultrafine and spotlight modes have "lower" and "upper" parts to the
% pulse.
% output_meta.RadarCollection.RefFreqIndex=uint32(0); % Absence of this field means all frequencies are true values
num_pulse_parts = str2double(xp.evaluate(...
    ['count(' xpath_str({'product','sourceAttributes','radarParameters','pulseLength'}) ')'],...
    xml_domnode));
for i=1:num_pulse_parts
    output_meta.RadarCollection.Waveform.WFParameters(i).TxRFBandwidth = str2double(xp.evaluate(...
        [xpath_str({'product','sourceAttributes','radarParameters','pulseBandwidth'}) '[' num2str(i) ']'],...
        xml_domnode)); % Bandwidth
    output_meta.RadarCollection.Waveform.WFParameters(i).TxPulseLength = str2double(xp.evaluate(...
        [xpath_str({'product','sourceAttributes','radarParameters','pulseLength'}) '[' num2str(i) ']'],...
        xml_domnode));
    output_meta.RadarCollection.Waveform.WFParameters(i).RcvDemodType='CHIRP';
    sample_rate = str2double(xp.evaluate(...
        [xpath_str({'product','sourceAttributes','radarParameters','adcSamplingRate'}) '[' num2str(i) ']'],...
        xml_domnode));
    output_meta.RadarCollection.Waveform.WFParameters(i).RcvWindowLength = str2double(xp.evaluate(...
        xpath_str({'product','sourceAttributes','radarParameters','samplesPerEchoLine'}),...
        xml_domnode))/sample_rate;
    output_meta.RadarCollection.Waveform.WFParameters(i).ADCSampleRate = sample_rate;
    output_meta.RadarCollection.Waveform.WFParameters(i).RcvFMRate = 0; % True for RcvDemodType='CHIRP'
end
bw = sum([output_meta.RadarCollection.Waveform.WFParameters.TxRFBandwidth]);
output_meta.RadarCollection.TxFrequency.Min = fc-(bw/2); % fc calculated in Grid section
output_meta.RadarCollection.TxFrequency.Max = fc+(bw/2);
% Assumes pulse parts are exactly adjacent in bandwidth
output_meta.RadarCollection.Waveform.WFParameters(1).TxFreqStart = ...
    output_meta.RadarCollection.TxFrequency.Min;
for i=2:num_pulse_parts
    output_meta.RadarCollection.Waveform.WFParameters(i).TxFreqStart = ...
        output_meta.RadarCollection.Waveform.WFParameters(i-1).TxFreqStart + ...
        output_meta.RadarCollection.Waveform.WFParameters(i-1).TxRFBandwidth;
end
% Polarization
pols = textscan(char(xp.evaluate(...
    xpath_str({'product','sourceAttributes','radarParameters','polarizations'}),...
    xml_domnode)),'%s');
pols = pols{1};
M = struct('H','H','V','V','C','RHC');
tx_pols = unique(cellfun(@(x) x(1), pols));
for i=1:numel(pols)
    output_meta.RadarCollection.RcvChannels.ChanParameters(i).TxRcvPolarization = ...
        [M.(pols{i}(1)) ':' M.(pols{i}(2))];
end
if isscalar(tx_pols) % Only one transmit polarization
    output_meta.RadarCollection.TxPolarization = M.(tx_pols);
else % Multiple transmit polarizations
    output_meta.RadarCollection.TxPolarization = 'SEQUENCE';
    for i = 1:numel(tx_pols)
        output_meta.RadarCollection.TxSequence.TxStep(i).TxPolarization = M.(tx_pols(i));
    end
end
% Another way to get polarimetric channels:
% num_pol_bands = str2double(xp.evaluate(...
%     ['count(' xpath_str({'product','imageAttributes','fullResolutionImageData'}) ')'],...
%     xml_domnode));
% for i=1:num_pol_bands
%     pol = char(xp.evaluate(...
%         [xpath_str({'product','imageAttributes','fullResolutionImageData'}) '[' num2str(i) ']/@pole'],...
%         xml_domnode));
% end

%% Timeline
output_meta.Timeline.CollectStart = rawDataStartTime + (rawDataStartTimeFrac/SECONDS_IN_A_DAY);
if strcmp(gen, 'RS2')
    prf_xp_str = {'pulseRepetitionFrequency'};
elseif strcmp(gen, 'RCM')
    prf_xp_str = {'prfInformation', 'pulseRepetitionFrequency'};
end
prf = str2double(xp.evaluate(...
    xpath_str([{'product','sourceAttributes', 'radarParameters'} prf_xp_str]),...
    xml_domnode));
num_lines_entries = str2double(xp.evaluate(...
    ['count(' xpath_str({'product','imageGenerationParameters','sarProcessingInformation','numberOfLinesProcessed'}) ')'],...
    xml_domnode));
num_lines_processed = zeros(num_lines_entries,1);
for i = 1:num_lines_entries
    num_lines_processed(i) = str2double(xp.evaluate(...
        [xpath_str({'product','imageGenerationParameters','sarProcessingInformation','numberOfLinesProcessed'}) '[@pole="' pols{i} '"]'],...
        xml_domnode));
end
num_lines_processed = num_lines_processed(1) * numel(tx_pols);
if num_lines_entries ~= numel(pols) || ~all(num_lines_processed == num_lines_processed(1))
    % This should never happen, but we'll throw an error if it does.
    warning('META2SICD_RS_XML:UnableToComputeCollectDuration', 'Unhandled data condition.');
end
prf = prf * num_pulse_parts;
if num_pulse_parts==2 && ...
        strcmp(output_meta.CollectionInfo.RadarMode.ModeType,'STRIPMAP')
    % Why????
    % This seems to be necessary to make CollectDuration match CA ranges
    % and to make 1/prf roughly equal to ss_zd_s (which is generally true
    % for STRIPMAP).  But we already doubled the prf above to account for
    % the pulse parts (to make real vs effective prf), so why do we have to
    % do it again? And why don't we have to do it for SPOTLIGHT?
    prf = 2*prf;
end
output_meta.Timeline.CollectDuration = num_lines_processed/prf;
output_meta.Timeline.IPP.Set.TStart = 0;
output_meta.Timeline.IPP.Set.TEnd = 0; % Apply real value later.  Just a placeholder.
output_meta.Timeline.IPP.Set.IPPStart = uint32(0);
output_meta.Timeline.IPP.Set.IPPEnd = uint32(num_lines_processed);
output_meta.Timeline.IPP.Set.IPPPoly = [0; prf];
output_meta.Timeline.IPP.Set.TEnd = output_meta.Timeline.CollectDuration;

%% Image Formation
output_meta.ImageFormation.RcvChanProc = ...
    struct('NumChanProc', uint32(1), ... % Assumes not a MODEX collect
    'PRFScaleFactor', 1/max(num_pulse_parts, numel(tx_pols))); % Either polarimetric or multi-step, but not both.
output_meta.ImageFormation.ImageFormAlgo = 'RMA';
output_meta.ImageFormation.TStartProc = 0;
output_meta.ImageFormation.TEndProc = output_meta.Timeline.CollectDuration;
output_meta.ImageFormation.TxFrequencyProc.MinProc = ...
    output_meta.RadarCollection.TxFrequency.Min;
output_meta.ImageFormation.TxFrequencyProc.MaxProc = ...
    output_meta.RadarCollection.TxFrequency.Max;
output_meta.ImageFormation.STBeamComp = 'GLOBAL';
output_meta.ImageFormation.ImageBeamComp = 'SV';
output_meta.ImageFormation.AzAutofocus = 'NO';
output_meta.ImageFormation.RgAutofocus = 'NO';

%% RMA.INCA
output_meta.RMA.RMAlgoType = 'OMEGA_K';
output_meta.RMA.ImageType = 'INCA';
output_meta.SCPCOA.SideOfTrack = char(xp.evaluate(...
    xpath_str({'product','sourceAttributes','radarParameters','antennaPointing'}),...
    xml_domnode));  % Should always be right looking for RCM
output_meta.SCPCOA.SideOfTrack = upper(output_meta.SCPCOA.SideOfTrack(1));
if output_meta.SCPCOA.SideOfTrack=='L'
    ss_zd_s = -ss_zd_s;
    % In addition to left/right, RS2 data can independently be in
    % increasing/decreasing line order.
    if (round((zd_first-zd_last)*SECONDS_IN_A_DAY) + ...
        (zd_first_frac-zd_last_frac)) < 0 % zd_last occurred after zd_first
        zd_first = zd_last;
        zd_first_frac = zd_last_frac;
    end
    look = 1;
else
    if (round((zd_first-zd_last)*SECONDS_IN_A_DAY) + ...
        (zd_first_frac-zd_last_frac)) > 0 % zd_last occurred before zd_first
        zd_first = zd_last;
        zd_first_frac = zd_last_frac;
    end
    look = -1;
end
% Zero doppler time of SCP relative to collect start
zd_t_scp = round((zd_first-rawDataStartTime)*SECONDS_IN_A_DAY) + ... % Convert days to seconds
    (zd_first_frac-rawDataStartTimeFrac) + ... % Handle fractional seconds
    (double(output_meta.ImageData.SCPPixel.Col) * ss_zd_s);
if strcmp(gen, 'RS2')
    near_range = str2double(xp.evaluate(...
        xpath_str({'product','imageGenerationParameters','sarProcessingInformation','slantRangeNearEdge'}),...
        xml_domnode)); % in meters
elseif strcmp(gen, 'RCM')
    near_range = str2double(xp.evaluate(...
        xpath_str({'product','sceneAttributes','imageAttributes','slantRangeNearEdge'}),...
        xml_domnode)); % in meters
end
output_meta.RMA.INCA.R_CA_SCP = near_range + ...
    (double(output_meta.ImageData.SCPPixel.Row)*output_meta.Grid.Row.SS);
output_meta.RMA.INCA.FreqZero = fc;
% Doppler Rate (We do this first since some other things are dependent on
% it.)
% For the purposes of the DRateSFPoly computation, we ignore any
% changes in velocity over the azimuth dimension.
pos_coefs = [P_x(:) P_y(:) P_z(:)];
% Velocity is derivate of position.
vel_coefs=pos_coefs(1:end-1,:).*repmat(((size(pos_coefs,1)-1):-1:1)',[1 3]);
vel_x = polyval(vel_coefs(:,1), zd_t_scp);
vel_y = polyval(vel_coefs(:,2), zd_t_scp);
vel_z = polyval(vel_coefs(:,3), zd_t_scp);
vm_ca_sq = vel_x.^2 + vel_y.^2 + vel_z.^2; % Magnitude of the velocity squared
r_ca = [output_meta.RMA.INCA.R_CA_SCP; 1]; % Polynomial representing range as a function of range distance from SCP
if strcmp(gen, 'RS2')
    drc_xp_str = {'product','imageGenerationParameters','dopplerRateValues', 'dopplerRateValuesCoefficients'};
elseif strcmp(gen, 'RCM')
    drc_xp_str = {'product','dopplerRate','dopplerRateEstimate', 'dopplerRateCoefficients'};
end
drr_xp_str = [drc_xp_str{1:(end-1)} {'dopplerRateReferenceTime'}];
dop_rate_coefs = str2num(xp.evaluate(xpath_str(drc_xp_str),... % Multiple numbers.  We need str2num instead of str2double
    xml_domnode)); % Shifted (origin at dop_rate_ref_t, not SCP) and scaled (sec, not m) version of SICD DopCentroidPoly
dop_rate_ref_t = str2double(xp.evaluate(xpath_str(drr_xp_str),...
    xml_domnode)); % Reference time of Doppler rate polynomial
dop_rate_coefs_shifted = polyshift(dop_rate_coefs, ... % Shift so SCP is reference
    (output_meta.RMA.INCA.R_CA_SCP*2/SPEED_OF_LIGHT) - ... % SICD reference time (SCP)
    dop_rate_ref_t); % Reference time of native Doppler Centroid polynomial
dop_rate_coefs_scaled = dop_rate_coefs_shifted .*  ... % Scale from seconds to meters
    (2/SPEED_OF_LIGHT) .^ (0:(length(dop_rate_coefs)-1));
output_meta.RMA.INCA.DRateSFPoly = - conv(dop_rate_coefs_scaled.',r_ca) * ... % Multiplication of two polynomials is just a convolution of their coefficients
    SPEED_OF_LIGHT / (2 * fc * vm_ca_sq(1)); % Assumes a SGN of -1

%% Fields dependent on Doppler rate
% This computation of SS is actually better than the claimed SS
% (sampledLineSpacing) in many ways, because this makes all of the metadata
% internally consistent.  This must be the sample spacing exactly at SCP
% (which is the definition for SS in SICD), if the other metadata from
% which is it computed is correct and consistent. Since column SS can vary
% slightly over a RGZERO image, we don't know if the claimed sample spacing
% in the native metadata is at our chosen SCP, or another point, or an
% average across image or something else.
output_meta.Grid.Col.SS = sqrt(vm_ca_sq(1)) * abs(ss_zd_s) * ...
    output_meta.RMA.INCA.DRateSFPoly(1,1);
output_meta.Grid.Col.ImpRespBW = dop_bw*abs(ss_zd_s)/output_meta.Grid.Col.SS; % Convert to azimuth spatial bandwidth (cycles per meter)
output_meta.RMA.INCA.TimeCAPoly = [zd_t_scp; ss_zd_s/output_meta.Grid.Col.SS];
if strcmpi(output_meta.Grid.Col.WgtType.WindowName,'KAISER') % The usual RS2 weigting
    output_meta.Grid.Col.WgtType.Parameter.name = 'BETA';
    output_meta.Grid.Col.WgtType.Parameter.value = char(xp.evaluate(...
        xpath_str({'product','imageGenerationParameters','sarProcessingInformation','azimuthWindow','windowCoefficient'}),...
        xml_domnode));
    beta_col = str2double(output_meta.Grid.Col.WgtType.Parameter.value);
    % We don't use the Mathworks Kaiser function, so we won't be dependent on the Signal Processing Toolbox
    output_meta.Grid.Col.WgtFunct = kaiser_nosptb(NUM_SAMPLES,beta_col);
    imp_resp = abs(fft(output_meta.Grid.Col.WgtFunct, round(NUM_SAMPLES*OVERSAMPLE))); % Oversampled response function
    imp_resp = imp_resp/sum(output_meta.Grid.Col.WgtFunct); % Normalize to unit peak
    ind = find(imp_resp<1/sqrt(2),1,'first')+[-1 -0]; % Samples surrounding half-power point
    ind = interp1(imp_resp(ind), ind, 1/sqrt(2)); % Linear interpolation to solve for half-power point
    col_broadening_factor = 2*(ind - 1)/OVERSAMPLE;
    output_meta.Grid.Col.ImpRespWid = col_broadening_factor/output_meta.Grid.Col.ImpRespBW;
end

%% Doppler Centroid
if strcmp(gen, 'RS2')
    dc_xp_str = {'product','imageGenerationParameters','dopplerCentroid'};
elseif strcmp(gen, 'RCM')
    dc_xp_str = {'product','dopplerCentroid','dopplerCentroidEstimate'};
end
dop_cent_coefs = str2num(xp.evaluate(... % Multiple numbers.  We need str2num instead of str2double
    xpath_str([dc_xp_str,{'dopplerCentroidCoefficients'}]),...
    xml_domnode)); % Shifted (origin at dop_cent_ref_t, not SCP) and scaled (sec, not m) version of SICD DopCentroidPoly
dop_cent_ref_t = str2double(xp.evaluate(...
    xpath_str([dc_xp_str,{'dopplerCentroidReferenceTime'}]),...
    xml_domnode)); % Reference time of Doppler Centroid polynomial
dop_cent_coefs_shifted = polyshift(dop_cent_coefs, ... % Shift so SCP is reference
    (output_meta.RMA.INCA.R_CA_SCP*2/SPEED_OF_LIGHT) - ... % SICD reference time (SCP)
    dop_cent_ref_t); % Reference time of native Doppler Centroid polynomial
dop_cent_coefs_scaled = dop_cent_coefs_shifted .*  ... % Scale from seconds to meters
    (2/SPEED_OF_LIGHT) .^ (0:(length(dop_cent_coefs)-1));
output_meta.RMA.INCA.DopCentroidPoly=dop_cent_coefs_scaled.';
% Adjust Doppler Centroid for spotlight
if strcmp(output_meta.CollectionInfo.RadarMode.ModeType,'SPOTLIGHT')
    [dop_est, dop_est_frac] = datenum_w_frac(char(xp.evaluate(...  % Doppler estimate time
        xpath_str([dc_xp_str 'timeOfDopplerCentroidEstimate']), xml_domnode)));
    dop_est_t = abs(round((dop_est - rawDataStartTime)*SECONDS_IN_A_DAY) + ... % Convert from days to secs
        (dop_est_frac - rawDataStartTimeFrac)); % Handle fractional seconds
    dop_est_col = (dop_est_t - zd_t_scp)/ss_zd_s; % This is the column where the doppler centroid was computed.
    % Column-dependent variation in DopCentroidPoly due to spotlight
    output_meta.RMA.INCA.DopCentroidPoly(1,2) = ...
        -look * fc * (2 / SPEED_OF_LIGHT) * sqrt(vm_ca_sq(1)) / ...
        output_meta.RMA.INCA.R_CA_SCP;
    % dopplerCentroid in native metadata was defined at specific column,
    % which might not be our SCP column.  Adjust so that SCP column is
    % correct.
    output_meta.RMA.INCA.DopCentroidPoly(1,1) = ...
        output_meta.RMA.INCA.DopCentroidPoly(1,1) - ...
        (output_meta.RMA.INCA.DopCentroidPoly(1,2) * ...
        dop_est_col * output_meta.Grid.Col.SS);
end
output_meta.Grid.Col.DeltaKCOAPoly = ...
    output_meta.RMA.INCA.DopCentroidPoly * ss_zd_s / output_meta.Grid.Col.SS;
% Compute Col.DeltaK1/K2 from DeltaKCOAPoly
% This is not always straightforward to do generically for any possible
% DeltaKCOAPoly 2D polynomial, since you would have to compute all 2D roots
% and edge cases.  However, for the RS case, this can be solved exactly,
% since its usually a 1D polynomial.  Even the spotlight case only brings
% in a linear variation in the second dimensions, so its still easily
% solved.
% Min/max in row/range must exist at edges or internal local min/max
minmax = roots(polyder(output_meta.Grid.Col.DeltaKCOAPoly(end:-1:1,1)));
rg_bounds_m = (double([0 (output_meta.ImageData.NumRows-1)]) - ...
     double(output_meta.ImageData.SCPPixel.Row)) * output_meta.Grid.Row.SS;
possible_bounds_rg = [rg_bounds_m minmax(minmax>min(rg_bounds_m) & minmax<max(rg_bounds_m))];
% Constant or (linearly increasing\decreasing for spotlight) in column, so
% edges must contain max/min.
possible_bounds_az = (double([0 (output_meta.ImageData.NumCols-1)]) - ...
     double(output_meta.ImageData.SCPPixel.Col)) * output_meta.Grid.Col.SS;
possible_bounds_deltak = sicd_polyval2d(output_meta.Grid.Col.DeltaKCOAPoly, ...
    possible_bounds_az, possible_bounds_rg);
output_meta.Grid.Col.DeltaK1 = min(possible_bounds_deltak(:)) - ...
    (output_meta.Grid.Col.ImpRespBW/2);
output_meta.Grid.Col.DeltaK2 = max(possible_bounds_deltak(:)) + ...
    (output_meta.Grid.Col.ImpRespBW/2);
% Wrapped spectrum
if (output_meta.Grid.Col.DeltaK1 < -(1/output_meta.Grid.Col.SS)/2) || ...
        (output_meta.Grid.Col.DeltaK2 > (1/output_meta.Grid.Col.SS)/2)
    output_meta.Grid.Col.DeltaK1 = -(1/output_meta.Grid.Col.SS)/2;
    output_meta.Grid.Col.DeltaK2 = -output_meta.Grid.Col.DeltaK1;
end
%% TimeCOAPoly
% TimeCOAPoly=TimeCA+(DopCentroid/dop_rate)
% Since we can't evaluate this equation analytically, we will evaluate
% samples of it across our image and fit a 2D polynomial to it.
POLY_ORDER = 2; % Order of polynomial which we want to compute
grid_samples = POLY_ORDER + 1; % in each dimension
coords_az_m = linspace(-double(output_meta.ImageData.SCPPixel.Col),...
    double(output_meta.ImageData.NumCols-output_meta.ImageData.SCPPixel.Col-1), grid_samples) * ...
    output_meta.Grid.Col.SS;
coords_rg_m = linspace(-double(output_meta.ImageData.SCPPixel.Row),...
    double(output_meta.ImageData.NumRows-output_meta.ImageData.SCPPixel.Row-1), grid_samples) * ...
    output_meta.Grid.Row.SS;
timeca_sampled = sicd_polyval2d(output_meta.RMA.INCA.TimeCAPoly(:).',coords_az_m,coords_rg_m);
dopcentroid_sampled = sicd_polyval2d(output_meta.RMA.INCA.DopCentroidPoly,coords_az_m,coords_rg_m);
doprate_sampled = sicd_polyval2d(dop_rate_coefs_scaled.',coords_az_m,coords_rg_m);
timecoapoly_sampled = timeca_sampled+(dopcentroid_sampled./doprate_sampled);
% Least squares fit for 2D polynomial
% A*x = b
[coords_az_m, coords_rg_m] = ndgrid(coords_az_m, coords_rg_m);
a = zeros(grid_samples^2, (POLY_ORDER+1)^2);
for i = 0:POLY_ORDER
    for j = 0:POLY_ORDER
        a(:,i*(POLY_ORDER+1)+j+1) = (coords_rg_m(:).^j).*(coords_az_m(:).^i);
    end
end
b_coa = zeros((POLY_ORDER+1)^2,1);
for i=1:((POLY_ORDER+1)^2)
   b_coa(i)=sum(timecoapoly_sampled(:).*a(:,i)); % center of aperture
end
A=zeros((POLY_ORDER+1)^2);
for i=1:((POLY_ORDER+1)^2)
    for j=1:((POLY_ORDER+1)^2)
        A(i,j)=sum(a(:,i).*a(:,j));
    end
end
old_warning_state=warning('off','MATLAB:nearlySingularMatrix');
x=A\b_coa; % MATLAB often flags this as badly scaled, but results still appear valid
warning(old_warning_state);
output_meta.Grid.TimeCOAPoly=reshape(x, POLY_ORDER+1, POLY_ORDER+1);
if strcmp(output_meta.CollectionInfo.RadarMode.ModeType,'SPOTLIGHT')
    output_meta.Grid.TimeCOAPoly = output_meta.Grid.TimeCOAPoly(1);
    % This field required to compute TimeCOAPoly, but not allowed for
    % spotlight in SICD.
    output_meta.RMA.INCA = rmfield(output_meta.RMA.INCA, 'DopCentroidPoly');
else % This field also not allowed for spotlight in SICD.
    output_meta.RMA.INCA.DopCentroidCOA=true;
end

%% GeoData
% Now that sensor model fields have been populated, we can populate
% GeoData.SCP more precisely.
ecf = point_image_to_ground([output_meta.ImageData.SCPPixel.Row;output_meta.ImageData.SCPPixel.Col],output_meta);
output_meta.GeoData.SCP.ECF.X=ecf(1);
output_meta.GeoData.SCP.ECF.Y=ecf(2);
output_meta.GeoData.SCP.ECF.Z=ecf(3);
llh=ecf_to_geodetic([output_meta.GeoData.SCP.ECF.X output_meta.GeoData.SCP.ECF.Y output_meta.GeoData.SCP.ECF.Z]);
output_meta.GeoData.SCP.LLH.Lat=llh(1);
output_meta.GeoData.SCP.LLH.Lon=llh(2);
output_meta.GeoData.SCP.LLH.HAE=llh(3);

%% Radiometric
if exist('beta_domnode','var')
    % Offset always zero for SLC, so we only need gains
    betas = str2num(xp.evaluate(xpath_str({'lut','gains'}),beta_domnode)); %#ok<ST2NM>
    % Of the provided LUTs, we really only work with beta here, since it is
    % the radiometric term most commonly kept constant, and the others
    % (sigma/gamma) can always be derived from it.
    % if any(strcmp({'Constant-beta', 'Point Target', 'Point Target-1', ...
    %         'Calibration-1', 'Calibration-2', 'Ship-1', 'Ship-2', ...
    %         'Ship-3', 'Unity'}, ... % Known modes with constant beta
    %         xp.evaluate(xpath_str({'product','imageGenerationParameters', ...
    %         'sarProcessingInformation','lutApplied'}), xml_domnode)))
    if all(betas==betas(1)) % In case we missed some modes, this condition may be more reliable
        output_meta.Radiometric.BetaZeroSFPoly = 1/betas(1)^2;
    else  % Otherwise fit a 1D polynomial in range
        % RS2 has value for every row
        coords_rg_m = (double(0:(output_meta.ImageData.NumRows-1)) - ...
            double(output_meta.ImageData.SCPPixel.Row)) * output_meta.Grid.Row.SS;
        if strcmp(gen, 'RCM') % RCM subsamples the rows
            rng_indices = ((str2double(xp.evaluate(xpath_str({'lut','pixelFirstAnglesValue'}), beta_domnode)):...
                ... % Should be this, but simulated datasets are inconsistent with spec document
                ... % crm_indices = (xp.evaluate(xpath_str({'lut','pixelFirstLutValue'}), beta_domnode):...
                (str2double(xp.evaluate(xpath_str({'lut','numberOfValues'}), beta_domnode))-1)) * ... % First row is zero
                str2double(xp.evaluate(xpath_str({'lut','stepSize'}), beta_domnode))) + 1;
            coords_rg_m = coords_rg_m(rng_indices);
        end
        % For the datasets we have seen, this function is very close to
        % linear.  For the "Mixed" LUT, there is a tiny linear piecewise
        % deviation from a single overall linear.
        betapoly = polyfit(coords_rg_m, 1./betas.^2, 2);
        output_meta.Radiometric.BetaZeroSFPoly = betapoly(end:-1:1).';
    end
    % RCS, Sigma, and Gamma will be computed below in derived_sicd_fields
    % derived_sicd_fields.
    output_meta.Radiometric.NoiseLevel.NoiseLevelType = 'ABSOLUTE';
    if strcmp(gen, 'RS2')
        beta0_str = [xpath_str({'product', 'sourceAttributes', ...
            'radarParameters','referenceNoiseLevel'}) ...
            '[@incidenceAngleCorrection="Beta Nought"]'];
        noise_domnode = xml_domnode;  % RS2 noise is in main product.xml
    elseif strcmp(gen, 'RCM') % RCM noise is in separate file
        beta0_str = [xpath_str({'noiseLevels', 'referenceNoiseLevel'}) ...
            '/*[text()=''Beta Nought'']/..'];
    end
    if exist('noise_domnode','var')
        pfv = str2double(xp.evaluate([beta0_str ...
            '/*[local-name()=''pixelFirstNoiseValue'']'], noise_domnode)); % Index of first row defined to be zero
        step = str2double(xp.evaluate([beta0_str ...
            '/*[local-name()=''stepSize'']'], noise_domnode));
        beta0s = str2num(xp.evaluate([beta0_str ...
            '/*[local-name()=''noiseLevelValues'']'], noise_domnode)); %#ok<ST2NM>
        range_coords = output_meta.Grid.Row.SS * ...
            ((((1:numel(beta0s))-1) * step) + pfv - double(output_meta.ImageData.SCPPixel.Row));
        noisepoly = polyfit(range_coords, beta0s - (10*log10(polyval(...
            output_meta.Radiometric.BetaZeroSFPoly(end:-1:1), range_coords))), 2);
        output_meta.Radiometric.NoiseLevel.NoisePoly = noisepoly(end:-1:1).';
    end
end

%% SCPCOA
% All of these fields are derivable for more fundamental fields.
output_meta = derived_sicd_fields(output_meta);

%% Process fields specific to each polarimetric band
band_independent_meta = output_meta; % Values that are consistent across all bands
grouped_meta = cell(numel(pols),1);
for i=1:numel(pols)
    output_meta = band_independent_meta;

    output_meta.ImageFormation.RcvChanProc.ChanIndex = i;
    output_meta.ImageFormation.TxRcvPolarizationProc = ...
        output_meta.RadarCollection.RcvChannels.ChanParameters(i).TxRcvPolarization;

    grouped_meta{i} = output_meta;
end
output_meta = grouped_meta; % Cell array with metadata struct for each band

end

% Creates an XPath query that is a namespace insensitive search for an XML
% heirachy.  Input is a cell array of strings specifying the XML fields,
% starting with the root node and working its way down the XML tree to the
% leaf nodes.  This is required since RS2 XML files specify a default
% namespace, so XPath queries with no namespace speficied will not work.
% (That only searches for elements associated with no namespace at all.)
% Note: This seems to be an issue only in MATLAB 2010a and above, as
% previous versions used another XPATH library that was more tolerant.
function out_str = xpath_str(in_cell_array)
    out_str='';
    for j=1:length(in_cell_array)
        out_str=[out_str '/*[local-name()=''' in_cell_array{j} ''']'];
    end
end

% MATLAB's datenum function won't handle precise times down under a
% millisecond, because 1) It won't accept a format with more than 3 .FFF in
% the string description of the date format and 2) the resulting serial
% date number is stored in days from 00-JAN-0000 and just doesn't have
% enough bits to handle fractional seconds to the level we want.  Here we
% handle the fractional seconds separately so we can read date with the
% precision we need.
function [datenum_s, datenum_frac] = datenum_w_frac(datestring)
    datenum_s = datenum(datestring,'yyyy-mm-ddTHH:MM:SS');
    datenum_frac = str2double(regexp(datestring,'\.\d*','match'));
    if isnan(datenum_frac), datenum_frac = 0; end;
end

% //////////////////////////////////////////
% /// CLASSIFICATION: UNCLASSIFIED       ///
% //////////////////////////////////////////


function computeModelCoefficients_batchHN_Old(pathExperiments,nExp,imbalance,nBatch,matlabPATH,seed)
% -------------------------------------------------------------------------
% function computeModelCoefficients_batchHN(pathExperiments,nExp,fSetNames,imbalance,matlabPATH)
% -------------------------------------------------------------------------
% DESCRIPTION:
% This function computes the final logistic regression coefficients and
% bootstrap confidence intervals of the final models obtained for all
% outcomes analyzed in the HN study. See ref.[1] for more details.
% -------------------------------------------------------------------------
% REFERENCE:
% [1] Vallieres, M. et al. (2015). FDG-PET/CT radiomics models for the
%     early prediction of different tumour outcomes in head and neck cancer.
%     The Journal of Nuclear Medicine, aa(bb), xxx-yyy.
%     doi:
% -------------------------------------------------------------------------
% INPUTS:
% 1. pathExperiments: Full path to the directory containing all experiments.
%                     --> Ex: '/myProject/WORKSPACE/CV-BASED_RESULTS'
% 2. nExp: Numerical value specifying the number of experiments to analyze.
%          --> Ex: 10
% 3. imbalance: String specifying the type of imbalance-adjustement strategy
%               employed. Either 'IABR' for imbalance-adjusted bootstrap
%               resampling (see ref.[1]), or 'IALR' for imbalance-adjusted
%               logistic regression (see ref.[2]).
%               --> Ex: 'IALR'
% 4. nBatch: Number of parallel batch.
%            --> Ex: 8
% 5. matlabPATH: Full path to the MATLAB excutable on the system.
%                --> 'matlab' if a symbolic link to the matlab executable
%                     was previously created.
% -------------------------------------------------------------------------
% OUTPUTS: Final coefficients, model response and confidence intervals
%          saved in the corresponding ../experiment/outcome/fSetName
%          folder.
% -------------------------------------------------------------------------
% AUTHOR(S): Martin Vallieres <mart.vallieres@gmail.com>
% -------------------------------------------------------------------------
% HISTORY:
% - Creation: March 2016
%--------------------------------------------------------------------------
% STATEMENT:
% This file is part of <https://github.com/mvallieres/radiomics/>,
% a package providing MATLAB programming tools for radiomics analysis.
% --> Copyright (C) 2015  Martin Vallieres
%
%    This package is free software: you can redistribute it and/or modify
%    it under the terms of the GNU General Public License as published by
%    the Free Software Foundation, either version 3 of the License, or
%    (at your option) any later version.
%
%    This package is distributed in the hope that it will be useful,
%    but WITHOUT ANY WARRANTY; without even the implied warranty of
%    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
%    GNU General Public License for more details.
%
%    You should have received a copy of the GNU General Public License
%    along with this package.  If not, see <http://www.gnu.org/licenses/>.
% -------------------------------------------------------------------------

startpath = pwd;
time = 30; % Number of seconds to wait before checking if parallel computations are done

for exp = 1:nExp
    cd(fullfile(pathExperiments,['Experiment',num2str(exp)])), load('training')
    cd('FINAL_MODELS'), pathFinalModels = pwd;
    mkdir('batchLog_Coeff'), cd('batchLog_Coeff'), pathBatch = pwd;
    nameOutcomes = fieldnames(training); nOutcomes = numel(nameOutcomes);
    for o = 1:nOutcomes
        outcomes.(nameOutcomes{o}) = training.(nameOutcomes{o}).outcome;
    end
    setNames = fieldnames(training.(nameOutcomes{1}).text);
    [param] = batchExperiments(setNames,outcomes,nBatch); nBatch = length(param);

    % PRODUCE BATCH COMPUTATIONS
    save('workspace','pathFinalModels','training','param','pathBatch','imbalance','seed'), pause(3)
    for i = 1:nBatch
        nameScript = ['batch',num2str(i),'_script.m'];
        fid = fopen(nameScript,'w');
        fprintf(fid,'tic\n');
        fprintf(fid,'load(''workspace'')\n');
        for j = 1:numel(param{i})
            fprintf(fid,['cd(fullfile(pathFinalModels,param{',num2str(i),'}{',num2str(j),'}{2},param{',num2str(i),'}{',num2str(j),'}{1}))\n']);
            fprintf(fid,'load(''finalModel'')\n');
            fprintf(fid,['fprintf(''COMPUTING THE LOGISTIC REGRESSION COEFFICIENTS OF THE FINAL MODEL OF "',param{i}{j}{2},'" OUTCOME, "',param{i}{j}{1},'" FEATURE SET ... '')']);
            fprintf(fid,'\n');
            fprintf(fid,['[coeff,response,modelCI] = computeModelCoefficients_HN(finalModel.Data,training.',param{i}{j}{2},'.outcome,imbalance,seed);\n']);
            fprintf(fid,['fprintf(''DONE!\\n'')']);
            fprintf(fid,'\n');
            fprintf(fid,'save(''coeff'',''coeff''), save(''response'',''response''), save(''modelCI'',''modelCI'')\n');
        end
        fprintf(fid,'cd(pathBatch)\n');
        fprintf(fid,['system(''touch batch',num2str(i),'_end'');\n']);
        fprintf(fid,'clear all\n');
        fprintf(fid,'toc');
        fclose(fid);
        system([matlabPATH,' -nojvm -nodisplay -nodesktop -nosplash < ',nameScript,' >& ',nameScript(1:end-1),'log &']);
    end
    waitBatch(pathBatch,time,nBatch)
    delete('workspace.mat')
end

cd(startpath)
end


HeadModelPath = [hlp_getSiftRoot filesep 'resources' filesep 'headmodels' filesep 'standard-Colin27-385ch.mat'];
hmObj = hlp_validateHeadModelObject(HeadModelPath);

%% Simulate VAR model
[EEGsim] = pop_sim_varmodel([]);
if length(EEGsim)>1
    EEGtrue = EEGsim(2);
    EEGsim  = EEGsim(1);
end

%% Simulate scalp activity using forward model

% these are the source locations
sourceRois = {'Cingulum_Mid_L','Occipital_Mid_L','Parietal_Sup_L','Frontal_Sup_Medial_R','Precentral_R'};

cfg = arg_guipanel('Function',@sim_simulateSources, ...
                   'Parameters',{ ...
                        'hmObj',HeadModelPath, ...
                        'sourceAtlasLabels', setdiff_bc(hmObj.atlas.label,{'Thalamus_L','Thalamus_R'}), ...
                        'Channels',hmObj.label, ...
                        'sourceShape' {'gausspatch' 'roiOrdered' sourceRois, 'sigma', 10}, ...
                        'addNoise', {'SignalToNoise' 20} ...
                        },'PanelOnly',false);
[scalpData srcData LFM] = sim_simulateSources('sourceAmps',EEGsim.data,cfg);

%%

chanlabels = hmObj.getChannelLabels();
elocs = hmObj.channelSpace;
EEG = EEGsim;
EEG.data       = scalpData;
EEG.nbchan     = size(EEG.data,1);
EEG.setname    = 'VAR Simulation';
EEG.condition  = 'VAR Simulation';
EEG.srcpot_all = srcData;
EEG = rmfield(EEG,'chanlocs');
for k=1:EEG.nbchan
    EEG.chanlocs(k) = struct(...
        'X',elocs(k,1), ...
        'Y',elocs(k,2), ...
        'Z',elocs(k,3), ...
        'labels',chanlabels{k}, ...
        'type','EEG', ...
        'theta',[], ...
        'radius',0.5, ...
        'sph_theta',[],...
        'sph_phi',[],...
        'sph_radius',[],...
        'urchan',k, ...
        'ref','');
end
EEG.chanlocs = convertlocs(EEG.chanlocs,'cart2all');

%% visualize
latency = [];
gobj=vis_csd('hmObj',HeadModelPath, ...
         'signal',EEG,  ...
         'cortexMesh',[], ...
         'times',latency,        ...
         'avgtimes',false,       ...
         'frameskip',10,          ...
         'cortexlims',99,       ...
         'scalplims',[99], ...
         'showpower',false, ...
         'title','True');


function [err, Qd, s, termname, nterms, sindices, dfbothSS, modw, modwo, tnames, dfterm, dfe, Contr] = PreProc(n, NF, group, varnames, FL, Contr, cov, unbalanced)
%
%   [err, Qd, Rd, sindices, dfboth, modw, modwo, dfx] = PreProc(n,group,varnames)
%
%Purpose:
%
%  Returns QR decomposition results for the design matrix projected to null space
%
%Input Parameters:
%   n: number of datasets = total # of combinations including repeats
%   group: cell array of factor levels
%   varnames:
%
%Output Parameters:
%   err : 0 No Problem
%       : 1  Problems
%
%
%
%Key Terms:
%
%More Info :
%
%
%
%
%     Author : Gang Chen
%     Date : Tue Mar 23 13:57:52 EST 2004
%     SSCC/NIMH/ NIH, Bethesda MD 20892

%Define the function name for easy referencing
FuncName = 'PreProc.m';

%Debug Flag
DBG = 1;

%initailize return variables
err = 1;

% Don't worry about NaN's at this point.
group = group(:);    % what's it for?
ng = length(group);  % number of factors
termlist = makemodel(ng, ng);  % Generate terms for all main effects plus various interactions

for j=1:ng
   gj = group{j};
   if (size(gj,1) == 1), gj = gj(:); end
   if (size(gj,1) ~= n)
      error('Factor %d must have %d elements.',j,n);
   end
   if (ischar(gj)), gj = cellstr(gj); end
   group{j} = gj;
end

gdum = cell(ng,1);
dfvar = zeros(ng,1);

if (unbalanced.yes == 0), % Balanced designs
   vconstr = cell(ng,1);
   %vmean = cell(ng,1);   %vmean is never used!!!
end

for j=1:ng   % for each factor
   gj = group{j};
   [gij,gnj] = grp2idx(gj);   % Create index vector from a grouping variable: gij is a vector
	                           % taking integer values from 1 up to the number of unique entries in gj
										% gnj is a cell array of names, so that gnj(gij) reproduces gj
   nlevels = size(gnj,1);     % levels for this factor
   dfvar(j) = nlevels - 1;    % D. F. for this factor

   if (unbalanced.yes == 0),	% balanced
      if (cov.do & j==cov.marker)
	      gdum{j} = gj;
         dfvar(j) = 1;           % D. F. = 1
         vconstr{j} = zeros(0,1);
%        vmean{j} = 1;
      else
         gdum{j} = idummy(gij, 3);
         vconstr{j} = idummy(1:nlevels)';
%        vmean{j} = ones(1,nlevels) / nlevels;  % array (1Xnlevels) of one ones, but vmean is never used in the code!!!!!!!!!1
	   end
	else % Unbalanced designs
	   if (cov.do & j==cov.marker)
	      gdum{j} = gj;
         dfvar(j) = 1;           % D. F. = 1
         vconstr{j} = zeros(0,1);
%        vmean{j} = 1;
      else
		   gdum{j} = idummy(gij, 3);
		end
	end % if (unbalanced.yes == 0): Only for balanced designs

end

% Create dummy variable arrays for each term in the model.
nterms = size(termlist,1);             % Number of rows (1st dimension) in termlist
[sterms,sindex] = sortrows(termlist);  % Sort terms in ascending order.
ncols = 1;
nconstr = 0;

termdum = cell(nterms, 1);        % cell array of dummy variables which are design matrix cols
termconstr = cell(nterms,1);      % constraints to make each term well defined
levelcodes = cell(nterms, 1);     % codes for levels of each M row
tnames = cell(nterms, 1);         % name for entire term, e.g. A*B
dfterm0 = zeros(nterms, 1);       % nominal d.f. for each term
termvars = cell(nterms, 1);       % list of vars in each term
termlength = zeros(size(sindex)); % length of each term (number of columns)

%randomterm = find(termlist*randomvar > 0);  % indices of random terms

% For each term,
for j=1:nterms
   % Loop over elements of the term
   df0 = 1;
   tm = sterms(j,:);
   tdum = [];         % empty term so far
   tconstr = 1;       % empty constraints so far
   tn = '';           % blank name so far
   vars = find(tm);   % Find indices of nonzero elements
   for varidx = 1:length(vars)
      % Process each variable participating in this term
      varnum = vars(varidx);          % variable name
      tm(varnum) = 0;                 % term without this variable
      df0 = df0 * dfvar(varnum);      % d.f. so far

      % Combine its dummy variable with the part computed so far
      G = gdum{varnum};           % dummy vars for this grouping var
      nlevterm = size(tdum,2);    % levels for term so far
      nlevgrp  = size(G,2);       % levels for this grouping var
      tdum = termcross(G,tdum);   % combine G into term dummy vars

      % Construct the term name and constraints matrix
%      if (ismember(varnum, NF) & cov.do),    % for the covariate, which is the last factor
		if (ismember(varnum, cov.marker) & cov.do),    % for the covariate, which is the last factor
         vconstr = ones(0,1);
      else
         vconstr = ones(1, nlevgrp);
      end
      if (isempty(tn))
         tn = varnames{varnum};
         tconstr = vconstr;
      else
         tn = [varnames{varnum} '*' tn];
         tconstr = [kron(vconstr,eye(size(tconstr,2)));
                    kron(eye(length(vconstr)),tconstr)];   % Kronecker
      end

      % If the rest of this term is computed, take advantage of that
      prevterms = sterms(1:j-1,:);
      oldtm = find((prevterms * tm') == sum(tm));      % same vars in old term
      oldtm = oldtm((prevterms(oldtm,:) * ~tm') == 0); % and no others
      if (length(oldtm) > 0)
         k = sindex(oldtm(1));
         tdum = termcross(termdum{k}, tdum);
         oconstr = termconstr{k};
         tconstr = [kron(tconstr,              eye(size(oconstr,2)));
                    kron(eye(size(tconstr,2)), oconstr)];
         tn = [tn '*' tnames{k}];
         df0 = df0 * dfterm0(k);
         break;
      end
   end

   % Store this term's dummy variables and name
   k = size(tdum, 2);
   termlength(sindex(j),1) = k;
   ncols = ncols + k;
   sj = sindex(j);
   termdum{sj} = tdum;
   termconstr{sj} = tconstr;
   termvars{sj} = vars;
   levelcodes{sj} = fullfact(dfvar(vars)+1);
   if (isempty(tn)), tn = 'Constant'; end
   tnames{sj,1} = tn;
   dfterm0(sj) = df0;
   nconstr = nconstr + size(tconstr,1);
end
tnames{length(tnames)+1,1} = 'Error';

% Create the full design matrix
dmat = ones(n, ncols);        % to hold design matrix of nXncols
cmat = zeros(nconstr,ncols);  % to hold constraints matrix
cbase = 0;                    % base from which to fill in cmat
termname = zeros(ncols,1);
termstart = cumsum([2; termlength(1:end-1)]);
termend = termstart + termlength - 1;
for j=1:nterms
   clist = termstart(j):termend(j);
   dmat(:, clist) = termdum{j};
   C = termconstr{j};
   nC = size(C,1);
   cmat(cbase+1:cbase+nC,clist) = C;
   termname(clist) = j;
   cbase = cbase + nC;
end

[err, Qd, dfx, dmat2] = QRDecom(dmat, cmat);
%dfe = n - dfx;

% Determine which models to compare for testing each term
ssw  = -ones(nterms, 1);      % sum of squares with this term
sswo = ssw;                   % sum of squares without this term
dfw  = ssw;                   % residual d.f. with this term
dfwo = ssw;                   % residual d.f. without this term

if (unbalanced.yes == 1)
   modw = tril(ones(nterms)); % get model with this term
   k = nterms;                % locations of model with all terms
else

% Only apply type III for sums of squares
modw = ones(nterms);
%k = 1:nterms;
%TnotC = termsnotcontained(termlist);

end % if (unbalanced.yes == 1)

modw = logical(modw);                  % get model with this term
modwo = logical(modw - eye(nterms));   % get model without this term

dfw(1:nterms) = dfx;

% Fit each model separately
dfboth = [dfw; dfwo];
dfbothSS = dfboth;  % For usage in ss.m

% Consider interactions before their components for type 3 ss, so
% examine the terms in decreasing order of the number factors in the term

if (unbalanced.yes == 1)
   sindices = [(1:size(termlist,1)), (1:size(termlist,1))]';
else
termsize = sum(termlist,2)';      %sum of all the elements along 2nd dimension (row)
[stermsize,sindices] = sort(termsize); % sort in ascending order, output is stored in stermsize; sindices is the index for the new array
sindices = [sindices(:); sindices(:)];

end % if (unbalanced.yes == 1)

% Here QR decomposition is done, which is voxel independent

s(length(sindices)).Qdt = [];

for j=length(sindices):-1:1
   % Find the next model index to fit
   k = sindices(j);

   % Look in unsorted arrays to see if we have already fit this model
   if j>nterms
      k0 = k+nterms;
   else
      k0 = k;
   end
   if dfboth(k0)~=-1
      continue
   end

   % Find the model with this index
   if (j > nterms)
      thismod = modwo(k, :);
   else
      thismod = modw(k, :);
   end

   % Get the design matrix for this model
   keepterms = find(thismod);
   clist = ismember(termname, [0 keepterms]);
   X = dmat2(:,clist);
   C = cmat(:,clist);

   % Fit this term
	[err, s(j).Qdt, dfx0] = QRDecom(X, C);

   % Use these results for each term that requires them

   mod0 = repmat(thismod, nterms, 1);
   k = find(all(modw == mod0, 2));
   dfw(k) = dfx0;
   dfboth(k) = 0;

   k = find(all(modwo == mod0, 2));
   dfwo(k) = dfx0;
   dfboth(nterms+k) = 0;
end
clear mod0

dfterm = dfw - dfwo;
dfe = n-dfx;   %residual degrees of freedom

if (Contr.do == 1),

   % In design matrix dmat, the first column is all one's, for the total mean. Then there are totally
   % FL(1).N_level + FL(2).N_level + FL(3).N_level + FL(4).N_level columns for the main effects.
   % Next 2nd order interactions, 3rd order interaction, and 4th order interactions.

   % Store only those mean columns in design matrix.

   %num_col0 = 1;  % the 1st column is for grand mean (0 order)

   num_col(1) = 0;
   for (i = 1:1:NF),
      num_col(1) = num_col(1) + FL(i).N_level;   %
   end

   %  Ignore 1st col since it is total mean
   %dmat_mean = dmat(:, (num_col0 + 1):(num_col0 + num_col(1)));

   % Get the number in the sum for each mean, which happens to be in the diagonal of X'X.
   % This can also be otained through the user input variables, but it is generic with
   % the matrix operation, especially for unbalanced design.
   %sum_num = diag(dmat_mean' * dmat_mean);

   if (NF > 1),
      num_col(2) = 0;
      for (i = 1:1:(NF-1)),
      for (j = (i+1):1:NF),
         num_col(2) = num_col(2) + FL(i).N_level*FL(j).N_level;    %Columns for 2nd order interactions
      end
      end
   end

   if (NF > 2),
      num_col(3) = 0;
      for (i = 1:1:(NF-2)),
      for (j = (i+1):1:(NF-1)),
   	for (k = (j+1):1:NF),
         num_col(3) = num_col(3) + FL(i).N_level*FL(j).N_level*FL(k).N_level;    %Columns for 3rd order interactions
      end
      end
   	end
   end

   %if (NF == 4),
   %   num_col(4) = 1;
   %	for (i = 1:1:NF),
   %	   num_col(4) = num_col(4)*FL(i).N_level;  %Columns for 4th order interactions
   %	end
   %end

   % for every design
   if (Contr.ord1.tot > 0),		% 1st order contrasts
      [err, Contr] = ContrVec(1, n, NF, group, dmat, Contr, FL, num_col);
   end   % if (Contr1.tot > 0)

   if (NF > 1 & Contr.ord2.tot > 0),  % 2nd order contrasts
      [err, Contr] = ContrVec(2, n, NF, group, dmat, Contr, FL, num_col);
   end   % if (Contr2.tot > 0)

   if (NF > 2 & Contr.ord3.tot > 0),  % 3rd order contrasts
      [err, Contr] = ContrVec(3, n, NF, group, dmat, Contr, FL, num_col);
   end   % if (Contr3.tot > 0)

	if (NF > 3 & Contr.ord4.tot > 0),  % 4th order contrasts
      [err, Contr] = ContrVec(4, n, NF, group, dmat, Contr, FL, num_col);
   end   % if (Contr3.tot > 0)

end % if (Contr.do == 1)

err = 0;
return;


function test_ft_megrealign

% MEM 2gb
% WALLTIME 00:10:00
% DEPENDENCY ft_megrealign ft_prepare_neighbours ft_transform_geometry

datainfo = ref_datasets;
sel      = match_str({datainfo.datatype},{'bti148' 'bti248' 'ctf151' 'ctf275' 'itab153' 'yokogawa160'}');
datainfo = datainfo(sel);

% as of yet, the code does not explicitly test anything, it just checks
% whether all runs through smoothly
for k = 1:numel(datainfo)
  fname = fullfile(datainfo(k).origdir,'latest/raw',datainfo(k).type,['preproc_',datainfo(k).datatype]);
  load(fname);

  cfg = [];
  cfg.channel = 'MEG';
  cfg.demean = 'yes';
  data = ft_preprocessing(cfg, data);

  vol      = [];
  vol.o    = [0 0 4];
  vol.r    = 10;
  vol.unit = 'cm';

  % ensure units in the gradiometer array and volume conductor to be equal
  data.grad = ft_convert_units(data.grad, 'cm');
  vol       = ft_convert_units(vol, data.grad.unit);

  % make 2 copies
  data2 = data;
  data3 = data;

  data2.grad = ft_transform_geometry([[eye(3) [0 0 -2]'];[0 0 0 1]], data.grad);
  data3.grad = ft_transform_geometry([1 0 0 0;0 cos(0.1) -sin(0.1) 0;0 sin(0.1) cos(0.1) 0;0 0 0 1], data.grad);

  cfg = [];
  cfg.template{1} = data.grad;
  cfg.template{2} = data2.grad;
  cfg.template{3} = data3.grad;
  cfg.inwardshift = 1;
  cfg.headmodel   = vol;

  interp = ft_megrealign(cfg, data);
  interp2 = ft_megrealign(cfg, data2);
  interp3 = ft_megrealign(cfg, data3);

end