function DPMpcOneBeam9(leak, spectrum_File, planC_File, nhist, OutputError, whichBeam, PBMaxWidth, gradsense, MCsolver, saveIM, sourceModel, doseToWater, fillWater, useWedge, inputPB, Softening, batch)
% written by KZ, JC
% Calculate doseV dose3D directly from DPM, without compressing it.
% Use generateDPMdose.m instead of DPMInfluenceJing.m
% Calculate IM struct, call DPM, get
%
% Usage: IIMCalc = calculateBeamIM(0.018, 'DPM_10beams_planC', 1, 1, 5)
%   leak = 0.018
%   spectrum_File = file name of photon spectrum. eg. photon6MV.spectrum
%   planC_File = file name contained planC, eg. 'DPM_10beams_planC'
%   indexBeam = Beam Index: 1, 2, ...
%   imin = Beamlet index
%   imax = Beamlet index
%
%   eval(['load Beam',num2str(indexBeam),'_w']);
%   where, Beam1_w.mat (Beam2_w.mat) need to be in the current directory. It's the weight of
%   the beamlets for each beam.
%
% JC Feb 27 06
% Call in DPMInfluence.m; use sum(10*clock) as the random seeds input to DPM.
%
% JC. Aug 10, 2005, Need to make planC read from the disk, since in the
% stand-alone mode, no matlab run is expected.
%
% JC Dec 18 06
% Add 'saveIM' input,
%   If it's larger than 0, say 1, then save IM
%   If it's 0, don't save IM
%
% LM: JC Jan 26 2007
% Include non-zero couchAngle
% The desination coordinates should be the patient support
% system.
% The previous assumption is that the "fixed system" is the same as
% the "Patient support system".
% test
%
% LM: JC Mar 03, 2007
% Score dose to water flag. Only valid for VMC++ for now. "doseToWater"
% Add "fillWater" flag. It 1, fill the skin strucutere with all water
% density, i.e. CT value = 1024.
%
% LM: JC Mar 29, 2007
% get rid of 'stateS' arguments to 'generateVMCdose.m', 'generateDPMdose.m'
% get rid of the call for 'IMRTP_plancheck'
% add 'useWedge' flag. default = 0; when useWedge == 1, use Elekta
% universal wedge.
% only implemented for DPM, openField == 0, sourceModel == 0;
%
% open planC_File
% In matlab release 14, "eval" can be compiled.
Error = []; %Initialize Error to NULL.
load(planC_File);
%eval(['load ',planC_File]);
% This planC_File should contains planC and stateS
%leak = 0.018; %leakage in per *100
%
% Copyright 2010, Joseph O. Deasy, on behalf of the CERR development team.
%
% This file is part of The Computational Environment for Radiotherapy Research (CERR).
%
% CERR development has been led by:  Aditya Apte, Divya Khullar, James Alaly, and Joseph O. Deasy.
%
% CERR has been financially supported by the US National Institutes of Health under multiple grants.
%
% CERR is distributed under the terms of the Lesser GNU Public License.
%
%     This version of CERR is free software: you can redistribute it and/or modify
%     it under the terms of the GNU General Public License as published by
%     the Free Software Foundation, either version 3 of the License, or
%     (at your option) any later version.
%
% CERR is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
% without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
% See the GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with CERR.  If not, see <http://www.gnu.org/licenses/>.

if ischar(leak)
    leak = str2num(leak)
end

if ischar(nhist)
    nhist = str2num(nhist)
end

if ischar(OutputError)
    OutputError = str2num(OutputError)
end

if ischar(whichBeam)
    whichBeam = str2num(whichBeam)
end

if ischar(PBMaxWidth)
    PBMaxWidth = str2num(PBMaxWidth)
end

if ischar(gradsense)
    gradsense = str2num(gradsense)
end

% If MCsolver == 1, use DPM, default
% If MCsolver == 2, use VMC++
% other, throw out error
if ischar(MCsolver)
    MCsolver = str2num(MCsolver)
end

% If saveIM == 0, calculate doseV, do not calc/save IM
% If saveIM ~= 0, calc/save IM, only valid for DPM for now, ie. MCsolver == 1.
if ischar(saveIM)
    saveIM = str2num(saveIM)
end

% If sourceModel == 0, no source model, only point source
% If sourceModel == 1, source model version 1: point source (Primary) + FF
% + electron contamination.
% Expandable to other source models for the future development.

if ischar(sourceModel)
    sourceModel = str2num(sourceModel)
end

if ischar(doseToWater)
    doseToWater = str2num(doseToWater)
end

if ischar(fillWater)
    fillWater = str2num(fillWater)
end

if ischar(useWedge)
    useWedge = str2num(useWedge)
end

if ischar(inputPB)
    inputPB = str2num(inputPB)           %Flag, inputPB == 1, ask user to input .mat file storing PB info.
                                 % That file can be generated using
                                 % function generateOpenfiledPB.m
end
% Now, only work for DPM, planC{7} not empty.

if ischar(Softening)
    Softening = str2num(Softening)           %Flag,  == 1, use off-axis-softening in dpm calc.

end
% Now, only work for DPM, planC{7} not empty.

if ischar(batch)
    batch = str2num(batch)
end

currentDir = cd;
if (~isempty(planC{7}))
    % When there are multiple beam definition in planC, use the first BEAM
    % data as the default.

    %for indexBeam = 1 : planC{7}(1).FractionGroupSequence.Item_1.NumberOfBeams
    for indexBeam = whichBeam

            bs = planC{7}(1).BeamSequence.(['Item_' num2str(indexBeam)]);

            %Parse BlockData into PB information.
            input = bs.BlockSequence.Item_1.BlockData;
            input = reshape(input, 2, [])';

            [xPosV, yPosV, beamlet_delta_x, beamlet_delta_y]=...
                BlockToPB(input, 10, 10);   %PB size here is in mm.
            w_field = ones(size(xPosV));

            gA = bs.ControlPointSequence.Item_1.GantryAngle;
            iC = bs.ControlPointSequence.Item_1.IsocenterPosition;
            IMBase.beams.gantryAngle = gA;
            % couchAngle is PatientSupportAngle, in DICOM, i.e. IEC format
            % (in deg)
            IMBase.beams.couchAngle = bs.ControlPointSequence.Item_1.PatientSupportAngle;
            % JC Apr. 10 2007,
            % Add Beam Limiting Deviceing Angle, i.e. collimator angle from
            % DICOM, in IED format. (in deg)
            % Not test for "Non-zero angle" yet.
            IMBase.beams.collimatorAngle = bs.ControlPointSequence.Item_1.BeamLimitingDeviceAngle

            try
            if ~isfield(planC{7}(1).PatientSetupSequence,(['Item_' num2str(indexBeam)]))
                position = {planC{7}(1).PatientSetupSequence.(['Item_' num2str(1)]).PatientPosition};
            else
                position = {planC{7}(1).PatientSetupSequence.(['Item_' num2str(indexBeam)]).PatientPosition};
            end
            catch
                position = 'HFS';
                disp('Use default patient posiont HFS')
            end

            if strcmpi(position, 'HFP')
                IMBase.beams(1).isocenter.x = iC(1)/10;
                IMBase.beams(1).isocenter.y = iC(2)/10;
                IMBase.beams(1).isocenter.z = -iC(3)/10;
            else
                IMBase.beams(1).isocenter.x = iC(1)/10;
                IMBase.beams(1).isocenter.y = -iC(2)/10;
                IMBase.beams(1).isocenter.z = -iC(3)/10;
            end

            IMBase.beams(1).isodistance = bs.SourceAxisDistance/10;

            IMBase.beams(1).beamEnergy = bs.ControlPointSequence.Item_1.NominalBeamEnergy;

            xPosV = xPosV/10;
            yPosV = yPosV/10;
            beamlet_delta_x = beamlet_delta_x/10;
            beamlet_delta_y = beamlet_delta_y/10;

            IMBase.goals.PBMargin = 0.5;
            IMBase.goals.structNum = 2;
            IMBase.params.xyDownsampleIndex = 1;
            IMBase.goals.isTarget(1) = 'y';
            IMBase.goals.xySampleRate = 2;
            IMBase.params.numCTSamplePts = 300;

            IMBase.beams(1).zRel = 0;
            IMBase.beams(1).xRel =  IMBase.beams(1).isodistance * sindeg(IMBase.beams(1).gantryAngle);
            IMBase.beams(1).yRel =  IMBase.beams(1).isodistance * cosdeg(IMBase.beams(1).gantryAngle);

            % JC In the above calc. for xRel and yRel, the assumption is the couchAngle
            % == 0. i.e. the "fixed system" is the same as the "patient support
            % system".
            % LM: JC Jan 26 2007
            % Include non-zero couchAngle
            % The desination coordinates should be the patient support
            % system.
            % The previous assumption is that the "fixed system" is the same as
            % the "Patient support system".
            % test

            %         patientVectorsM = zeros(size(RTOGVectorsM));
            patientxRel = cosdeg(IMBase.beams.couchAngle) * IMBase.beams(1).xRel- sindeg(IMBase.beams.couchAngle) * IMBase.beams(1).zRel;
            patientyRel = IMBase.beams(1).yRel;
            patientzRel = sindeg(IMBase.beams.couchAngle) * IMBase.beams(1).xRel + cosdeg(IMBase.beams.couchAngle) * IMBase.beams(1).zRel;
            IMBase.beams(1).xRel = patientxRel;
            IMBase.beams(1).yRel = patientyRel;
            IMBase.beams(1).zRel = patientzRel;
            clear patientxRel patientyRel patientzRel

            %IMBase.params.algorithm = 'VMC++';
            %IMCalc = IMRTP_MapCheck(IMBase);

            IMBase.params.algorithm = 'DPM';
            %      load PB_40x40.mat beamlet_delta_x beamlet_delta_y xPosV yPosV w_field
            %      load PB_15x15.mat beamlet_delta_x beamlet_delta_y xPosV yPosV w_field

            if inputPB         % get PB info from a specified .mat file.
                setappdata(0, 'usenativesystemdialogs', false)
                currentDir = cd;

                [FileName,path] = uigetfile('*.mat','Select MAT file containing beamlet_delta_x beamlet_delta_y xPosV yPosV w_field');

                if path == 0
                    errordlg('File Should exist');
                    error('File Should exist');
                end

                cd(path);
                load (FileName, 'beamlet_delta_x', 'beamlet_delta_y', 'xPosV', 'yPosV', 'w_field')
                cd(currentDir);
            end

            disp('Number of pencile beams are:')
            length(xPosV)

            sourceS = IMBase.beams(1);
            [RTOGPBVectorsM, RTOGPBVectorsM_MC, PBMaskM, rowPBV, colPBV, xPBPosV, yPBPosV, beamlet_delta_x, beamlet_delta_y] = ...
                    getPBRays(xPosV, yPosV, beamlet_delta_x, beamlet_delta_y, sourceS);
            IMCalc = IMBase;
            IMCalc.beams(1).RTOGPBVectorsM_MC = RTOGPBVectorsM_MC;
            IMCalc.beams(1).RTOGPBVectorsM    = RTOGPBVectorsM;
            IMCalc.beams(1).xPBPosV           = xPBPosV;
            IMCalc.beams(1).yPBPosV           = yPBPosV;
            IMCalc.beams(1).rowPBV            = rowPBV;
            IMCalc.beams(1).colPBV            = colPBV;
            % No need of this field
            IMCalc.beams(1).CTTraceS          = struct;
            IMCalc.beams(1).beamletDelta_x    = beamlet_delta_x;
            IMCalc.beams(1).beamletDelta_y    = beamlet_delta_y;

            %RTOG positions of sources
            IMCalc.beams(1).x = IMCalc.beams(1).xRel + IMCalc.beams(1).isocenter.x;
            IMCalc.beams(1).y = IMCalc.beams(1).yRel + IMCalc.beams(1).isocenter.y;
            IMCalc.beams(1).z = IMCalc.beams(1).zRel + IMCalc.beams(1).isocenter.z;
            %IMCalc = IMRTP_MapCheck(IMBase, planC, stateS, xPosV, yPosV, beamlet_delta_x, beamlet_delta_y, gA);
            % JC. 11 Aug, 05 Added more arguments to IMRTP_MapCheck,

            % JC. Aug. 3, 2005
            % Add fields to IMCalc, necessary for generateDPMInfluence.m
            IMCalc.beams.collimatorAngle = 0;
            IMCalc.params.ScatterMethod = 'exponential';
            % Use 0.002 instead the original default 0.01 as threshold value.
            % Change to 0.004, since the stored IM struct is very large.
            IMCalc.params.Scatter.Threshold = 0.004;
            IMCalc.params.Scatter.RandomStep = 30;
            IMCalc.beamlets = struct;

            %JC Add/porpulate VMC field in IM struct.
            IMCalc.params.VMC.NumParticles = nhist;
            IMCalc.params.VMC.NumBatches = 10;
            IMCalc.params.VMC.scoreDoseToWater = 'No';
            IMCalc.params.VMC.monoEnergy = 0;   %?
            IMCalc.params.VMC.repeatHistory = 0.2510;
            IMCalc.params.VMC.splitPhotons = 'Yes';
            IMCalc.params.VMC.photonSplitFactor = -40;
            IMCalc.params.VMC.base = 2;;
            IMCalc.params.VMC.dimension = 60;    %?
            IMCalc.params.VMC.skip = 0;  %? 1?
            IMCalc.params.VMC.includeError = 'No';
            IMCalc.params.VMC.spectrum = spectrum_File;
            IMCalc.beams.beamModality = 'photons';

            if doseToWater
            IMCalc.params.VMC.scoreDoseToWater = 'Yes';
            disp('VMC++ score dose to Water.');
            end

            % JC Jun 06 2006
            % Still need to fill in the structure of IMCalc, but don't need put
            % it back(?), since it's not to be used to calculate dose.
            % Will also need to use 'DoseScale' inside generateDPMdose.m,
            % since DPM output dose on a per partical base.
            % Need to use w_field as a way of telling DPM how to add beamlets
            % up.

            % JC Aug 30. 2006
            if (MCsolver == 1)

                [IMCalc doseV indV numberParticles] = generateDPMdose7(IMCalc,spectrum_File,nhist, OutputError,planC, indexBeam, w_field, saveIM, sourceModel, fillWater, useWedge, Softening);

            elseif (MCsolver == 2)
                % use VMC++
                [doseV indV] = generateVMCdose(IMCalc, planC, w_field, sourceModel, doseToWater, fillWater,saveIM);
            else
                error('INVALID input: MCsolver == 1, DPM; MCsolver == 2, VMC++')
            end

    end

