% run_hd_specialproc_auto.m
% Jamie Near, Sunnybrook Research Institute 2021.
%
% USAGE:
% [out1,out2,out1_w,out2_w]=run_hd_specialproc_auto(filestring,VoxIDs,aaDomain,tmaxin,iterin);
%
% DESCRIPTION:
% Processing script for Siemens Hadamard-encoded SPECIAL (HD-SPECIAL) MRS
% data in .dat format (twix raw data).  Includes combination of reciever
% channels, removal of bad averages, frequency drift correction,
% leftshifting, and Hadamard Reconstruction.
%
% INPUTS:
% filestring   = String variable for the name of the directory containing
%                   the water suppressed HD-SPECIAL .dat file.  Water unsuppressed
%                   .dat file should be contained in [filestring '_w/'];
% VoxIDs       = (Optional) Two-element cell array with elements that are string variables
%                   giving names to the voxel IDs. Default is {'Vox1','Vox2'}
% aaDomain     = (Optional) Perform the spectral registration (drift correction) using
%                   the full spectrum ('t'), or only a limited frequency
%                   range ('f').  Default is 'f'.
% tmaxin       = (Optional).  Duration (in sec.) of the time domain signal
%                   used in the spectral registration (drift correction).
%                   Default is 0.2 sec.
% iterin       = (Optional).  Maximum number of allowed iterations for the spectral
%                   registration to converge. Default is 20.
%
% OUTPUTS:
% out1         = Fully processed, water suppressed output spectrum from first voxel.
% out2         = Fully processed, water suppressed output spectrum from second voxel.
% out1_w       = Fully processed, water unsuppressed output spectrum from first voxel.
% out2_w       = Fully processed, water unsuppressed output spectrum from second voxel.

function [out1,out2,out1_w,out2_w]=run_hdspecialproc_auto(filestring,VoxIDs,aaDomain,tmaxin,iterin)

if nargin<5
    iterin=20;
    if nargin<4
        tmaxin=0.2;
        if nargin<3
            aaDomain='f';
            if nargin<2
                VoxIDs={'Vox1','Vox2'};
            end
        end
    end
end

%Ensure that filestring is an absolute path, otherwise you can get yourself
%in a tangle later inserting figures at the report stage.
if ~java.io.File(filestring).isAbsolute
    filestring = cd(cd(filestring));
end

%make a new directory for the output report and figures:
reportDir = fullfile(filestring,'report');
mkdir(reportDir);
reportFigsDir = fullfile(filestring,'report','figs');
mkdir(reportFigsDir);

%Find the filename of the SPECIAL dataset
close all
unixString1=fullfile(filestring,'*.dat');
[filename]=dir(unixString1);
filename=filename.name(1:end);

unixStringw=fullfile([filestring '_w'],'*.dat');
[filenamew]=dir(unixStringw);
if ~isempty(filenamew)
    filenamew=filenamew.name(1:end);
    water=true
else
    water=false
    out_w=struct();
    out_w_noproc=struct();
end

%read in the data:
out_raw=io_loadspec_twix(fullfile(filestring,filename));

%load water unsuppressed data and find the coil phases:
if water
    disp('***FOUND WATER UNSUPPRESSED DATA***');
    out_w_raw=io_loadspec_twix(fullfile([filestring '_w/'], filenamew));
    coilcombos1=op_getcoilcombos(op_combinesubspecs(op_fourStepCombine(out_w_raw,1),'diff'),2);
    coilcombos2=op_getcoilcombos(op_combinesubspecs(op_fourStepCombine(out_w_raw,3),'diff'),2);
else
    disp('***WATER UNSUPPRESSED DATA NOT FOUND***');
    coilcombos1=op_getcoilcombos_specReg(op_combinesubspecs(op_fourStepCombine(op_averaging(out_raw),1),'diff'),0,0.01,2);
    coilcombos2=op_getcoilcombos_specReg(op_combinesubspecs(op_fourStepCombine(op_averaging(out_raw),3),'diff'),0,0.01,2);
    out_w='';
    out_w_noproc='';
end

%now combine the coil channels:
[out1_cc,fid1_pre,spec1_pre,ph1,sig1]=op_addrcvrs(out_raw,2,'w',coilcombos1);
[out2_cc,fid2_pre,spec2_pre,ph2,sig2]=op_addrcvrs(out_raw,2,'w',coilcombos2);
if water
    [out1_w_cc,fid1_w_pre,spec1_w_pre,ph1_w,sig1_w]=op_addrcvrs(out_w_raw,2,'w',coilcombos1);
    [out2_w_cc,fid2_w_pre,spec2_w_pre,ph2_w,sig2_w]=op_addrcvrs(out_w_raw,2,'w',coilcombos2);
end

%Do the hadamard reconstruction:
out1_hd=op_fourStepCombine(out1_cc,1);
out2_hd=op_fourStepCombine(out2_cc,3);
if water
    out1_w_hd=op_fourStepCombine(out1_w_cc,1);
    out2_w_hd=op_fourStepCombine(out2_w_cc,3);
end

%make the un-processed spectra, which may be optionally output from this function:
out1_noproc=op_combinesubspecs(op_averaging(out1_hd),'diff');
out2_noproc=op_combinesubspecs(op_averaging(out2_hd),'diff');
if water
    out1_w_noproc=op_combinesubspecs(op_averaging(out1_w_hd),'diff');
    out2_w_noproc=op_combinesubspecs(op_averaging(out2_w_hd),'diff');
end

%plot the data before and after coil phasing:
%figure('position',[0 50 560 420]);
h=figure('visible','off');
subplot(1,3,1);
plot(out_raw.ppm,out_raw.specs(:,:,1,1));xlim([-1 7]);
set(gca,'FontSize',12);
set(gca,'XDir','reverse');
xlabel('Frequency (ppm)','FontSize',10);
ylabel('Amplitude (a.u.)','FontSize',10);
title('Before correction','FontSize',12);
box off;
subplot(1,3,2);
plot(out_raw.ppm,spec1_pre(:,:,1,1));xlim([-1 7]);
set(gca,'FontSize',12);
set(gca,'XDir','reverse');
xlabel('Frequency (ppm)','FontSize',10);
ylabel('Amplitude(a.u.)','FontSize',10);
title('After correction','FontSize',12);
box off;
subplot(1,3,3);
plot(out_raw.ppm,spec2_pre(:,:,1,1));xlim([-1 7]);
set(gca,'FontSize',12);
set(gca,'XDir','reverse');
xlabel('Frequency (ppm)','FontSize',10);
ylabel('Amplitude(a.u.)','FontSize',10);
title('After correction','FontSize',12);
box off;
set(h,'PaperUnits','centimeters');
set(h,'PaperPosition',[0 0 20 10]);
saveas(h,fullfile(reportFigsDir,'coilReconFig'),'jpg');
saveas(h,fullfile(reportFigsDir,'coilReconFig'),'fig');
close(h);

if water
    %figure('position',[0 550 560 420]);
    h=figure('visible','off');
    subplot(1,3,1);
    plot(out_w_raw.ppm,out_w_raw.specs(:,:,1,1));xlim([4 5]);
    set(gca,'FontSize',12);
    set(gca,'XDir','reverse');
    xlabel('Frequency (ppm)','FontSize',10);
    ylabel('Amplitude (a.u.)','FontSize',10);
    title('Before correction','FontSize',12);
    box off;
    subplot(1,3,2);
    plot(out_w_raw.ppm,spec1_w_pre(:,:,1,1));xlim([4 5]);
    set(gca,'FontSize',12);
    set(gca,'XDir','reverse');
    xlabel('Frequency (ppm)','FontSize',10);
    ylabel('Amplitude(a.u.)','FontSize',10);
    title('After correction','FontSize',12);
    box off;
    subplot(1,3,3);
    plot(out_w_raw.ppm,spec2_w_pre(:,:,1,1));xlim([4 5]);
    set(gca,'FontSize',12);
    set(gca,'XDir','reverse');
    xlabel('Frequency (ppm)','FontSize',10);
    ylabel('Amplitude(a.u.)','FontSize',10);
    title('After correction','FontSize',12);
    box off;
    set(h,'PaperUnits','centimeters');
    set(h,'PaperPosition',[0 0 20 10]);
    saveas(h,fullfile(reportFigsDir,'coilReconFig_w'),'jpg');
    saveas(h,fullfile(reportFigsDir,'coilReconFig_w'),'fig');
    close(h);
end

%%%%%%%%%%%%%%%%%%%%%OPTIONAL ALIGNMENT OF SUBSPECTRA%%%%%%%%%%%%%%%%
close all;
fs1_ai=[];
phs1_ai=[];
fs2_ai=[];
phs2_ai=[];
alignISIS='y'
if strcmp(alignISIS,'y') || strcmp(alignISIS,'Y')
    %What we're actually doing is aligning the averages, then aligning the
    %subspectra, then aligning the averages again, and then aligning the
    %subspectra again.
    [out1_ai,fs1_temp,phs1_temp]=op_alignAverages(out1_hd,0.4,'y');
    [out2_ai,fs2_temp,phs2_temp]=op_alignAverages(out2_hd,0.4,'y');
    fs1_ai=fs1_temp;
    fs2_ai=fs2_temp;
    phs1_ai=phs1_temp;
    phs2_ai=phs2_temp;

    [out1_ai,fs1_temp,phs1_temp]=op_alignISIS(out1_ai,0.4);
    [out2_ai,fs2_temp,phs2_temp]=op_alignISIS(out2_ai,0.4);
    fs1_ai(:,2)=fs1_ai(:,2)+fs1_temp;
    fs2_ai(:,2)=fs2_ai(:,2)+fs2_temp;
    phs1_ai(:,2)=phs1_ai(:,2)+phs1_temp;
    phs2_ai(:,2)=phs2_ai(:,2)+phs2_temp;

    [out1_ai,fs1_temp,phs1_temp]=op_alignAverages(out1_ai,0.4,'y');
    [out2_ai,fs2_temp,phs2_temp]=op_alignAverages(out2_ai,0.4,'y');
    fs1_ai=fs1_ai+fs1_temp;
    fs2_ai=fs2_ai+fs2_temp;
    phs1_ai=phs1_ai+phs1_temp;
    phs2_ai=phs2_ai+phs2_temp;

    [out1_ai,fs1_temp,phs1_temp]=op_alignISIS(out1_ai,0.4);
    [out2_ai,fs2_temp,phs2_temp]=op_alignISIS(out2_ai,0.4);
    fs1_ai(:,2)=fs1_ai(:,2)+fs1_temp;
    fs2_ai(:,2)=fs2_ai(:,2)+fs2_temp;
    phs1_ai(:,2)=phs1_ai(:,2)+phs1_temp;
    phs2_ai(:,2)=phs2_ai(:,2)+phs2_temp;

    %for fs_ai and phs_ai, take the average across both subspecs:
    fs1_ai=mean(fs1_ai,2);
    fs2_ai=mean(fs2_ai,2);
    phs1_ai=mean(phs1_ai,2);
    phs2_ai=mean(phs2_ai,2);

    if water
        %Now repeat above for water unsuppressed data:
        [out1_w_ai,fs1_w_temp,phs1_w_temp]=op_alignAverages(out1_w_hd,0.4,'y');
        [out2_w_ai,fs2_w_temp,phs2_w_temp]=op_alignAverages(out2_w_hd,0.4,'y');
        fs1_w_ai=fs1_w_temp;
        fs2_w_ai=fs2_w_temp;
        phs1_w_ai=phs1_w_temp;
        phs2_w_ai=phs2_w_temp;

        [out1_w_ai,fs1_w_temp,phs1_w_temp]=op_alignISIS(out1_w_ai,0.4);
        [out2_w_ai,fs2_w_temp,phs2_w_temp]=op_alignISIS(out2_w_ai,0.4);
        fs1_w_ai(:,2)=fs1_w_ai(:,2)+fs1_w_temp;
        fs2_w_ai(:,2)=fs2_w_ai(:,2)+fs2_w_temp;
        phs1_w_ai(:,2)=phs1_w_ai(:,2)+phs1_w_temp;
        phs2_w_ai(:,2)=phs2_w_ai(:,2)+phs2_w_temp;

        [out1_w_ai,fs1_w_temp,phs1_w_temp]=op_alignAverages(out1_w_ai,0.4,'y');
        [out2_w_ai,fs2_w_temp,phs2_w_temp]=op_alignAverages(out2_w_ai,0.4,'y');
        fs1_w_ai=fs1_w_ai+fs1_w_temp;
        fs2_w_ai=fs2_w_ai+fs2_w_temp;
        phs1_w_ai=phs1_w_ai+phs1_w_temp;
        phs2_w_ai=phs2_w_ai+phs2_w_temp;

        [out1_w_ai,fs1_w_temp,phs1_w_temp]=op_alignISIS(out1_w_ai,0.4);
        [out2_w_ai,fs2_w_temp,phs2_w_temp]=op_alignISIS(out2_w_ai,0.4);
        fs1_w_ai(:,2)=fs1_w_ai(:,2)+fs1_w_temp;
        fs2_w_ai(:,2)=fs2_w_ai(:,2)+fs2_w_temp;
        phs1_w_ai(:,2)=phs1_w_ai(:,2)+phs1_w_temp;
        phs2_w_ai(:,2)=phs2_w_ai(:,2)+phs2_w_temp;

        %for fs_w_ai and phs_w_ai, take the average across both subspecs:
        fs1_w_ai=mean(fs1_w_ai,2);
        fs2_w_ai=mean(fs2_w_ai,2);
        phs1_w_ai=mean(phs1_w_ai,2);
        phs2_w_ai=mean(phs2_w_ai,2);
    end

    %Now check the plots to make sure that they look okay:
    %First make combined subspecs plots:
    out1_hd_temp=op_combinesubspecs(out1_hd,'diff');
    out2_hd_temp=op_combinesubspecs(out2_hd,'diff');
    out1_ai_cc_temp=op_combinesubspecs(out1_ai,'diff');
    out2_ai_cc_temp=op_combinesubspecs(out2_ai,'diff');
    if water
        out1_w_hd_temp=op_combinesubspecs(out1_w_hd,'diff');
        out2_w_hd_temp=op_combinesubspecs(out2_w_hd,'diff');
        out1_w_ai_cc_temp=op_combinesubspecs(out1_w_ai,'diff');
        out2_w_ai_cc_temp=op_combinesubspecs(out2_w_ai,'diff');
    end

    %Now plot them
    close all
    %figure('position',[0 0 560 420]);
    h=figure('visible','off');
    subplot(2,2,1);
    plot(out1_hd_temp.ppm,out1_hd_temp.specs);
    set(gca,'FontSize',12);
    set(gca,'XDir','reverse');
    xlabel('Frequency (ppm)','FontSize',10);
    ylabel('Amplitude(a.u.)','FontSize',10);
    title('Subspecs not aligned: (all averages)','FontSize',12);
    xlim([0.2 5.2]);
    box off;
    subplot(2,2,3);
    plot(out2_hd_temp.ppm,out2_hd_temp.specs);
    set(gca,'FontSize',12);
    set(gca,'XDir','reverse');
    xlabel('Frequency (ppm)','FontSize',10);
    ylabel('Amplitude(a.u.)','FontSize',10);
    title('Subspecs not aligned: (all averages)','FontSize',12);
    xlim([0.2 5.2]);
    box off;
    subplot(2,2,2);
    plot(out1_ai_cc_temp.ppm,out1_ai_cc_temp.specs);
    set(gca,'FontSize',8);
    set(gca,'XDir','reverse');
    xlabel('Frequency (ppm)','FontSize',10);
    ylabel('Amplitude(a.u.)','FontSize',10);
    title('Subspecs aligned: (all averages)','FontSize',12);
    xlim([0.2 5.2]);
    box off;
    subplot(2,2,4);
    plot(out2_ai_cc_temp.ppm,out2_ai_cc_temp.specs);
    set(gca,'FontSize',8);
    set(gca,'XDir','reverse');
    xlabel('Frequency (ppm)','FontSize',10);
    ylabel('Amplitude(a.u.)','FontSize',10);
    title('Subspecs aligned: (all averages)','FontSize',12);
    xlim([0.2 5.2]);
    box off;
    set(h,'PaperUnits','centimeters');
    set(h,'PaperPosition',[0 0 20 15]);
    saveas(h,[filestring '/report/figs/alignSubSpecs_prePostFig'],'jpg');
    saveas(h,[filestring '/report/figs/alignSubSpecs_prePostFig'],'fig');
    close(h);

    if water
        %figure('position',[0 550 560 420]);
        h=figure('visible','off');
        subplot(2,2,1);
        plot(out1_w_hd_temp.ppm,out1_w_hd_temp.specs);
        xlim([3.7 5.7]);
        set(gca,'FontSize',12);
        set(gca,'XDir','reverse');
        xlabel('Frequency (ppm)','FontSize',10);
        ylabel('Amplitude(a.u.)','FontSize',10);
        title('Subspecs not aligned: (all averages)','FontSize',12);
        box off;
        subplot(2,2,3);
        plot(out2_w_hd_temp.ppm,out2_w_hd_temp.specs);
        xlim([3.7 5.7]);
        set(gca,'FontSize',12);
        set(gca,'XDir','reverse');
        xlabel('Frequency (ppm)','FontSize',10);
        ylabel('Amplitude(a.u.)','FontSize',10);
        title('Subspecs not aligned: (all averages)','FontSize',12);
        box off;
        subplot(1,2,2);
        plot(out1_w_ai_cc_temp.ppm,out1_w_ai_cc_temp.specs);
        xlim([3.7 5.7]);
        set(gca,'FontSize',8);
        set(gca,'XDir','reverse');
        xlabel('Frequency (ppm)','FontSize',10);
        ylabel('Amplitude(a.u.)','FontSize',10);
        title('Subspecs aligned: (all averages)','FontSize',12);
        box off;
        subplot(2,2,4);
        plot(out2_w_ai_cc_temp.ppm,out2_w_ai_cc_temp.specs);
        xlim([3.7 5.7]);
        set(gca,'FontSize',8);
        set(gca,'XDir','reverse');
        xlabel('Frequency (ppm)','FontSize',10);
        ylabel('Amplitude(a.u.)','FontSize',10);
        title('Subspecs aligned: (all averages)','FontSize',12);
        box off;
        set(h,'PaperUnits','centimeters');
        set(h,'PaperPosition',[0 0 20 15]);
        saveas(h,fullfile(reportFigsDir,'alignSubSpecs_w_prePostFig'),'jpg');
        saveas(h,fullfile(reportFigsDir,'alignSubSpecs_w_prePostFig'),'fig');
        close(h);
    end

%     figure('position',[570 50 560 420]);
%     subplot(2,1,1);
%     plot([1:length(fs_ai)],fs_ai);
%     xlabel('Scan Number');
%     ylabel('Frequency Drift (Hz)');
%     title('Estimated Frequency Drift');
%     subplot(2,1,2);
%     plot([1:length(phs_ai)],phs_ai);
%     xlabel('Scan Number');
%     ylabel('Phase Drift (Degrees)');
%     title('Estimated Phase Drift');

    %sat=input('are you satisfied with alignment of subspecs? ','s');
    sat='y';
    if strcmp(sat,'n') || strcmp(sat,'N')
        out1_ai=out1_hd;
        out2_ai=out2_hd;
        if water
            out1_w_ai=out1_w_hd;
            out2_w_ai=out2_w_hd;
        end
    end

else
    out1_ai=out1_hd;
    out2_ai=out2_hd;
    out1_w_ai=out1_w_hd;
    out2_w_ai=out2_w_hd;
    fs1_ai=zeros(size(out1_hd.fids,out1_hd.dims.averages),1);
    fs2_ai=zeros(size(out2_hd.fids,out2_hd.dims.averages),1);
    phs1_ai=zeros(size(out1_hd.fids,out1_hd.dims.averages),1);
    phs2_ai=zeros(size(out2_hd.fids,out2_hd.dims.averages),1);
end

%Now combine the subspecs
out1_cs=op_combinesubspecs(out1_ai,'diff');
out2_cs=op_combinesubspecs(out2_ai,'diff');
if water
    out1_w_cs=op_combinesubspecs(out1_w_ai,'diff');
    out2_w_cs=op_combinesubspecs(out2_w_ai,'diff');
end

%%%%%%%%%%%%%%%%%%%%%END OF ALIGNMENT OF SUBSPECTRA%%%%%%%%%%%%%%%%%%

%%%%%%%%%%%%%%%%%%%%%OPTIONAL REMOVAL OF BAD AVERAGES%%%%%%%%%%%%%%%%
close all;
out1_cs2=out1_cs;
out2_cs2=out2_cs;
nBadAvgTotal1=0;
nBadAvgTotal2=0;
nbadAverages1=1;
nbadAverages2=1;
allAveragesLeft1=[1:out1_cs.sz(out1_cs.dims.averages)]';
allAveragesLeft2=[1:out2_cs.sz(out2_cs.dims.averages)]';
allBadAverages1=[];
allBadAverages2=[];
rmbadav='y';
close all;
if rmbadav=='n' || rmbadav=='N'
    out1_rm=out1_cs;
    out2_rm=out2_cs;
    nsd='N/A';
else
    sat='n'
    while sat=='n'||sat=='N'
        nsd=3; %setting the number of standard deviations;
        iter1=1;
        iter2=1;
        nbadAverages1=1;
        nbadAverages2=1;
        nBadAvgTotal1=0;
        nBadAvgTotal2=0;
        allAveragesLeft1=[1:out1_cs.sz(out1_cs.dims.averages)]';
        allAveragesLeft2=[1:out2_cs.sz(out2_cs.dims.averages)]';
        allBadAverages1=[];
        allBadAverages2=[];
        out1_cs2=out1_cs;
        out2_cs2=out2_cs;
        while nbadAverages1>0;
            [out1_rm,metric1{iter1},badAverages1]=op_rmbadaverages(out1_cs2,nsd,'t');
            allBadAverages1=[allBadAverages1; allAveragesLeft1(badAverages1)];
            badavMask1_temp=zeros(length(allAveragesLeft1),1);
            badavMask1_temp(badAverages1)=1;
            allAveragesLeft1=allAveragesLeft1(~badavMask1_temp);
            nbadAverages1=numel(badAverages1);
            nBadAvgTotal1=nBadAvgTotal1+nbadAverages1;
            out1_cs2=out1_rm;
            iter1=iter1+1;
            disp([num2str(nbadAverages1) ' bad averages removed from spectrum #1 on this iteration.']);
            disp([num2str(nBadAvgTotal1) ' bad averages removed from spectrum #1 in total.']);
            close all;
        end
        while nbadAverages2>0;
            [out2_rm,metric2{iter2},badAverages2]=op_rmbadaverages(out2_cs2,nsd,'t');
            allBadAverages2=[allBadAverages2; allAveragesLeft2(badAverages2)];
            badavMask2_temp=zeros(length(allAveragesLeft2),1);
            badavMask2_temp(badAverages2)=1;
            allAveragesLeft2=allAveragesLeft2(~badavMask2_temp);
            nbadAverages2=numel(badAverages2);
            nBadAvgTotal2=nBadAvgTotal2+nbadAverages2;
            out2_cs2=out2_rm;
            iter2=iter2+1;
            disp([num2str(nbadAverages2) ' bad averages removed from spectrum #2 on this iteration.']);
            disp([num2str(nBadAvgTotal2) ' bad averages removed from spectrum #2 in total.']);
            close all;
        end

        %figure('position',[0 50 560 420]);
        h=figure('visible','off');
        subplot(2,2,1);
        plot(out1_cs.ppm,out1_cs.specs);xlim([1 5]);
        set(gca,'FontSize',8);
        set(gca,'XDir','reverse');
        xlabel('Frequency (ppm)','FontSize',10);
        ylabel('Amplitude(a.u.)','FontSize',10);
        title('Before removal of bad averages:','FontSize',12);
        box off;
        subplot(2,2,3);
        plot(out2_cs.ppm,out2_cs.specs);xlim([1 5]);
        set(gca,'FontSize',8);
        set(gca,'XDir','reverse');
        xlabel('Frequency (ppm)','FontSize',10);
        ylabel('Amplitude(a.u.)','FontSize',10);
        title('Before removal of bad averages:','FontSize',12);
        box off;
        subplot(2,2,2);
        plot(out1_rm.ppm,out1_rm.specs);xlim([1 5]);
        set(gca,'FontSize',8);
        set(gca,'XDir','reverse');
        xlabel('Frequency (ppm)','FontSize',10);
        ylabel('Amplitude(a.u.)','FontSize',10);
        title('After removal of bad averages:','FontSize',12);
        box off;
        subplot(2,2,4);
        plot(out2_rm.ppm,out2_rm.specs);xlim([1 5]);
        set(gca,'FontSize',8);
        set(gca,'XDir','reverse');
        xlabel('Frequency (ppm)','FontSize',10);
        ylabel('Amplitude(a.u.)','FontSize',10);
        title('After removal of bad averages:','FontSize',12);
        box off;
        set(h,'PaperUnits','centimeters');
        set(h,'PaperPosition',[0 0 20 15]);
        saveas(h,fullfile(reportFigsDir,'rmBadAvg_prePostFig'),'jpg');
        saveas(h,fullfile(reportFigsDir,'rmBadAvg_prePostFig'),'fig');
        close(h);

        %figure('position',[0 550 560 420]);
        h=figure('visible','off');
        plot(1:out1_cs.sz(out1_cs.dims.averages),metric1{1},'.r','MarkerSize',16)
        hold on
        removedAvgs1 = find(~ismember(1:out1_cs.sz(out1_cs.dims.averages),allAveragesLeft1));
        plot(removedAvgs1,metric1{1}(removedAvgs1,1),'ko','MarkerSize',20)
        set(gca,'FontSize',8);
        xlabel('Scan Number','FontSize',10);
        ylabel('Deviation Metric','FontSize',10);
        legend('Original averages','Removed Avg')
        legend boxoff;
        title('Deviation Metric','FontSize',12);
        box off;
        set(h,'PaperUnits','centimeters');
        set(h,'PaperPosition',[0 0 20 10]);
        saveas(h,fullfile(reportFigsDir,'rmBadAvg_vox1_scatterFig1'),'jpg');
        saveas(h,fullfile(reportFigsDir,'rmBadAvg_vox1_scatterFig1'),'fig');
        close(h);

        h=figure('visible','off');
        plot(1:out2_cs.sz(out2_cs.dims.averages),metric2{1},'.r','MarkerSize',16)
        hold on
        removedAvgs2 = find(~ismember(1:out2_cs.sz(out2_cs.dims.averages),allAveragesLeft2));
        plot(removedAvgs2,metric2{1}(removedAvgs2,1),'ko','MarkerSize',20)
        set(gca,'FontSize',8);
        xlabel('Scan Number','FontSize',10);
        ylabel('Deviation Metric','FontSize',10);
        legend('Original averages','Removed Avg')
        legend boxoff;
        title('Deviation Metric','FontSize',12);
        box off;
        set(h,'PaperUnits','centimeters');
        set(h,'PaperPosition',[0 0 20 10]);
        saveas(h,fullfile(reportFigsDir,'rmBadAvg_vox2_scatterFig'),'jpg');
        saveas(h,fullfile(reportFigsDir,'rmBadAvg_vox2_scatterFig'),'fig');
        close(h);

        %sat=input('are you satisfied with bad averages removal? ','s');
        sat='y';
    end
end

close all;