else %If it's a conventional plan, planC{7} is empty

    indexBeam = whichBeam;
    if (length(planC{6}(indexBeam).file) > 5)
        [xPosV, yPosV, beamlet_delta_x, beamlet_delta_y, beamGeometry]=...
            planCToPB(planC, indexBeam, 1, 1);
    else
        % The field is shaped by Jaws, not MLC.
        [xPosV, yPosV, beamlet_delta_x, beamlet_delta_y, beamGeometry]=...
            planCToPBJaws(planC, indexBeam, 1, 1);
    end

    IMBase.beams(1).isocenter.x = beamGeometry.isocenter(1);
    IMBase.beams(1).isocenter.y = beamGeometry.isocenter(2);
    IMBase.beams(1).isocenter.z = beamGeometry.isocenter(3);
    % RTOG file has the same couchAngle definition as in IEC1217.
    IMBase.beams(1).couchAngle = beamGeometry.couchAngle;
    % RTOG file has different gantryAngle definition from IEC1217.
    % Use IEC 1217 coordinates.
    IMBase.beams(1).gantryAngle = - beamGeometry.gantryAngle;
    IMBase.beams(1).isodistance = beamGeometry.isoDistance;

    IMBase.goals.PBMargin = 0.5;
    IMBase.goals.structNum = 2;
    IMBase.params.xyDownsampleIndex = 1;
    IMBase.goals.isTarget(1) = 'y';
    IMBase.goals.xySampleRate = 1;
    IMBase.params.numCTSamplePts = 300;
    IMBase.beams(1).beamEnergy = planC{6}(1).beamEnergyMeV;

    IMBase.beams(1).zRel = 0;
    IMBase.beams(1).xRel =  IMBase.beams(1).isodistance * sindeg(IMBase.beams(1).gantryAngle);
    IMBase.beams(1).yRel =  IMBase.beams(1).isodistance * cosdeg(IMBase.beams(1).gantryAngle);

    % JC In the above calc. for xRel and yRel, the assumption is the couchAngle
    % == 0. i.e. the "fixed system" is the same as the "patient support
    % system".
    % LM: JC Jan 26 2007
    % Include non-zero couchAngle
    % The desination coordinates should be the patient support
    % system.
    % The previous assumption is that the "fixed system" is the same as
    % the "Patient support system".

    patientxRel = cosdeg(IMBase.beams.couchAngle) * IMBase.beams(1).xRel- sindeg(IMBase.beams.couchAngle) * IMBase.beams(1).zRel;
    patientyRel = IMBase.beams(1).yRel;
    patientzRel = sindeg(IMBase.beams.couchAngle) * IMBase.beams(1).xRel + cosdeg(IMBase.beams.couchAngle) * IMBase.beams(1).zRel;
    IMBase.beams(1).xRel = patientxRel;
    IMBase.beams(1).yRel = patientyRel;
    IMBase.beams(1).zRel = patientzRel;
    clear patientxRel patientyRel patientzRel

    IMBase.beams(1).collimatorAngle = beamGeometry.collimatorAngle;

    disp('Number of pencile beams are:')
            length(xPosV)

    % JC Mar 29, 2007
    % Repace 'IMRTP_plancheck' by the following command
            sourceS = IMBase.beams(1);
            [RTOGPBVectorsM, RTOGPBVectorsM_MC, PBMaskM, rowPBV, colPBV, xPBPosV, yPBPosV, beamlet_delta_x, beamlet_delta_y] = ...
                    getPBRays(xPosV, - yPosV, beamlet_delta_x, beamlet_delta_y, sourceS);
    % Note: In the above line, use '- yPosV' instead of 'yPosV'. Since IEC and RTOG have different definition about yPosV.
            IMCalc = IMBase;
            IMCalc.beams(1).RTOGPBVectorsM_MC = RTOGPBVectorsM_MC;
            IMCalc.beams(1).RTOGPBVectorsM    = RTOGPBVectorsM;
            IMCalc.beams(1).xPBPosV           = xPBPosV;
            IMCalc.beams(1).yPBPosV           = yPBPosV;
            IMCalc.beams(1).rowPBV            = rowPBV;
            IMCalc.beams(1).colPBV            = colPBV;
            % No need of this field
            IMCalc.beams(1).CTTraceS          = struct;
            IMCalc.beams(1).beamletDelta_x    = beamlet_delta_x;
            IMCalc.beams(1).beamletDelta_y    = beamlet_delta_y;

            %RTOG positions of sources
            IMCalc.beams(1).x = IMCalc.beams(1).xRel + IMCalc.beams(1).isocenter.x;
            IMCalc.beams(1).y = IMCalc.beams(1).yRel + IMCalc.beams(1).isocenter.y;
            IMCalc.beams(1).z = IMCalc.beams(1).zRel + IMCalc.beams(1).isocenter.z;

    % JC Mar 29, 2007
    % Replace 'IMRTP_MapCheck' by above commands.
    % IMCalc = IMRTP_MapCheck(IMBase, planC, stateS, xPosV, - yPosV, beamlet_delta_x, beamlet_delta_y, gA);
    clear IMBase beamlet_delta_x beamlet_delta_y PlanC_File xPosV yPosV

    % Add fields to IMCalc, necessary for generateDPMInfluence.m
    IMBase.params.algorithm = 'DPM';
    IMCalc.params.ScatterMethod = 'exponential';
    IMCalc.params.Scatter.Threshold = 0.004;
    IMCalc.params.Scatter.RandomStep = 30;
    IMCalc.beamlets = struct;

    % For the conventional RT. No change in w_field
    w_field = ones(length(IMCalc.beams.beamletDelta_x),1);
    %[doseV indV] = generateDPMdose(IMCalc,spectrum_File,nhist, OutputError,planC,stateS, indexBeam, w_field);
    % Dec 18, 2006
    % Use the new modified DPM, with Source Model.

    %JC Add/porpulate VMC field in IM struct.
    IMCalc.params.VMC.NumParticles = nhist;
    IMCalc.params.VMC.NumBatches = 10;
    IMCalc.params.VMC.scoreDoseToWater = 'No';
    IMCalc.params.VMC.monoEnergy = 0;   %?
    IMCalc.params.VMC.repeatHistory = 0.2510;
    IMCalc.params.VMC.splitPhotons = 'Yes';
    IMCalc.params.VMC.photonSplitFactor = -40;
    IMCalc.params.VMC.base = 2;;
    IMCalc.params.VMC.dimension = 60;    %?
    IMCalc.params.VMC.skip = 0;  %? 1?
    IMCalc.params.VMC.includeError = 'No';
    IMCalc.params.VMC.spectrum = spectrum_File;
    IMCalc.beams.beamModality = 'photons';

    if doseToWater
            IMCalc.params.VMC.scoreDoseToWater = 'Yes';
            disp('VMC++ score dose to Water.');
     end

    if (MCsolver == 1)

        [IMCalc doseV indV numberParticles] = generateDPMdose7(IMCalc,spectrum_File,nhist, OutputError,planC, indexBeam, w_field, saveIM, sourceModel, fillWater, useWedge, Softening);

    elseif (MCsolver == 2)
        % use VMC++
        [doseV indV] = generateVMCdose(IMCalc, planC, w_field, sourceModel, doseToWater, fillWater,saveIM);
    else
        error('INVALID input: MCsolver == 1, DPM; MCsolver == 2, VMC++')
    end

    % ?? Right place to put end???
end