%Now remove the entries from fs_ai and phs_ai that correspond to
%the bad-averages that were removed.
BadAvgMask1=zeros(length(fs1_ai),1);
BadAvgMask2=zeros(length(fs2_ai),1);
BadAvgMask1(allBadAverages1)=1;
BadAvgMask2(allBadAverages2)=1;
size(BadAvgMask1)
size(BadAvgMask2)
size(fs1_ai)
size(fs2_ai)
fs1_ai=fs1_ai(~BadAvgMask1);
fs2_ai=fs2_ai(~BadAvgMask2);
phs1_ai=phs1_ai(~BadAvgMask1);
phs2_ai=phs2_ai(~BadAvgMask2);

%%%%%%%%%%%%%%%%%%%%END OF BAD AVERAGES REMOVAL%%%%%%%%%%%%%%%%%%%%

%now align averages;
%driftCorr=input('Would you like to perform the frequency drift correction?  ','s');
driftCorr='y';
if driftCorr=='n'|| driftCorr=='N'
    out1_aa=out1_rm;
    out2_aa=out2_rm;
    if water
        out1_w_aa=out1_w_cs;
        out2_w_aa=out2_w_cs;
    end
else
    sat='n'
    while sat=='n' || sat=='N'
        out1_rm2=out1_rm;
        out2_rm2=out2_rm;
        fsPoly1=100;
        fsPoly2=100;
        phsPoly1=1000;
        phsPoly2=1000;
        fsCum1=fs1_ai;
        fsCum2=fs2_ai;
        phsCum1=phs1_ai;
        phsCum2=phs2_ai;
        iter1=1;
        iter2=1;
        while (abs(fsPoly1(1))>0.001 || abs(phsPoly1(1))>0.01) && iter1<iterin
            close all
            if aaDomain=='t' || aaDomain=='T'
                tmax=input('input tmax for drift correction: ');
                [out1_aa,fs1,phs1]=op_alignAverages(out1_rm2,tmax,'n');
            elseif aaDomain=='f' || aaDomain=='F'
                tmax=tmaxin+0.04*randn(1);
                fmin=1.8+0.1*randn(1);
                fmaxarray=[2.4,2.85,3.35,4.2,4.4,5.2];
                fmax=fmaxarray(randi(6,1))
                [out1_aa,fs1,phs1]=op_alignAverages_fd(out1_rm2,fmin,fmax,tmax,'n');
            end
            if water
                [out1_w_aa,fs1_w,phs1_w]=op_alignAverages(out1_w_cs,5*tmax,'n');
            end

            fsCum1=fsCum1+fs1;
            phsCum1=phsCum1+phs1;
            fsPoly1=polyfit([1:out1_aa.sz(out1_aa.dims.averages)]',fs1,1)
            phsPoly1=polyfit([1:out1_aa.sz(out1_aa.dims.averages)]',phs1,1)
            iter1
            out1_rm2=out1_aa;
            if water;
                out1_w_cs=out1_w_aa;
            end
            iter1=iter1+1;
        end
        while (abs(fsPoly2(1))>0.001 || abs(phsPoly2(1))>0.01) && iter2<iterin
            close all
            if aaDomain=='t' || aaDomain=='T'
                tmax=input('input tmax for drift correction: ');
                [out2_aa,fs2,phs2]=op_alignAverages(out2_rm2,tmax,'n');
            elseif aaDomain=='f' || aaDomain=='F'
                tmax=tmaxin+0.04*randn(1);
                fmin=1.8+0.1*randn(1);
                fmaxarray=[2.4,2.85,3.35,4.2,4.4,5.2];
                fmax=fmaxarray(randi(6,1))
                [out2_aa,fs2,phs2]=op_alignAverages_fd(out2_rm2,fmin,fmax,tmax,'n');
            end
            if water
                [out2_w_aa,fs2_w,phs2_w]=op_alignAverages(out2_w_cs,5*tmax,'n');
            end

            fsCum2=fsCum2+fs2;
            phsCum2=phsCum2+phs2;
            fsPoly2=polyfit([1:out2_aa.sz(out2_aa.dims.averages)]',fs2,1)
            phsPoly2=polyfit([1:out2_aa.sz(out2_aa.dims.averages)]',phs2,1)
            iter2
            out2_rm2=out2_aa;
            if water;
                out2_w_cs=out2_w_aa;
            end
            iter2=iter2+1;
        end
        %Now plot the cumulative frequency drift correction:
        %figure('position',[0 550 1125 420]);
        h=figure('visible','off');
        subplot(2,2,1);
        plot(out1_rm.ppm,out1_rm.specs);xlim([0 6]);
        set(gca,'FontSize',8);
        set(gca,'XDir','reverse');
        xlabel('Frequency (ppm)','FontSize',10);
        ylabel('Amplitude(a.u.)','FontSize',10);
        title('Before drift correction:');
        box off;
        subplot(2,2,2);
        plot(out2_rm.ppm,out2_rm.specs);xlim([0 6]);
        set(gca,'FontSize',8);
        set(gca,'XDir','reverse');
        xlabel('Frequency (ppm)','FontSize',10);
        ylabel('Amplitude(a.u.)','FontSize',10);
        title('Before drift correction:');
        box off;
        subplot(2,2,3);
        plot(out1_aa.ppm,out1_aa.specs);xlim([0 6]);
        set(gca,'FontSize',8);
        set(gca,'XDir','reverse');
        xlabel('Frequency (ppm)','FontSize',10);
        ylabel('Amplitude(a.u.)','FontSize',10);
        title('After drift correction:');
        box off;
        subplot(2,2,4);
        plot(out2_aa.ppm,out2_aa.specs);xlim([0 6]);
        set(gca,'FontSize',8);
        set(gca,'XDir','reverse');
        xlabel('Frequency (ppm)','FontSize',10);
        ylabel('Amplitude(a.u.)','FontSize',10);
        title('After drift correction:');
        box off;
        set(h,'PaperUnits','centimeters');
        set(h,'PaperPosition',[0 0 20 15]);
        saveas(h,fullfile(reportFigsDir,'alignAvgs_prePostFig'),'jpg');
        saveas(h,fullfile(reportFigsDir,'alignAvgs_prePostFig'),'fig');
        close(h);

        %figure('position',[0 50 560 420]);
        h=figure('visible','off');
        subplot(2,1,1);
        plot([1:out1_aa.sz(out1_aa.dims.averages)],phsCum1);
        set(gca,'FontSize',8);
        xlabel('Scan Number','FontSize',10);
        ylabel('Phase Drift (deg.)','FontSize',10);
        title('Estimated Phase Drift','FontSize',12);
        box off;
        subplot(2,1,2);
        plot([1:out2_aa.sz(out2_aa.dims.averages)],phsCum2);
        set(gca,'FontSize',8);
        xlabel('Scan Number','FontSize',10);
        ylabel('Phase Drift (deg.)','FontSize',10);
        title('Estimated Phase Drift','FontSize',12);
        box off;
        legend('Phase drift','Location','SouthEast');
        legend boxoff;
        set(h,'PaperUnits','centimeters');
        set(h,'PaperPosition',[0 0 10 10]);
        saveas(h,fullfile(reportFigsDir,'phaseDriftFig'),'jpg');
        saveas(h,fullfile(reportFigsDir,'phaseDriftFig'),'fig');
        close(h);

        %figure('position',[570 50 560 420]);
        h=figure('visible','off');
        subplot(2,1,1);
        plot([1:out1_aa.sz(out1_aa.dims.averages)],fsCum1);
        set(gca,'FontSize',8);
        xlabel('Scan Number','FontSize',10);
        ylabel('Frequency Drift [Hz]','FontSize',10);
        box off;
        subplot(2,1,2);
        plot([1:out2_aa.sz(out2_aa.dims.averages)],fsCum2);
        set(gca,'FontSize',8);
        xlabel('Scan Number','FontSize',10);
        ylabel('Frequency Drift [Hz]','FontSize',10);
        box off;
        legend('Frequency Drift','Location','SouthEast');
        legend boxoff;
        title('Estimated Frequency Drift','FontSize',12);
        set(h,'PaperUnits','centimeters');
        set(h,'PaperPosition',[0 0 10 10]);
        saveas(h,fullfile(reportFigsDir,'freqDriftFig'),'jpg');
        saveas(h,fullfile(reportFigsDir,'freqDriftFig'),'fig');
        close(h);

        %sat=input('Are you satisfied with the drift correction? ','s');
        sat='y';
        totalFreqDrift1=mean(max(fsCum1)-min(fsCum1));
        totalFreqDrift2=mean(max(fsCum2)-min(fsCum2));
        totalPhaseDrift1=mean(max(phsCum1)-min(phsCum1));
        totalPhaseDrift2=mean(max(phsCum2)-min(phsCum2));
    end

end

%now do the averaging and left shift to get rid of first order phase:
out1_av=op_leftshift(op_averaging(out1_aa),out1_aa.pointsToLeftshift);
out2_av=op_leftshift(op_averaging(out2_aa),out2_aa.pointsToLeftshift);
if water
    out1_w_av=op_leftshift(op_averaging(out1_w_aa),out1_w_aa.pointsToLeftshift);
    out2_w_av=op_leftshift(op_averaging(out2_w_aa),out2_w_aa.pointsToLeftshift);
end

%Now do an automatic phase correction (Use Creatine peak):
out1_av_zp=op_zeropad(out1_av,16);
out2_av_zp=op_zeropad(out2_av,16);
index1=find(abs(out1_av_zp.specs)==max(abs(out1_av_zp.specs(out1_av_zp.ppm>2.85 & out1_av_zp.ppm<3.15,1))));
index2=find(abs(out2_av_zp.specs)==max(abs(out2_av_zp.specs(out2_av_zp.ppm>2.85 & out2_av_zp.ppm<3.15,1))));
ph01=-phase(out1_av_zp.specs(index1,1))*180/pi;
ph02=-phase(out2_av_zp.specs(index2,1))*180/pi;
out1_ph=op_addphase(out1_av,ph01);
out2_ph=op_addphase(out2_av,ph02);
out1_noproc=op_addphase(op_leftshift(out1_noproc,out1_noproc.pointsToLeftshift),ph01);
out2_noproc=op_addphase(op_leftshift(out2_noproc,out2_noproc.pointsToLeftshift),ph02);
%And now for water unsuppressed data (use water peak):
if water
    out1_w_av_zp=op_zeropad(out1_w_av,16);
    out2_w_av_zp=op_zeropad(out2_w_av,16);
    index1w=find(abs(out1_w_av_zp.specs)==max(abs(out1_w_av_zp.specs(out1_w_av_zp.ppm>4 & out1_w_av_zp.ppm<5.5))));
    index2w=find(abs(out2_w_av_zp.specs)==max(abs(out2_w_av_zp.specs(out2_w_av_zp.ppm>4 & out2_w_av_zp.ppm<5.5))));
    ph01w=-phase(out1_w_av_zp.specs(index1w))*180/pi;
    ph02w=-phase(out2_w_av_zp.specs(index2w))*180/pi;
    out1_w_ph=op_addphase(out1_w_av,ph01w);
    out2_w_ph=op_addphase(out2_w_av,ph02w);
    out1_w_noproc=op_addphase(op_leftshift(out1_w_noproc,out1_w_noproc.pointsToLeftshift),ph01w);
    out2_w_noproc=op_addphase(op_leftshift(out2_w_noproc,out2_w_noproc.pointsToLeftshift),ph02w);
    out1_w_ph_zp=op_addphase(out1_w_av_zp,ph01w);
    out2_w_ph_zp=op_addphase(out2_w_av_zp,ph02w);
end

%Frequency shift all spectra so that Creatine appears at 3.027 ppm:
[~,frqShift1]=op_ppmref(out1_av_zp,2.9,3.1,3.027);
[~,frqShift2]=op_ppmref(out2_av_zp,2.9,3.1,3.027);
out1=op_freqshift(out1_ph,frqShift1);
out2=op_freqshift(out2_ph,frqShift2);
out1_noproc=op_freqshift(out1_noproc,frqShift1);
out2_noproc=op_freqshift(out2_noproc,frqShift2);
%And now for water unsuppressed data (user water peak and set to 4.65 ppm):
if water
    [~,frqShift1w]=op_ppmref(out1_w_ph_zp,4,5.5,4.65);
    [~,frqShift2w]=op_ppmref(out2_w_ph_zp,4,5.5,4.65);
    out1_w=op_freqshift(out1_w_ph,frqShift1w);
    out2_w=op_freqshift(out2_w_ph,frqShift2w);
    out1_w_noproc=op_freqshift(out1_w_noproc,frqShift1w);
    out2_w_noproc=op_freqshift(out2_w_noproc,frqShift2w);
end

%Make figure to show the final spectrum:
h=figure('visible','off');
subplot(1,2,1);
plot(out1.ppm,real(out1.specs),'linewidth',2);xlim([0.2 5.2]);
set(gca,'FontSize',8);
set(gca,'XDir','reverse');
xlabel('Frequency (ppm)','FontSize',10);
ylabel('Amplitude(a.u.)','FontSize',10);
box off;
subplot(1,2,2);
plot(out2.ppm,real(out2.specs),'linewidth',2);xlim([0.2 5.2]);
set(gca,'FontSize',8);
set(gca,'XDir','reverse');
xlabel('Frequency (ppm)','FontSize',10);
ylabel('Amplitude(a.u.)','FontSize',10);
box off;
title('Result: Final Spectra','FontSize',12);
set(h,'PaperUnits','centimeters');
set(h,'PaperPosition',[0 0 20 10]);
saveas(h,fullfile(reportFigsDir,'finalSpecFig'),'jpg');
saveas(h,fullfile(reportFigsDir,'finalSpecFig'),'fig');

wrt='y';
if wrt=='y' || wrt=='Y'
    RF1=io_writelcm(out1,fullfile(filestring,[VoxIDs{1} '_lcm']),out1.te);
    RF2=io_writelcm(out2,fullfile(filestring,[VoxIDs{2} '_lcm']),out2.te);
    RF1=io_writelcm(out1_noproc,fullfile(filestring,[VoxIDs{1} '_unprocessed_lcm']),out1_noproc.te);
    RF2=io_writelcm(out2_noproc,fullfile(filestring,[VoxIDs{2} '_unprocessed_lcm']),out2_noproc.te);
    if water
        RF1=io_writelcm(out1_w,fullfile(filestring,[VoxIDs{1} '_w_lcm']),out1_w.te);
        RF2=io_writelcm(out2_w,fullfile(filestring,[VoxIDs{2} '_w_lcm']),out2_w.te);
        RF1=io_writelcm(out1_w_noproc,fullfile(filestring,[VoxIDs{1} '_w_unprocessed_lcm']),out1_w_noproc.te);
        RF2=io_writelcm(out2_w_noproc,fullfile(filestring,[VoxIDs{2} '_w_unprocessed_lcm']),out2_w_noproc.te);
    end
end

close all

%write an html report:
fid=fopen(fullfile(reportDir,'report.html'),'w+');
fprintf(fid,'<!DOCTYPE html>');
fprintf(fid,'\n<html>');
logoPath=which('FID-A_LOGO.jpg');
fprintf(fid,'\n<img src= " %s " width="120" height="120"></body>',logoPath);
fprintf(fid,'\n<h1>FID-A Processing Report</h1>');
fprintf(fid,'\n<h2>Processing pipeline applied to HD-SPECIAL data using run_hdspecialproc_auto.m</h2>');
fprintf(fid,'\n<p>FILENAME: %s/%s/%s </p>',fullfile(filestring,filename));
fprintf(fid,'\n<p>DATE: %s </p>',date);
fprintf(fid,'\n\n<p> </p>');
fprintf(fid,'\n\n<h2>Results of multi-coil combination:</h2>');
fprintf(fid,'\n<img src= " %s " width="800" height="400"></body>',fullfile(reportFigsDir,'coilReconFig.jpg'));
fprintf(fid,'\n\n<p> </p>');
fprintf(fid,'\n\n<h2>Results of alignment of SPECIAL sub-spectra:</h2>');
fprintf(fid,'\n<img src= " %s " width="800" height="400"></body>',fullfile(reportFigsDir,'alignSubSpecs_prePostFig.jpg'));
fprintf(fid,'\n\n<p> </p>');
fprintf(fid,'\n\n<h2>Results of removal of bad averages:</h2>');
fprintf(fid,'\n<p>Original number of averages: \t%5.6f </p>',out_raw.sz(out_raw.dims.averages));
fprintf(fid,'\n<p>Number of bad Averages removed from vox1:  \t%5.6f </p>',nBadAvgTotal1);
fprintf(fid,'\n<p>Number of bad Averages removed from vox2:  \t%5.6f </p>',nBadAvgTotal2);
fprintf(fid,'\n<p>Number of remaining averages in processed dataset (vox1):  \t%5.6f </p>',out1_rm.sz(out1_rm.dims.averages));
fprintf(fid,'\n<p>Number of remaining averages in processed dataset (vox2):  \t%5.6f </p>',out2_rm.sz(out2_rm.dims.averages));
fprintf(fid,'\n<p>Bad Averages Removal Threshold was:  \t%2.2f </p>',nsd);
fprintf(fid,'\n<img src= " %s " width="800" height="600"><img src= " %s " width="800" height="400"><img src= " %s " width="800" height="400">',fullfile(reportFigsDir,'rmBadAvg_prePostFig.jpg'),fullfile(reportFigsDir,'rmBadAvg_vox1_scatterFig.jpg'),fullfile(reportFigsDir,'rmBadAvg_vox2_scatterFig.jpg'));
fprintf(fid,'\n\n<p> </p>');
fprintf(fid,'\n\n<h2>Results of spectral registration:</h2>');
fprintf(fid,'\n<p>Total frequency drift from vox1 was: \t%5.6f </p>',max(totalFreqDrift1));
fprintf(fid,'\n<p>Total frequency drift from vox2 was: \t%5.6f </p>',max(totalFreqDrift2));
fprintf(fid,'\n<p>Total phase drift from vox1 was: \t%5.6f </p>',max(totalPhaseDrift1));
fprintf(fid,'\n<p>Total phase drift from vox2 was: \t%5.6f </p>',max(totalPhaseDrift2));
fprintf(fid,'\n<img src= " %s " width="800" height="600">',fullfile(reportFigsDir,'alignAvgs_prePostFig.jpg'));
fprintf(fid,'\n\n<p> </p>');
fprintf(fid,'\n<img src= " %s " width="400" height="400"><img src=" %s " width="400" height="400">',fullfile(reportFigsDir,'freqDriftFig.jpg'),fullfile(reportFigsDir,'phaseDriftFig.jpg'));
fprintf(fid,'\n\n<p> </p>');
fprintf(fid,'\n\n<h2>Final Result:</h2>');
fprintf(fid,'\n<img src= " %s " width="800" height="400">',fullfile(reportFigsDir,'finalSpecFig.jpg'));
fclose(fid);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


function nav=adjnav(nav,opt)
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%adjust the wavelength of priority frequencies for BDS2 and BDS3
%eph and geph struct to eph and geph matrix
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

global glc

%adjust eph
if nav.n>0
    eph0=zeros(nav.n,40);
    for i=1:nav.n
        eph=nav.eph(i);
        eph0(i,:)=[eph.sat, eph.iode, eph.iodc, eph.sva, eph.svh, eph.week, eph.code, eph.flag, eph.toc.time, eph.toc.sec,...
            eph.toe.time, eph.toe.sec, eph.ttr.time, eph.ttr.sec, eph.A, eph.e, eph.i0, eph.OMG0, eph.omg, eph.M0,...
            eph.deln, eph.OMGd, eph.idot, eph.crc, eph.crs, eph.cuc, eph.cus, eph.cic, eph.cis, eph.toes,...
            eph.fit, eph.f0, eph.f1, eph.f2, eph.tgd, eph.Adot, eph.ndot];
    end
    nav=rmfield(nav,'eph');
    nav.eph=eph0;
end

%adjust geph
if nav.ng>0
    geph0=zeros(nav.ng,22);
    for i=1:nav.ng
        geph=nav.geph(i);
        geph0(i,:)=[geph.sat, geph.iode, geph.frq, geph.svh, geph.sva, geph.age, geph.toe.time, geph.toe.sec, geph.tof.time, geph.tof.sec,...
            geph.pos', geph.vel', geph.acc', geph.taun,...
            geph.gamn, geph.dtaun];
    end
    nav=rmfield(nav,'geph');
    nav.geph=geph0;
end

% adjust wavelength
bds_frq_flag=1;
lam=nav.lam; nav=rmfield(nav,'lam'); nav.lam=zeros(glc.MAXSAT,glc.NFREQ);
if glc.NFREQ>3&&(size(opt.bd2frq,2)<=3||size(opt.bd3frq,2)<=3)&&~isempty(strfind(opt.navsys,'C'))
    bds_frq_flag=0;
    fprintf('Warning:Specified frequency of BDS less than used number of frequency!\n');
end
for i=1:glc.MAXSAT
    [sys,prn]=satsys(i);
    if sys==glc.SYS_BDS
        if prn<19 %BD2
            if ~bds_frq_flag,continue;end
            frq=opt.bd2frq;
            for j=1:glc.NFREQ
               nav.lam(i,j)=lam(i,frq(j));
            end
        else %BD3
            if ~bds_frq_flag,continue;end
            frq=opt.bd3frq;
            for j=1:glc.NFREQ
                nav.lam(i,j)=lam(i,frq(j));
            end
        end
    else %GPS GLO GAL QZS
        nav.lam(i,:)=lam(i,1:3);
    end
end

return


classdef ParadigmPAL < ParadigmBaseSimplified
    % Experimental implementation of the Pattern Alignment Learning (PAL) method.
    %
    % This paradigm is not yet completed, please move on! :-)
    %
    % Name:
    %   Wave Alignment, work in progress
    %
    %                            Christian Kothe, Swartz Center for Computational Neuroscience, UCSD
    %                            2011-08-28

    methods

        function defaults = preprocessing_defaults(self)
            % define the default pre-processing parameters of this paradigm
            defaults = {'SpectralSelection',[0.1 35],'EpochExtraction',[-0.5 1.5], 'Resampling',100};
        end

        function model = calibrate_simple(self,varargin)
            % Override this function to implement your calibration code
            % Model = calibrate_simple(Signal,Arguments...)
            %
            % In:
            %   Signal : a single continuous EEGLAB data set (annotated with target markers; see
            %            set_targetmarkers for more info)
            %
            %   Arguments : further optional user arguments
            %
            % Out:
            %   Model : a model struct with a mandatory field .filter_graph and arbitrary other content
            %           * The .filter_graph filed is a 1x1 cell array that contains the desciption of
            %             filter steps that is to be applied to the data, and is usually passed as either
            %             the .tracking.online_expression field of the processed Signal or the processed
            %             Signal itself. If no signal processing is performed by this paradigm, the raw
            %             signal may be passed.
            %
            %           * May have optional fields .prediction_function, .prediction_window and
            %             .prediction_channels - though these are generally auto-deduced
            %
            % Notes:

            args = arg_define(varargin, ...
                arg_norep('signal'), ...
                arg_sub({'flt','SignalProcessing'}, self.preprocessing_defaults(), @flt_pipeline, 'Signal processing stages. These parameters control filter stages that run on the signal level; they can be enabled, disabled and configured for the given paradigm. The prediction operates on the outputs of this stage.'), ...
                arg({'lambdas','Lambdas'},[2.^(8:-0.125:-5)],[0 Inf],'Regularization parameter. Larger means stronger regularization, 1.0 is the Bayesian ad hoc choice.'), ...
                arg({'duration','WaveDuration'},0.8,[0 Inf],'Duration of Waveform. This is the length of the waveform that will be searched in the epoch.'), ...
                arg({'stepping','Stepping'},3,uint32([1 100]),'Relative jitter stepping. This is the precision (in samples) at which relative jitters will be considered.'), ...
                arg({'admm_iters','ADMMIterations'},30,uint32([1 1000]),'Max Iterations. Number of iterations for the overall ADMM solver.'), ...
                arg({'inner_iters','InnerIterations'},10,uint32([1 1000]),'Max Inner Iterations. Number of iterations for the inner ADMM solver that solves the sparsity vs trace norm.'), ...
                arg({'cg_iters','CgIterations'},30,uint32([1 1000]),'Max CG Iterations. Number of iterations for the inner conjugate-gradient solver.'), ...
                arg({'lanczos_iters','LanczosIterations'},10,uint32([1 1000]),'Max Lanczos Iterations. Number of iterations for the inner Lanczos solver.'), ...
                arg({'abs_tol','AbsoluteTolerance'},1e-6,[],'Absolute tolerance.'), ...
                arg({'rel_tol','RelativeTolerance'},1e-3,[],'Relative tolerance.'), ...
                arg({'alpha','OverRelaxation'},1,[1 1.8],'Over-relaxation parameter. Some references suggest that values between 1.5 and 1.8 can improve convergence.'), ...
                arg({'rho','AugmentedLagrangian'},1.0,[0 Inf],'Augmented Lagrangian parameter.'), ...
                arg({'rho_update','RhoUpdate'},true,[],'Update Rho. Whether to update rho dynamically according to 3.4.1 in [1]'), ...
                arg({'rho_cutoff','RhoUpdateThreshold'},10.0,[0 Inf],'Rho update threshold.'), ...
                arg({'rho_incr','RhoUpdateIncr'},2.0,[1 Inf],'Rho update increment factor.'), ...
                arg({'rho_decr','RhoUpdateDecr'},2.0,[1 Inf],'Rho update decrement factor.'), ...
                arg({'kappa','SparsityTraceTradeoff'},0.75,[0 1],'Sparsity (=1) vs. Trace norm (=0) tradeoff. Between 0 and 1, similar to the elastic net criterion.'), ...
                arg({'patterns','NumPatterns'},20,uint32([1 1000]),'Number of weight patterns. This is the maximum number of (possibly distinct) weight patterns to recover.'), ...
                arg({'verbose','Verbose'},'really',[],'Verbose output.'), ...
                arg({'scaling','Scaling'}, 'std', {'none','center','std','minmax','whiten'}, 'Pre-scaling of the data. For the regulariation to work best, the features should either be naturally scaled well, or be artificially scaled.'), ...
                arg({'fast','Fast'},true,[],'Fast mode.'), ...
                arg({'arg_dialogsel','ConfigLayout'},self.dialog_layout_defaults(),[],'Parameters displayed in the config dialog. Cell array of parameter names to display (dot-notation allowed); blanks are translated into empty rows in the dialog. Referring to a structure argument lists all parameters of that struture, except if it is a switchable structure - in this case, a pulldown menu with switch options is displayed.','type','cellstr','shape','row'));

            % first pre-process the data (symbolically)
            signal = flt_pipeline('signal',args.signal, args.flt); %#ok<*NODEF>

            % evaluate this in an optimized fashion
            signal = exp_eval_optimized(signal);

            % get trial labels
            labels = set_gettarget(signal);

            % build meta-data
            model.args = rmfield(args,'signal');
            model.classes = unique(labels,'rows');
            model.tracking.filter_graph = signal;

            if length(model.classes) > 2
                error('Currently, only two classes are supported here.'); end

            % extract time-shifted windows from each trial...
            duration = round(args.duration * signal.srate);
            offsets = 1 : args.stepping : (signal.pnts - duration);
            trials = cell(1,length(offsets));

            % get the entire epoch and figure out an appropriate rescaling
            alldata = reshape(permute(signal.data,[2 1 3]),[],signal.trials);
            sc_info = hlp_findscaling(alldata,args.scaling);

            % for each time shift...
            for o = offsets
                % determine the window to cut out of the data
                cutwindow = o + (1:duration);
                % extract from all trials
                data = signal.data(:,cutwindow,:);
                % vectorize into #trials x #features, and rescale
                data = permute(data,[2 1 3]);
                trials{o} = hlp_applyscaling(reshape(data,[],signal.trials),sc_info)';
            end

            T = signal.trials;
            fprintf('#trials=%.0f; #shifts=%.0f; #features=%.0f; #channels=%.0f; #windows=%.0f\n',T,length(offsets),signal.nbchan*length(cutwindow),signal.nbchan,length(cutwindow));

            % vertically concatenate all shifts (into #shifts*#trials x #features)
            A = vertcat(trials{:});

            % remap the labels to -1 / +1
            labels(labels == model.classes(1)) = -1;
            labels(labels == model.classes(2)) = +1;

            % and generate shift-replicated target matrix b
            b = repmat(labels,1,length(offsets));
            b = b(:);

            C0 = -[b A];   % (bias is first elem, followed by rest...)
            ccp = C0';

            % weight vectors
            [m,n] = size(A);
            Wz = zeros(n+1,m);          % consensus variable
            Wx = zeros(n+1,m);          % data weights variable, FxN; n+1 accounts for the added bias (this is actually just the initial conditions vector...)
            Wu = zeros(n+1,m);          % scaled dual parameter u for Wx

            alpha = args.alpha;         % over-relaxation parameter (if 1, OR turned off)
            rho = args.rho;             % augmented Lagrangian parameter; probably determines sort of the tightness of the constraints...
            kappa = args.kappa;         % blend factor between trialwise sparsity and trialwise agreement
            K = args.patterns;          % the maximum number of SV's to recover

            rescale = 1;                  % scale of the left-hand side term (could be m/length(offsets)
            for mu = args.lambdas       % regularization parameter (should be decreasing)
                fprintf('Now using lambda = %.3f\n',mu);
                if args.verbose
                    fprintf('%3s\t%10s\t%10s\t%10s\t%10s\t%10s\n', 'iter', ...
                        'r norm', 'eps pri', 's norm', 'eps dual', 'objective');
                end

                for k = 1:args.admm_iters
                    Wzold = Wz;
                    fprintf('Outer step #%.0f\n',k);

                    %% x-update
                    Wx = ParadigmPAL.minimize_batch(Wx, @ParadigmPAL.x_objective_batch, args.cg_iters, Wz, Wu, ccp, rho);
                    Wx_hat = alpha*Wx + (1-alpha)*Wzold;  % (optional over-relaxation)

                    %% z-update
                    Wz = Wx_hat + Wu;
                    [Wz,fuuu,U,V] = ParadigmPAL.prox_sum(Wz,rescale,mu,kappa,rho,K,T,m,args);

                    %% dual parameter update
                    fprintf('  updating u''s\n');
                    Wu = Wu + (Wx_hat - Wz);

                    %% diagnostics, reporting, termination checks
                    fprintf('  diagnostics...\n');
                    history.objval(k)  = 0; %ParadigmPAL.full_objective(ccp, mu, Wx, Wz);
                    history.r_norm(k)  = norm(Wx(:) - Wz(:));
                    history.s_norm(k)  = norm(rho*(Wz(:) - Wzold(:)));
                    history.eps_pri(k) = args.abs_tol + args.rel_tol*max(norm(Wx(:)), norm(Wz(:)));
                    history.eps_dual(k)= args.abs_tol + args.rel_tol*norm(rho*Wu(:));

                    if args.verbose
                        fprintf('%3d\t%10.4f\t%10.4f\t%10.4f\t%10.4f\t%10.2f\n', k, ...
                            history.r_norm(k), history.eps_pri(k), ...
                            history.s_norm(k), history.eps_dual(k), history.objval(k));
                        if strcmp(args.verbose,'really') && nnz(fuuu)
                            f=figure; set(f,'Position',get(f,'Position')-[0 500 0 0]);
                            imagesc(Wz); ylabel('features'); xlabel('shifted trials'); title(sprintf('ADMM iter #%.0f, lambda=%.3f',k,mu));
                            figure;
                            % weight patterns
                            N = nnz(fuuu);
                            cols = ceil(sqrt(N));
                            rows = ceil(N/cols);
                            for p=1:N
                                subplot(cols,rows,p);
                                plot((0:round(args.duration * signal.srate)-1)/signal.srate,reshape(U(2:end,p),length(cutwindow),signal.nbchan)); title(sprintf('weight array #%.0f',p)); xlabel('time (s)'); ylabel('weight');
                            end
                            % topoplots...
                            f=figure; set(f,'Position',get(f,'Position')-[550 0 0 0]);
                            N = nnz(fuuu);
                            cols = ceil(sqrt(N));
                            rows = ceil(N/cols);
                            for p=1:N
                                subplot(cols,rows,p);
                                mat = reshape(U(2:end,p),length(cutwindow),signal.nbchan);
                                mat_inner = mat(round(end*1/4):round(end*3/4),:);
                                [dummy,peakidx] = max(mean(abs(mat_inner),2)); % #ok<NASGU>
                                topoplot(mat_inner(peakidx,:),signal.chanlocs);
                            end
                            drawnow;
                        end
                    end

                    if history.r_norm(k) < history.eps_pri(k) && history.s_norm(k) < history.eps_dual(k)
                        break; end

                    %% update of rho
                    if args.rho_update
                        if history.r_norm(k) > args.rho_cutoff * history.s_norm(k)
                            disp('  incrementing rho.');
                            rho = rho .* args.rho_incr;
                            Wu = Wu ./ args.rho_incr;
                        elseif history.s_norm(k) > args.rho_cutoff * history.r_norm(k)
                            disp('  decrementing rho.');
                            rho = rho ./ args.rho_incr;
                            Wu = Wu .* args.rho_incr;
                        end
                    end
                end
            end
            1
            %% plotting...

%             % full weight matrix
%             figure;imagesc(Wz); title('full weight matrix'); xlabel('features'); ylabel('shifted trials');
%
%             % shift matrix (?)
%             figure; imagesc(reshape(V(:,1),length(offsets),[])); title('shift matrix'); xlabel('trials'); ylabel('shifts');
        end

        function outputs = predict_simple(self,signal,model)
            % Override this function to implement your prediction code
            % Outputs = predict_simple(Sginal,Model)
            %
            % In:
            %   Signal : a signal pre-processed according to the model's filter graph
            %
            %   Model : a predictive model as created by your calibrate_simple() function
            %
            % Out:
            %   Outputs : a prediction/estimate for the most recent time point in the data (or one for
            %             every epoch if the signal is epoched); see ml_predict for the allowed formats

            error('This paradigm implements no predict() function.');
        end

        function visualize(self,model)
            % Optionally override this function to implement your visualization code
            % visualize(Model)
            %
            % In:
            %   Model: a model as created by your calibrate() function;
            %          a plot or GUI will be produced to inspect the model
            %

            error('This paradigm implements no visualize() function.');
        end

        % --- implementation ----

        function model = calibrate(self,varargin)
            % Implementation of the calibrate() function (see ParadigmBase)
            % Model = calibrate(Collection,GoalIdentifier,Arguments...)
            %
            % This is essentially a wrapper around calibrate_simple() which peels off any additional
            % data structure (for multiple collections and stream bundles) before passing it on.
            %
            % In:
            %   Collection : a collection (cell array) of stream bundles
            %
            %   GoalIdentifier: the goal-identifier
            %
            %   Arguments... : further optional user arguments
            %
            % Out:
            %   Model : The returned model struct

            % get the collection argument and extract the signal argument
            collection = arg_extract(varargin,{'collection','Collection'},[],{});
            if ~isempty(collection)
                if length(collection) > 1
                    error('This paradigm does not support collections of more than one recording.'); end
                if length(collection{1}.streams) > 1
                    error('This paradigm does not support more than one stream in parallel.'); end
                signal = collection{1}.streams{1};
            else
                signal = {};
            end

            % call calibrate_simple with the signal passed in as additional argument
            model = self.calibrate_simple('signal',signal,varargin{:});
        end

        function outputs = predict(self,bundle,model)
            % Override this function to implement your prediction code
            % Outputs = predict(Bundle,Model)
            %
            % This is a wrapper around the predict_simple() function which peels off the stream bundle
            % representation and just passes on the contained signal.
            %
            % In:
            %   Bundle : stream bundle preprocessed by the model's filter graph
            %
            %   Model : a predictive model
            %
            % Out:
            %   Outputs : the outputs of calibrate_simple()

            if length(bundle.streams) > 1
                error('This paradigm does not support more than one stream in parallel.'); end
            outputs = self.predict_simple(bundle.streams{1},model);
        end

    end

    methods(Static)
        function obj = full_objective(ccp, mu, Wx, Wz)
            % this is the overall objective function of alignment learning
            obj = sum(log(1 + exp(sum(ccp.*Wx)))) + mu*(norm_nuc(Wz) + sum(norms(Wz))) * size(ccp,2);
        end

        function [val,grad] = x_objective(x, C, z, u, rho)
            % this is the objective function for the decoupled x criterion (basically l2 logreg)
            ecx = exp(C*x);
            val = sum(log(1 + ecx)) + (rho/2)*norm(x - z + u).^2;
            grad = C'*(ecx./(1 + ecx)) + rho*(x - z + u);
        end

        function [vals,grads] = x_objective_batch(Wx,Wz,Wu,ccp,rho)
            % this is the batch version of the above (for all samples & variables in parallel)
            ecx = exp(sum(ccp.*Wx));
            d = Wx - Wz + Wu;
            vals = log(1 + ecx) + (rho/2)*sum(d.*d);
            grads = bsxfun(@times,ccp,(ecx./(1 + ecx))) + rho*d;
        end

        % l1/l2 shrinkage operator
        function Wz = shrinkage(Wa, kappa)
            Wz = pos(1 - kappa/norm(Wa))*Wa;
        end

        % l1 shrinkage operator
        function z = shrinkage_l1(a, kappa)
            z = max(0, a-kappa) - max(0, -a-kappa);
        end

        % trace norm proximal operator
        function [W,fuuu,U,V] = prox_tr(W,rescale,mu,kappa,rho,K,args)
            fprintf('  trace norming r\n');
            [U,S,V] = pca(W,K,args.lanczos_iters);
            fuuu = ParadigmPAL.shrinkage_l1(diag(S),(rescale*mu*(1-kappa))/rho); nn = length(fuuu); % we shrink the SV spectrum using prox operator
            Ws = sparse(1:nn,1:nn,fuuu,size(S,1),size(S,2));
            % clamp minority of V's members at 0
            smv = sign(median(V));
            V(:,smv==-1) = min(0,V(:,smv==-1));
            V(:,smv==1) = max(0,V(:,smv==1));
            W = U*(Ws*V');

            if strcmp(args.verbose,'ultra')
                f=figure; set(f,'Position',get(f,'Position')-[0 500 0 0]);
                imagesc(W); ylabel('features'); xlabel('shifted trials'); title(sprintf('lambda=%.3f',mu));
                figure;
                % weight patterns
                N = nnz(spec);
                cols = ceil(sqrt(N));
                rows = ceil(N/cols);
                signal=args.signal;
                for p=1:N
                    subplot(cols,rows,p);
                    plot((0:(numel(U(2:end,p))/signal.nbchan)-1)/signal.srate,reshape(U(2:end,p),[],signal.nbchan)); title(sprintf('weight array #%.0f',p)); xlabel('time (s)'); ylabel('weight');
                end
                drawnow;
            end

        end

        % group-sparse proximal operator
        function W = prox_gl(W,rescale,mu,kappa,rho,T,m)
            fprintf('  sparsifying z\n');
            for i = 1:m
                W(2:end,i) = ParadigmPAL.shrinkage(W(2:end,i), (rescale*mu*kappa/10)/rho); end
        end

        % summed proximal operator (using some splitting scheme, see Bauscke, 2008)
        function [x,fuuu,U,V] = prox_sum(r,rescale,mu,kappa,rho,K,T,m,args)
            y = r;
            p = zeros(size(y));
            q = zeros(size(y));
            for n=1:args.inner_iters
                [x,fuuu,U,V] = ParadigmPAL.prox_tr(y+q,rescale,mu,kappa,rho,K,args);
                q = y+q-x;
                y = ParadigmPAL.prox_gl(x+p,rescale,mu,kappa,rho,T,m);
                p = x+p-y;
                if norm(x(:) - y(:)) < args.rel_tol*max(norm(x(:)),norm(y(:)))
                    break; end
            end
        end

        function X = minimize_batch(X, f, len, Wz,Wu,ccp,rho)
            % Copyright (C) 2001 - 2010 by Carl Edward Rasmussen, 2010-01-03

            INT = 0.1;    % don't reevaluate within 0.1 of the limit of the current bracket         % const
            EXT = 3.0;                  % extrapolate maximum 3 times the current step-size         % const
            MAX = 20;                         % max 20 function evaluations per line search         % const
            RATIO = 10;                                       % maximum allowed slope ratio         % const
            SIG = 0.1; RHO = SIG/2; % SIG and RHO are the constants controlling the Wolfe-          % const
            if max(size(len)) == 2, red=len(2); len=len(1); else red=1; end                         % scalar

            [f0 df0] = feval(f,X,Wz,Wu,ccp,rho);          % get function value and gradient
            N = length(f0);

            [A,B,d1,d2,d3,d4,f1,f2,f3,f4,x1,x2,x4] = deal(zeros(1,N)); % initialize a few variables...
            df3 = zeros(size(X));
            i = 0;                                            % zero the run length counter         % scalar
            ls_failed = false(1,N);                             % no previous line search has failed% boolean vector
            i = i + (len<0);                                            % count epochs?!            % scalar
            s = -df0;                                 % initial search direction (steepest)         % column MATRIX
            d0 = -sum(s.*s);                                                    % and slope         % row vector
            x3 = red./(1-d0);                                  % initial step is red/(|s|+1)        % row vector

            keep_going = true(1,N);                                                                 % mask for the outermost loop updates...
            while any(keep_going) && i < abs(len)                                      % while not finished            % scalar
                i = i + (len>0);                                      % count iterations?!          % scalar
                fprintf('  Cg step #%.0f\n',i);

                X0 = X;                                                                             % column MATRIX
                F0 = f0;                                                                            % row vector
                dF0 = df0;                   % make a copy of current values                        % column MATRIX
                if len>0, M = MAX; else M = min(MAX, -len-i); end                                   % scalar

                om = keep_going;                                                                    % mask for outer-loop updates
                while 1                             % keep extrapolating as long as necessary
                    x2(om) = 0;                                                                     % row vector
                    f2(om) = f0(om);                                                                % row vector
                    d2(om) = d0(om);                                                                % row vector
                    f3(om) = f0(om);                                                                % row vector
                    df3(:,om) = df0(:,om);                                                          % column matrix
                    ok(om) = false;                                                                 % boolean vector
                    while ~all(ok(om)) && M > 0                                                     % mask check
                        M = M - 1; i = i + (len<0);                         % count epochs?!        % scalar
                        nokom = ~ok&om;
                        [f3(nokom) df3(:,nokom)] = feval(f, X(:,nokom) + bsxfun(@times,x3(nokom),s(:,nokom)), Wz(:,nokom), Wu(:,nokom), ccp(:,nokom), rho); % masked array op
                        ok(om) = ok(om) | ~(isnan(f3(om)) | isinf(f3(om)) | any(isnan(df3(om))+isinf(df3(om))));            % mask update
                        x3(~ok&om) = (x2(~ok&om)+x3(~ok&om))/2;         % bisect and try again      % maked update
                    end
                    mask = f3<F0;                                       % keep best values          % mask generation
                    X0(:,mask&om) = X(:,mask&om) + bsxfun(@times,x3(mask&om),s(:,mask&om));         % masked updates
                    F0(mask&om) = f3(mask&om);
                    dF0(:,mask&om) = df3(:,mask&om);

                    d3(om) = sum(df3(:,om).*s(:,om));                                    % new slope% row vector

                    om(d3 > SIG*d0 | f3 > f0 + x3.*d0*RHO) = false;             % update outer mask
                    if ~any(om) || M == 0  % are we done extrapolating?
                        break; end

                    x1(om) = x2(om); f1(om) = f2(om); d1(om) = d2(om); % move point 2 to point 1    % row vectors
                    x2(om) = x3(om); f2(om) = f3(om); d2(om) = d3(om); % move point 3 to point 2    % row vectors
                    A(om) = 6*(f1(om)-f2(om))+3*(d2(om)+d1(om)).*(x2(om)-x1(om));% make cubic extrap.% row vectors
                    B(om) = 3*(f2(om)-f1(om))-(2*d1(om)+d2(om)).*(x2(om)-x1(om));
                    x3(om) = x1(om)-d1(om).*(x2(om)-x1(om)).^2./(B(om)+sqrt(B(om).*B(om)-A(om).*d1(om).*(x2(om)-x1(om)))); % num. error possible, ok!

                    % implement fixups: num prob | wrong sign | new point beyond extrapolation limit?
                    issue_mask = ~isreal(x3) | isnan(x3) | isinf(x3) | x3 < 0 | x3 > x2*EXT;
                    x3(om & issue_mask) = x2(om & issue_mask)*EXT;
                    % new point too close to previous point?
                    issue_mask = ~issue_mask & (x3 < x2+INT*(x2-x1));
                    x3(om & issue_mask) = x2(om & issue_mask) + INT*(x2(om & issue_mask) - x1(om & issue_mask));
                end                                                       % end extrapolation

                while 1
                    om = keep_going & (abs(d3) > -SIG*d0 | f3 > f0+x3.*d0*RHO);    % update outer mask
                    if ~(any(om) && M > 0)
                        break; end
                    tog = d3 > 0 | f3 > f0+x3.*d0*RHO;           % subinterval toggle
                    x4(om&tog) = x3(om&tog);
                    f4(om&tog) = f3(om&tog);
                    d4(om&tog) = d3(om&tog);
                    x2(om&~tog) = x3(om&~tog);
                    f2(om&~tog) = f3(om&~tog);
                    d2(om&~tog) = d3(om&~tog);
                    tog=f4>f0;                                   % interpolation toggle
                    x3(om&tog) = x2(om&tog) - (0.5*d2(om&tog).*(x4(om&tog)-x2(om&tog)).^2) ./ (f4(om&tog)-f2(om&tog)-d2(om&tog).*(x4(om&tog)-x2(om&tog)));  % quadratic interpolation
                    A(om&~tog) = 6*(f2(om&~tog)-f4(om&~tog))./(x4(om&~tog)-x2(om&~tog)) + 3*(d4(om&~tog)+d2(om&~tog));                    % cubic interpolation
                    B(om&~tog) = 3*(f4(om&~tog)-f2(om&~tog)) - (2*d2(om&~tog)+d4(om&~tog)).*(x4(om&~tog)-x2(om&~tog));
                    x3(om&~tog) = x2(om&~tog) + (sqrt(B(om&~tog).*B(om&~tog) - A(om&~tog).*d2(om&~tog).*(x4(om&~tog)-x2(om&~tog)).^2)-B(om&~tog))./A(om&~tog);  % num. error possible, ok!
                    numprob = isnan(x3) | isinf(x3);             % if we had a numerical problem then bisect
                    x3(om&numprob) = (x2(om&numprob)+x4(om&numprob))/2;

                    x3(om) = max(min(x3(om), x4(om)-INT*(x4(om)-x2(om))),x2(om)+INT*(x4(om)-x2(om)));  % don't accept too close
                    [f3(om) df3(:,om)] = feval(f, X(:,om) + bsxfun(@times,x3(om),s(:,om)), Wz(:,om), Wu(:,om), ccp(:,om), rho); % masked array op
                    mask = om & f3<F0;                                      % keep best values
                    X0(:,mask) = X(:,mask) + bsxfun(@times,x3(mask),s(:,mask));
                    F0(mask) = f3(mask);
                    dF0(:,mask) = df3(:,mask);
                    M = M - 1; i = i + (len<0);                             % count epochs?!
                    d3(om) = sum(df3(:,om).*s(:,om));                                    % new slope% row vector
                end

                % --- we are here ---

                okay = abs(d3) < -SIG*d0 & f3 < f0+x3.*d0*RHO;

                upd = okay & keep_going;
                X(:,upd) = X(:,upd) + bsxfun(@times,x3(upd),s(:,upd));
                f0(upd) = f3(upd);
                s(:,upd) = bsxfun(@times,sum(df3(:,upd).*df3(:,upd)) - sum(df0(:,upd).*df3(:,upd)) ./ sum(df0(:,upd).*df0(:,upd)),s(:,upd)) - df3(:,upd);
                df0(:,upd) = df3(:,upd);                                % swap derivatives
                d3(upd) = d0(upd);
                d0(upd) = sum(df0(:,upd).*s(:,upd));
                mask = upd & d0 > 0;                         % otherwise use steepest direction
                s(:,mask) = -df0(:,mask);
                d0(mask) = -sum(s(:,mask).*s(:,mask));
                x3(upd) = x3(upd) .* min(RATIO, d3(upd)./(d0(upd)-realmin));  % slope ratio but max RATIO
                ls_failed(upd) = false;                              % this line search did not fail

                upd = ~okay & keep_going;
                X(:,upd) = X0(:,upd);
                f0(upd) = F0(upd);
                df0(:,upd) = dF0(:,upd);

                keep_going(upd) = keep_going(upd) & ~ls_failed(upd); % line search failed twice in a row
                                                                     % so we give up on these guys
                upd = upd & keep_going;
                if i <= abs(len)
                    s(:,upd) = -df0(:,upd);
                    d0(:,upd) = -sum(s(:,upd).*s(:,upd));                             % try steepest
                    x3(upd) = 1./(1-d0(upd));
                    ls_failed(upd) = true;
                end
            end
        end

        function layout = dialog_layout_defaults(self)
            layout = {'SignalProcessing.Resampling.SamplingRate','SignalProcessing.SpectralSelection.FrequencySpecification', ...
                'SignalProcessing.EpochExtraction','','Lambdas','WaveDuration','Stepping','ADMMIterations','CgIterations',...
                'LanczosIterations','NumPatterns','SparsityTraceTradeoff'};
        end

    end
end

% (turn off a few editor warnings because some actual implementations are missing in this file)
%#ok<*INUSD,*STOUT,*MANU>


function ap = context_rescore(train_set, train_year)
% Train context rescoring SVMs and rescore the test predictions.
%   ap = context_rescore(train_set, train_year)
%
% Return value
%   ap            AP scores for all classes after context rescoring
%
% Arguments
%   train_set     Training dataset
%   train_year    Training dataset year

% AUTORIGHTS
% -------------------------------------------------------
% Copyright (C) 2011-2012 Ross Girshick
% Copyright (C) 2008, 2009, 2010 Pedro Felzenszwalb, Ross Girshick
%
% This file is part of the voc-releaseX code
% (http://people.cs.uchicago.edu/~rbg/latent/)
% and is available under the terms of an MIT-like license
% provided in COPYING. Please retain this notice and
% COPYING if you use this file (or a portion of it) in
% your project.
% -------------------------------------------------------

if nargin < 2
  conf = voc_config();
  train_year = conf.pascal.year;
  if nargin < 1
    train_set = conf.training.train_set_fg;
  end
end

context_train(train_set, train_year);
ap = context_test();


function [newdata] = mne_compensate_to(data,to)
%
% [newdata] = mne_compensate_to(data,to)
%
% Apply compensation to the data as desired
%

%
%   Author : Matti Hamalainen, MGH Martinos Center
%   License : BSD 3-clause
%
%
%   Revision 1.4  2006/04/23 15:29:40  msh
%   Added MGH to the copyright
%
%   Revision 1.3  2006/04/18 20:44:46  msh
%   Added reading of forward solution.
%   Use length instead of size when appropriate
%
%   Revision 1.2  2006/04/14 15:49:49  msh
%   Improved the channel selection code and added ch_names to measurement info.
%
%   Revision 1.1  2006/04/12 10:51:19  msh
%   Added projection writing and compensation routines
%
%

me='MNE:mne_compensate_to';

newdata = data;
now     = mne_get_current_comp(newdata.info);
%
%   Are we there already?
%
if now == to
    fprintf(1,'Data are already compensated as desired\n');
end
%
%   Make the compensator and apply it to all data sets
%
comp = mne_make_compensator(newdata.info,now,to);
for k = 1:length(newdata.evoked)
   newdata.evoked(k).epochs = comp*newdata.evoked(k).epochs;
end
%
%  Update the compensation info in the channel descriptors
%
newdata.info.chs = mne_set_current_comp(newdata.info.chs,to);
return;


function [dat, label, time, cfg] = preproc(dat, label, time, cfg, begpadding, endpadding)

% PREPROC applies various preprocessing steps on a single piece of EEG/MEG data
% that has been read from a data file.
%
% This low-level function serves as a subfunction for all FieldTrip modules that want
% to preprocess the data, such as FT_PREPROCESSING, FT_ARTIFACT_XXX,
% FT_TIMELOCKANALYSIS, etc. It ensures consistent handling of both MEG and EEG data
% and consistency in the use of all preprocessing configuration options.
%
% Use as
%   [dat, label, time, cfg] = preproc(dat, label, time, cfg, begpadding, endpadding)
%
% The required input arguments are
%   dat         Nchan x Ntime data matrix
%   label       Nchan x 1 cell-array with channel labels
%   time        Ntime x 1 vector with the latency in seconds
%   cfg         configuration structure, see below
% and the optional input arguments are
%   begpadding  number of samples that was used for padding (see below)
%   endpadding  number of samples that was used for padding (see below)
%
% The output is
%   dat         Nchan x Ntime data matrix
%   label       Nchan x 1 cell-array with channel labels
%   time        Ntime x 1 vector with the latency in seconds
%   cfg         configuration structure, optionally with extra defaults set
%
% Note that the number of input channels and the number of output channels can be
% different, for example when the user specifies that he/she wants to add the
% implicit EEG reference channel to the data matrix.
%
% The filtering of the data can introduce artifacts at the edges, hence it is better
% to pad the data with some extra signal at the begin and end. After filtering, this
% padding is removed and the other preprocessing steps are applied to the remainder
% of the data. The input fields begpadding and endpadding should be specified in
% samples. You can also leave them empty, which implies that the data is not padded.
%
% The configuration can contain
%   cfg.lpfilter      = 'no' or 'yes'  lowpass filter
%   cfg.hpfilter      = 'no' or 'yes'  highpass filter
%   cfg.bpfilter      = 'no' or 'yes'  bandpass filter
%   cfg.bsfilter      = 'no' or 'yes'  bandstop filter
%   cfg.dftfilter     = 'no' or 'yes'  line noise removal using discrete fourier transform
%   cfg.medianfilter  = 'no' or 'yes'  jump preserving median filter
%   cfg.lpfreq        = lowpass  frequency in Hz
%   cfg.hpfreq        = highpass frequency in Hz
%   cfg.bpfreq        = bandpass frequency range, specified as [low high] in Hz
%   cfg.bsfreq        = bandstop frequency range, specified as [low high] in Hz
%   cfg.dftfreq       = line noise frequencies for DFT filter, default [50 100 150] Hz
%   cfg.lpfiltord     = lowpass  filter order (default set in low-level function)
%   cfg.hpfiltord     = highpass filter order (default set in low-level function)
%   cfg.bpfiltord     = bandpass filter order (default set in low-level function)
%   cfg.bsfiltord     = bandstop filter order (default set in low-level function)
%   cfg.medianfiltord = length of median filter
%   cfg.lpfilttype    = digital filter type, 'but' (default) or 'firws' or 'fir' or 'firls'
%   cfg.hpfilttype    = digital filter type, 'but' (default) or 'firws' or 'fir' or 'firls'
%   cfg.bpfilttype    = digital filter type, 'but' (default) or 'firws' or 'fir' or 'firls'
%   cfg.bsfilttype    = digital filter type, 'but' (default) or 'firws' or 'fir' or 'firls'
%   cfg.lpfiltdir     = filter direction, 'twopass' (default), 'onepass' or 'onepass-reverse' or 'onepass-zerophase' (default for firws) or 'onepass-minphase' (firws, non-linear!)
%   cfg.hpfiltdir     = filter direction, 'twopass' (default), 'onepass' or 'onepass-reverse' or 'onepass-zerophase' (default for firws) or 'onepass-minphase' (firws, non-linear!)
%   cfg.bpfiltdir     = filter direction, 'twopass' (default), 'onepass' or 'onepass-reverse' or 'onepass-zerophase' (default for firws) or 'onepass-minphase' (firws, non-linear!)
%   cfg.bsfiltdir     = filter direction, 'twopass' (default), 'onepass' or 'onepass-reverse' or 'onepass-zerophase' (default for firws) or 'onepass-minphase' (firws, non-linear!)
%   cfg.lpinstabilityfix = deal with filter instability, 'no', 'reduce', 'split' (default  = 'no')
%   cfg.hpinstabilityfix = deal with filter instability, 'no', 'reduce', 'split' (default  = 'no')
%   cfg.bpinstabilityfix = deal with filter instability, 'no', 'reduce', 'split' (default  = 'no')
%   cfg.bsinstabilityfix = deal with filter instability, 'no', 'reduce', 'split' (default  = 'no')
%   cfg.lpfiltdf      = lowpass transition width (firws, overrides order, default set in low-level function)
%   cfg.hpfiltdf      = highpass transition width (firws, overrides order, default set in low-level function)
%   cfg.bpfiltdf      = bandpass transition width (firws, overrides order, default set in low-level function)
%   cfg.bsfiltdf      = bandstop transition width (firws, overrides order, default set in low-level function)
%   cfg.lpfiltwintype = lowpass window type, 'hann' or 'hamming' (default) or 'blackman' or 'kaiser' (firws)
%   cfg.hpfiltwintype = highpass window type, 'hann' or 'hamming' (default) or 'blackman' or 'kaiser' (firws)
%   cfg.bpfiltwintype = bandpass window type, 'hann' or 'hamming' (default) or 'blackman' or 'kaiser' (firws)
%   cfg.bsfiltwintype = bandstop window type, 'hann' or 'hamming' (default) or 'blackman' or 'kaiser' (firws)
%   cfg.lpfiltdev     = lowpass max passband deviation (firws with 'kaiser' window, default 0.001 set in low-level function)
%   cfg.hpfiltdev     = highpass max passband deviation (firws with 'kaiser' window, default 0.001 set in low-level function)
%   cfg.bpfiltdev     = bandpass max passband deviation (firws with 'kaiser' window, default 0.001 set in low-level function)
%   cfg.bsfiltdev     = bandstop max passband deviation (firws with 'kaiser' window, default 0.001 set in low-level function)
%   cfg.dftreplace    = 'zero' or 'neighbour', method used to reduce line noise, 'zero' implies DFT filter, 'neighbour' implies spectrum interpolation (default = 'zero')
%   cfg.dftbandwidth  = bandwidth of line noise frequencies, applies to spectrum interpolation, in Hz (default = [1 2 3])
%   cfg.dftneighbourwidth = bandwidth of frequencies neighbouring line noise frequencies, applies to spectrum interpolation, in Hz (default = [2 2 2])
%   cfg.plotfiltresp  = 'no' or 'yes', plot filter responses (firws, default = 'no')
%   cfg.usefftfilt    = 'no' or 'yes', use fftfilt instead of filter (firws, default = 'no')
%   cfg.demean        = 'no' or 'yes'
%   cfg.baselinewindow = [begin end] in seconds, the default is the complete trial
%   cfg.detrend       = 'no' or 'yes', this is done on the complete trial
%   cfg.polyremoval   = 'no' or 'yes', this is done on the complete trial
%   cfg.polyorder     = polynome order (default = 2)
%   cfg.derivative    = 'no' (default) or 'yes', computes the first order derivative of the data
%   cfg.hilbert       = 'no', 'abs', 'complex', 'real', 'imag', 'absreal', 'absimag' or 'angle' (default = 'no')
%   cfg.rectify       = 'no' or 'yes'
%   cfg.precision     = 'single' or 'double' (default = 'double')
%   cfg.absdiff       = 'no' or 'yes', computes absolute derivative (i.e.first derivative then rectify)
%
% Preprocessing options that you should only use for EEG data are
%   cfg.reref         = 'no' or 'yes' (default = 'no')
%   cfg.refchannel    = cell-array with new EEG reference channel(s)
%   cfg.refmethod     = 'avg', 'median', 'rest', 'bipolar' or 'laplace' (default = 'avg')
%   cfg.groupchans    = 'yes' or 'no', should channels be rereferenced in separate groups
%                       for bipolar and laplace methods, this requires channnels to be
%                       named using an alphanumeric code, where letters represent the
%                       group and numbers represent the order of the channel whithin
%                       its group (default = 'no')
%   cfg.leadfield     = matrix or cell-array, this is required when refmethod is 'rest'
%                       The leadfield can be a single matrix (channels X sources) which
%                       is calculated by using the forward theory, based on the
%                       electrode montage, head model and equivalent source model.
%                       It can also be the output of FT_PREPARE_LEADFIELD based on a
%                       realistic head model.
%   cfg.implicitref   = 'label' or empty, add the implicit EEG reference as zeros (default = [])
%   cfg.montage       = 'no' or a montage structure (default = 'no')
%
% See also FT_READ_DATA, FT_READ_HEADER

% Copyright (C) 2004-2012, Robert Oostenveld
%
% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org
% for the documentation and details.
%
%    FieldTrip 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.
%
%    FieldTrip 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 FieldTrip. If not, see <http://www.gnu.org/licenses/>.
%
% $Id$

% compute fsample
fsample = 1./nanmean(diff(time));

if nargin<5 || isempty(begpadding)
  begpadding = 0;
end
if nargin<6 || isempty(endpadding)
  endpadding = 0;
end

if iscell(cfg)
  % recurse over the subsequent preprocessing stages
  if begpadding>0 || endpadding>0
    ft_error('multiple preprocessing stages are not supported in combination with filter padding');
  end
  for i=1:length(cfg)
    tmpcfg = cfg{i};
    if nargout==1
      [dat                     ] = preproc(dat, label, time, tmpcfg, begpadding, endpadding);
    elseif nargout==2
      [dat, label              ] = preproc(dat, label, time, tmpcfg, begpadding, endpadding);
    elseif nargout==3
      [dat, label, time        ] = preproc(dat, label, time, tmpcfg, begpadding, endpadding);
    elseif nargout==4
      [dat, label, time, tmpcfg] = preproc(dat, label, time, tmpcfg, begpadding, endpadding);
      cfg{i} = tmpcfg;
    end
  end
  % ready with recursing over the subsequent preprocessing stages
  return
end

% set the defaults for the rereferencing options
cfg.reref                = ft_getopt(cfg, 'reref',       'no');
cfg.refchannel           = ft_getopt(cfg, 'refchannel',  {});
cfg.refmethod            = ft_getopt(cfg, 'refmethod',   'avg');
cfg.implicitref          = ft_getopt(cfg, 'implicitref', []);
cfg.leadfield            = ft_getopt(cfg, 'leadfield',   []);
cfg.groupchans           = ft_getopt(cfg, 'groupchans', 'no');
% set the defaults for the signal processing options
cfg.polyremoval          = ft_getopt(cfg, 'polyremoval', 'no');
cfg.polyorder            = ft_getopt(cfg, 'polyorder',   2);
cfg.detrend              = ft_getopt(cfg, 'detrend',     'no');
cfg.demean               = ft_getopt(cfg, 'demean',      'no');
cfg.baselinewindow       = ft_getopt(cfg, 'baselinewindow', 'all');
cfg.dftfilter            = ft_getopt(cfg, 'dftfilter', 'no');
cfg.dftfreq              = ft_getopt(cfg, 'dftfreq', [50 100 150]);
cfg.lpfilter             = ft_getopt(cfg, 'lpfilter',  'no');
cfg.hpfilter             = ft_getopt(cfg, 'hpfilter',  'no');
cfg.bpfilter             = ft_getopt(cfg, 'bpfilter',  'no');
cfg.bsfilter             = ft_getopt(cfg, 'bsfilter',  'no');
cfg.lpfiltord            = ft_getopt(cfg, 'lpfiltord', []);
cfg.hpfiltord            = ft_getopt(cfg, 'hpfiltord', []);
cfg.bpfiltord            = ft_getopt(cfg, 'bpfiltord', []);
cfg.bsfiltord            = ft_getopt(cfg, 'bsfiltord', []);
cfg.lpfilttype           = ft_getopt(cfg, 'lpfilttype', 'but');
cfg.hpfilttype           = ft_getopt(cfg, 'hpfilttype', 'but');
cfg.bpfilttype           = ft_getopt(cfg, 'bpfilttype', 'but');
cfg.bsfilttype           = ft_getopt(cfg, 'bsfilttype', 'but');
if strcmp(cfg.lpfilttype, 'firws'), cfg.lpfiltdir = ft_getopt(cfg, 'lpfiltdir', 'onepass-zerophase'); else, cfg.lpfiltdir = ft_getopt(cfg, 'lpfiltdir', 'twopass'); end
if strcmp(cfg.hpfilttype, 'firws'), cfg.hpfiltdir = ft_getopt(cfg, 'hpfiltdir', 'onepass-zerophase'); else, cfg.hpfiltdir = ft_getopt(cfg, 'hpfiltdir', 'twopass'); end
if strcmp(cfg.bpfilttype, 'firws'), cfg.bpfiltdir = ft_getopt(cfg, 'bpfiltdir', 'onepass-zerophase'); else, cfg.bpfiltdir = ft_getopt(cfg, 'bpfiltdir', 'twopass'); end
if strcmp(cfg.bsfilttype, 'firws'), cfg.bsfiltdir = ft_getopt(cfg, 'bsfiltdir', 'onepass-zerophase'); else, cfg.bsfiltdir = ft_getopt(cfg, 'bsfiltdir', 'twopass'); end
cfg.lpinstabilityfix     = ft_getopt(cfg, 'lpinstabilityfix', 'no');
cfg.hpinstabilityfix     = ft_getopt(cfg, 'hpinstabilityfix', 'no');
cfg.bpinstabilityfix     = ft_getopt(cfg, 'bpinstabilityfix', 'no');
cfg.bsinstabilityfix     = ft_getopt(cfg, 'bsinstabilityfix', 'no');
cfg.lpfiltdf             = ft_getopt(cfg, 'lpfiltdf',  []);
cfg.hpfiltdf             = ft_getopt(cfg, 'hpfiltdf', []);
cfg.bpfiltdf             = ft_getopt(cfg, 'bpfiltdf', []);
cfg.bsfiltdf             = ft_getopt(cfg, 'bsfiltdf', []);
cfg.lpfiltwintype        = ft_getopt(cfg, 'lpfiltwintype', 'hamming');
cfg.hpfiltwintype        = ft_getopt(cfg, 'hpfiltwintype', 'hamming');
cfg.bpfiltwintype        = ft_getopt(cfg, 'bpfiltwintype', 'hamming');
cfg.bsfiltwintype        = ft_getopt(cfg, 'bsfiltwintype', 'hamming');
cfg.lpfiltdev            = ft_getopt(cfg, 'lpfiltdev', []);
cfg.hpfiltdev            = ft_getopt(cfg, 'hpfiltdev', []);
cfg.bpfiltdev            = ft_getopt(cfg, 'bpfiltdev', []);
cfg.bsfiltdev            = ft_getopt(cfg, 'bsfiltdev', []);
cfg.plotfiltresp         = ft_getopt(cfg, 'plotfiltresp', 'no');
cfg.usefftfilt           = ft_getopt(cfg, 'usefftfilt', 'no');
cfg.medianfilter         = ft_getopt(cfg, 'medianfilter', 'no');
cfg.medianfiltord        = ft_getopt(cfg, 'medianfiltord', 9);
cfg.hilbert              = ft_getopt(cfg, 'hilbert', 'no');
cfg.derivative           = ft_getopt(cfg, 'derivative', 'no');
cfg.rectify              = ft_getopt(cfg, 'rectify', 'no');
cfg.boxcar               = ft_getopt(cfg, 'boxcar', 'no');
cfg.absdiff              = ft_getopt(cfg, 'absdiff', 'no');
cfg.precision            = ft_getopt(cfg, 'precision', []);
cfg.conv                 = ft_getopt(cfg, 'conv', 'no');
cfg.montage              = ft_getopt(cfg, 'montage', 'no');
cfg.dftinvert            = ft_getopt(cfg, 'dftinvert', 'no');
cfg.standardize          = ft_getopt(cfg, 'standardize', 'no');
cfg.denoise              = ft_getopt(cfg, 'denoise', '');
cfg.subspace             = ft_getopt(cfg, 'subspace', []);
cfg.custom               = ft_getopt(cfg, 'custom', '');
cfg.resample             = ft_getopt(cfg, 'resample', '');

% test whether the MATLAB signal processing toolbox is available
if strcmp(cfg.medianfilter, 'yes') && ~ft_hastoolbox('signal')
  ft_error('median filtering requires the MATLAB signal processing toolbox');
end

% do a sanity check on the filter configuration
if strcmp(cfg.bpfilter, 'yes') && ...
    (strcmp(cfg.hpfilter, 'yes') || strcmp(cfg.lpfilter,'yes'))
  ft_error('you should not apply both a bandpass AND a lowpass/highpass filter');
end

% do a sanity check on the hilbert transform configuration
if strcmp(cfg.hilbert, 'yes') && ~strcmp(cfg.bpfilter, 'yes')
  ft_warning('Hilbert transform should be applied in conjunction with bandpass filter')
end

% do a sanity check on hilbert and rectification
if strcmp(cfg.hilbert, 'yes') && strcmp(cfg.rectify, 'yes')
  ft_error('Hilbert transform and rectification should not be applied both')
end

% do a sanity check on the rereferencing/montage
if ~strcmp(cfg.reref, 'no') && ~strcmp(cfg.montage, 'no')
  ft_error('cfg.reref and cfg.montage are mutually exclusive')
end

% lnfilter is no longer used
if isfield(cfg, 'lnfilter') && strcmp(cfg.lnfilter, 'yes')
  ft_error('line noise filtering using the option cfg.lnfilter is not supported any more, use cfg.bsfilter instead')
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% do the rereferencing in case of EEG
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
if ~isempty(cfg.implicitref) && ~any(match_str(cfg.implicitref,label))
  label = {label{:} cfg.implicitref}';
  dat(end+1,:) = 0;
end

if strcmp(cfg.reref, 'yes')
  switch cfg.refmethod
    case {'avg', 'median'}
      % mean or median based derivation of specified or all channels
      cfg.refchannel = ft_channelselection(cfg.refchannel, label);
      refindx = match_str(label, cfg.refchannel);
      if isempty(refindx),ft_error('reference channel was not found');end
      dat = ft_preproc_rereference(dat, refindx, cfg.refmethod);

    case {'rest'}
      cfg.refchannel = ft_channelselection(cfg.refchannel, label);
      refindx = match_str(label, cfg.refchannel);
      if isempty(refindx)
        ft_error('reference channel was not found')
      end

      if isempty(cfg.leadfield)
        ft_error('A leadfield is required to re-refer to REST');
      end
      % check the leadfield, apparently the code contributor here wants to
      % support 3 case, either a matrix, a struct, or a cell-array. Yet the
      % original code is almost impossible to parse, with a lot of try and
      % catch statements, so the current version below is an attempt to clean
      % this up
      if isnumeric(cfg.leadfield)
        Nchann_lf = size(cfg.leadfield,1); % No. of channels in leadfield
        lf_label  = {};
        G         = cfg.leadfield;
      elseif isstruct(cfg.leadfield)
        Nchann_lf = numel(cfg.leadfield.label);
        lf_label  = cfg.leadfield.label;
        G         = cat(2, cfg.leadfield.leadfield{:});
      elseif iscell(cfg.leadfield)
        Nchann_lf = size(cfg.leadfield{1},1); % No. of channels in leadfield
        lf_label  = {};
        G         = cat(2, cfg.leadfield{:});
      end

      if Nchann_lf ~= length(label)
        ft_error('channels in the leadfield are not equal to those in the data');
      end

      if ~isempty(lf_label)
        [indx1, indx2] = match_str(lf_label(refindx), label(refindx));
        if ~isequal(indx1, indx2)
          ft_error('The order in leadfield may be NOT the same as in the data, please check the leadfield!');
        end
      else
        ft_warning('There is no label info in the leadfield, there is no guarantee that the order of the channels in leadfield is the same as in the data');
      end

      dat   = ft_preproc_rereference(dat, refindx, cfg.refmethod, [], G); % re-referencing
      label = label(refindx); % re-referenced channel labels

    case {'bipolar', 'laplace', 'doublebanana', 'longitudinal', 'circumferential', 'transverse'}
      % this is implemented as a montage that the user does not get to see
      tmpcfg = keepfields(cfg, {'refmethod', 'implicitref', 'refchannel', 'channel', 'groupchans'});
      tmpcfg.showcallinfo = 'no';
      montage = ft_prepare_montage(tmpcfg);

      % convert the data temporarily to a raw structure
      tmpdata.trial = {dat};
      tmpdata.time  = {time};
      tmpdata.label = label;

      % apply the montage to the data
      tmpdata = ft_apply_montage(tmpdata, montage, 'feedback', 'none');
      dat     = tmpdata.trial{1}; % the number of channels can have changed
      label   = tmpdata.label;    % the output channels can be different than the input channels
      clear tmpdata

    otherwise
      ft_error('unsupported value for cfg.refmethod');
  end % switch refmethod
end % if reref

if ~strcmp(cfg.montage, 'no') && ~isempty(cfg.montage)
  % convert the data temporarily to a raw structure
  tmpdata.trial = {dat};
  tmpdata.time  = {time};
  tmpdata.label = label;
  % apply the montage to the data
  tmpdata = ft_apply_montage(tmpdata, cfg.montage, 'feedback', 'none');
  dat   = tmpdata.trial{1}; % the number of channels can have changed
  label = tmpdata.label;    % the output channels can be different than the input channels
  clear tmpdata
end

if any(isnan(dat(:)))
  % filtering is not possible for at least a selection of the data
  ft_warning('FieldTrip:dataContainsNaN', 'data contains NaNs, not all processing methods are robust to NaNs, so the NaNs might spread');
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% do the filtering on the padded data
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
if ~isempty(cfg.denoise)
  hflag    = isfield(cfg.denoise, 'hilbert') && strcmp(cfg.denoise.hilbert, 'yes');
  datlabel = match_str(label, cfg.denoise.channel);
  reflabel = match_str(label, cfg.denoise.refchannel);
  tmpdat   = ft_preproc_denoise(dat(datlabel,:), dat(reflabel,:), hflag);
  dat(datlabel,:) = tmpdat;
end

% The filtering should in principle be done prior to the demeaning to
% ensure that the resulting mean over the baseline window will be
% guaranteed to be zero (even if there are filter artifacts).
% However, the filtering benefits from the data being pulled towards zero,
% causing fewer edge artifacts. That is why we start by removing the slow
% drift, then filter, and then repeat the demean/detrend/polyremove. Note
% that this might interfere with a situation where the requested baseline
% window is not present in the local time axis of the trial, e.g. when the
% trials are of different length
if any(strcmp({cfg.polyremoval cfg.detrend cfg.demean}, 'yes'))
  if strcmp(cfg.polyremoval, 'yes')
    polyorder = cfg.polyorder;
  elseif strcmp(cfg.detrend, 'yes')
    polyorder = 1;
  elseif strcmp(cfg.demean, 'yes')
    polyorder = 0;
  end
  datorig   = dat;
  nsamples  = size(dat,2);
  begsample = 1        + begpadding;
  endsample = nsamples - endpadding;
  dat       = ft_preproc_polyremoval(dat, polyorder, begsample, endsample); % this will also demean and detrend
  datdiff   = datorig - dat;
end

if strcmp(cfg.medianfilter, 'yes'), dat = ft_preproc_medianfilter(dat, cfg.medianfiltord); end
if strcmp(cfg.lpfilter, 'yes'),     dat = ft_preproc_lowpassfilter(dat, fsample, cfg.lpfreq, cfg.lpfiltord, cfg.lpfilttype, cfg.lpfiltdir, cfg.lpinstabilityfix, cfg.lpfiltdf, cfg.lpfiltwintype, cfg.lpfiltdev, cfg.plotfiltresp, cfg.usefftfilt); end
if strcmp(cfg.hpfilter, 'yes'),     dat = ft_preproc_highpassfilter(dat, fsample, cfg.hpfreq, cfg.hpfiltord, cfg.hpfilttype, cfg.hpfiltdir, cfg.hpinstabilityfix, cfg.hpfiltdf, cfg.hpfiltwintype, cfg.hpfiltdev, cfg.plotfiltresp, cfg.usefftfilt); end
if strcmp(cfg.bpfilter, 'yes'),     dat = ft_preproc_bandpassfilter(dat, fsample, cfg.bpfreq, cfg.bpfiltord, cfg.bpfilttype, cfg.bpfiltdir, cfg.bpinstabilityfix, cfg.bpfiltdf, cfg.bpfiltwintype, cfg.bpfiltdev, cfg.plotfiltresp, cfg.usefftfilt); end
if strcmp(cfg.bsfilter, 'yes')
  for i=1:size(cfg.bsfreq,1)
    % apply a bandstop filter for each of the specified bands, i.e. cfg.bsfreq should be Nx2
    dat = ft_preproc_bandstopfilter(dat, fsample, cfg.bsfreq(i,:), cfg.bsfiltord, cfg.bsfilttype, cfg.bsfiltdir, cfg.bsinstabilityfix, cfg.bsfiltdf, cfg.bsfiltwintype, cfg.bsfiltdev, cfg.plotfiltresp, cfg.usefftfilt);
  end
end
if strcmp(cfg.polyremoval, 'yes')
  % the begin and endsample of the polyremoval period correspond to the complete data minus padding
  nsamples  = size(dat,2);
  begsample = 1        + begpadding;
  endsample = nsamples - endpadding;
  dat = ft_preproc_polyremoval(dat, cfg.polyorder, begsample, endsample);
end
if strcmp(cfg.detrend, 'yes')
  % the begin and endsample of the detrend period correspond to the complete data minus padding
  nsamples  = size(dat,2);
  begsample = 1        + begpadding;
  endsample = nsamples - endpadding;
  dat = ft_preproc_detrend(dat, begsample, endsample);
end
if strcmp(cfg.demean, 'yes')
  if ischar(cfg.baselinewindow) && strcmp(cfg.baselinewindow, 'all')
    % the begin and endsample of the baseline period correspond to the complete data minus padding
    nsamples  = size(dat,2);
    begsample = 1        + begpadding;
    endsample = nsamples - endpadding;
    dat       = ft_preproc_baselinecorrect(dat, begsample, endsample);
  else
    % determine the begin and endsample of the baseline period and baseline correct for it
    begsample = nearest(time, cfg.baselinewindow(1));
    endsample = nearest(time, cfg.baselinewindow(2));
    if begsample==endsample && ...
        ((begsample==1 && time(begsample)>cfg.baselinewindow(1)) || (begsample==numel(time) && time(begsample)<cfg.baselinewindow(2)))
      ft_warning('requested baselinewindow does not have any samples in the time axis, no baseline correction applied in this trial');
      dat = dat + datdiff; % add back the previously subtracted stuff
    else
      if begsample==endsample
        ft_warning('requested baselinewindow just has a single sample in the time axis')
      end
      dat = ft_preproc_baselinecorrect(dat, begsample, endsample);
    end
  end
end
if strcmp(cfg.dftfilter, 'yes')
  datorig = dat;
  optarg = {};
  if isfield(cfg, 'dftreplace')
    optarg = cat(2, optarg, {'dftreplace', cfg.dftreplace});
    if strcmp(cfg.dftreplace, 'neighbour') && (begpadding>0 || endpadding>0)
      ft_error('Padding by data mirroring is not supported for spectrum interpolation.');
    end
  end
  if isfield(cfg, 'dftbandwidth')
    optarg = cat(2, optarg, {'dftbandwidth', cfg.dftbandwidth});
  end
  if isfield(cfg, 'dftneighbourwidth')
    optarg = cat(2, optarg, {'dftneighbourwidth', cfg.dftneighbourwidth});
  end
  dat     = ft_preproc_dftfilter(dat, fsample, cfg.dftfreq, optarg{:});
  if strcmp(cfg.dftinvert, 'yes')
    dat = datorig - dat;
  end
end
if ~strcmp(cfg.hilbert, 'no')
  dat = ft_preproc_hilbert(dat, cfg.hilbert);
end
if strcmp(cfg.rectify, 'yes')
  dat = ft_preproc_rectify(dat);
end
if isnumeric(cfg.boxcar)
  numsmp = round(cfg.boxcar*fsample);
  if ~rem(numsmp,2)
    % the kernel should have an odd number of samples
    numsmp = numsmp+1;
  end
  % kernel = ones(1,numsmp) ./ numsmp;
  % dat    = convn(dat, kernel, 'same');
  dat = ft_preproc_smooth(dat, numsmp); % better edge behavior
end
if isnumeric(cfg.conv)
  kernel = (cfg.conv(:)'./sum(cfg.conv));
  if ~rem(length(kernel),2)
    kernel = [kernel 0];
  end
  dat = convn(dat, kernel, 'same');
end
if strcmp(cfg.derivative, 'yes')
  dat = ft_preproc_derivative(dat, 1);
end
if strcmp(cfg.absdiff, 'yes')
  % this implements abs(diff(data), which is required for jump detection
  dat = abs([diff(dat, 1, 2) zeros(size(dat,1),1)]);
end
if strcmp(cfg.standardize, 'yes')
  dat = ft_preproc_standardize(dat, 1, size(dat,2));
end
if ~isempty(cfg.subspace)
  dat = ft_preproc_subspace(dat, cfg.subspace);
end
if ~isempty(cfg.custom)
  if ~isfield(cfg.custom, 'nargout')
    cfg.custom.nargout = 1;
  end
  if cfg.custom.nargout==1
    dat = feval(cfg.custom.funhandle, dat, cfg.custom.varargin);
  elseif cfg.custom.nargout==2
    [dat, time] = feval(cfg.custom.funhandle, dat, cfg.custom.varargin);
  end
end
if strcmp(cfg.resample, 'yes')
  if ~isfield(cfg, 'resamplefs')
    cfg.resamplefs = fsample./2;
  end
  if ~isfield(cfg, 'resamplemethod')
    cfg.resamplemethod = 'resample';
  end
  [dat               ] = ft_preproc_resample(dat,  fsample, cfg.resamplefs, cfg.resamplemethod);
  [time, dum, fsample] = ft_preproc_resample(time, fsample, cfg.resamplefs, cfg.resamplemethod);
end
if ~isempty(cfg.precision)
  % convert the data to another numeric precision, i.e. double, single or int32
  dat = cast(dat, cfg.precision);
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% remove the filter padding and do the preprocessing on the remaining trial data
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
if begpadding~=0 || endpadding~=0
  dat = ft_preproc_padding(dat, 'remove', begpadding, endpadding);
  if strcmp(cfg.demean, 'yes') || nargout>2
    time = ft_preproc_padding(time, 'remove', begpadding, endpadding);
  end
end