%%%%%%%%%%%%%%%
    siz = getUniformizedSize(planC);
    dose3D = zeros(siz);

    if (OutputError == 1)            % OutputError only valid for DPM,ie., MCsolver == 1
        doseV = doseV(:,1);
        Error_3D = zeros(siz);
        Error_3D(indV') = doseV(:,2);
    else
        disp('Do not output error')
        dose3D(indV') = doseV;
    end

    %        filename = ['doseV_',num2str(indexBeam),'_', num2str(nhist), '_', num2str(batch)];
    %        save(filename, 'doseV', 'indV');

    filename = ['dose3D_',num2str(indexBeam),'_', num2str(nhist), '_', num2str(batch)];
    save(filename, 'dose3D');

    % Only Output w_field for the first batch
    if(batch == 1)
        filename = ['w_field',num2str(indexBeam)];
        save(filename, 'w_field');%dose3D = (dose3D/max(dose3D(:)));
    end

if (saveIM == 1)
%saveIM  %Only valid for DPM, i.e. IM
    filename = ['IMCalc_Beam',num2str(indexBeam),'_', num2str(nhist), '_', num2str(batch)];
    save(filename, 'IMCalc');
end
clear IMCalc dose3D w_field

end


function result = isInGeodesicROI(mesh_total,wholepath,face)

wholepath=wholepath(2:end);
verticesinfaces=mesh_total.faces(face,:);
b = intersect(verticesinfaces,wholepath);

resultat = size(b,2) < 1;
result = resultat(1);


function [okay] = inspect(mesh,varargin)
%INSPECT helper routine to safely query a MESH structure.

%-----------------------------------------------------------
%   Darren Engwirda
%   github.com/dengwirda/jigsaw-matlab
%   19-Dec-2018
%   darren.engwirda@columbia.edu
%-----------------------------------------------------------
%

    okay = false; base = ''; item = '';

    if (nargin >= +2), base = varargin{1}; end
    if (nargin >= +3), item = varargin{2}; end

    if (nargin <= +1 || nargin >= +4)
        error('inspect:incorrectNumbInputs' , ...
            'Incorrect number of arguments!') ;
    end

    if (~isstruct(mesh))
        error('inspect:incorrectInputClass' , ...
            'MESH must be a valid struct.') ;
    end
    if (~isempty(base) && ~ischar(base))
        error('inspect:incorrectInputClass' , ...
            'BASE must be a valid string!') ;
    end
    if (~isempty(item) && ~ischar(item))
        error('inspect:incorrectInputClass' , ...
            'ITEM must be a valid string!') ;
    end

    if (isempty(item))
%-- default ITEM kinds given BASE types
    switch (lower(base))
        case 'point', item = 'coord' ;
        case 'edge2', item = 'index' ;
        case 'tria3', item = 'index' ;
        case 'quad4', item = 'index' ;
        case 'tria4', item = 'index' ;
        case 'hexa8', item = 'index' ;
        case 'wedg6', item = 'index' ;
        case 'pyra5', item = 'index' ;
        case 'bound', item = 'index' ;
    end
    end

    if (isempty(item))
%-- check whether MESH.BASE exists
    okay = isfield(mesh,base) && ...
            ~isempty(mesh.(base)) ;
    else
%-- check whether MESH.BASE.ITEM exists
    okay = isfield(mesh,base) && ...
        isfield(mesh.(base),item) && ...
            ~isempty(mesh.(base).(item)) ;
    end

end


function X = uminus(X)
% UMINUS (overloaded)

X.basis = -X.basis;


function net = caffe_load_model( net_def_file, net_weights_file, phase )
%
% This file is part of the code that implements the following ICCV2015 accepted paper:
% title: "Object detection via a multi-region & semantic segmentation-aware CNN model"
% authors: Spyros Gidaris, Nikos Komodakis
% institution: Universite Paris Est, Ecole des Ponts ParisTech
% Technical report: http://arxiv.org/abs/1505.01749
% code: https://github.com/gidariss/mrcnn-object-detection
%
% AUTORIGHTS
% --------------------------------------------------------
% Copyright (c) 2015 Spyros Gidaris
%
% "Object detection via a multi-region & semantic segmentation-aware CNN model"
% Technical report: http://arxiv.org/abs/1505.01749
% Licensed under The MIT License [see LICENSE for details]
% ---------------------------------------------------------

if ~exist('phase', 'var'),  phase = 'test'; end
assert(exist(net_def_file,'file')>0);
net = caffe.Net(net_def_file, phase); % create net but not load weights

assert(iscell(net_weights_file))
num_weights_files = length(net_weights_file);
for i = 1:num_weights_files
    assert(exist(net_weights_file{i},'file')>0);
    net.copy_from(net_weights_file{i});
end

end


function planC = warp_dose(deformS,doseCreationScanNum,movDoseNum,movPlanC,planC,tmpDirPath,inverseFlag)
% function planC = warp_dose(deformS,doseCreationScanNum,movPlanC,planC)
%
% APA, 07/19/2012

if ~exist('inverseFlag','var')
    inverseFlag = 0;
end

if ~exist('tmpDirPath','var') || isempty(tmpDirPath)
    tmpDirPath = fullfile(getCERRPath,'ImageRegistration','tmpFiles');
end

indexMovS = movPlanC{end};
indexS = planC{end};

optS = getCERROptions();
plmFlag = 0; elxFlag = 0; antsFlag = 0;
% Create b-spline coefficients file
if isstruct(deformS)
    if ~inverseFlag
        baseScanUID = deformS.baseScanUID;
        movScanUID  = deformS.movScanUID;
    else
        baseScanUID = deformS.movScanUID;
        movScanUID  = deformS.baseScanUID;
    end
%     algorithm = deformS.algorithm;
    registration_tool = deformS.registrationTool;
    switch upper(registration_tool)
        case 'PLASTIMATCH'
            plmFlag = 1;
            disp('Plastimatch selected');
            bspFileName = fullfile(tmpDirPath,['bsp_coeffs_',baseScanUID,'_',movScanUID,'.txt']);
            success = write_bspline_coeff_file(bspFileName,deformS.algorithmParamsS);
        case 'ELASTIX'
            elxFlag = 1;
            disp('Elastix selected');
        case 'ANTS'
            antsFlag = 1;
            disp('ANTs selected');
    end

else
    bspFileName = deformS;
    indexS = planC{end};
    indexMovS = movPlanC{end};
    movScanNum = getDoseAssociatedScan(movDoseNum,movPlanC);
    movScanUID = movPlanC{indexMovS.scan}(movScanNum).scanUID;
    baseScanUID = planC{indexS.scan}(doseCreationScanNum).scanUID;
end

% Convert dose to .mha
movDoseUID = movPlanC{indexMovS.dose}(movDoseNum).doseUID;
randPart = floor(rand*1000);
movDoseUniqName = [movDoseUID,num2str(randPart)];
movDoseFileName = fullfile(tmpDirPath,['movDose_',movDoseUniqName,'.mha']);

% Write .mha file for this dose
success = createMhaDosesFromCERR(movDoseNum, movDoseFileName, movPlanC);

%write target space reference mha scan
refScanNum = findScanByUID(planC,baseScanUID);
[refScanUniqName, ~] = genScanUniqName(planC,refScanNum);
refScanFileName = fullfile(tmpDirPath,['baseScan_',refScanUniqName,'.mha']);
success = createMhaScansFromCERR(refScanNum, refScanFileName, planC);

% Generate name for the output .mha file
warpedMhaFileName = fullfile(tmpDirPath,['warped_dose_',baseScanUID,'_',movScanUID,'.mha']);

if plmFlag
    % Switch to plastimatch directory if it exists
    prevDir = pwd;
    plmCommand = 'plastimatch warp ';
    optName = fullfile(getCERRPath,'CERROptions.json');
    optS = opts4Exe(optName);
    if exist(optS.plastimatch_build_dir,'dir') && isunix
        cd(stateS.optS.plastimatch_build_dir)
        plmCommand = ['./',plmCommand];
    end

    % Issue plastimatch warp command with nearest neighbor interpolation
    %fail = system([plmCommand, '--input ', movDoseFileName, ' --output-img ',
    %warpedMhaFileName, ' --xf ', bspFileName, ' --algorithm itk']); %
    %ITK-based warping, consider adding it an an option.
    fail = system([plmCommand, '--input ', movDoseFileName, ' --output-img ',...
        warpedMhaFileName, ' --xf ', bspFileName, ' --fixed ', refScanFileName]);
    if fail % try escaping slashes
        system([plmCommand, '--input ', escapeSlashes(movDoseFileName),...
            ' --output-img ', escapeSlashes(warpedMhaFileName),...
            ' --xf ', escapeSlashes(bspFileName),...
            ' --fixed ', escapeSlashes(refScanFileName)])
    end
end

if antsFlag
            % ANTs path setup
        if ~exist(optS.antspath_dir,'dir')
            error(['ANTSPATH ' optS.antspath_dir ' not found on filesystem. Please review CERROptions.']);
        end
        antspath = fullfile(optS.antspath_dir,'bin');
        setenv('ANTSPATH', antspath);
        antsScriptPath = fullfile(optS.antspath_dir, 'Scripts');
        antsCERRScriptPath = fullfile(getCERRPath,'CERR_core','ImageRegistration','antsScripts');
        if isunix
            setenv('PATH',[antspath ':' antsScriptPath ':' antsCERRScriptPath ':' getenv('PATH')]);
            antsCommand = '';
        else
            setenv('PATH',[antspath ';' antsScriptPath ';' antsCERRScriptPath ';' getenv('PATH')]);
            antsCommand = '';
        end
        % generate transform command
        transformParams = buildAntsTransform(deformS.algorithmParamsS.antsWarpProducts);
        antsCommand = [antsCommand ' antsApplyTransforms -d 3 -r ' refScanFileName ' -i ' movDoseFileName ' -o ' warpedMhaFileName ' ' transformParams]; % ' ' interpParams];
        disp(antsCommand);
        system(antsCommand);
        disp('Warp complete, importing warped scan to planC...');

end

% Read the warped output .mha file within CERR
infoS  = mha_read_header(warpedMhaFileName);
data3M = mha_read_volume(infoS);
% [data3M,infoS] = readmha(warpedMhaFileName);
doseName = movPlanC{indexMovS.dose}(movDoseNum).fractionGroupID;
assocScanUID = planC{indexS.scan}(doseCreationScanNum).scanUID;
planC = dose2CERR(flipdim(permute(data3M,[2,1,3]),3),[],...
    ['Warped_',doseName],[],[],'UniformCT',[],'no',...
    assocScanUID,planC);

% Cleanup
try
    delete(movDoseFileName)
    delete(warpedMhaFileName)
    %delete(bspFileName)
end
try
    if isstruct(deformS)
        delete(bspFileName)
    end
end

% Switch back to the previous directory
% cd(prevDir)

% Get DICOMHeader for the original dose
dcmHeaderS = [];
if isfield(movPlanC{indexMovS.dose}(movDoseNum),'DICOMHeaders')
    dcmHeaderS = movPlanC{indexMovS.dose}(movDoseNum).DICOMHeaders;
end
if ~isempty(dcmHeaderS)
    dcmHeaderS.PixelSpacing = [];
    dcmHeaderS.ImagePositionPatient = [];
    dcmHeaderS.ImageOrientationPatient = [];
    dcmHeaderS.GridFrameOffsetVector = [];
    dcmHeaderS.Rows = [];
    dcmHeaderS.Columns = [];
    dcmHeaderS.SliceThickness = [];
end
planC{indexS.dose}(end).DICOMHeaders = dcmHeaderS;


function varargout = process_inverse_2016( varargin )
% PROCESS_INVERSE_2016: Compute an inverse model.

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

eval(macro_method);
end

%% ===== GET DESCRIPTION =====
function sProcess = GetDescription() %#ok<DEFNU>
    % ===== PROCESS =====
    % Description the process
    sProcess.Comment     = 'Compute sources [2016]';
    sProcess.Category    = 'Custom';
    sProcess.SubGroup    = 'Sources';
    sProcess.Index       = 326;
    sProcess.Description = 'https://neuroimage.usc.edu/brainstorm/Tutorials/SourceEstimation';
    % Definition of the input accepted by this process
    sProcess.InputTypes  = {'data', 'raw'};
    sProcess.OutputTypes = {'results', 'results'};
    sProcess.nInputs     = 1;
    sProcess.nMinFiles   = 1;
    % Option: Output
    sProcess.options.output.Comment = {'Kernel only: shared', 'Kernel only: one per file', 'Full results: one per file'};
    sProcess.options.output.Type    = 'radio';
    sProcess.options.output.Value   = 1;
    % Options: MNE options
    sProcess.options.inverse.Comment = {'panel_inverse_2016', 'Source estimation options: '};
    sProcess.options.inverse.Type    = 'editpref';
    sProcess.options.inverse.Value   = bst_inverse_linear_2016();
end

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

%% ===== RUN =====
function OutputFiles = Run(sProcess, sInputs) %#ok<DEFNU>
    OutputFiles = {};

    % ===== GET OPTIONS =====
    % Default inverse options
    OPTIONS = Compute();
    % Get options edited by the user
    OPTIONS = struct_copy_fields(OPTIONS, sProcess.options.inverse.Value, 1);
    % Output
    switch (sProcess.options.output.Value)
        % Kernel only: shared
        case 1
            [sChannels, iStudies] = bst_get('ChannelForStudy', unique([sInputs.iStudy]));
            iStudies = unique(iStudies);
            iDatas   = [];
            OPTIONS.ComputeKernel = 1;
        % Kernel only: one per file
        case 2
            iStudies = [sInputs.iStudy];
            iDatas   = [sInputs.iItem];
            OPTIONS.ComputeKernel = 1;
        % Full results: one per file
        case 3
            iStudies = [sInputs.iStudy];
            iDatas   = [sInputs.iItem];
            OPTIONS.ComputeKernel = 0;
    end
    % No messages
    OPTIONS.DisplayMessages = 0;

    % ===== START COMPUTATION =====
    % Call head modeler
    [AllFiles, errMessage] = Compute(iStudies, iDatas, OPTIONS);
    % Report errors
    if isempty(AllFiles) && ~isempty(errMessage)
        bst_report('Error', sProcess, sInputs, errMessage);
        return;
    elseif ~isempty(errMessage)
        bst_report('Warning', sProcess, sInputs, errMessage);
    end
    % For shared kernels: Return only the source files corresponding to the recordings that were in input
    if isempty(iDatas)
        % Loop on the output files (all links): find the ones that match the input
        for iFile = 1:length(AllFiles)
            % Resolve link: get data file
            [ResFile, DataFile] = file_resolve_link(AllFiles{iFile});
            % Find one that matches the inputs
            iInput = find(file_compare({sInputs.FileName}, file_short(DataFile)));
            % If founf: add to the output files
            if ~isempty(iInput)
                OutputFiles{end+1} = AllFiles{iFile};
            end
        end
    else
        OutputFiles = AllFiles;
    end
end

%% ===== COMPUTE INVERSE SOLUTION =====
% USAGE:      OPTIONS = Compute()
%         OutputFiles = Compute(iStudies, iDatas, OPTIONS)
%
% Authors: Sylvain Baillet, October 2002
%          Esen Kucukaltun-Yildirim, 2004
%          Syed Ashrafulla, John Mosher, Rey Ramirez, 2009-2012
%          Francois Tadel, John Mosher, 2009-2016
%
function [OutputFiles, errMessage] = Compute(iStudies, iDatas, OPTIONS)
    % Initialize returned variables
    OutputFiles = {};
    errMessage = [];
    % Default options settings
    Def_OPTIONS = struct(...
        'InverseMethod',       'minnorm', ... % A string that specifies the imaging method
        'InverseMeasure',      'dspm', ...
        'SourceOrient',        'fixed', ...
        'DataTypes',           [], ...     % Cell array of strings: list of modality to use for the reconstruction (MEG, MEG GRAD, MEG MAG, EEG)
        'Comment',             '', ...     % Inverse solution description (optional)
        'DisplayMessages',     1, ...
        'ComputeKernel',       1);         % If 1, compute MN kernel to be applied subsequently to data instead of full ImageGridAmp array
    % Return default options
    if (nargin < 2)
        OutputFiles = Def_OPTIONS;
        return;
    end
    % Use default options
    if (nargin < 3) || isempty(OPTIONS)
        OPTIONS = Def_OPTIONS;
    else
        % Check field names of passed OPTIONS and fill missing ones with default values
        OPTIONS = struct_copy_fields(OPTIONS, Def_OPTIONS, 0);
    end

    %% ===== GET INPUT INFORMATION =====
    isShared = isempty(iDatas);
    % Get all the study structures
    sStudies = bst_get('Study', unique(iStudies));
    % Get channel studies
    if isShared
        sChanStudies = sStudies;
    else
        [tmp, iChanStudies] = bst_get('ChannelForStudy', unique(iStudies));
        sChanStudies = bst_get('Study', iChanStudies);
    end
    % Check that there are channel files available
    if any(cellfun(@isempty, {sChanStudies.Channel}))
        errMessage = 'No channel file available.';
        return;
    end
    % Check head model
    if any(cellfun(@isempty, {sChanStudies.HeadModel}))
        errMessage = 'No head model available.';
        return;
    end
    % Check noise covariance
    if any(cellfun(@isempty, {sChanStudies.NoiseCov}))
        errMessage = 'No noise covariance matrix available.';
        return;
    end
    % Loop through all the channel files to find the available modalities and head model types
    AllMod = {};
    HeadModelType = 'surface';
    MEGMethod = [];
    nSamplesNoise = [];
    nSamplesData  = [];
    for i = 1:length(sChanStudies)
        AllMod = union(AllMod, sChanStudies(i).Channel.DisplayableSensorTypes);
        if isempty(sChanStudies(i).HeadModel(sChanStudies(i).iHeadModel).MEGMethod)
            AllMod = setdiff(AllMod, {'MEG GRAD','MEG MAG','MEG'});
        end
        if isempty(sChanStudies(i).HeadModel(sChanStudies(i).iHeadModel).EEGMethod)
            AllMod = setdiff(AllMod, {'EEG'});
        end
        if isempty(sChanStudies(i).HeadModel(sChanStudies(i).iHeadModel).ECOGMethod)
            AllMod = setdiff(AllMod, {'ECOG'});
        end
        if isempty(sChanStudies(i).HeadModel(sChanStudies(i).iHeadModel).SEEGMethod)
            AllMod = setdiff(AllMod, {'SEEG'});
        end
        if ~strcmpi(sChanStudies(i).HeadModel(sChanStudies(i).iHeadModel).HeadModelType, 'surface')
            HeadModelType = sChanStudies(i).HeadModel(sChanStudies(i).iHeadModel).HeadModelType;
        end
        if ~isempty(sChanStudies(i).HeadModel(sChanStudies(i).iHeadModel).MEGMethod) && isempty(MEGMethod)
            MEGMethod = sChanStudies(i).HeadModel(sChanStudies(i).iHeadModel).MEGMethod;
        end
        % First file only: Load the number of samples from the covariance files
        if (i == 1)
            % Noise covariance
            if (length(sChanStudies(i).NoiseCov) >= 1) && ~isempty(sChanStudies(i).NoiseCov(1).FileName)
                covMat = load(file_fullpath(sChanStudies(i).NoiseCov(1).FileName), 'nSamples');
                if isfield(covMat, 'nSamples') && ~isempty(covMat.nSamples)
                    nSamplesNoise = covMat.nSamples;
                end
            end
            % Data covariance
            if (length(sChanStudies(i).NoiseCov) >= 2) && ~isempty(sChanStudies(i).NoiseCov(2).FileName)
                covMat = load(file_fullpath(sChanStudies(i).NoiseCov(2).FileName), 'nSamples');
                if isfield(covMat, 'nSamples') && ~isempty(covMat.nSamples)
                    nSamplesData = covMat.nSamples;
                end
            end
        end
    end
    % Keep only MEG and EEG
    if any(ismember(AllMod, {'MEG GRAD','MEG MAG'}))
        AllMod = intersect(AllMod, {'MEG GRAD', 'MEG MAG', 'EEG', 'ECOG', 'SEEG'});
    else
        AllMod = intersect(AllMod, {'MEG', 'EEG', 'ECOG', 'SEEG'});
    end
    % Check that at least one modality is available
    if isempty(AllMod)
        errMessage = 'No valid sensor types to estimate sources: please calculate an appropriate headmodel.';
        return;
    end

    %% ===== SELECT INVERSE METHOD =====
    % Select method
    if OPTIONS.DisplayMessages
        % Options dialog window
        sMethod = gui_show_dialog('Compute sources', @panel_inverse_2016, 1, [], AllMod, isShared, HeadModelType, nSamplesNoise, nSamplesData);
        if isempty(sMethod)
            return;
        end
        % Override default options
        OPTIONS = struct_copy_fields(OPTIONS, sMethod, 1);

        % === BRAINENTROPY MEM ===
        % Display additional option windows
        if strcmpi(OPTIONS.InverseMethod, 'mem')
            % No data files found
            if isShared
                errMessage = 'Cannot compute shared kernels with this method.';
                return
            end
            % Install/load brainentropy plugin
            [isInstalled, errMessage] = bst_plugin('Install', 'brainentropy', 1);
            if ~isInstalled
                return;
            end
            % Default options
            MethodOptions = be_main();
            % Interface to edit options
            MethodOptions = gui_show_dialog('MEM options', @panel_brainentropy, [], [], MethodOptions);
            % Add fields that are not defined by the options of the MEM interface
            if ~isempty(MethodOptions)
                switch (HeadModelType)
                    case {'surface', 'ImageGrid'}
                        MethodOptions.SourceOrient{1} = 'fixed';
                    case 'volume'
                        MethodOptions.SourceOrient{1} = 'free';
                        MethodOptions.flagSourceOrient = [0 0 2 0];
                end
            end
            % Canceled by user
            if isempty(MethodOptions)
                return
            end
            % Add options to list
            OPTIONS = struct_copy_fields(OPTIONS, MethodOptions, 1);
        end
    end
    % If no MEG and no EEG selected
    if isempty(OPTIONS.DataTypes)
        errMessage = 'Please select at least one modality.';
        return;
    end
    % Tags corresponding to the different methods
    methodTag = panel_inverse_2016('GetMethodComment', OPTIONS.InverseMethod, OPTIONS.InverseMeasure);

    %% ===== COMMENT =====
    % Base comment: "METHOD: MODALITIES"
    if isempty(OPTIONS.Comment)
        OPTIONS.Comment = [methodTag, ': ' GetModalityComment(OPTIONS.DataTypes)];
    end
    % Add source orientation option string
    strOptions = '';
    if isempty(OPTIONS.SourceOrient)
        strOptions = 'Mixed';
    elseif ~strcmpi(OPTIONS.InverseMethod, 'mem')
        switch (OPTIONS.SourceOrient{1})
            case 'fixed',      strOptions = 'Constr';
            case 'loose',      strOptions = 'Loose';
            case 'free',       strOptions = 'Unconstr';
        end
    end
    % Add Kernel/Full option string
    if ~OPTIONS.ComputeKernel
        if ~isempty(strOptions)
            strOptions = [',' strOptions];
        end
        strOptions = ['Full', strOptions];
    end
    % Final comment
    if ~isempty(strOptions)
        OPTIONS.Comment = [OPTIONS.Comment, '(', strOptions, ')'];
    end

    %% ===== LOOP ON INPUT FILES =====
    % Initializations
    initOPTIONS = OPTIONS;
    % Display progress bar
    bst_progress('start', 'Compute sources', 'Initialize...', 0, 3*length(iStudies) + 1);
    % Process each input
    for iEntry = 1:length(iStudies)
        OPTIONS = initOPTIONS;

        % ===== LOAD CHANNEL FILE =====
        bst_progress('text', 'Reading channel information...');
        % Get study structure
        iStudy = iStudies(iEntry);
        sStudy = bst_get('Study', iStudy);
        % Check if default study
        isDefaultStudy = strcmpi(sStudy.Name, bst_get('DirDefaultStudy'));
        % Get channel file for study
        [sChannel, iStudyChannel] = bst_get('ChannelForStudy', iStudy);
        ChannelFile = sChannel.FileName;
        % Load channel file
        ChannelMat = in_bst_channel(ChannelFile, 'Channel', 'Projector');

        % ===== LOAD DATA FILES =====
        bst_progress('text', 'Getting bad channels...');
        % Single inverse file
        if ~isShared
            % Get only one file
            DataFile = sStudy.Data(iDatas(iEntry)).FileName;
            % Load data file info (only 'mem' requires the recordings to be loaded here)
            if strcmpi(OPTIONS.InverseMethod, 'mem')
                DataMat = in_bst_data(DataFile, 'ChannelFlag', 'Time', 'nAvg', 'Leff', 'F');
            else
                DataMat = in_bst_data(DataFile, 'ChannelFlag', 'Time', 'nAvg', 'Leff');
            end
            ChannelFlag = DataMat.ChannelFlag;
            nAvg        = DataMat.nAvg;
            Leff        = DataMat.Leff;
            Time        = DataMat.Time;
            % Is it a Raw file?
            isRaw = strcmpi(sStudy.Data(iDatas(iEntry)).DataType, 'raw');
        % Shared inverse kernel
        else
            % Get all the dependent data files
            [iRelatedStudies, iRelatedData] = bst_get('DataForStudy', iStudy);
            % List all the data files
            nAvgAll     = zeros(1,length(iRelatedStudies));
            LeffAll     = zeros(1,length(iRelatedStudies));
            BadChannels = [];
            nChannels   = [];
            for i = 1:length(iRelatedStudies)
                % Get data file
                sStudyRel = bst_get('Study', iRelatedStudies(i));
                DataFull = file_fullpath(sStudyRel.Data(iRelatedData(i)).FileName);
                % Read bad channels and nAvg
                DataMat = load(DataFull, 'ChannelFlag', 'nAvg', 'Leff');
                if isfield(DataMat, 'nAvg') && ~isempty(DataMat.nAvg)
                    nAvgAll(i) = DataMat.nAvg;
                else
                    nAvgAll(i) = 1;
                end
                if isfield(DataMat, 'Leff') && ~isempty(DataMat.Leff)
                    LeffAll(i) = DataMat.Leff;
                else
                    LeffAll(i) = 1;
                end
                % Count number of times the channe is bad
                if isempty(BadChannels)
                    BadChannels = double(DataMat.ChannelFlag < 0);
                else
                    BadChannels = BadChannels + (DataMat.ChannelFlag < 0);
                end
                % Channel number
                if isempty(nChannels)
                    nChannels = length(DataMat.ChannelFlag);
                elseif (nChannels ~= length(DataMat.ChannelFlag))
                    errMessage = 'All data files must have the same number of channels.';
                    continue;
                end
            end
            % Get list of sensors selected for inversion
            iChanInv = good_channel(ChannelMat.Channel, [], OPTIONS.DataTypes);
            % Mark all the channels that are not selected here as good
            BadChannels(setdiff(1:length(BadChannels), iChanInv)) = 0;

            % === CHECK nAVG ===
            % if ~isempty(iRelatedStudies) && any(nAvgAll ~= nAvgAll(1)) && isFirstWarnAvg
            %     % Display a warning in a dialog window
            %     if OPTIONS.DisplayMessages
            %         isConfirm = java_dialog('confirm', ...
            %             ['Warning: You should estimate separataley the sources for the averages and the single trials.', 10 ...
            %             'The level of noise in the files might be different, this may cause inaccurate results.' 10 10 ...
            %             ' - For several averages: compute sources separately for each file.' 10 ...
            %             ' - For single trials: compute a shared solution, and move the averages in another condition.' 10 10 ...
            %             'Ignore this warning and compute sources ?'], 'Compute sources');
            %         if ~isConfirm
            %             return
            %         end
            %     % Return a warning message
            %     else
            %         errMessage = [errMessage 'Mixing averages and single trials. Result might be inaccurate.' 10];
            %     end
            %     isFirstWarnAvg = 0;
            % end
            nAvg = min([nAvgAll 1]);
            Leff = min([LeffAll 1]);

            % === BAD CHANNELS ===
            if any(BadChannels)
                % Display a warning in a dialog window
                if OPTIONS.DisplayMessages
                    % Build list of bad channels
                    strBad = '';
                    iBad = find(BadChannels);
                    for i = 1:length(iBad)
                        strBad = [strBad sprintf('%d(%d)   ', iBad(i), BadChannels(iBad(i)))];
                        if (mod(i,6) == 0)
                            strBad = [strBad 10];
                        end
                    end
                    % Ask user confirmation
                    [res, isCancel] = java_dialog('input', ...
                        ['Some channels are bad in at least one file (total ' num2str(length(iRelatedStudies)) ' files): ' 10 ...
                         '(in parentheses, the number of files for which the channel is bad)' 10 10 ...
                         strBad 10 10 ...
                         'The following channels will be considered as BAD for' 10 ...
                         'all the recordings and excluded from the source estimation:' 10 10], ...
                        'Exclude bad channels', [], sprintf('%d ', iBad));
                    if isCancel
                        continue;
                    elseif (~isempty(res) && isempty(str2num(res)))
                        errMessage = 'Invalid bad channel list of indices.';
                        continue;
                    end
                    % Get bad channels
                    BadChannels = 0 * BadChannels;
                    if ~isempty(res)
                        iBad = str2num(res);
                        BadChannels(iBad) = 1;
                    end
                % Return a warning message
                else
                    errMessage = [errMessage 'Bad channels for all the trials: ' sprintf('%d ', find(BadChannels)) 10];
                end
            end
            % Build a resulting ChannelFlag
            ChannelFlag = ones(length(ChannelMat.Channel), 1);
            ChannelFlag(BadChannels > 0) = -1;
            % No data loaded
            DataFile = [];
            Time = [];
        end

        % ===== CHANNEL FLAG =====
        % Get the list of good channels
        GoodChannel = good_channel(ChannelMat.Channel, ChannelFlag, OPTIONS.DataTypes);
        if isempty(GoodChannel)
            errMessage = [errMessage 'No good channels available.' 10];
            break;
        end

        % ===== LOAD NOISE COVARIANCE =====
        % Get channel study
        sStudyChannel = bst_get('Study', iStudyChannel);
        % Load NoiseCov file
        NoiseCovMat = load(file_fullpath(sStudyChannel.NoiseCov(1).FileName));
        % Check for NaN values in the noise covariance
        if ~isempty(NoiseCovMat.NoiseCov) && (nnz(isnan(NoiseCovMat.NoiseCov(GoodChannel, GoodChannel))) > 0)
            errMessage = [errMessage 'The noise covariance contains NaN values. Please re-calculate it after tagging correctly the bad channels in the recordings.' 10];
            break;
        end
        % Divide noise covariance by number of trials
        if ~isempty(nAvg) && (nAvg > 1)
            NoiseCovMat.NoiseCov = NoiseCovMat.NoiseCov ./ nAvg;
        end

        % ===== LOAD DATA COVARIANCE =====
        % Load DataCov file
        if (length(sStudyChannel.NoiseCov) >= 2) && ~isempty(sStudyChannel.NoiseCov(2).FileName)
            DataCovMat = load(file_fullpath(sStudyChannel.NoiseCov(2).FileName));
            % Check for NaN values in the noise covariance
            if ~isempty(DataCovMat.NoiseCov) && (nnz(isnan(DataCovMat.NoiseCov(GoodChannel, GoodChannel))) > 0)
                errMessage = [errMessage 'The data covariance contains NaN values. Please re-calculate it after tagging correctly the bad channels in the recordings.' 10];
                break;
            end
            % Divide data covariance by number of trials
            if isempty(nAvg) && (nAvg > 1)
                DataCovMat.NoiseCov = DataCovMat.NoiseCov ./ nAvg;
            end
        else
            DataCovMat = [];
        end
        % Beamformers: Require a data covariance matrix
        if strcmpi(OPTIONS.InverseMethod, 'lcmv') && isempty(DataCovMat)
            errMessage = [errMessage 'You need to calculate a data covariance before using the "beamformer" option.' 10];
            break;
        end
        % Shrinkage: Require the FourthMoment matrix
        if strcmpi(OPTIONS.NoiseMethod, 'shrink') && ...
                ((~isempty(DataCovMat)  && (~isfield(DataCovMat, 'FourthMoment')  || isempty(DataCovMat.FourthMoment))) || ...
                 (~isempty(NoiseCovMat) && (~isfield(NoiseCovMat, 'FourthMoment') || isempty(NoiseCovMat.FourthMoment))))
            errMessage = [errMessage 'Please recalculate the noise and data covariance matrices for using the "automatic shrinkage" option.' 10];
            break;
        end

        % ===== LOAD HEAD MODEL =====
        bst_progress('text', 'Loading head model...');
        bst_progress('inc', 1);
        % Get headmodel file
        HeadModelFile = sStudyChannel.HeadModel(sStudyChannel.iHeadModel).FileName;
        % Load head model
        HeadModel = in_bst_headmodel(HeadModelFile, 0, 'Gain', 'GridLoc', 'GridOrient', 'GridAtlas', 'SurfaceFile', 'MEGMethod', 'EEGMethod', 'ECOGMethod', 'SEEGMethod', 'HeadModelType');
        % Apply current SSP projectors
        if ~isempty(ChannelMat.Projector)
            % Rebuild projector in the expanded form (I-UUt)
            Proj = process_ssp2('BuildProjector', ChannelMat.Projector, [1 2]);
            % Apply projectors
            if ~isempty(Proj)
                % Get all sensors for which the gain matrix was successfully computed
                iGainSensors = find(sum(isnan(HeadModel.Gain), 2) == 0);
                % Apply projectors to gain matrix
                HeadModel.Gain(iGainSensors,:) = Proj(iGainSensors,iGainSensors) * HeadModel.Gain(iGainSensors,:);
                % Apply SSPs on both sides of the noise covariance matrix
                NoiseCovMat.NoiseCov = Proj * NoiseCovMat.NoiseCov * Proj';
                if ~isempty(DataCovMat)
                    DataCovMat.NoiseCov = Proj * DataCovMat.NoiseCov * Proj';
                end
            end
        end
        % Select only good channels
        HeadModel.Gain = HeadModel.Gain(GoodChannel, :);
        % Apply average reference: separately SEEG, ECOG, EEG
        if any(ismember(unique({ChannelMat.Channel.Type}), {'EEG','ECOG','SEEG'}))
            % Create average reference montage
            sMontage = panel_montage('GetMontageAvgRef', [], ChannelMat.Channel(GoodChannel), ChannelFlag(GoodChannel), 0);
            HeadModel.Gain = sMontage.Matrix * HeadModel.Gain;
            % Apply average reference operator on both sides of the noise covariance matrix
            NoiseCovMat.NoiseCov(GoodChannel, GoodChannel) = sMontage.Matrix * NoiseCovMat.NoiseCov(GoodChannel, GoodChannel) * sMontage.Matrix';
            if ~isempty(DataCovMat)
                DataCovMat.NoiseCov(GoodChannel, GoodChannel) = sMontage.Matrix * DataCovMat.NoiseCov(GoodChannel, GoodChannel) * sMontage.Matrix';
            end
        end
        % Copy initial head model
        HeadModelInit = HeadModel;
        % Get number of sources
        nSources =  size(HeadModelInit.Gain,2) / 3;

        % ===== MIXED HEADMODEL =====
        if strcmpi(HeadModelInit.HeadModelType, 'mixed') && ~isempty(HeadModel.GridAtlas) && ~isempty(HeadModel.GridAtlas(1).Scouts)
            % Only supported for wMNE
            if ~ismember(OPTIONS.InverseMethod, {'minnorm', 'gls', 'lcmv'})
                errMessage = [errMessage 'The mixed headmodel is currently only supported for the following inverse solutions: Minimum norm, dipole fitting, beamformer.' 10];
                break;
            end
            % Initialize variable
            HeadModel.Gain      = [];
            HeadModel.GridAtlas = [];
            HeadModel           = repmat(HeadModel, 1, length(HeadModelInit.GridAtlas(1).Scouts));
            iVert2Grid = [];
            iAllGrid   = [];
            iAllSource = [];
            iOffset    = 0;
            % Split the head model in multiple entries
            for iScout = 1:length(HeadModelInit.GridAtlas(1).Scouts)
                % Get indices in the Gain matrix
                sScout = HeadModelInit.GridAtlas(1).Scouts(iScout);
                iGainRows = sort([3*sScout.GridRows-2, 3*sScout.GridRows-1, 3*sScout.GridRows]);
                % Create the headmodel structure for the current region
                HeadModel(iScout).Gain       = HeadModelInit.Gain(:, iGainRows);
                HeadModel(iScout).GridLoc    = HeadModelInit.GridLoc(sScout.GridRows, :);
                HeadModel(iScout).GridOrient = HeadModelInit.GridOrient(sScout.GridRows, :);
                switch (sScout.Region(3))
                    case 'C',  OPTIONS.SourceOrient{iScout} = 'fixed';  nComp = 1;
                    case 'U',  OPTIONS.SourceOrient{iScout} = 'free';   nComp = 3;
                    case 'L',  OPTIONS.SourceOrient{iScout} = 'loose';  nComp = 3;
                end
                % In the case of a surface region, add the match of the vertices in the cortex surface and the GridLoc matrix
                if strcmpi(sScout.Region(2), 'S')
                    iVert2Grid = [iVert2Grid; sScout.Vertices(:), sScout.GridRows(:)];
                end
                % Add to the scout definition the indices in the ImageGrid
                iAllGrid   = [iAllGrid,   reshape(repmat(sScout.GridRows,nComp,1), 1, [])];
                iAllSource = [iAllSource, iOffset + (1:nComp*length(sScout.GridRows))];
                iOffset = iOffset + nComp*length(sScout.GridRows);
            end
            % Create sparse conversion matrices between indices
            if ~isempty(iVert2Grid)
                HeadModelInit.GridAtlas(1).Vert2Grid = logical(sparse(iVert2Grid(:,2), iVert2Grid(:,1), ones(size(iVert2Grid,1),1)));
            else
                HeadModelInit.GridAtlas(1).Vert2Grid = [];
            end
            if ~isempty(iAllSource)
                HeadModelInit.GridAtlas(1).Grid2Source = logical(sparse(iAllSource, iAllGrid, ones(size(iAllSource))));
            else
                HeadModelInit.GridAtlas(1).Grid2Source = [];
            end
        end

        %% ===== COMPUTE INVERSE SOLUTION =====
        bst_progress('text', 'Estimating sources...');
        bst_progress('inc', 1);
        % NoiseCov: keep only the good channels
        OPTIONS.NoiseCovMat = NoiseCovMat;
        OPTIONS.NoiseCovMat.NoiseCov = OPTIONS.NoiseCovMat.NoiseCov(GoodChannel, GoodChannel);
        if isfield(OPTIONS.NoiseCovMat, 'FourthMoment') && ~isempty(OPTIONS.NoiseCovMat.FourthMoment)
            OPTIONS.NoiseCovMat.FourthMoment = OPTIONS.NoiseCovMat.FourthMoment(GoodChannel, GoodChannel);
        end
        if isfield(OPTIONS.NoiseCovMat, 'nSamples') && ~isempty(OPTIONS.NoiseCovMat.nSamples)
            OPTIONS.NoiseCovMat.nSamples = OPTIONS.NoiseCovMat.nSamples(GoodChannel, GoodChannel);
        end
        % DataCov: keep only the good channels
        if ~isempty(DataCovMat)
            OPTIONS.DataCovMat = DataCovMat;
            OPTIONS.DataCovMat.NoiseCov     = OPTIONS.DataCovMat.NoiseCov(GoodChannel, GoodChannel);
            OPTIONS.DataCovMat.FourthMoment = OPTIONS.DataCovMat.FourthMoment(GoodChannel, GoodChannel);
            OPTIONS.DataCovMat.nSamples     = OPTIONS.DataCovMat.nSamples(GoodChannel, GoodChannel);
        end
        % Get channels types
        OPTIONS.ChannelTypes = {ChannelMat.Channel(GoodChannel).Type};
        % Switch depending on the selected inverse method
        switch( OPTIONS.InverseMethod )
            case {'minnorm', 'gls', 'lcmv'}
                % Call John's wmne function
                % NOTE: The output HeadModel param is used here in return to save LOTS of memory in the bst_inverse_linear_2016 function,
                %       event if it seems to be absolutely useless. Having a parameter in both input and output have the
                %       effect in Matlab of passing them "by reference".
                try
                    [Results, OPTIONS] = bst_inverse_linear_2016(HeadModel, OPTIONS);
                catch e
                    if bst_get('MatlabVersion') == 904
                        errMsg = ['Note: Matlab 2018a changed the behavior of the SVD() function. ' ...
                            10 'If issues arise, we recommend using another version.'];
                        e = MException(e.identifier, [e.message 10 10 errMsg]);
                        throw(e);
                    else
                        rethrow(e);
                    end
                end
            case 'mem'
                % Add options needed by the MEM functions
                OPTIONS.DataFile      = DataFile;
                OPTIONS.DataTime      = Time;
                OPTIONS.Channel       = ChannelMat.Channel(GoodChannel);
                OPTIONS.Data          = DataMat.F(GoodChannel,:);
                OPTIONS.ChannelFlag   = ChannelFlag(GoodChannel);
                OPTIONS.ResultFile    = [];
                OPTIONS.HeadModelFile = HeadModelFile;
                OPTIONS.GoodChannel   = GoodChannel;
                OPTIONS.FunctionName  = 'mem';
                % Call the mem solver
                [Results, OPTIONS] = be_main(HeadModel, OPTIONS);
                Results.nComponents = round(max(size(Results.ImageGridAmp,1),size(Results.ImagingKernel,1)) / nSources);
                % Get outputs
                DataFile = OPTIONS.DataFile;
                Time     = OPTIONS.DataTime;
            otherwise
                error('Unknown method');
        end
        % Error handling
        if isempty(Results)
            errMessage = [errMessage 'The inverse function returned an empty structure.' 10];
            break;
        end
        % Copy outputs to a standard results structure
        ResultsMat = db_template('resultsmat');
        ResultsMat = struct_copy_fields(ResultsMat, Results, 1);

        % ===== COMPUTE FULL RESULTS =====
        % Full results
        if (OPTIONS.ComputeKernel == 0) && ~isempty(ResultsMat.ImagingKernel) && ~isempty(DataFile)
            % Load data
            DataMat = in_bst_data(DataFile, 'F');
            % Incompatible options: Full results + raw files (impossible to view after)
            if isstruct(DataMat.F)
                errMessage = [errMessage 'Cannot compute full results for raw files: import the files first or compute an inversion kernel only.' 10];
                break;
            end
            % Multiply inversion kernel with the recordings
            ResultsMat.ImageGridAmp = ResultsMat.ImagingKernel * DataMat.F(GoodChannel, :);
            ResultsMat.ImagingKernel = [];
        end
        % If kernel only on raw data: do not save the Time vector
        if ~isempty(ResultsMat.ImagingKernel) && ~isShared && isRaw
            Time = [];
        % Check if no time dimension
        elseif ~isempty(ResultsMat.ImageGridAmp) && (size(ResultsMat.ImageGridAmp, 2) == 1)
            % If the output is not a timecourse: replicate the results to get two time instants
            ResultsMat.ImageGridAmp = repmat(ResultsMat.ImageGridAmp, [1,2]);
            % Keep only the first and last time instants
            Time = [Time(1), Time(end)];
        end

        %% ===== SAVE RESULTS FILE =====
        bst_progress('text', 'Saving results...');
        bst_progress('inc', 1);
        % ===== OUTPUT FILENAME =====
        % Add method name
        strMethod = methodTag;
        % Add modality
        for i = 1:length(OPTIONS.DataTypes)
            strMethod = [strMethod, '_', file_standardize(OPTIONS.DataTypes{i})];
        end
        % Add kernel tag
        if OPTIONS.ComputeKernel
            strMethod = [strMethod, '_KERNEL'];
        end
        % Output folder
        if isempty(DataFile)
            OutputDir = bst_fileparts(file_fullpath(ChannelFile));
        else
            OutputDir = bst_fileparts(file_fullpath(DataFile));
        end
        % Output filename
        ResultFile = bst_process('GetNewFilename', OutputDir, ['results_', strMethod]);

        % ===== CREATE FILE STRUCTURE =====
        ResultsMat.Comment       = [OPTIONS.Comment ' 2016'];
        ResultsMat.Function      = OPTIONS.FunctionName;
        ResultsMat.Time          = Time;
        ResultsMat.DataFile      = DataFile;
        ResultsMat.HeadModelFile = HeadModelFile;
        ResultsMat.HeadModelType = HeadModelInit.HeadModelType;
        ResultsMat.ChannelFlag   = ChannelFlag;
        ResultsMat.GoodChannel   = GoodChannel;
        ResultsMat.SurfaceFile   = file_short(HeadModelInit.SurfaceFile);
        switch lower(ResultsMat.HeadModelType)
            case 'volume'
                ResultsMat.GridLoc    = HeadModelInit.GridLoc;
                % ResultsMat.GridOrient = [];
            case 'surface'
                ResultsMat.GridLoc    = [];
                % ResultsMat.GridOrient = [];    % THE ORIENTATION CAN BE RETURNED BY THE INVERSE METHOD ('optim')
            case 'mixed'
                ResultsMat.GridLoc    = HeadModelInit.GridLoc;
                ResultsMat.GridOrient = HeadModelInit.GridOrient;
        end
        ResultsMat.GridAtlas = HeadModelInit.GridAtlas;
        ResultsMat.nAvg      = nAvg;
        ResultsMat.Leff      = Leff;
        ResultsMat.Options   = OPTIONS;
        % History
        ResultsMat = bst_history('add', ResultsMat, 'compute', ['Source estimation: ' OPTIONS.InverseMethod]);
        % Make file comment unique
        if ~isempty(sStudy.Result)
            ResultsMat.Comment = file_unique(ResultsMat.Comment, {sStudy.Result.Comment});
        end
        % Save new file structure
        bst_save(ResultFile, ResultsMat, 'v6');

        % ===== REGISTER NEW FILE =====
        % Create new results structure
        newResult = db_template('results');
        newResult.Comment       = ResultsMat.Comment;
        newResult.FileName      = file_short(ResultFile);
        newResult.DataFile      = DataFile;
        newResult.isLink        = 0;
        newResult.HeadModelType = ResultsMat.HeadModelType;
        % Add new entry to the database
        iResult = length(sStudy.Result) + 1;
        sStudy.Result(iResult) = newResult;
        % Update Brainstorm database
        bst_set('Study', iStudy, sStudy);

        % ===== UPDATE DISPLAY =====
        % Update tree
        panel_protocols('UpdateNode', 'Study', iStudy);
        % Update links
        if isShared
            if isDefaultStudy
                % If added to a 'default_study' node: need to update results links
                OutputLinks = db_links('Subject', sStudy.BrainStormSubject);
                % Update whole tree display
                panel_protocols('UpdateTree');
            else
                % Update links to the new results file
                OutputLinks = db_links('Study', iStudy);
                % Update display of the study node
                panel_protocols('UpdateNode', 'Study', iStudy);
            end
            % Find in the links the ones that are based on the node that was just calculated
            isNewLink = ~cellfun(@(c)isempty(strfind(c, newResult.FileName)), OutputLinks);
            OutputFiles = cat(2, OutputFiles, OutputLinks(isNewLink));
        else
            % Store output filename
            OutputFiles{end+1} = newResult.FileName;
        end
        % Expand data node
        panel_protocols('SelectNode', [], newResult.FileName);
    end
    % Save database
    db_save();
    % Hide progress bar
    bst_progress('stop');
end

%% ===== GET MODALITY COMMENT =====
function Comment = GetModalityComment(Modalities)
    % Replace "MEG GRAD+MEG MAG" with "MEG ALL"
    if all(ismember({'MEG GRAD', 'MEG MAG'}, Modalities))
        Modalities = setdiff(Modalities, {'MEG GRAD', 'MEG MAG'});
        Modalities{end+1} = 'MEG ALL';
    end
    % Loop to build comment
    Comment = '';
    for im = 1:length(Modalities)
        if (im >= 2)
            Comment = [Comment, '+'];
        end
        Comment = [Comment, Modalities{im}];
    end
end


hydro = struct();

hydro = readCAPYTAINE(hydro,'coer_comp_full.nc');
hydro = radiationIRF(hydro,10,[],[],[],[]);
hydro = radiationIRFSS(hydro,[],[]);
hydro = excitationIRF(hydro,20,[],[],[],[]);
writeBEMIOH5(hydro)
plotBEMIO(hydro)


function varargout = panel_anova(varargin)
% PANEL_ANOVA: Creation and management of list of files to apply some batch proccess.
%
% USAGE:  bstPanelNew = panel_anova('CreatePanel')

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

eval(macro_method);
end

%% ===== CREATE PANEL =====
function bstPanelNew = CreatePanel()
    panelName = 'ANOVA';
    % Java initializations
    import java.awt.*;
    import javax.swing.*;
    % Create list of nodes (tree-table)
    nodelist = panel_nodelist('CreatePanel', 'Anova', 'Files', 'table');
    % Create the BstPanel object that is returned by the function
    % => constructor BstPanel(jHandle, panelName, sControls)
    bstPanelNew = BstPanel(panelName, ...
                           nodelist.jPanel, ...
                           struct('nodelist', nodelist));
end

%% =========================================================================
%  ===== PROCESSING FUNCTIONS ==============================================
%  =========================================================================


function y = dec2base(varargin)
% Works for empty input
% Allows inputs to be char
if isempty(varargin{1})
    y = '';
else
    if ischar(varargin{1})
        varargin{1} = double(varargin{1});
    end
    if nargin>1 && ischar(varargin{2})
        varargin{2} = double(varargin{2});
    end
    if nargin>2 && ischar(varargin{3})
        varargin{3} = double(varargin{3});
    end
    y = builtin('dec2base', varargin{:});
end
end


function cosmo_wizard_set_config()
% GUI-based 'wizard' to set CoSMoMVPA configuration file
%
% cosmo_wizard_set_config()
%
% This 'wizard' can be used to set the CoSMoMVPA configuration paths
% for exercises and demonstration outputs (in CoSMoMVPA's 'examples/'
% directory).
%
% It asks to select the directories for the 'tutorial_data_path'
% and 'output_data_path'. The former requires tutorial data, which can be
% downloaded from cosmomvpa.org/download.html
%
% It will store a '.cosmomvpa.cfg' file in either the user's home directory
% (on Linux / OSX), or in any available userpath() directory.
%
% Selecting cancel at any time will close this wizard.
%
% #   For CoSMoMVPA's copyright information and license terms,   #
% #   see the COPYING file distributed with CoSMoMVPA.           #

% simulate go-to
while true
    if isempty(which('cosmo_config'))
        % store original path.
        % (should we ask to user whether the path should be set)
        orig_path=path();
        path_resetter=onCleanup(@()path(orig_path));

        addpath(get_cosmo_mvpa_dir());
        cosmo_set_path();
    end

    % read existing configuration, if it exists
    [config,fn]=get_cosmo_config();

    if isempty(fn)
        % no existing configuration, find location where to store it
        [fn,error_msg]=get_output_filename();

        if ~isempty(error_msg)
            break
        end
    end

    % ask for paths and update configuration
    [config,error_msg]=update_config(config);
    if ~isempty(error_msg)
        break;
    end

    % store configuration
    error_msg=write_config(fn,config);

    if ~isempty(error_msg)
        break;
    end

    % all done without error, quit this function
    msgbox('Done! Happy CoSMoMVPA-ing!');
    return
end

error('%s did not complete, reason: %s',mfilename(),error_msg);

function mvpa_dir=get_cosmo_mvpa_dir()
    cur_dir=fileparts(mfilename('fullpath'));
    mvpa_dir=fullfile(fileparts(cur_dir),'mvpa');

function error_msg=write_config(fn,config)
    config_str=config2str(config);

    title='Confirm writing CoSMoMVPA configuration';
    body=sprintf(['The following CoSMoMVPA configuration\n\n%s\n'...
                    'will be written in this file:\n\n%s\n\nContinue?'],...
                    config_str,fn);

    if show_ok_cancel_dialog(title,body)
        error_msg='';
        cosmo_config(fn,config);
        return;
    end

    error_msg='Writing configuration file was cancelled';

function str=config2str(config)
    rows=cellfun(@(key)sprintf('%s=%s\n',key,config.(key)),...
                        fieldnames(config),...
                        'UniformOutput',false);
    str=sprintf('%s',rows{:});

function response_is_ok=show_ok_cancel_dialog(title,body)
    response=questdlg(body,...
                        title,...
                        'Cancel','OK','OK');

    response_is_ok=strcmp(response,'OK');

function [config,error_msg]=update_config(config)
    % get keys
    path_keys=get_path_keys();
    path_key_fields=fieldnames(path_keys);
    n_paths=numel(path_key_fields);

    start_path='';
    for k=1:n_paths
        key=path_key_fields{k};
        desc=path_keys.(key);

        title=sprintf('Set ''%s''',key);
        body=sprintf(['In the next dialog, select '...
                                'the directory ' desc]);

        if isfield(config,key)
            current_config_path=config.(key);
            if isdir(current_config_path)
                % if it already has a value, use that as starting value
                start_path=current_config_path;
                body=sprintf(['%s\n\nThe current setting '...
                                'for ''%s'' is:\n%s'],...
                                body,key,start_path);
            end
        end

        response_is_ok=show_ok_cancel_dialog(title,body);

        if response_is_ok

            result_dir=uigetdir(start_path,desc);

            if ischar(result_dir)
                start_path=result_dir;
                config.(key)=result_dir;
                continue;
            end
        end

        % no valid response
        error_msg=sprintf(['Selecting directory for key ''%s'' '...
                                'was cancelled'],key);
        return;
    end

    error_msg='';

function path_keys=get_path_keys()
    path_keys=struct();
    path_keys.tutorial_data_path=[...
                        'where CoSMoMVPA tutorial and example'...
                        'data is stored. This data is required '...
                        'for running the exercises and '...
                        'demonstrations that come with '...
                        'CoSMoMVPA (in the examples/ '...
                        'directory)\n'...
                        'If you have not '...
                        'downloaded this data, select ''Cancel'' '...
                        'and see the '...
                        'website for download instructions:\n\n'...
                        '    http://cosmomvpa.org/download.html'...
                        ''];
    path_keys.output_data_path=[...
                        'where output from running the exercises and '...
                        'demonstrations (such as fMRI volumes with '...
                        'searchlight result maps) is be stored. This '...
                        'can be '...
                        'any directory of your liking that you have '...
                        'write permissions to'];

function [path_fn,error_msg]=get_output_filename()
    fn='.cosmomvpa.cfg';
    error_msg='';

    if isunix()
        home_directory=getenv('HOME');
        candidate_paths={home_directory};
    else
        candidate_paths={};
    end

    if ~isempty(which('userpath','builtin'))
        % this does not work on GNU Octave
        candidate_paths{end+1}=userpath();
    end

    % not preferred, but no other options available
    % (typically, GNU Octave on Windows)
    candidate_paths{end+1}=get_cosmo_mvpa_dir();

    sep=pathsep();
    candidates_strjoined=cosmo_strjoin(candidate_paths,sep);
    candidates_paths=cosmo_strsplit(candidates_strjoined,sep);

    n_candidates=numel(candidates_paths);
    for k=1:n_candidates
        p=candidates_paths{k};
        if isempty(p)
            continue;
        end

        path_fn=fullfile(p,fn);
        if can_write(path_fn);
            return
        end
    end

    path_fn=[];
    error_msg=sprintf(['No suitable directory found where %s '...
                'could be written. The following directories were '...
                'tried but failed: ''%s''\n\n'],...
                fn,cosmo_strjoin(candidates_paths,''', '''));

function tf=can_write(fn)
    file_existed=exist(fn,'file');

    % try to open in append mode
    fid=fopen(fn,'a');
    file_closer=onCleanup(@()fclose(fid));
    tf=fid>0;

    clear file_closer;

    if ~file_existed
        delete(fn);
    end

function [config,fn]=get_cosmo_config()
    warning_state=cosmo_warning();
    cleaner=onCleanup(@()cosmo_warning(warning_state));
    cosmo_warning('off');

    [config,fn]=cosmo_config();


function [outStr] = paddStr(str, desLength)
%paddStr Summary of this function goes here
%   Detailed explanation goes here
    outStr = str;
    while(length(outStr) < desLength)
        outStr = [outStr, ' '];
    end
end


function TestDereverbNet_Regression_Rpi
addpath('local');
dnn1 = load('nnet/Dereverb.noCMN.DeltaByEqn.MbSize20.U52013.771-LSTM-2048-771.L2_3E-4.LR_5E-3/nnet.itr10.LR4.39E-4.CV1630.073.mat');
dnn2 = load('nnet/DereverbMask.noCMN.DeltaByEqn.MbSize20.U52013.771-LSTM-1500-771.L2_3E-4.LR_5E-3/nnet.itr11.LR1.46E-4.CV1801.505.mat');

[layer1, para1] = PrepareProcessing(dnn1);
[layer2, para2] = PrepareProcessing(dnn2);

wavroot = 'D:\Data\RpiRecording';
wavfiles = findFiles(wavroot, 'wav');

for i=2:length(wavfiles)
    [wav, fs] = audioread(wavfiles{i});
    wav = StoreWavInt16(wav(:,2))';
    DataTmp(1).data{1} = wav(fs*10+1: min(fs*20,length(wav)) );

    output{1} = FeatureTree2(DataTmp, para1, layer1);
    output{2} = FeatureTree2(DataTmp, para2, layer2);

    for j = 1:2
        noisyLogSpec{j} = gather(output{j}{1}{1});
        enhancedLogSpec{j} = gather(output{j}{1}{2});
        noisySTFT{j} = gather(output{j}{1}{3});
        phase = angle(noisySTFT{j});
        noisy_wav = abs2wav(exp(noisyLogSpec{j}(1:257,:)/2)', phase', 400, 240);
        enhanced_wav{j} = abs2wav(exp(enhancedLogSpec{j}(1:257,:)/2)', phase', 400, 240);
    end
    imagesc(CMN([noisyLogSpec{1}; enhancedLogSpec{1}(1:257,:); enhancedLogSpec{2}(1:257,:)]')');
    title(regexprep(wavfiles{i}, '_', '\\_'));  pause(0.1);
    soundsc(noisy_wav, 16000); pause
    soundsc(enhanced_wav{1}, 16000);                pause
    soundsc(enhanced_wav{2}, 16000);                pause
end

end

function [layer, para] = PrepareProcessing(dnn)

layer = dnn.layer;
para = dnn.para;
para.topology.useFileName = 1;
para.local.seglen = 100;
para.local.segshift = 100;
para.useGPU = 0;

input_idx = ReturnLayerIdxByName(layer, 'input');
layer = layer(1:input_idx(end)-1);
para.IO = RemoveIOStream(para.IO, 2);
if isfield(para.topology, 'useCMN') && para.topology.useCMN==0
    log_idx = ReturnLayerIdxByName(layer, 'Log');
    para.out_layer_idx = [log_idx(1) length(layer)];
else
    cmn_idx = ReturnLayerIdxByName(layer, 'CMN');
    para.out_layer_idx = [cmn_idx(1) length(layer)];
end
stft_idx = ReturnLayerIdxByName(layer, 'stft');
para.out_layer_idx = [para.out_layer_idx stft_idx(1) 1];

reverb_root = ChoosePath4OS({'D:/Data/REVERB_Challenge', '/media/xiaoxiong/OS/data1/G/REVERB_Challenge'});   % you can set two paths, first for windows OS and second for Linux OS.
para.local.wavroot = [reverb_root];
para.local.wsjcam0root = ChoosePath4OS({'D:/Data/wsjcam0', '/media/xiaoxiong/OS/data1/G/wsjcam0_wav'});
para.local.wsjcam0ext = ChoosePath4OS({'wav', 'wv1'});
para.local.useFileName = 1;      % if set to 0, load all training data to memory. otherwise, only load file names.
para.local.loadLabel = 0;

end


function [] = mainExp(class,trainId,paramStruct)
if(nargin<4)
    paramStruct = {};
end

mainTrain(class,trainId,paramStruct);

% Test with keypoints
mainTest(class,trainId,'withKps',{'opt.rotationPredNum','1'...
'opt.relaxInit','{}','opt.relaxOpt','{''scale'',''translation'',''rotation'',''kps''}'});

%mainTest(class,trainId,strcat(trainId,'withKpsAllTest'),{'opt.rotationPredNum','1',...
%'opt.relaxInit','{}','opt.relaxOpt','{''scale'',''translation'',''rotation'',''kps''}'});

%  mainTest(class,trainId,strcat(trainId,'withKpsSDS'),{'opt.rotationPredNum','1',...
%  'opt.relaxInit','{''mask''}','opt.relaxOpt','{''scale'',''translation'',''rotation'',''kps''}',...
%  'opt.lambda','[1 1 200 0.2 0.2 5]','opt.truncatedLoss','1'});
%
% % % Pose Hypotheses w/o keypoints
%  mainTest(class,trainId,strcat(trainId,'poseHyp1'),{'opt.rotationPredNum','1',...
%  'opt.relaxInit','{''scale'',''translation'',''rotation'',''kps''}',...
%  'opt.relaxOpt','{''scale'',''translation'',''rotation''}','opt.useOrigTri','1'});
%
%  mainTest(class,trainId,strcat(trainId,'poseHyp1SDS'),{'opt.rotationPredNum','1',...
%  'opt.relaxInit','{''scale'',''translation'',''rotation'',''kps'',''mask''}',...
%  'opt.relaxOpt','{''scale'',''translation'',''rotation''}','opt.useOrigTri','1',...
%  'opt.lambda','[1 1 200 0.2 0.2 5]','opt.truncatedLoss','1'});

% mainTest(class,trainId,strcat(trainId,'poseHyp2'),{'opt.rotationPredNum','2',...
% 'opt.relaxInit','{''scale'',''translation'',''rotation'',''kps''}',...
% 'opt.relaxOpt','{''scale'',''translation'',''rotation''}','opt.useOrigTri','1'});
%
% mainTest(class,trainId,strcat(trainId,'poseHyp3'),{'opt.rotationPredNum','3',...
% 'opt.relaxInit','{''scale'',''translation'',''rotation'',''kps''}',...
% 'opt.relaxOpt','{''scale'',''translation'',''rotation''}','opt.useOrigTri','1'});
%
% mainTest(class,trainId,strcat(trainId,'poseHyp4'),{'opt.rotationPredNum','4',...
% 'opt.relaxInit','{''scale'',''translation'',''rotation'',''kps''}',...
% 'opt.relaxOpt','{''scale'',''translation'',''rotation''}','opt.useOrigTri','1'});
%
% Test with keypoints
%mainTest(class,trainId,strcat(trainId,'withKpsGtSubtype'),{...
%'opt.relaxInit','{}','opt.relaxOpt','{''scale'',''translation'',''rotation'',''kps''}'});
%mainTest(class,trainId,strcat(trainId,'withKpsPredSubtype'),{'opt.useGtSubtypes','0',...
%'opt.relaxInit','{}','opt.relaxOpt','{''scale'',''translation'',''rotation'',''kps''}'});

% Test without keypoints
%mainTest(class,trainId,strcat(trainId,'woKpsGtSubtype'),{...
%'opt.relaxInit','{''scale'',''translation'',''rotation'',''kps''}',...
%'opt.relaxOpt','{''scale'',''translation'',''rotation''}'});
%mainTest(class,trainId,strcat(trainId,'woKpsPredSubtype'),{...
%'opt.relaxInit','{''scale'',''translation'',''rotation'',''kps''}',...
%'opt.relaxOpt','{''scale'',''translation'',''rotation''}'});
end
% Run locally
%evalMeshesAll(classes,trainId,0);
%evalMeshesAll(classes,strcat(trainId,'withKpsgtSubtype'),1);
%evalMeshesAll(classes,strcat(trainId,'predSubtype'),1);
%[scoresMeshes, scoresDmaps] = collectEvals(classes,trainId,0);
%[scoresMeshes, scoresDmaps] = collectEvals(classes,strcat(trainId,'predSubtype'),1);
%[scoresMeshes, scoresDmaps] = collectEvals(classes,strcat(trainId,'gtSubtype'),1);
%And the same for IoU sets


function [nodes, edges, grayPath] = loadGrayNodes(hemisphere, grayPath, numLayers)
%
% [nodes, edges, grayPath] = loadGrayNodes(hemisphere, [grayPath], [numLayers])
%
% Loads gray nodes from specified gray class file.
% If the file is not fully specified, opens dialog box to get it from the user.
%
% hemisphere: 'right' or 'left'
% grayPath: path string to file
%    If not specified, opens dialog box to have user find the file below:
%       /usr/local/mri/anatomy/<subject>/<hemisphere>
%
% djh, 8/98
%
% 022699 - Get anatomy path from getAnatomyPath - WAP
% 061899	- Browse with uigetfile - arw
% 080299 - fixed bug with returned restofpath,
%          change into correct directory before browsing - arw
% 080800 - allowed returning null nodes and edges if restofpath is sent in null -- djh, bw
% 120100 - fixed bug in which when restofPath was sent in it was never
%          appended to grayPath.  BW
% 120100 - AW reorganised the logic a little. Exits immediately if restOfPath
%          is null.
% 2/5/2001, djh - cleaned this up (major rewrite)
%          wrote getPathStrDialog.m to get the code for that outta this function
%          dumped restOfPath, replaced it with full grayPath
% 2008.01.?? RFD: added code to automatically grow gray matter in given a class file.
% 2008.02.04 RFD: when growing from a class file, we now prompt for the number of layers
%            if it isn't provided, rather than using a hard-coded default.

global mrSESSION

% Open dialog box to find gray class file
if notDefined('grayPath')

    % default initial directory: anatomy dir / hemisphere
    Hemi = hemisphere; Hemi(1) = upper(Hemi(1)); % e.g., left -> Left
    startDir = fullfile(getAnatomyPath(mrSESSION.subject), Hemi);
    % if 'Left' dir does not exist go back to top dir
    if ~exist(startDir,'dir')
	  startDir = getAnatomyPath(mrSESSION.subject);
    end

    filterspec = {'*.nii.gz;*.nii', 'NIFTI class file'; '*.*', 'All files'};
    grayPath = getPathStrDialog(startDir, 'Select class file', filterspec);
end

if exist(grayPath, 'file')
    disp(['Building gray graph from: ', grayPath]);
    class = readClassFile(grayPath, 0, 0, hemisphere);
    if(~exist('numLayers','var')||isempty(numLayers))
        a = inputdlg('Number of gray layers:','Gray Layers',1,{'5'});
        if(isempty(a)), error('user canceled.'); end
        numLayers = str2double(a{1});
    end
    [nodes,edges] = mrgGrowGray(class,numLayers);
elseif isempty(grayPath)
    fprintf('No file selected, so no coords loaded. \n');
    nodes = [];
    edges = [];

else
    myWarnDlg(['File ',grayPath,' does not exist. No gray nodes loaded']);
    nodes=[];
    edges=[];
end

return;